VB中定制DllRegisterServer、DllUnregisterServer

server

VB中定制DllRegisterServer、DllUnregisterServer

-阿鬼(heroyin)

VB作为一种简单容易上手的语言,可以让开发者快速上手,开发速度快,效率高。但它过分的封装也给开发者带来诸多不便。

问题的由来
最近本人在开发一个插件结构的项目中就遇到了一个麻烦,我的项目是采用COM架构,框架由DELPHI开发,插件为COM组件,插件可以由其他语言开发,当然也包括VB。每个插件必须注册为一个固定的组件类别(Categories)。在其他语言如VC、DELPHI中实现起来非常简单,只需要重载DllRegisterServer、DllUnregisterServer在两个函数里加入注册注销类别的代码,然后输出就可以了。但到了VB就碰到麻烦了。

VB正常情况下是不能直接开发标准DLL,所以也就不支持自定义导出函数,开发ACTIVE DLL的时候,是由VB自动导出必须的四个函数DllGetClassObject, DllCanUnloadNow, DllRegisterServer, DllUnregisterServer。要加入注册注销类别代码就必须重新导出DllRegisterServer, DllUnregisterServer。

好了,问题就转换成了如何在VB写的DLL中导出函数了

VB编译内幕
VB的编译过程大致如下,当我们在编辑环境中编写完代码后,VB调用C2将所有的模块(包括CLASS)编译成OBJ文件(能够为机器语言识别的代码)。一下是C2的一些编译参数说明(E文):

- the the name of the prefixed one used for the names of the rowscontaining ' precompilato', one

risen of intermediate tails (from which name of the switch) temporary; these rows are 5 and finish withi suffissi GL, SY, FORMER, IN and DB; they contained are not documented

- f the name of the rows to compile

- W3 warning level 3, level of ' attenzione' dedicating to i warnings

- Gy it qualifies the connection to level of function (function-level linking)

- G5 optimization for the Pentium

- Gs4096 it allows not to insert the code for the control of stack (stack probe) if a function does not

use more than 4096 byte of stack

- dos not documented

- Z1 it removes the name of the bookcase of default from the rows.OBJ

- Fofileobj the name of rows OBJ to generate (rows output)

- Qifdiv it puts in action the corrections for the bug of the division of the Pentium (FDIV bug)

- MILILITER it creates rows eseguibile single-threaded

- basic it indicates the compiler C2 the fact that the compilation it happens for a plan basic

C2完成编译后,VB会调用LINK.EXE将所有的OBJ文件连接成EXE文件,完成编译过程。下面是一段命令行演示如何调用LINK.EXE:

LINK C:\Test\Form1.obj C:\Test\Modulo1.obj C:\Test\Progetto1.obj C:\Programmi\Microsoft Visual Studio\VB98\VBAEXE6.lib /ENTRY:__ vbaS /OUT:C:\Test\Progetto1.exe /BASE:0x400000 /SUBSYSTEM:WINDOWS, 4.0 /VERSION:1.0 /DEBUG /DEBUGTYPE:CV /INCREMENTAL:NOT /OPT:REF/MERGE:.rdata =.text /IGNORE:4078
对于我们来说这些参数没有什么意义,用默认的就行了。这段命令行中并没有包括输出函数,如果我们希望输出函数,可以定义一个.def文件,按照格式加入要输出的函数列表,然后在命令行后面加上 “/DEF: 文件名”(当然也可以直接加/ EXPORTS参数),再调用命令行编译,用的denpendency工具查看你就会发现你要输出的函数了。

Def文件格式的定义:

LIBRARY 程序名称

DESCRIPTION "MyDLL - (C) Antonio Giuliana, 2004"

EXPORTS

函数名= ?函数名@函数所在模块名@@AAGXXZ

例:

LIBRARY MyDLL

DESCRIPTION "MyDLL - (C) Antonio Giuliana, 2004"

EXPORTS

DllRegisterServer= ?DllRegisterServer@SymExp@@AAGXXZ

DllUnregisterServer= ?DllUnregisterServer@SymExp@@AAGXXZ

注意:函数名和模块名是区分大小写的

找到了解决方案了,但是,由于VB编译完成后就会自动删除OBJ文件,如何再C2生成完OBJ文件后中断编译过程取得OBJ文件呢,网上有种方法就是替换调VB的LINK文件,然后中断一下,将OBJ文件拷贝出来,在用命令行编译。这是个很好的办法,但不够智能化,本人在国外网站上发现了一个更为有效的方法。

制作VB的辅助编译工具
新建一个工程,命名为LINK,在MAIN过程中加入代码:

Public Sub Main()
Dim cmd As String
Dim fOut As Long
Dim sOut As String
Dim sDef As String

cmd = Command$
If InStr(cmd, "/DLL") > 0 And InStr(cmd, "VBAEXE6.LIB") > 0 Then
fOut = InStr(cmd, "/OUT:")
sOut = Mid(cmd, fOut + 6, InStr(cmd, "/BASE:") - fOut - 8)
sDef = Left(sOut, Len(sOut) - 3) + "def"
If Len(Dir(sDef)) Then
cmd = cmd & "/DEF:"" "& sDef &" "" "
End If
End If
Shell "LINK32.EXE "& cmd
End Sub
然后编译成LINK.EXE,先将VB目录下的LINK.EXE改名成LINK32.EXE,再将LINK.EXE拷贝到VB目录下,这样做的好处是不改变原有的编译过程,只是在编译DLL的时候才插入“/DEF:”参数,不会对编译其他程序造成影响。

完成了替换操作后,如果要输出其他函数,只需要在程序输出目录下编辑一个和输出文件同名的后缀为“.DEF”的文件就可以了。

开始替换DllRegisterServer、DllUnregisterServer
1、制作注册和注销的工具库ComRegisterDll

新建一个工程,引入两个库:ISA helper Com component 1.0 type library和 TypeLib Information。再创建一个类ComRegister,在ComRegister类中实现一个方法:

RegTypelib(sLib As String, ByVal bState As Boolean)用来实现注册和注销ACTIVEX。代码如下:

Option Explicit

Public Type GUID

Data1 As Long

Data2 As Integer

Data3 As Integer

Data4(0 To 7) As Byte

End Type

Private Enum eSYSKIND

SYS_WIN16 = 0&

SYS_WIN32 = 1&

SYS_MAC = 2&

End Enum

Private Declare Function LoadTypeLib Lib "oleaut32.dll" ( _

pFileName As Byte, pptlib As Object) As Long

Private Declare Function RegisterTypeLib Lib "oleaut32.dll" ( _

ByVal ptlib As Object, szFullPath As Byte, _

szHelpFile As Byte) As Long

Private Declare Function UnRegisterTypeLib Lib "oleaut32.dll" ( _

libID As GUID, _

ByVal wVerMajor As Integer, _

ByVal wVerMinor As Integer, _

ByVal lCID As Long, _

ByVal tSysKind As eSYSKIND _

) As Long

Private Declare Function CLSIDFromString Lib "ole32.dll" (lpsz As Byte, pclsid As GUID) As Long

Private Declare Function GetModuleFileName Lib "kernel32" Alias "GetModuleFileNameA" (ByVal hModule As Long, ByVal lpFileName As String, ByVal nSize As Long) As Long

Private Declare Function GetModuleHandle Lib "kernel32" Alias "GetModuleHandleA" (ByVal lpModuleName As String) As Long

‘取得模块的路径

Private Function GetModulePath(bModuleName As String) As String

Dim ModleId As Long

Dim Path As String * 254

ModleId = GetModuleHandle(bModuleName)

Call GetModuleFileName(ModleId, Path, 254)

GetModulePath = Path

End Function

‘sLib为模块名称,无须路径和后缀 bState 为FALSE时是注销,反之为注册

Public Function RegTypelib(sLib As String, ByVal bState As Boolean, ByVal bType As PSDK.EPluginCategory) As Long

Dim suLib() As Byte

Dim errOK As Long

Dim tlb As Object

Dim cTLI As TypeLibInfo

Dim tGUID As GUID, sCLSID As String

Dim iMajor As Integer, iMinor As Integer

Dim lCID As Long

Dim DllPath As String

DllPath = GetModulePath(sLib)

If bState Then

' Basic automatically translates strings to Unicode Byte arrays

' but doesn't null-terminate, so you must do it yourself

suLib = DllPath & vbNullChar

' Pass first byte of array

errOK = LoadTypeLib(suLib(0), tlb)

If errOK = 0 Then

errOK = RegisterTypeLib(tlb, suLib(0), 0)

End If

RegTypelib = errOK

Else

Set cTLI = TLI.TypeLibInfoFromFile(DllPath)

sCLSID = cTLI.GUID

iMajor = cTLI.MajorVersion

iMinor = cTLI.MinorVersion

lCID = cTLI.lCID

Set cTLI = Nothing

suLib = sCLSID & vbNullChar

errOK = CLSIDFromString(suLib(0), tGUID)

If errOK = 0 Then

errOK = UnRegisterTypeLib(tGUID, iMajor, iMinor, lCID, SYS_WIN32)

RegTypelib = errOK

End If

End If

End Function

将工程编译成ComRegiterDll.dll,注册。这个库为公用库,所有需要重新定制DllRegisterServer、DllUnregisterServer的工程都可以方便的引用它来方便的完成注册和注销过程。

2、定制DllRegisterServer、DllUnregisterServer

新建一个工程MyDll,引入ComRegiterDll.dll,添加一个MODULE mainmodule,在mainmodule中新建两个函数DllRegisterServer、DllUnregisterServer:

Declare Function MessageBox Lib "user32" Alias "MessageBoxA" (ByVal hwnd As Long, ByVal lpText As String, ByVal lpCaption As String, ByVal wType As Long) As Long

Public Function DllRegisterServer() As Long

Dim ComReg As ComRegister

Set ComReg = New ComRegister

‘加入你的处理代码

MessageBox 0, ”注册过程”, “MYDLL”, 0

DllRegisterServer = ComReg.RegTypelib("MyDll", True)

End Function

Public Function DllUnregisterServer() As Long

Dim ComReg As ComRegister

Set ComReg = New ComRegister

‘加入你的处理代码

MessageBox 0, ”注销过程”, “MYDLL”, 0

DllUnregisterServer = ComReg.RegTypelib("MyDll", False)

End Function

在程序目录中新建一个mydll.def,def文件内容如下:

LIBRARY MyDLL

DESCRIPTION "MyDLL - (C) Antonio Giuliana, 2004"

EXPORTS

DllRegisterServer= ?DllRegisterServer@ mainmodule @@AAGXXZ

DllUnregisterServer= ?DllUnregisterServer@ mainmodule @@AAGXXZ

保存,编译,运行regsvr32注册mydll你就发现弹出对话框“注册过程“

结束语
本人使用导出函数的方法引自:http://www.visual-basic.it/uploads/articoli/tecnici/agDllByVB.htm

本人在实现过程中还碰到了一些问题,就是在导出的函数过程中使用很多方法都会有限制,可能是由于VB的一下全局对象未初始化引起的,所以就把注册方法封装到ComRegisterDll.dll中,再去引用,如果不依靠ComRegisterDll.dll而直接将ComRegister放到要注册的DLL的单元中去引用就会出错,欢迎大家与我一起探讨这个问题的解决方案。

时间: 2024-11-02 15:08:21

VB中定制DllRegisterServer、DllUnregisterServer的相关文章

VB中利用MapX创建用户定制工具

摘要 本文介绍了VB中如何利用MapX创建用户定制地图工具,详细地说明了整个创建过程,以及在创建定制工具的过程中所使用的关键方法. 1.前言 随着地理信息系统的发展,国内外已出现了不少GIS(地理信息系统)软件,其中MapX是MapInfo公司的ActiveX控件产品.由于它是一种基于Windows操作系统的标准控件,因而MapX4.0支持绝大多数标准的可视化开发环境,如:VisualBasic,Delphi,PowerBuilder,VisualC++等面向对象语言,而且可以使用Lotus s

怎样在VB中控制WORD

使用VB编程时,有时需要调用Microsoft Word对文字进行编辑.排版及输出.为实现这种调用,可以使用Shell函数.OLE自动化.在包容器中嵌入Word对象等方法.经过试用和比较,总结出了这几种方法的各自特点. 1 使用Shell函数直接调用 语法:Shell (pathname[,windowstyle]).Pathname是指要执行的程序的名字和任何必须的参数或命令行开关,可以包括目录和驱动器名:Windowstyle是执行程序的窗口风格的数字. 使用Shell调用Word比较简单,

vb中,用变量作为数组名可以吗

问题描述 vb中,用变量作为数组名可以吗 vb中,用变量作为数组名可以吗? 例如 arrrrstr()=("as","se") for i=0 to 1 for j=1 to 25 linestr=aeerrstr(i)(j)&"," next j next i 解决方案 将arrstr(i)作为变量名?可以的 解决方案二: 好久不用VB了, 好像是這樣的 dim arrrrstr(2,25) as string 解决方案三: Agoni

ASP进阶:VB中的模块,类,ActiveX与API的使用

active|activex 这几天搞了点c/s结构的程序设计, 由于ASP用得比较熟练,所以VB6.0自然成为我首选的开发工具. 在学习过程中,我结合自己的经验总结下VB中的一些高级应用,并且体会这种应用给我们所带来的好处. 一.模块 (Module) 在VB中,模块会优先执行,其实说白了,模块就象是全局的过程和函数调用.这是初步的提高代码可复用性的途径.我想有经验的ASP(VBSCRIPT,以后都只用VBScript来写Asp程序)程序员都用过<!--#Include file="&q

在VB中动态创建数据库

在VB中动态创建数据库新建工程.添加控件 对应写上代码 以下为引用的内容:Private Sub Command2_Click()    Dim myDB As DAO.Database    'Set myDB = DAO.OpenDatabase("d:\mydb3.mdb")    Set myDB = DAO.OpenDatabase(App.Path + "/mydb.mdb")    Dim d As String    d = "delete

在VB中兼容非ACCESS数据库的技巧

本文从VB数据库体系结构的角度出发,结合一个具体实例,阐述了在VB中兼容非ACCESS格式数据库的具体方法和技巧. 关键词:VB.非ACCESS数据库.数据存取对象 一个完整的数据库管理系统(DBMS)应是能兼容市面上各种较流行数据格式的系统,它充分考虑了不同用户的实际要求.鉴于目前市面上有多种数据库格式(如Foxpro.DBase.Paradox等)流行,因而在VB数据库应用程序中兼容非ACCESS数据库就显得尤为重要了. 作为一种流行的开发平台,VB提供了强大的数据库功能.主要有以下三种:数

浅谈crystal reports在VB中的调用

环境:VB6.0,crystal reports 9.0 在一个项目中用到了crystal reports,总结一些经验和教训. 以做一张单据的套打为例. 单据包括单据头,单据体.单据头和单据体可能是一对多的关系.并且他们分别存在于两张表当中,用字段FID做关联.要求能够动态的传入参数FID,显示不同的结果. 首先,打开crystal reports,做好一张单据的模板rpt.制作报表的方法有很多,比如:用它的图形化工具直接建立几个表之间的连接,然后将想要显示的字段托到报表当中:或是调用一个已经

VB中通过WMI控制DNS服务器,可在ASP中调用

dns服务器|控制 在VB中要使用Scripting API for WMI,必须引用 Microsoft WMI Scripting V1.1 Library 下面介绍Scripting API For WMI的几个对象 SWbemLocator--用于取得SWbemServices对象,他代表了本地或远程计算机上名字空间的一个连接.SWbemService--代表名字空间的一个连接,可用于处理它的部件SWbemObject--代表一个单独的类定义或一个对象实例SWbemOjbectSet--

如何实现给定日期的若干天以后的日期(有点类似VB中的DateAdd)

如何实现给定日期的若干天以后的日期(有点类似VB中的DateAdd)/*  豆腐制作  都是精品  http://www.asp888.net 豆腐技术站  如转载 请保留完整版权信息*/这几天突然有很多的人问这样的问题,就是如何在PHP中实现在VB中的DateAdd的函数,呵呵!这个可是问个正着.本来这个问题是 豆腐 去 华为 应聘的时候的一个考试题,不过当时是用C++实现的.没有想到这样的大公司,竟然用这样的小儿科来考试:),后来我没有去,这两天 应 http://www.chinaspx.