I created a local server (EXE) COM project with support for ConnectionPoint events.
idl pseudo:
interface IAtlClass : IDispatch{
[id(1)] HRESULT f1();
};
library ComExeLib
{
dispinterface _IAtlClassEvents
{
methods:
[id(1)] HRESULT f2();
};
[
uuid(...
]
coclass AtlClass
{
[default] interface IAtlClass;
[default, source] dispinterface _IAtlClassEvents;
};
};
Server is built in 32 bit configuration. Client code, built in 64 bit:
HRESULT hr = CoInitialize(NULL);
IAtlClass* atlClass;
hr = CoCreateInstance(CLSID_AtlClass, NULL,
CLSCTX_LOCAL_SERVER,
IID_IAtlClass,
reinterpret_cast<void**>(&atlClass));
hr = atlClass->f1();
IConnectionPointContainer* pICPC = NULL;
hr = atlClass->QueryInterface(IID_IConnectionPointContainer, (VOID **)&pICPC);
IConnectionPoint* pICP = NULL;
hr = pICPC->FindConnectionPoint(DIID__IAtlClassEvents, &pICP);
hr = pICP->Advise((_IAtlClassEvents*)&sink, &cookie);
Sink class:
class Sink : public _IAtlClassEvents
{
public:
HRESULT STDMETHODCALLTYPE QueryInterface(
/* [in] */ REFIID riid,
/* [iid_is][out] */ void __RPC_FAR *__RPC_FAR *ppvObject)
{
if ((DIID__IAtlClassEvents == riid))
{
*ppvObject = static_cast<_IAtlClassEvents*>(this);
AddRef();
return(S_OK);
}
*ppvObject = NULL;
return(E_NOINTERFACE);
}
ULONG STDMETHODCALLTYPE AddRef(void)
{
return(InterlockedIncrement(&m_iRef));
}
ULONG STDMETHODCALLTYPE Release(void)
{
if (0 == InterlockedDecrement(&m_iRef))
{
delete this;
return(0);
}
return(m_iRef);
}
HRESULT STDMETHODCALLTYPE GetTypeInfoCount()
HRESULT STDMETHODCALLTYPE GetTypeInfo()
HRESULT STDMETHODCALLTYPE GetIDsOfNames()
HRESULT STDMETHODCALLTYPE Invoke()
};
Registered the server with ComExe.exe /RegServer and the proxy with regsvr32 ComExePS.dll, under folder C:\windows\SysWOW\ and under c:\Windows\System32.
Not sure if necessary but tried to compile the proxy stub under 64 bit as well but it will not build since auto-generated ComExe_p.c contains an #if !defined(AMD64).. so the project can only be built in 32 bit (seems related to this post only I use VS2013).
All client code commands except the last (Advise) succeed (They succeed even without registering the ComExePS.dll) . Advise invokes the sink QueryInterface method (and none but it), five time with following iid's:
{00000003-0000-0000-C000-000000000046}
{ECC8691B-C1DB-4DC0-855E-65F6C551AF49}
{00000003-0000-0000-C000-000000000046}
{0000001B-0000-0000-C000-000000000046}
{IID_IUnknown}
None of which are related to IAtlClass (some standard MS iid's - this guy was getting something similar).
Eventually Advise returns E_UNEXPECTED Catastrophic failure and the question is what am I doing wrong?
Related
I made a Windows Service process that can be started/stopped/paused/continued.
The service is created with CreateService() and the service starts a service controller with RegisterServiceCtrlHandlerExA().
Even though the service can subscribe to power setting notifications using RegisterPowerSettingNotification() I find that these only represent events like battery/mains for laptops, and such. Not for suspend/sleep of the OS.
How can I tell the SCM to automatically pause my service before the OS suspends/sleeps? And continue my service after it wakes up again?
This requires calling the PowerRegisterSuspendResumeNotification() function.
For this, you need to #include <powrprof.h> and link against powrprof.lib.
The callback itself looks like:
static ULONG DeviceNotifyCallbackRoutine
(
PVOID Context,
ULONG Type, // PBT_APMSUSPEND, PBT_APMRESUMESUSPEND, or PBT_APMRESUMEAUTOMATIC
PVOID Setting // Unused
)
{
LOGI("DeviceNotifyCallbackRoutine");
if (Type == PBT_APMSUSPEND)
{
turboledz_pause_all_devices();
LOGI("Devices paused.");
}
if (Type == PBT_APMRESUMEAUTOMATIC)
{
turboledz_paused = 0;
LOGI("Device unpaused.");
}
return 0;
}
static DEVICE_NOTIFY_SUBSCRIBE_PARAMETERS notifycb =
{
DeviceNotifyCallbackRoutine,
NULL,
};
And then register it with:
HPOWERNOTIFY registration;
const DWORD registered = PowerRegisterSuspendResumeNotification
(
DEVICE_NOTIFY_CALLBACK,
¬ifycb,
®istration
);
if (registered != ERROR_SUCCESS)
{
const DWORD err = GetLastError();
LOGI("PowerRegisterSuspendResumeNotification failed with error 0x%lx", err);
}
Update 2021-04-20: The code presented here is for illustration purposes only. As pointed out by Simon Mourier, for marshaling in-process of such a simple class there is no need for all the TLB shenanigans. In reality, the TLB is provided by a third-party, with the interface in question serving for callbacks.
The object calling the interface resides in another process, however, so I really do have to marshal the interface after implementing it. As demonstrating the whole inter-process flow is tedious, I opted for something simpler - in-process inter-apartment marshaling.
Suppose I have the following type library:
import "oaidl.idl";
import "ocidl.idl";
[
uuid(99CF9EB9-9B6E-4D44-B73C-6BB8FCD45B82),
version(1.0),
]
library IsThisRealMarshal
{
[
uuid(80997EA1-0144-41EC-ABCF-5FAD08D5A498),
nonextensible,
]
dispinterface IMyInterface
{
properties:
methods:
[id(1)]
void Method();
};
};
I would like to marshal IMyInterface to another apartment. Since it's a dispinterface, I would like to use the OLE marshaler for this. And so, I register the type library:
Windows Registry Editor Version 5.00
[HKEY_CURRENT_USER\SOFTWARE\Classes\TypeLib\{99CF9EB9-9B6E-4D44-B73C-6BB8FCD45B82}]
[HKEY_CURRENT_USER\SOFTWARE\Classes\TypeLib\{99CF9EB9-9B6E-4D44-B73C-6BB8FCD45B82}\1.0]
[HKEY_CURRENT_USER\SOFTWARE\Classes\TypeLib\{99CF9EB9-9B6E-4D44-B73C-6BB8FCD45B82}\1.0\0]
[HKEY_CURRENT_USER\SOFTWARE\Classes\TypeLib\{99CF9EB9-9B6E-4D44-B73C-6BB8FCD45B82}\1.0\0\win32]
#="path\\to\\library.tlb"
And the interface (setting the proxy CLSID to that of the OLE marshaler):
Windows Registry Editor Version 5.00
[HKEY_CURRENT_USER\SOFTWARE\Classes\Interface\{80997EA1-0144-41EC-ABCF-5FAD08D5A498}]
[HKEY_CURRENT_USER\SOFTWARE\Classes\Interface\{80997EA1-0144-41EC-ABCF-5FAD08D5A498}\ProxyStubClsid32]
#="{00020424-0000-0000-C000-000000000046}"
[HKEY_CURRENT_USER\SOFTWARE\Classes\Interface\{80997EA1-0144-41EC-ABCF-5FAD08D5A498}\TypeLib]
#="{99CF9EB9-9B6E-4D44-B73C-6BB8FCD45B82}"
"Version"="1.0"
And I try to marshal (error-checking omitted for brevity):
CoInitializeEx(nullptr, COINIT_MULTITHREADED);
CComPtr<IMyInterface> object {};
object.Attach(new MyObject);
CComPtr<IGlobalInterfaceTable> git {};
git.CoCreateInstance(CLSID_StdGlobalInterfaceTable, nullptr, CLSCTX_INPROC_SERVER);
DWORD cookie = 0;
git->RegisterInterfaceInGlobal(object, __uuidof(IMyInterface), &cookie);
auto thread = std::thread([cookie]
{
CoInitializeEx(nullptr, COINIT_APARTMENTTHREADED);
CComPtr<IGlobalInterfaceTable> git {};
git.CoCreateInstance(CLSID_StdGlobalInterfaceTable, nullptr, CLSCTX_INPROC_SERVER);
CComPtr<IMyInterface> object {};
git->GetInterfaceFromGlobal(cookie, __uuidof(IMyInterface), (void **)&object);
});
thread.join();
Where the MyObject class implements the bare minimum COM functionality:
class MyObject : public IMyInterface
{
private:
std::atomic<ULONG> _refcount = 1;
public:
MyObject() = default;
MyObject(MyObject const &) = delete;
MyObject & operator=(MyObject const &) = delete;
HRESULT QueryInterface(const IID& riid, void** ppvObject) override
{
if (nullptr == ppvObject)
{
return E_POINTER;
}
if (riid == __uuidof(IUnknown))
{
*ppvObject = static_cast<IUnknown *>(this);
}
else if (riid == __uuidof(IDispatch))
{
*ppvObject = static_cast<IDispatch *>(this);
}
else if (riid == __uuidof(IMyInterface))
{
*ppvObject = static_cast<IMyInterface *>(this);
}
else
{
*ppvObject = nullptr;
return E_NOINTERFACE;
}
static_cast<IUnknown *>(*ppvObject)->AddRef();
return S_OK;
}
ULONG AddRef() override
{
return ++_refcount;
}
ULONG Release() override
{
auto const new_refcount = --_refcount;
if (0 == new_refcount)
{
delete this;
}
return new_refcount;
}
HRESULT GetTypeInfoCount(UINT* pctinfo) override
{
return E_NOTIMPL;
}
HRESULT GetTypeInfo(UINT iTInfo, LCID lcid, ITypeInfo** ppTInfo) override
{
return E_NOTIMPL;
}
HRESULT GetIDsOfNames(const IID& riid, LPOLESTR* rgszNames, UINT cNames, LCID lcid, DISPID* rgDispId) override
{
return E_NOTIMPL;
}
HRESULT Invoke(DISPID dispIdMember, const IID& riid, LCID lcid, WORD wFlags, DISPPARAMS* pDispParams,
VARIANT* pVarResult, EXCEPINFO* pExcepInfo, UINT* puArgErr) override
{
return E_NOTIMPL;
}
};
Unfortunately, the call to GetInterfaceFromGlobal fails with E_FAIL.
Debugging reveals that none of the IDispatch methods are called, only the IUnknown ones. Additionally, it appears that the E_FAIL originates from combase!CheckTypeInfo. First, this function uses ITypeInfo::GetTypeAttr to retrieve information about IMyInterface:
It then proceeds to check whether the flags TYPEFLAG_FDUAL (0x40) or TYPEFLAG_FOLEAUTOMATION (0x100) are present in the wTypeFlags field of the TYPEATTR structure:
Since neither of these flags are present (the field has the value 0x1080, and indeed the IDL doesn't mark the interface as either [oleautomation] or [dual]), the function fails with E_FAIL.
What am I doing wrong? And if the OLE marshaler indeed cannot marshal this interface, is there anything I can do apart from implementing IMarshal myself, assuming I cannot modify the IDL?
With the help of Simon Mourier's code, I managed to find the problem. The problem was that I used the PSOAInterface proxy ({00020424-0000-0000-C000-000000000046}). Since IMyInterface is not an OLE Automation interface (i.e. not marked with [oleautomation]), this rightly failed.
The solution is to use the PSDispatch proxy ({00020420-0000-0000-C000-000000000046}), which is capable of marshaling pure IDispatch interfaces.
I am implementing a COM component
import "unknwn.idl";
[
uuid(CF86E2E0-B12D-4C6A-9C5A-D7AA65101E91),
]
coclass CClassic
{
[default] interface IClassic;
}
[
object,
uuid(CF86E2E0-B12D-4C6A-9C5A-D7AA65101E90),
pointer_default(unique),
oleautomation
]
interface IClassic : IUnknown
{
HRESULT Hello();
}
and encapsulated it in an in-process dll and an out-of-process exe respectively.
In the dll case , I can call the interface method successfully.
But in the exe case, got the 80040154 error. Following this doc, I registered the proxy and checked the registry, all the needed registry keys/values are there.
How does this happen? I doubt something is missing in the above idl file. Many thanks!!!
Update
comexe.cpp
#include <atlbase.h>
#include <atlcom.h>
/*
cl /nologo /EHsc comexe.cpp comobj.cpp classic/classic_i.c /link /debug
cl /nologo /EHsc /DREGISTER_PROXY_DLL classic/classic_p.c classic/classic_i.c classic/dlldata.c /link /dll /out:comproxy.dll /export:DllGetClassObject /export:DllCanUnloadNow /export:DllRegisterServer /export:DllUnregisterServer /export:GetProxyDllInfo rpcrt4.lib
*/
struct MyModule : CAtlExeModuleT<MyModule>
{
HRESULT RegisterClassObjects(
_In_ DWORD dwClsContext,
_In_ DWORD dwFlags)
{
auto hr = CAtlExeModuleT::RegisterClassObjects(dwClsContext,dwFlags);
printf("RegisterClassObjects 0x%x\n",hr);
return hr;
}
HRESULT RegisterServer(
_In_ BOOL bRegTypeLib = FALSE,
_In_opt_ const CLSID* pCLSID = NULL)
{
auto hr = CAtlExeModuleT::RegisterServer(false, pCLSID);
if(FAILED(hr))
printf("Toaster register failed 0x%x\n",hr);
return hr;
}
};
MyModule mod;
int main(int argc, char *args[])
{
mod.WinMain(SW_HIDE);
}
comobj.cpp
#include <atlbase.h>
#include <atlcom.h>
#include <typeinfo>
//#include "comobj.h"
#include "classic/classic.h"
CComModule _Module;
extern "C" CLSID CLSID_CClassic;
struct Toast : CComObjectRoot,CComCoClass<Toast,&CLSID_CClassic>,IClassic
{
DECLARE_REGISTRY(CLSID_CClassic,"Toaster.1.0","Toaster","Toast Server",THREADFLAGS_BOTH);
DECLARE_CLASSFACTORY();
DECLARE_OBJECT_DESCRIPTION("Toast Server");
HRESULT STDMETHODCALLTYPE Hello( void)
{
printf("Hello\n");
return S_OK;
}
Toast()
{
printf("Toast alive\n");
printf("%s\n",typeid(_ClassFactoryCreatorClass).name());
}
BEGIN_COM_MAP(Toast)
COM_INTERFACE_ENTRY(IClassic)
END_COM_MAP();
};
OBJECT_ENTRY_AUTO(CLSID_CClassic,Toast);
I have a large solution with 50+ unmanaged projects in it. I have recently added a project with managed code in it to the solution. The managed code accesses Windows.Devices.Sensors in a .NET dll. This dll is eventually wrapped by unmanaged code and called from another unmanaged project.
My problem is that I get the following access violation before main() even executes.
Unhandled exception at 0x744b8ea0 in myApplication.exe: 0xC0000005: Access violation.
Managed code:
#using <Windows.winmd>
using namespace Windows::Devices::Sensors;
#include <math.h>
namespace TabletSensors
{
namespace NET
{
public ref class DotNetDllClass
{
public:
DotNetDllClass()
{
Initialization();
}
~DotNetDllClass()
{
}
float* GetQuaternion()
{
OrientationSensorReading^ reading = _orientation->GetCurrentReading();
if( reading != nullptr )
{
float* quat = new float[4];
quat[0] = reading->Quaternion->X;
quat[1] = reading->Quaternion->Y;
quat[2] = reading->Quaternion->Z;
quat[3] = reading->Quaternion->W;
return quat;
}
else
{
return NULL;
}
}
private:
void Initialization()
{
_orientation = OrientationSensor::GetDefault();
if( _orientation != nullptr )
{
_orientation->ReportInterval = 16;
}
else
{
// not good ... throw exception or something
}
}
OrientationSensor^ _orientation;
};
}
}
Wrapper header file:
namespace TabletSensors
{
namespace NETWrapper
{
class DLLEXPORT_SENSORS WrapperClass
{
public:
__stdcall WrapperClass();
__stdcall ~WrapperClass();
float* __stdcall GetQuaternion();
};
}
}
Wrapper cpp file:
#define MIXSENSORS_BUILD
#include <gcroot.h>
#include "DotNetWrapper.h"
#include "DotNetDll.h"
using namespace TabletSensors::NETWrapper;
using namespace TabletSensors::NET;
static gcroot<TabletSensors::NET::DotNetDllClass^> Sensors = nullptr;
static System::UInt16 refCount = 0;
#pragma managed
inline TabletSensors::NET::DotNetDllClass^ GetSensors(void)
{
return (TabletSensors::NET::DotNetDllClass^)Sensors;
}
void Init()
{
++refCount;
if(GetSensors() == nullptr)
{
Sensors = gcnew TabletSensors::NET::DotNetDllClass();
}
}
void CleanUp()
{
if( refCount > 0 )
{
--refCount;
}
}
float* GetQuaternion_()
{
return Sensors->GetQuaternion();
}
#pragma unmanaged
TabletSensors::NETWrapper::WrapperClass::WrapperClass()
{
Init();
}
TabletSensors::NETWrapper::WrapperClass::~WrapperClass()
{
CleanUp();
}
float* TabletSensors::NETWrapper::WrapperClass::GetQuaternion()
{
float* x = new float[4];
return GetQuaternion_();
}
#pragma managed
Unmanaged project referencing my wrapper class:
#include "DotNetWrapper.h"
.
.
.
void UnmanagedProject::Update()
{
// if this line is present, I get an access violation without hitting any breakpoints.
TabletSensors::NETWrapper::WrapperClass _tabletSensors;
.
.
.
}
Since the managed code is trying to access Tablet Sensors I understand why it doesn't work on my Windows 7 desktop. What I don't understand it why it won't even allow me to debug my code at all. No breakpoints are hit before the Access Violation occurs.
What I would really like to figure out is how to use exception handling or #ifdefs to keep this crash from happening. But I have had very little luck.
Any ideas?
The fix is to Delay Load the managed DLL. The allows the application to run until that DLL is explicitly called. Thanks to Ben Voight for his answer here: https://stackoverflow.com/a/28467701/1454861
I have a 3rd party COM object that I'm working with. Mostly fine, but I'm stuck on reading a GUID property from the object.
The relevant part of the auto-generated component wrappers/headers looks like this:
// *********************************************************************//
// DispIntf: IFoo
// Flags: (4416) Dual OleAutomation Dispatchable
// GUID: {5DE5DAAF-5419-4B2B-9771-58EAEE780799}
// *********************************************************************//
template<class T>
class IFooDispT : public TAutoDriver<IFoo>
{
...
BSTR __fastcall get_FileName(void);
HRESULT __fastcall get_ProjectGUID(/*AUTO_PARAM_ERROR(System::TGUID* Value)*/ VARIANT* Value);
HRESULT __fastcall get_ProjectName(BSTR* Value/*[out,retval]*/);
__property BSTR FileName = {read = get_FileName};
__property BSTR ProjectName = {read = get_ProjectName};
Note how the ProjectGUID property is marked AUTO_PARAM_ERROR, and it doesn't appear in list of properties.
I've tried to read it directly via get_ProjectGUID() but it always returns HRESULT = 0x80070057 (E_INVALID_ARGS).
The IDL of the dispinterface from OleView looks like this:-
[
uuid(5DE5DAAF-5419-4B2B-9771-58EAEE780799),
version(1.0),
helpstring("Dispatch interface for xpCOMFoo Object"),
dual
]
dispinterface IFoo {
properties:
methods:
<...snipped...>
[id(0x000000cf), propget]
BSTR FileName();
[id(0x000000d0), propget]
GUID ProjectGUID();
[id(0x000000d1), propget]
BSTR ProjectName();
};
I've tested the same object from Delphi (although, not using the late bindings shown above) and I'm happy that the COM object itself is not at fault.
Try calling it like this:
TAutoDriver<IFoo> foo;
// ...
GUID guid;
memset(&guid, 0, sizeof(guid));
// use -> to access the raw dual interface
HRESULT hr = foo->get_ProjectGUID(&guid);