C++/COM/Proxy Dlls: method override/method forwarding (COM implementation inheritance) - c++

Hello and good day to you.
Situation:
For some reason, from time to time I run into situation when I need to override one or two methods of a COM interface (that is being used for some older application without source code), which is normally Direct3D/DirectInput related (i.e. it is created by calling a DLL method, not by CoCreateInstance). Normally I deal with situation by writing a proxy DLL that overrides a method that creates interface I need to "modify", and replace original interface with my own. Normally this is required to make some older application work properly without crashing/artifacts.
Compiler:
I use Visual Studio express 2008 on windows machine, so there are no C++0x features. The system has msysgit, msys, python, perl, gnu utilities (awk/sed/wc/bash/etc), gnu make and qmake (Qt-4.7.1) installed (and available within PATH).
Problem:
Overriding one method of a COM interface is a pain (especially if original interface has a hundred of methods or so), because I need to forward many calls to original interface, and currently I see no way to simplify or automate the process. For example, override of IDirect3D9 looks like this:
class MyD3D9: public IDirect3D9{
protected:
volatile LONG refCount;
IDirect3D9 *orig;
public:
STDMETHOD(QueryInterface)(THIS_ REFIID riid, LPVOID * ppvObj){
if (!ppvObj)
return E_INVALIDARG;
*ppvObj = NULL;
if (riid == IID_IUnknown || riid == IID_IDirect3D9){
*ppvObj = (LPVOID)this;
AddRef();
return NOERROR;
}
return E_NOINTERFACE;
}
STDMETHOD_(ULONG,AddRef)(THIS){
InterlockedIncrement(&refCount);
return refCount;
}
STDMETHOD_(ULONG,Release)(THIS){
ULONG ref = InterlockedDecrement(&refCount);
if (refCount == 0)
delete this;
return ref;
}
/*** IDirect3D9 methods ***/
STDMETHOD(RegisterSoftwareDevice)(THIS_ void* pInitializeFunction){
if (!orig)
return E_FAIL;
return orig->RegisterSoftwareDevice(pInitializeFunction);
}
STDMETHOD_(UINT, GetAdapterCount)(THIS){
if (!orig)
return 0;
return orig->GetAdapterCount();
}
STDMETHOD(GetAdapterIdentifier)(THIS_ UINT Adapter,DWORD Flags, D3DADAPTER_IDENTIFIER9* pIdentifier){
if (!orig)
return E_FAIL;
return orig->GetAdapterIdentifier(Adapter, Flags, pIdentifier);
}
STDMETHOD_(UINT, GetAdapterModeCount)(THIS_ UINT Adapter,D3DFORMAT Format){
if (!orig)
return 0;
return orig->GetAdapterModeCount(Adapter, Format);
}
/* some code skipped*/
MyD3D9(IDirect3D9* origD3D9)
:refCount(1), orig(origD3D9){
}
~MyD3D9(){
if (orig){
orig->Release();
orig = 0;
}
}
};
As you can see, this is very inefficient, error-prone and requires a lot of copy-pasting.
Question:
How can I simplify overriding of a single method of a COM interface in this situation? I would like to specify only method I change, but I currently see no way to do so. I also don't see a way to elegantly shorten "forwarded" methods with macros or templates or macros, because they have variable number of arguments. Another approach I saw is to use directly patch method table returned by another method (modify access right using VirtualProtect, then write into method table), which I don't exactly like.
Limitations:
I would prefer to solve in C++ source code (macros/templates) and without code generators (unless code generator usage is extremely simple/elegant - i.e. writing code generator is not ok, using already available code generator I can set up in minutes and solve the whole thing in one line of code is ok). Boost is okay only if it doesn't add extra DLL dependency. MS-specific compiler directives and language extensions are also ok.
Ideas? Thanks in advance.

Okay, since I don't like unanswered questions...
To implement "COM implementation inheritance" there's currently no sane and compact solution written in pure C++. This is mostly because in C++ it is forbidden to create an instance of abstract class or manipulate virtual method table directly. As a result, there are 2 commonly used solutions:
Write method forwarding for every method manually.
Hack dispatch table.
Advantage of #1 is that this approach is safe and you can store additional data within custom class.
Disadvantage of #1 is that writing a wrapper for every single method is extremely tedious procedure.
Advantage of #2 is that this approach is compact. You replace single method.
Disadvantage of #2 is that dispatch table might be located in write-protected space (most likely it wouldn't happen, but it could happen in theory) and you can't store custom data in hacked interface. As a result, although it is simple/short, it is quite limiting.
And there's a 3rd approach. (which nobody has suggested for some reason)
Short description: instead of using virtual method table provided by C++, write non-virtual class that will emulate virtual method table.
Example:
template<typename T1, typename T2> void unsafeCast(T1 &dst, const T2 &src){
int i[sizeof(dst) == sizeof(src)? 1: -1] = {0};
union{
T2 src;
T1 dst;
}u;
u.src = src;
dst = u.dst;
}
template<int Index> void __declspec(naked) vtblMapper(){
#define pointerSize 4 //adjust for 64bit
static const int methodOffset = sizeof(void*)*Index;
__asm{
mov eax, [esp + pointerSize]
mov eax, [eax + pointerSize]
mov [esp + pointerSize], eax
mov eax, [eax]
add eax, methodOffset
mov eax, [eax]
jmp eax
};
#undef pointerSize
}
struct MyD3DIndexBuffer9{
protected:
VtblMethod* vtbl;
IDirect3DIndexBuffer9* orig;
volatile LONG refCount;
enum{vtblSize = 14};
DWORD flags;
bool dynamic, writeonly;
public:
inline IDirect3DIndexBuffer9*getOriginalPtr(){
return orig;
}
HRESULT __declspec(nothrow) __stdcall QueryInterface(REFIID riid, LPVOID * ppvObj){
if (!ppvObj)
return E_INVALIDARG;
*ppvObj = NULL;
if (riid == IID_IUnknown || riid == IID_IDirect3DIndexBuffer9){
*ppvObj = (LPVOID)this;
AddRef();
return NOERROR;
}
return E_NOINTERFACE;
}
ULONG __declspec(nothrow) __stdcall AddRef(){
InterlockedIncrement(&refCount);
return refCount;
}
ULONG __declspec(nothrow) __stdcall Release(){
ULONG ref = InterlockedDecrement(&refCount);
if (refCount == 0)
delete this;
return ref;
}
MyD3DIndexBuffer9(IDirect3DIndexBuffer9* origIb, DWORD flags_)
:vtbl(0), orig(origIb), refCount(1), flags(flags_), dynamic(false), writeonly(false){
dynamic = (flags & D3DUSAGE_DYNAMIC) != 0;
writeonly = (flags & D3DUSAGE_WRITEONLY) != 0;
vtbl = new VtblMethod[vtblSize];
initVtbl();
}
HRESULT __declspec(nothrow) __stdcall Lock(UINT OffsetToLock, UINT SizeToLock, void** ppbData, DWORD Flags){
if (!orig)
return E_FAIL;
return orig->Lock(OffsetToLock, SizeToLock, ppbData, Flags);
}
~MyD3DIndexBuffer9(){
if (orig){
orig->Release();
orig = 0;
}
delete[] vtbl;
}
private:
void initVtbl(){
int index = 0;
for (int i = 0; i < vtblSize; i++)
vtbl[i] = 0;
#define defaultInit(i) vtbl[i] = &vtblMapper<(i)>; index++
//STDMETHOD(QueryInterface)(THIS_ REFIID riid, void** ppvObj) PURE;
unsafeCast(vtbl[0], &MyD3DIndexBuffer9::QueryInterface); index++;
//STDMETHOD_(ULONG,AddRef)(THIS) PURE;
unsafeCast(vtbl[1], &MyD3DIndexBuffer9::AddRef); index++;
//STDMETHOD_(ULONG,Release)(THIS) PURE;
unsafeCast(vtbl[2], &MyD3DIndexBuffer9::Release); index++;
// IDirect3DResource9 methods
//STDMETHOD(GetDevice)(THIS_ IDirect3DDevice9** ppDevice) PURE;
defaultInit(3);
//STDMETHOD(SetPrivateData)(THIS_ REFGUID refguid,CONST void* pData,DWORD SizeOfData,DWORD Flags) PURE;
defaultInit(4);
//STDMETHOD(GetPrivateData)(THIS_ REFGUID refguid,void* pData,DWORD* pSizeOfData) PURE;
defaultInit(5);
//STDMETHOD(FreePrivateData)(THIS_ REFGUID refguid) PURE;
defaultInit(6);
//STDMETHOD_(DWORD, SetPriority)(THIS_ DWORD PriorityNew) PURE;
defaultInit(7);
//STDMETHOD_(DWORD, GetPriority)(THIS) PURE;
defaultInit(8);
//STDMETHOD_(void, PreLoad)(THIS) PURE;
defaultInit(9);
//STDMETHOD_(D3DRESOURCETYPE, GetType)(THIS) PURE;
defaultInit(10);
//STDMETHOD(Lock)(THIS_ UINT OffsetToLock,UINT SizeToLock,void** ppbData,DWORD Flags) PURE;
//defaultInit(11);
unsafeCast(vtbl[11], &MyD3DIndexBuffer9::Lock); index++;
//STDMETHOD(Unlock)(THIS) PURE;
defaultInit(12);
//STDMETHOD(GetDesc)(THIS_ D3DINDEXBUFFER_DESC *pDesc) PURE;
defaultInit(13);
#undef defaultInit
}
};
To swap it with real interface, you'll have to use reinterpret_cast.
MyD3DIndexBuffer9* myIb = reinterpret_cast<MyD3DIndexBuffer9*>(pIndexData);
As you can see this method requires assembly, macros, templates combined together with casting class method pointer to void*. Also it is compiler-dependent(msvc, although you should be able to do same trick with g++) and architecture-dependent (32/64-bit). Plus it is unsafe (as with dispatch table hacking).
The advantage compared to dispatch tables you can use custom class and store additional data within interface. However:
All virtual methods are forbidden. (as far as I know, any attempt to use virtual method will instantly insert invisible 4-bytes pointer at the beginning of the class, which will break everything).
Calling convention must be stdcall (should work with cdecl, though, but for everything else you'll need different wrapper)
You have to initialize entire vtable yourself (very error-prone). One mistake, and everything will crash.

Related

C++ casting Windows IAction

I'm working on retrieving some info from windows task scheduler.
MSDN indicates there are several types of action. I want to deal with them separately. I tried:
IAction* pAction = NULL;
pActionCollection->get_Item(_variant_t(i), &pAction);
if (IExecAction* pExecAction = dynamic_cast<IExecAction*>(pAction)) { /*my work...*/ }
if (IComHandlerAction* pComHandlerAction = dynamic_cast<IComHandlerAction*>(pAction)) { /*my work...*/ }
if (IEmailAction* pEmailAction = dynamic_cast<IEmailAction*>(pAction)) { /*my work...*/ }
if (IShowMessageAction* pShowMessageAction = dynamic_cast<IShowMessageAction*>(pAction)) { /*my work...*/ }
But this program throws exception at the first dynamic_cast.
Exception thrown at 0x00007FFB516365A5 (vcruntime140d.dll) in myProgram.exe: 0xC0000005: Access violation reading location 0x00000130BAFEDB04.
The definition in taskschd.h shows IExecAction is a derived class from IAction.
This works well:
if (IExecAction* pExecAction = ((IExecAction*)pAction)) { /*my work...*/ }
But what if I want to do some type checking?
How could I use it properly?
for get pointer of com interface from another com interface on the same object we need use only QueryInterface method, which is always implemented by any interface. so code in your case need to be next:
IAction* pAction;
IExecAction* pExecAction;
IEmailAction* pEmailAction;
HRESULT hr;
if (SUCCEEDED(hr = pAction->QueryInterface(IID_PPV_ARGS(&pExecAction))))
{
// use pExecAction
pExecAction->Release();
}
if (SUCCEEDED(hr = pAction->QueryInterface(IID_PPV_ARGS(&pEmailAction))))
{
// use pExecAction
pEmailAction->Release();
}
even if one interface inherit from another use c/c++ cast is always wrong. for example
pExecAction = static_cast<IExecAction*>(pAction);
pEmailAction = static_cast<IEmailAction*>(pAction);
this code is correct from c++ syntax, because both IExecAction : IAction and IEmailAction : IAction inherit from IAction. and this cast (if take to account layout of this 3 interfaces) give you equal binary values for pExecAction and pEmailAction. but pExecAction can not have the same binary value as pEmailAction. must be
assert((void*)pEmailAction != (void*)pExecAction);
why ? because have pEmailAction and pExecAction have different virtual functions at the same position in vtable. for example on the 10-th position in the table of IExecAction must be pointer to get_Path method. from another side on the 10-th position in the table of IEmailAction must be pointer to get_Server method. if (void*)pEmailAction == (void*)pExecAction - they will be have the same pointers to vtable. but pointer to which function - get_Path or get_Server will be in the 10-th position ? as result pointer to this 2 interfaces can not be the same (point to the same memory). so how minimum one static_cast here (may be and both) give wrong result. for understand how QueryInterface work and why pointers to pExecAction and pEmailAction wiil be different - we need look for implementation. implementation of interfaces - this is some class, which inherit (usually) from all this interfaces and implement it like this:
class CAction : IExecAction, IEmailAction
{
virtual ULONG STDMETHODCALLTYPE AddRef( );
virtual HRESULT STDMETHODCALLTYPE QueryInterface( REFIID riid, void **ppvObject)
{
PVOID pvObject;
if (riid == __uuidof(IAction))
{
pvObject = static_cast<IExecAction*>(this);
// or can be
pvObject = static_cast<IEmailAction*>(this);
}
else if (riid == __uuidof(IExecAction))
{
pvObject = static_cast<IExecAction*>(this);
}
else if (riid == __uuidof(IEmailAction))
{
pvObject = static_cast<IExecAction*>(this);
}
else
{
*ppvObject = 0;
return E_NOINTERFACE;
}
*ppvObject = pvObject;
AddRef();
return S_OK;
}
};
look that static_cast<IExecAction*>(this); will always give another binary value compare static_cast<IEmailAction*>(this); - the CAction will be containing 2 different vtables - one for IExecAction and one for IEmailAction. they have common initial part (9 entries) but then different. and static_cast<IExecAction*>(this); and static_cast<IEmailAction*>(this); return 2 different (always) pointers to this 2 different vtables. when for IAction* we select return or first or second vtable pointer. both will be correct. what pointer return implementation - we can not know (the layout of actual class which implement IExecAction, IEmailAction is unknown for us)

How to resolve access violation writing location when calling dll method

I'm using GetProcAddress to gain access to a standard Isapi Filter DLL method - the GetFilterVersion method which takes a pointer to a HTTP_FILTER_VERSION structure.
https://msdn.microsoft.com/en-us/library/ms525822(v=vs.90).aspx
https://msdn.microsoft.com/en-us/library/ms525465(v=vs.90).aspx
I've tested the code against a working Isapi filter that I've written and it works fine. I debug the code against an Isapi filter from a vendor (I don't have access to the source code or anything beyond the dll itself) and I get the exception, "access violation writing location". What could be the issue? (Both Isapi filters work in IIS.)
//Attempted to define function ptr several ways
typedef BOOL(__cdecl * TRIRIGAISAPIV)(PHTTP_FILTER_VERSION);
//typedef BOOL( * TRIRIGAISAPIV)(PHTTP_FILTER_VERSION);
//typedef BOOL(WINAPI * TRIRIGAISAPIV)(PHTTP_FILTER_VERSION);
void arbitraryMethod()
{
HINSTANCE hDLL; // Handle to DLL
TRIRIGAISAPIV lpfnDllFunc2; // Function pointer
DWORD lastError;
BOOL uReturnVal2;
hDLL = LoadLibrary(L"iisWASPlugin_http.dll"); //vendor's dll
//hDLL = LoadLibrary(L"KLTWebIsapi.dll //my dll
if (hDLL != NULL)
{
lpfnDllFunc2 = (TRIRIGAISAPIV)GetProcAddress(hDLL, "GetFilterVersion");
if (!lpfnDllFunc2)
{
lastError = GetLastError();
// handle the error
FreeLibrary(hDLL);
//return 1;
}
else
{
HTTP_FILTER_VERSION pVer = { 6 };
//Call the function via pointer; Works with my dll, fails with vendor's
uReturnVal2 = lpfnDllFunc2(&pVer);
//................ HELP!!!!!!!!!!!!!
}
}
}
One issue that I see is that your function pointer declaration is incorrect.
According to the Microsoft documentation, GetFilterVersion is prototyped as:
BOOL WINAPI GetFilterVersion(PHTTP_FILTER_VERSION pVer);
The WINAPI is a Windows macro that is actually defined as __stdcall, thus you are declaring the function pointer incorrectly when you used __cdecl.
What does WINAPI mean?
Thus, your declaration should be:
typedef BOOL(__stdcall * TRIRIGAISAPIV)(PHTTP_FILTER_VERSION);
It could be that there are actually some additional structure fields filled by the custom filter.
You can try to increase the size of the structure to see if that will work, like for example:
struct HTTP_FILTER_VERSION_EXTRA {
HTTP_FILTER_VERSION v;
char[1024] extra;
};
HTTP_FILTER_VERSION_EXTRA ver;
ver.v.dwServerFilterVersion = 6;
uReturnVal2 = lpfnDllFunc2(&ver.v);
It is sometimes the case with the WinAPI structures that they allow versioning, so adding fields is possible. If the function doesn't then check (or doesn't know) the actual structure version, it might try to use an extended one which might be different than the one supplied - if the size of the supplied struct is then lesser than the structure version the func tries to use, bad things can happen.
Also check if the DLL is 64-bit or 32-bit. You cannot use 64-bit DLL by 32-bit app and vice versa (but I expect that would already fail during the LoadLibrary call).

How to pass HWND with InvokeHelper for calling .NET COM object?

UPDATE
I have made a test project for the original question (that I moved below).
C# code:
namespace TestManagedCom
{
[ComVisible(true)]
public class DummyObject
{
public void Method1(int value)
{
IntPtr hwnd = new IntPtr(value);
MessageBox.Show(string.Format("[Method1] value={0:X}, hwnd={1}", value, hwnd));
}
public void Method2(long value)
{
IntPtr hwnd = new IntPtr(value);
MessageBox.Show(string.Format("[Method2] value={0:X}, hwnd={1}", value, hwnd));
}
}
}
C++ code:
class CDispatchWrapper : public COleDispatchDriver
{
public:
CDispatchWrapper(){}
CDispatchWrapper(LPDISPATCH pDispatch) : COleDispatchDriver(pDispatch) {}
CDispatchWrapper(const CDispatchWrapper& dispatchSrc) : COleDispatchDriver(dispatchSrc) {}
void CallMethod(DISPID dwDispID, int value)
{
static BYTE parms[] = VTS_I4;
InvokeHelper(dwDispID, DISPATCH_METHOD, VT_EMPTY, NULL, parms, value);
}
void CallMethod(DISPID dwDispID, long long value)
{
static BYTE parms[] = VTS_I8;
InvokeHelper(dwDispID, DISPATCH_METHOD, VT_EMPTY, NULL, parms, value);
};
};
template <typename T>
void Execute(const CString& progId, const CString& methodName, T value)
{
LPDISPATCH lpEventComponent = NULL;
_com_ptr_t<_com_IIID<IDispatch, &IID_IDispatch> > pCreateComp;
HRESULT hr = pCreateComp.CreateInstance(progId);
if(SUCCEEDED(hr) && pCreateComp != NULL)
{
hr = pCreateComp.QueryInterface(IID_IDispatch, (void**)&lpEventComponent);
if(SUCCEEDED(hr))
{
USES_CONVERSION;
DISPID dwFunctionID = 0;
OLECHAR FAR *szFunc = T2OLE(const_cast<LPTSTR>(methodName.GetString()));
hr = lpEventComponent->GetIDsOfNames(IID_NULL, &szFunc, 1, LOCALE_SYSTEM_DEFAULT, &dwFunctionID);
if(SUCCEEDED(hr) && dwFunctionID != -1)
{
lpEventComponent->AddRef(); // released by the dispatch driver
CDispatchWrapper wrapper(lpEventComponent);
wrapper.CallMethod(dwFunctionID, value);
}
}
}
}
Execute<int>(_T("TestManagedCom.DummyObject"), _T("Method1"), 0x11223344);
Execute<long long>(_T("TestManagedCom.DummyObject"), _T("Method2"), 0x1122334455667788LL);
It works well when the target is x64. It prints:
[Method1] value=11223344, hwnd=287454020
[Method2] value=1122334455667788, hwnd=1234605616436508552
The call to Method2 throws an exeption when the target is x86.
First-chance exception at 0x76A2B727 in TestOleDispatcher.exe:
Microsoft C++ exception: EEException at memory location 0x003FE3C4.
If there is a handler for this exception, the program may be safely
continued.
I have tried with both long long and __int64 and the error is obviously the same.
It seems that somehow it cannot correctly marshall VTS_I8 params on x86.
The original question
I have problems in some legacy code calling a method in a .NET class that represents a COM object with COleDispatchDriver::InvokeHelper. One of the parameters is the handle of a window.
The .NET code used to look like this (simplified):
[ComVisible(true)]
public class Sample
{
public void Method1(int hwndParent)
{
}
}
And the C++ code
class CSendEventWrapper : public COleDispatchDriver
{
public:
void CallMethod(DISPID dwDispID, long* hwnd)
{
static BYTE parms[] = VTS_PI4;
InvokeHelper(dwDispID, DISPATCH_METHOD, VT_EMPTY, NULL, parms, hwnd);
}
};
HWND hWnd = ...;
long lval = (long)hWnd;
o.CallMethod(dispId, &lval); // snippet for calling the method
This worked OK when the C++ app was 32-bit only. But on a 64-bit version, this is not correct, since HWND is 64-bit and long is just 32-bit, so you lose data.
So I started changing the .NET code to use IntPtr instead of int (as it should have been in the first place).
[ComVisible(true)]
public class Sample
{
public void Method1(IntPtr hwndParent)
{
}
}
But now the problem is how do I call it with InvokeHelper. I tried doing something like this:
void CallMethod(DISPID dwDispID, INT_PTR hwnd)
{
#ifdef _WIN64
static BYTE parms[] = VTS_PI8;
#else
static BYTE parms[] = VTS_PI4;
#endif
InvokeHelper(dwDispID, DISPATCH_METHOD, VT_EMPTY, NULL, parms, hwnd);
}
HWND hWnd = ...;
INT_PTR lval = (INT_PTR)hWnd; // 32- or 64-bit depending on the platform
o.CallMethod(dispId, &lval); // snippet for calling the method
However, this now results in an exception that says a parameter was in an incorrect format. IntPtr should be 32-bit or 64-bit depending on whether the process if 32-bit or 64-bit. I'm not sure what's wrong.
Any help for figuring how to correctly pass the HWND with InvokeHelper both for 32-bit and 64-bit versions is appreciated. (And no, I cannot replace the use of COleDispatchDriver).
Looks like you have mismatch of parameter types. Getting the handle from c# typically gives you the window handle in a IntPtr. This will be the actual handle, not a pointer to the handle. From your code it looks like you're expecting a pointer to handle. I can tell by the long* hWnd and VTS_PI4.
If the COM call really wants a INT_PTR (a pointer to the handle), you'll have to store the variable passed in and take the address of it to pass on. If it takes the window handle directly, then you'll need to modify the VTS_PI4/VTS_PI8 to VTS_I4/VTS_I8.

Win API wrapper classes for handles

Writing a wrapper class for a handle that only gets passed by value is relatively easy. I am trying to determine what the best way is to encapsulate handles that need to be passed by address.
For example, writing a wrapper for something like SC_HANDLE that gets passed by value to QueryServiceConfig() is not that difficult. One can either implement a member function like .GetHandle() or implement operator().
The problem (for me at least) is API functions like RegOpenKeyEx() that wants the address of a HKEY.
I've read that overloading operator & is generally a bad idea. What is the recommended way to keep encapsulation (or as much of it as possible) and especially resource collection while allowing API functions access?
You can always add another layer of indirection to avoid the awful overloading of operator& and ugly Attach or Detach and return a pre-wrapped instance from there.
If you can use C++0x in VS2010 or gcc, or have other ways of accessing std::unique_ptr<>, then you can do this (error checking omitted for brevity):
struct hkey_deleter
{
void operator()(HKEY hkey)
{
::RegCloseKey(hkey);
}
};
typedef std::unique_ptr<HKEY__, hkey_deleter> regkey;
regkey MyRegOpenKeyEx(HKEY hKey, LPCTSTR lpSubKey, DWORD ulOptions, REGSAM samDesired)
{
HKEY hOpenedKey = NULL;
::RegOpenKeyEx(hKey, lpSubKey, ulOptions, samDesired, &hOpenedKey);
return regkey(hOpenedKey);
}
void SomewhereElse()
{
...
regkey r = MyRegOpenKeyEx(HKEY_CLASSES_ROOT, nullptr, 0, KEY_READ);
...
}
The hkey_deleter will make sure that the registry key gets closed when the scope is exited or regkey::reset() is called.

COM problem between Unmanaged C++ and Delphi

I have a DLL in unmanaged C++ :
EditArticleManagerFactory.h:
class __declspec(dllexport) EditArticleManagerFactory : public NamedClassFactory<SCEditArticleManager>,
public SCBLEditArticle:ICOMEditArticleManagerFactory
{
public:
STDMETHODIMP CreateManager(BSTR bstrName, SCBLEditArticle::ICOMEditArticleManager** pEditArticleManager);
}
interface ICOMEditArticleManagerFactory : IUnknown
{
HRESULT CreateManager([in]BSTR bstrName, [out]ICOMEditArticleManager** pEditArticleManager);
}
EditArticleManagerFactory.cpp:
STDMETHODIMP EditArticleManagerFactory::CreateManager(BSTR bstrName, SCBLEditArticle::ICOMEditArticleManager** pEditArticleManager)
{
manager = factory->createManager(bstrName);
return manager->QueryInterface(__uuidof(SCBLEditArticle::ICOMEditArticleManager), (void**)&pEditArticleManager);
}
I'd like to call this method from Delphi and it should return an interface to a created manager.
Delphi:
function CreateManager(bstrName: wideString; pEditArticleManager: ICOMEditArticleManager): HResult; stdcall; external 'SCBLEditArticle.dll';
procedure CreateManager;
var
hr:HResult;
mCOMEditArticleManager: ICOMEditArticleManager;
begin
hr := CreateManager('MANAGER1', mCOMEditArticleManager);
end;
The problem is I get an access violation when it reaches the end; in this delphi method.
Do you have any ideea what could be wrong?
Thx, rufusz
Edit:
But I'm using a macro for implementing Release and
EditArticleManagerFactory.h :
IMPLEMENT_UNKNOWN_NODELETE(EditArticleManagerFactory) BEGIN_INTERFACE_TABLE(EditArticleManagerFactory) IMPLEMENTS_INTERFACE(SCBLEditArticle::ICOMEditArticleManagerFactory) END_INTERFACE_TABLE()
Inttable.cpp:
define IMPLEMENT_UNKNOWN_NODELETE(ClassName) \
STDMETHODIMP QueryInterface(REFIID riid, void **ppv) \ { \ HRESULT hr = InterfaceTableQueryInterface(this, GetInterfaceTable##ClassName(), riid, ppv);\ __if_exists(InheritedQueryInterface##ClassName) { if ( FAILED(hr) ) hr = InheritedQueryInterface##ClassName(riid, ppv); } \ return hr; \ }\ STDMETHODIMP_(ULONG) AddRef(void) { return 2; } \ STDMETHODIMP_(ULONG) Release(void) { return 1; }
Furthermore :
When I debug from Delphi, I get the access violation in the UnsetExceptionHandler
004046F7 3901 cmp [ecx],eax.
Maybe this can help to diagnose the problem.
Also if I declared an external function outside my C++ class, and called that from Delphi, I didn't get the access violation, but didn't get the interface pointer neither.
Also:
If I do nothing in the C++ method, I still get the AccessViolation.
Should be
function CreateManager(bstrName: wideString; OUT pEditArticleManager: ICOMEditArticleManager): HResult; stdcall; external 'SCBLEditArticle.dll';
Notice the "OUT", you somehow dropped a indirection. (an interface is only one *, not two)
It's odd you try to declare STDMETHODIMP CreateManager within your class in C++, this may be causing trouble.
Also COM provides a standard way of having an object-factory (class-object), which responds to CoCreateInstance calls, you may want to have a look at that.
Most likely there's something wrong with the reference counting.
When the function exists, the reference count to all interfaces is decremented. The Delphi compiler automatically creates the necessary code to call ICOMEditArticleManager::Release.
Either its implementation is flawed or you are not returning a valid IUnknown interface.
You can try the following:
In VC++ set a breakpoint at the implementation of ICOMEditArticleManager::Release
In Delphi switch to the CPU mode and single-step through the disassembled code.
That way you should find the cause or at least narrow it down.