How to share COM objects between 2 processes? - c++

I want Application1.exe to instantiate an instance of its Item class.
I want Application2.exe to call GetPrice() on this object.
I have followed steps 1-7 on the following website:
http://www.codeguru.com/Cpp/COM-Tech/activex/tutorials/article.php/c5567/
This is what I have so far.
Application1's main looks like this:
CoInitialize( NULL );
DWORD dwRegister;
ItemFactory *pFactory = new ItemFactory;
CoRegisterClassObject( CLSID_Item, pFactory, CLSCTX_LOCAL_SERVER, REGCLS_MULTIPLEUSE, &dwRegister );
_getch();
return 0;
Application2's main looks like this:
CoInitialize( NULL );
CoGetClassObject( CLSID_Item, CLSCTX_LOCAL_SERVER, NULL, IID_IItem, (LPVOID *)&pFactory );
My Issue (hopefully my only issue) is that I have no idea how to associate my Item class (or its interface, IItem) with CLSID_Item; this is just some random GUID I defined in another file. I've tried
CoRegisterPSClsid( IID_IItem, CLSID_Item );
After this line, I tried
Item *pItem;
CoCreateInstance( CLSID_Item, NULL, CLSCTX_LOCAL_SERVER, IID_IItem, (LPVOID *)&pItem );
I get an E_NOINTERFACE error.
Should I be creating a factory with CoCreateInstance? Ugh, so confused...

In order to use COM across process or thread boundraries, you must tell COM about your interfaces so it can marshal your function arguments/return values between processes. The easiest way to do this is to use an interface predefined in the system, such as IDispatch, but if you want to use your own, you must either register a proxy/stub DLL, or a type library. If you don't do this, then calls to QueryInterface for your custom interface across COM domains will fail with E_NOINTERFACE, as you are seeing.

Related

Marshalling D3D11Device and D3D11DeviceContext objects

This article says:
The ID3D11DeviceContext methods (except for those that exist on
ID3D11DeviceChild) are not free-threaded, that is, they require single
threading. Only one thread may safely be calling any of its methods
(Draw, Copy, Map, etc.) at a time.
I was wondering if I can make COM do the ID3D11DeviceContext synchronization for me.
Let's say I do this (in simplified code):
CoInitializeEx( NULL, COINIT_APARTMENTTHREADED );
// Create D3D11Device
CComPtr<ID3D11DeviceContext> pD3D11DeviceContext;
pD3D11Device->GetImmediateContext(&pD3D11DeviceContext);
Then I either marshall it:
IStream* pStreamD3D11DeviceContext = NULL;
CComPtr<IUnknown> pUnknownD3D11DeviceContext;
pD3D11DeviceContext->QueryInterface(IID_PPV_ARGS(&pUnknownD3D11DeviceContext));
::CoMarshalInterThreadInterfaceInStream( __uuidof(ID3D11DeviceContext), pUnknownD3D11DeviceContext, &pStreamD3D11DeviceContext );
Or us a GIT table:
CComPtr<IGlobalInterfaceTable> pIGlobalInterfaceTable;
::CoCreateInstance( CLSID_StdGlobalInterfaceTable, NULL, CLSCTX_INPROC_SERVER, IID_IGlobalInterfaceTable, (void **)&pIGlobalInterfaceTable );
CComPtr<IUnknown> pUnknownD3D11DeviceContext;
DWORD dwCookieD3D11DeviceContext = 0;
pD3D11DeviceContext->QueryInterface(IID_PPV_ARGS(&pUnknownD3D11DeviceContext));
pIGlobalInterfaceTable->RegisterInterfaceInGlobal( pUnknownD3D11DeviceContext, __uuidof(ID3D11DeviceContext), &dwCookieD3D11DeviceContext );
Unfortunately this doesn't seem to work.
CoMarshalInterThreadInterfaceInStream returns REGDB_E_IIDNOTREG (0x80040155, Interface not registered) and pStreamD3D11DeviceContext remains NULL.
GIT method goes one step further. I get the cookie, but when I try to use it on another MTA thread, the GetInterfaceFromGlobal returns E_INVALIDARG.
CComPtr<ID3D11DeviceContext> pD3D11DeviceContext;
hresult = pIGlobalInterfaceTable->GetInterfaceFromGlobal( dwCookieD3D11DeviceContext, __uuidof(ID3D11DeviceContext), (void**)&pD3D11DeviceContext );
The params for GetInterfaceFromGlobal seem okay, I tested getting the pointer back on the original thread and it worked.
The D3D11Device and D3D11DeviceContext appear to be unmarshallable. Obviously I have neither proxy DLL nor a typelib for the d3d11.
Am I missing anything?
Thank you.

How to dynamically linking sapi.dll and making it work?

I would like to dynamically bind into windows sapi.dll
and then call some code needed to 'speak' some text
on windows - some code like this below
(this snippet is copied from gamedev.net article by desmond lang)
I have no experience in using dll's especialy com. Could someone
tell me how this code should look like to work for my? much tnx
// The voice interface pointer
IspVoice* Voice = NULL;
// Initialize COM
CoInitialize ( NULL );
// Create the voice instance
CoCreateInstance ( CLSID_SpVoice, NULL, CLSCTX_ALL, IID_ISpVoice, (void**)&Voice );
// Our text to be spoken
WCHAR* TextBuffer = "Hello World";
// Use our voice interface to speak the contents of the buffer
Voice -> Speak ( TextBuffer, SPF_DEFAULT, NULL );

Visual C++: InvokeHelper() function

I am deciphering a huge project that uses COM, which I am completely new to. It's quite confusing and I can't figure out how everything interacts. All I see is InvokeHelper(...) where I would expect to see a big amount of code. What is InvokeHelper()? What does it do?
Thank you for any help.
Even though it's a late answer, I'd like to post it here as I have spent a couple of days to figure out how it's working. It may be interesting for someone else.
Below is the path how to get to the real code from InvokeHelper() call:
InvokeHelper() should be called for an object of a class, inherited from CWnd with DISPID specified, where DISPID is something like 0x00000261
The class should have inside a call to a method CreateControl() with a GUID of a COM class
The COM class with the GUID should be COM coclass with at least one IDL interface
The IDL interface should implement a method with the attribute [id(DISPID)]. This is the same DISPID as in item 1
Look for implementation of the interface and find the method with this id attribute
VoilĂ !
Sure, if you don't have a source code of the COM class with the CLSID you cannot take a look inside the method, but at least, you can find its name as follows:
DISPID dispidCommand = 0x1; /// This is the dispid, you're looking for
COleDispatchDriver driver;
BOOL bRes = driver.CreateDispatch(GetClsid());
ASSERT(bRes);
HRESULT hr;
CComPtr<ITypeInfo> pti;
hr = driver.m_lpDispatch->GetTypeInfo(0, GetUserDefaultLCID(), &pti);
ASSERT(SUCCEEDED(hr));
UINT nCount = 0;
CComBSTR bstrName; // Name of the method, which is called via DISPID
hr = pti->GetNames(dispidCommand, &bstrName, 1, &nCount);
ASSERT(SUCCEEDED(hr));
I hope it helps someone.
Take care.

#import lead to a HRESULT 0x80040154 "Not registered class"

I'm trying to use a COM DLL from VC++ 2005. I created a TestCOMlib.dll with ATL, created a simple interface ISimple and added one property (type LONG, name Property01) and one method (name Method01).
The DLL seems to be correctly registered in the system (I use OleView to check the entries).
I created a simple MFC dialog app to use the COM dll. I'm using the #import directive to incorporate information from the type library. Visual studio created for me the tlh and tli file.
Then I tried to obtain the ISimple interface but I'm obtaining the error 0x80040154.
The code I'm running inside the test application is the following:
HRESULT hr = S_OK;
hr = CoInitialize(NULL);
ISimplePtr myRef(__uuidof(ISimple));
// Test prop and method
myRef->Property01 = 5;
LONG test = myRef->Property01;
LONG ret = myRef->Method01(_T("Test input"));
ret = myRef->Method01(NULL);
myRef = NULL;
CoUninitialize();
The row returning the error 0x80040154 is ISimplePtr myRef(__uuidof(ISimple)).
OleView correctly display the interface and in the registry the entries seems to be good.
What am I doing wrong? Any idea?
Regards
The underlying class for these smart COM pointers is _com_ptr_t. You are trying to use this constructor:
// Constructs a smart pointer given the CLSID of a coclass. This
// function calls CoCreateInstance, by the member function
// CreateInstance, to create a new COM object and then queries for
// this smart pointer's interface type. If QueryInterface fails with
// an E_NOINTERFACE error, a NULL smart pointer is constructed.
explicit _com_ptr_t(
const CLSID& clsid,
IUnknown* pOuter = NULL,
DWORD dwClsContext = CLSCTX_ALL
);
Key point is that you have to pass the CLSID of the coclass, you are passing the IID of the interface. That's why __uuidof(Simple) works.

How to prevent crashing if com dll isnt registered

From some old c++ code im trying to use a com dll, it works fine when the dll is registered, but it crahses if the dll isnt registered.
// Initialize COM.
HRESULT hr = CoInitialize(NULL);
IGetTestPtr ptest(__uuidof(tester));
"Use method from the dll"
// Uninitialize COM.
CoUninitialize();
Is it anyway to check if the dll has been registered, before calling IGetTestPtr ptest(__uuidof(tester))?
Or what is the correct way to prevent the crash?
Calling CreateInstance on your object will return an HRESULT that can be tested for success:
IGetTestPtr p = null;
HRESULT hRes = p.CreateInstance( __uuidof(tester) );
bool bSuccess = SUCCEEDED(hRes);
This assumes you've created an interface wrapper around your type library using Visual Studio, where COM Smart Pointers are used in the interface (this gives you the CreateInstance method).
If the DLL is registered, there is a record in HKCR/CLSID/{uuidof(tester)} (curly brackets do matter).
Actually, if it's not, then CoCreateInstance will return an error. Check for this error before using the pointer.
If the COM class is not registered then CoCreateInstance will return REGDB_E_CLASSNOTREG. You should check for general success or failure by using the SUCCEEDED() or FAILED() macros. You also need to create the object properly - see MDSN for an introduction to COM. For example:
HRESULT hr = CoInitialize(NULL);
if (SUCCEEDED(hr))
{
IGetTestPtr ptr = NULL;
hr = CoCreateInstance(CLSID_MyObjectNULL, CLSCTX_INPROC_SERVER, IID_IMyInterface, ptr)
if (SUCCEEDED(hr))
{
Do something with your COM object
...
// Don't forget to release your interface pointer or the object will leak
ptr->Release();
}
hr = CoUninitialize();
}
return hr;
If I recall correctly this "#import" styled COM framework throws exceptions. Add a try-catch block around the code. Google tells me the exceptions are of type _com_error.
Unless you twiddle the params on #import to prevent this, all failing COM calls are going to throw exceptions. You're going to have to turn this off (#import "raw" interfaces I think does it - look at the #import docs) or get real familiar with these exceptions.