If you know the location of the COM Server Dll then you can load it with LoadLibrary, you can then use GetProcessAddress to get a function pointer to the entry point DllGetClassObject. Calling DllGetClassObject gets you an interface pointer to IClassFactory and then one can call CreateInstance on that interface. You'll need to know the GUID of the CoClass you want to instantiate as well as the GUID of the interface you're requesting.
Some of this can be written in VBA but getting a function pointer and calling on it are solidly C++ tasks. Here I present code which does the above. First the C++ which needs to be housed in a Win32 Dll project with exports (a .Def file).
#include "Objbase.h"
COMCREATEVIACLASSFACTORY_API HRESULT __stdcall ClassFactoryCreateInstance(
_In_ HMODULE hModule,
_In_ _GUID *clsiid,
_In_ _GUID *iid,
void** itfUnknown)
{
HRESULT hr = S_OK;
IClassFactory* pClassFactory;
// Declare a pointer to the DllGetClassObject function.
typedef HRESULT(__stdcall *PFNDLLGETCLASSOBJECT)(REFCLSID clsiid,
REFIID RIID, void** PPV);
PFNDLLGETCLASSOBJECT DllGetClassObject =
(PFNDLLGETCLASSOBJECT)::GetProcAddress(hModule, "DllGetClassObject");
// Call DllGetClassObject to get a pointer to the class factory.
hr = DllGetClassObject(*clsiid, *iid, (void**) &pClassFactory);
if (hr == S_OK)
{
// IClassFactory::CreateInstance and IUnknown::Release
hr = pClassFactory->CreateInstance(NULL, IID_IUnknown,
(void**) itfUnknown);
pClassFactory->Release();
}
return hr;
}
COMCREATEVIACLASSFACTORY_API void TestClassFactoryCreateInstance()
{
HMODULE hModule = 0;
hModule = LoadLibrary(L"C:\\Windows\\System32\\scrrun.dll");
_GUID clsiid, iid;
::CLSIDFromString(L"{EE09B103-97E0-11CF-978F-00A02463E06F}", &clsiid);
::CLSIDFromString(L"{00000000-0000-0000-C000-000000000046}", &iid);
HRESULT hr = S_OK;
IUnknown* pUnknown = 0;
ClassFactoryCreateInstance(hModule,
&clsiid,
&iid, (void**) &pUnknown);
}
And some client VBA
Option Explicit
Declare Function ClassFactoryCreateInstance Lib "ComCreateViaClassFactory.dll" _
(ByVal hModule As Long, _
ByRef pguidClass As GUID, _
ByRef pguidInterface As GUID, _
ByRef itfUnknown As stdole.IUnknown) As Long
Declare Sub TestClassFactoryCreateInstance Lib "ComCreateViaClassFactory.dll" ()
Declare Function LoadLibrary Lib "Kernel32" Alias "LoadLibraryA" _
(ByVal lpLibFileName As String) As Long
Const IID_IUnknown As String = "{00000000-0000-0000-C000-000000000046}"
Const IID_IClassFactory As String = "{00000001-0000-0000-C000-000000000046}"
Const IID_IClassFactory2 As String = "{B196B28F-BAB4-101A-B69C-00AA00341D07}"
Declare Function CLSIDFromString Lib "OLE32" _
(ByVal lpszCLSID As String, pclsid As GUID) As Long
Type GUID
Data1 As Long
Data2 As Integer
Data3 As Integer
Data4(7) As Byte
End Type
Sub Test_ClassFactoryCreateInstance()
Debug.Assert Dir(ThisWorkbook.Path & "\ComCreateViaClassFactory.dll") = _
"ComCreateViaClassFactory.dll"
Call LoadLibrary(ThisWorkbook.Path & "\ComCreateViaClassFactory.dll")
Dim clsiid As GUID
Debug.Assert CLSIDFromString(StrConv( _
"{EE09B103-97E0-11CF-978F-00A02463E06F}", vbUnicode), clsiid) = 0
Dim riid As GUID
Debug.Assert CLSIDFromString(StrConv(IID_IUnknown, vbUnicode), riid) = 0
Dim hModule As Long
hModule = LoadLibrary("C:\Windows\System32\scrrun.dll")
Dim itfUnknown As stdole.IUnknown, hr As Long
hr = ClassFactoryCreateInstance(hModule, clsiid, riid, itfUnknown)
If hr <> 0 Then Err.Raise hr
Dim oDict As Scripting.Dictionary
Set oDict = itfUnknown
oDict.Add "Foo", 2
oDict.Add "Bar", 3
Debug.Assert oDict.Keys()(0) = "Foo"
Debug.Assert oDict.Keys()(1) = "Bar"
End Sub
Sub Test_TestClassFactoryCreateInstance()
Debug.Assert Dir(ThisWorkbook.Path & "\ComCreateViaClassFactory.dll") = _
"ComCreateViaClassFactory.dll"
Call LoadLibrary(ThisWorkbook.Path & "\ComCreateViaClassFactory.dll")
Call TestClassFactoryCreateInstance
End Sub
A nice diagram here shows what we are doing
No comments:
Post a Comment