每个ActiveX Dll都应该有个DllGetClassObject函数,利用该函数就可以直接创建所需的com对象,而不需要通过注册表(或者注册),
STDAPI DllGetClassObject(
REFCLSID rclsid, //CLSID for the class object
REFIID riid, //Reference to the identifier of the interface
// that communicates with the class object
LPVOID * ppv //Address of output variable that receives the
// interface pointer requested in riid
);
这里必须知道两样东西,一个rclsid,就是需要创建的com对象的CLSID,另一个是 riid,该对象的一个接口的 id.
然而,调用DllGetClassObject,并不能直接创建所需要的对象,但可以得到对应的 IClassFactory,再由 IClassFactory.CreateInstance得到所需的对象.
vb实现代码大概如下:
需要用到一个库,http://www.mvps.org/emorcillo/download/vb6/tl_ole.zip
(引用页,http://www.mvps.org/emorcillo/en/code/vb6/wbframe.shtml)
另外,也将那个ActiveX Dll引用进工程,这里,并不是需要注册它,而是为了方便使用它的方法,因为并没有使用new来创建对象,
程序编译后即使不注册那个Dll文件都能够正常使用.
Option Explicit
'假设ActiveX Dll 的文件名为dllDemo.dll,并且处于工程同一目录
Private Declare Function DllGetClassObject Lib "dllDemo.dll" ( _
rclsid As UUID, riid As UUID, ByRef ppv As Any) As Long
'class id
Private Const ClsStr_Obj As String = "{C1A334BA-D1A4-48D0-98D5-47FE934961DF}"
'接口id
Private Const IidStr_Ins As String = "{231114D5-E046-4DAE-B192-0AB49D493A85}"
'IClassFactory id
Private Const strIID_IClassFactory As String = "{00000001-0000-0000-C000-000000000046}"
Private ClsId_Obj As UUID
Private Iid_Ins As UUID
Private iid_iunknow As UUID
Private iid_iclassfactory As UUID
Private Sub Command1_Click()
Dim tobj As olelib.IUnknown
Dim tobj2 As dllDemo.IDemo
Dim tFac As olelib.IClassFactory
Call DllGetClassObject(ClsId_Obj, iid_iclassfactory, tFac)
tFac.CreateInstance Nothing, iid_iunknow, tobj
Set tFac = Nothing
Set tobj2 = tobj
'调用IDemo.Test测试所创建的对象
tobj2.Test
End Sub
Private Sub Form_Load()
'将string转换为 UUID
CLSIDFromString ClsStr_Obj, ClsId_Obj
CLSIDFromString IidStr_Ins, Iid_Ins
CLSIDFromString IIDSTR_IUnknown, iid_iunknow
CLSIDFromString strIID_IClassFactory, iid_iclassfactory
End Sub
至此,问题似乎已经解决了,只要为不同的ActiveX Dll编写对应的DllGetClassObject函数就可以了,只是当文件名未定时就比较难办了,例如编写插件时.
解决办法是用LoadLibrary动态的调用各个dll上的DllGetClassObject.可惜的是vb不支持函数指针.我的办法是借助vc来解决.用vc写dll供vb调用,主要代码如下:
// CrCom.cpp : Defines the entry point for the DLL application.
//
#include "stdafx.h"
#include <unknwn.h>
#include <objbase.h>
typedef int (CALLBACK *MYPROC)(REFCLSID,REFIID,LPVOID *);
BOOL APIENTRY DllMain( HANDLE hModule,
DWORD ul_reason_for_call,
LPVOID lpReserved
)
{
return TRUE;
}
// if(riid==NULL)riid=&IID_IUnknown
int _stdcall CrComObj(
LPCSTR lpDll,
CLSID *rclsid,
IID *riid,
LPVOID * ppv)
{
HINSTANCE hinstLib;
MYPROC ProcAdd;
BOOL fFreeResult, fRunTimeLinkSuccess = FALSE;
int rtn=0;
// Get a handle to the DLL module.
hinstLib = LoadLibrary(lpDll);
// If the handle is valid, try to get the function address.
if (hinstLib != NULL)
{
ProcAdd =(MYPROC)GetProcAddress(hinstLib, "DllGetClassObject");
// If the function address is valid, call the function.
if (fRunTimeLinkSuccess = (ProcAdd != NULL))
{
if(rclsid==NULL)
{
FreeLibrary(hinstLib);
return 0;
}
if(riid==NULL)
riid=(IID *)&IID_IUnknown;
IClassFactory *pIf;
pIf=NULL;
if(ProcAdd(*rclsid,IID_IClassFactory,(void **)&pIf)==S_OK && pIf!=NULL)
{
if(pIf->CreateInstance(NULL,*riid,ppv)==S_OK)
rtn=(int)hinstLib;
pIf->Release();
pIf=NULL;
}
}
// Free the DLL module.
if(!rtn)fFreeResult = FreeLibrary(hinstLib);
}
return rtn;
}
// if strriid==NULL, use IID_IUnknown;
int _stdcall CrComObj2(
LPCSTR lpDll,
LPCSTR strrclsid,
LPCSTR strriid,
LPVOID * ppv )
{
HINSTANCE hinstLib;
MYPROC ProcAdd;
BOOL fFreeResult, fRunTimeLinkSuccess = FALSE;
int rtn=0;
// Get a handle to the DLL module.
hinstLib = LoadLibrary(lpDll);
// If the handle is valid, try to get the function address.
if (hinstLib != NULL)
{
ProcAdd =(MYPROC)GetProcAddress(hinstLib, "DllGetClassObject");
// If the function address is valid, call the function.
if (fRunTimeLinkSuccess = (ProcAdd != NULL))
{
CLSID rclsid;
IID riid;
if(strrclsid==NULL)
{
FreeLibrary(hinstLib);
return 0;
}
CLSIDFromString((LPOLESTR )strrclsid,&rclsid);
if(strriid!=NULL)
CLSIDFromString((LPOLESTR )strriid,&riid);
else
riid=IID_IUnknown;
IClassFactory *pIf=NULL;
if(ProcAdd(rclsid,IID_IClassFactory,(void **)&pIf)==S_OK && pIf!=NULL)
{
if(pIf->CreateInstance(NULL,riid,ppv)==S_OK)
rtn=(int)hinstLib;
pIf->Release();
pIf=NULL;
}
}
// Free the DLL module.
if(!rtn)fFreeResult = FreeLibrary(hinstLib);
}
return rtn;
}
在vb中的使用方法,CrComObj传递的是UUID,CrComObj2传递的是String,
'函数声明
Private Declare Function CrComObj Lib "CrCom.dll" ( _
ByVal lpDll As String, ByVal rclsid As Long, ByVal riid As Long, ByRef ppv As Any) As Long
Private Declare Function CrComObj2 Lib "CrCom.dll" ( _
ByVal lpDll As String, ByVal strrclsid As Long, ByVal strriid As Long, ByRef ppv As Any) As Long
Dim tobj As olelib.IUnknown
Dim tobj2 As dllDemo.IDemo
hlib = CrComObj(App.Path & "/dllDemo.dll", VarPtr(ClsId_Obj), 0, tobj)
Set tobj2 = tobj
tobj2.Test
'或者
hlib=CrComObj2(App.Path & "/dllDemo.dll", StrPtr(ClsStr_Obj), 0, tobj)
Set tobj2 = tobj
tobj2.Test
CrComObj与CrComObj2返回的是LoadLibrary的返回值,必要的时候需要用FreeLibrary释放.
后记:
我的多页面浏览器LE中,也实现了不注册调用ActiveX Dll,我是直接使用了一本书(Advanced Visual Basic)的代码,代码颇长,似乎也挺复杂,原先使用的时候也不明所以然,后来终于搞清楚了,其原理是一样的,但是因为vb不支持函数指针,于是它花了很大力气去处理这个问题.相比而言,我觉得还是借用一下vc比较好,这样的话简捷的多.
那本书(Advanced Visual Basic)中让vb能够函数指针的方法不错,但是要添加类型库,还要自己创建轻量com对象显得颇为麻烦.我想,不如直接利用vb自己建对象算了.
代码如下:
'建一class,如下
'---------------------------------------------------------------------------------------
' Module : cFucPtr
' DateTime : 2006-2-7 17:36
' Author : Lingll
' Email : lingll_xl@163.com
' HomePage : http://lingll.yeah.net/
' Purpose :
'---------------------------------------------------------------------------------------
Option Explicit
'存储加载dll后获得的函数地址
Private m_NewFucPtr As Long
Public Function DllGetClassObject( _
ByRef rclsid As UUID, ByRef riid As UUID, ByRef ppv As IClassFactory) As Long
End Function
Public Sub SetFunctionPtr(newptr&)
m_NewFucPtr = newptr
End Sub
'再建一module
Option Explicit
Public Declare Function LoadLibrary Lib "kernel32.dll" Alias "LoadLibraryA" (ByVal lpLibFileName As String) As Long
Public Declare Function FreeLibrary Lib "kernel32.dll" (ByVal hLibModule As Long) As Long
Public Declare Function GetProcAddress Lib "kernel32.dll" (ByVal hModule As Long, ByVal lpProcName As String) As Long
Public Declare Sub CopyMemory Lib "kernel32.dll" Alias "RtlMoveMemory" (ByRef Destination As Any, ByRef Source As Any, ByVal Length As Long)
Public Type typAsm
code(1) As Long
End Type
Public asm As typAsm
'然后,初始化时,让asm为如下值,
asm.code(0) = &HFF515859
asm.code(1) = &H90003460
'这个是汇编代码,具体是
'pop ecx
'pop eax
'push ecx
'jmp DWORD PTR [eax + 52]
'这是抄回来的,具体原理我不太清楚,如下是原注释
'Here's the magic asm for doing the function pointer call.
'The stack comes in with the following:
'esp: return address
'esp + 4: this pointer for FunctionDelegator
'All that we need to do is remove the this pointer from the
'stack, replace it with the return address, then jmp to the
'correct function. In other words, we're just squeezing the
'this pointer completely out of the picture.
'The code is:
'pop ecx (stores return address)
'pop eax (gets the this pointer)
'push ecx (restores the return address)
'jmp DWORD PTR [eax + 4] (jump to address at this + 4, 3 byte instruction)
'The corresponding byte stream for this is: 59 58 51 FF 60 04
'We pad these six bytes with two int 3 commands (CC CC) to get eight
'bytes, which can be stored in a Currency constant.
'Note that the memory location of this constant is not executable, so
'it must be copied into a currency variable. The address of the variable
'is then used as the forwarding function.
'下面是调用代码:
Dim tadd As Long, vTab&
Dim tobj As cFucPtr
Dim tLib&
Dim tUn As olelib.IUnknown
Dim tDem As dllDemo.IDemo
Dim tFac As olelib.IClassFactory
Set tobj = New cFucPtr
'加载dll
tLib = LoadLibrary(App.Path & "/dllDemo.dll")
If tLib <> 0 Then
tadd = GetProcAddress(tLib, "DllGetClassObject")
End If
Dim asmadd&
If tadd <> 0 Then
'获取vtable地址
CopyMemory vTab, ByVal ObjPtr(tobj), 4
asmadd = VarPtr(asm)
'替换掉cFucPtr.DllGetClassObject地址
CopyMemory ByVal (vTab + (8 - 1) * 4), asmadd, 4
'设置函数地址
tobj.SetFunctionPtr tadd
tobj.DllGetClassObject ClsId_Obj, iid_iclassfactory, tFac
If Not tFac Is Nothing Then
tFac.CreateInstance Nothing, iid_iunknow, tUn
Set tFac = Nothing
Set tDem = tUn
Set tUn = Nothing
tDem.test
End If
End If
Set tDem = Nothing
If tLib <> 0 Then FreeLibrary tLib
'一定要在所有对象都释放掉了才能使用FreeLibrary,不然会出错
<script type="text/javascript"> </script><script type="text/javascript" src="http://pagead2.googlesyndication.com/pagead/show_ads.js"> </script>