I have code running within a COM exe process. Specifically, I have an addin that operates within any Office product.
My code has an IDispatch* that is explicitly given to it. Specifically, I am given an IDispatch* through my implementation of _IDTExtensibility2::OnConnection.
The IDispatch* I receive is an interface to a specific coclass that is not the coclass which is marked with the [appobject] IDL attribute. Specifically, the interface is to a the coclass called Application (same name in all Office products), but the coclass marked [appobject] within any Office product typelib is called Global.
From within this process, is there a way to get the singleton instance of the the [appobject] coclass? Here's what I have tried so far:
STDMETHODIMP Addin::OnConnection(LPDISPATCH application, ext_ConnectMode connectMode, LPDISPATCH addInInst, SAFEARRAY** custom)
{
// get type info of Application object
ITypeInfoPtr typeInfoPtr;
application->GetTypeInfo(0, LOCALE_USER_DEFAULT, &typeInfoPtr);
// get type lib of Office product
ITypeLibPtr typeLibPtr;
UINT typeInfoIndex;
typeInfoPtr->GetContainingTypeLib(&typeLibPtr, &typeInfoIndex);
...
}
So I can get the ITypeInfo* and ITypeLib* for the Application class and its containing type library. I know I can use ITypeComp* to bind to global functions/variables by using the ITypeComp* returned from ITypeLib::GetTypeComp. Something like this:
// get type comp for the typelib
ITypeCompPtr typeCompPtr;
typeLibPtr->GetTypeComp(&typeCompPtr);
// bind to global property-get function "Application"
ITypeInfoPtr boundTypeInfoPtr;
DESCKIND descKind;
BINDPTR bindPtr;
ULONG hash = LHashValOfNameSys(SYS_WIN32, LOCALE_USER_DEFAULT, L"Application");
typeCompPtr->Bind(L"Application", hash, INVOKE_PROPERTYGET, &boundTypeInfoPtr, &descKind, &bindPtr);
...
However, this is for late-binding these function/variable calls. I actually want to perform early binding -- I have the necessary DISPIDs handy. What I don't have is a IDispatch* to the singleton instance of the [appobject] class.
Alternatively, I can scan the type lib for the coclass that has the [appobject] attribute. However, once I get the ITypeInfo* for that coclass, I don't know what to do with it. If I call CreateInstance on it, a new process is started as per usual COM activation rules.
How can I get an IDispatch* to this [appobject] instance?
Related
Problem Description
In the following function, we have an IUnkown object which is querying another interface.
hr = pI->QueryInterface(IID_IActiveIMMApp, (void**)&pIApp);
I don't understand how can pI query IActiveIMMMessagePumpOwner. How can we know that there is a handle from the Com Object to IActiveIMMMessagePumpOwner. How can the IID_IActiveIMMApp used in here tell us that yes there is a handle to the IActiveIMMMessagePumpOwner and we would like to get the a pointer to the pointer pointing to the interface?
If CLSID_CActiveIMM is the reason for knowing that the COM objects handles the interface. then why can't add as much interfaces Ids as we want in CoCreateInstance.
Source Code
global_ime_init(ATOM atom, HWND hWnd)
{
IUnknown *pI;
HRESULT hr;
if (pIApp != NULL || pIMsg != NULL)
return;
OleInitialize(NULL);
/*
* Get interface IUnknown
*/
hr = CoCreateInstance(CLSID_CActiveIMM, NULL, CLSCTX_SERVER,
IID_IUnknown, (void**)&pI);
if (FAILED(hr) || !pI)
return;
/*
* Get interface IActiveIMMApp
*/
hr = pI->QueryInterface(IID_IActiveIMMApp, (void**)&pIApp);
if (FAILED(hr))
pIApp = NULL;
/*
* Get interface IActiveIMMMessagePumpOwner
*/
hr = pI->QueryInterface(IID_IActiveIMMMessagePumpOwner, (void**)&pIMsg);
if (FAILED(hr))
pIMsg = NULL;
if (pIApp != NULL)
{
pIApp->Activate(TRUE);
pIApp->FilterClientWindows(&atom, 1);
}
if (pIMsg != NULL)
pIMsg->Start();
pI->Release();
s_hWnd = hWnd;
}
P.S. I started learning the COM Model 1 hour ago, so please bear with me.
When the IUnknown::QueryInterface is executed, you pass the execution to the actual object and so if it recognizes the arguments and it understands the IID_IActiveIMMApp you passed, it supplies you with the pointer you ask for.
This is described in every COM tutorial in the QueryInterface explanation, for example, here:
QueryInterface is the mechanism that allows clients to dynamically discover (at run time) whether or not an interface is supported by a COM component; at the same time, it is the mechanism that a client uses to get an interface pointer from a COM component. When an application wants to use some function of a COM component, it calls that object's QueryInterface, requesting a pointer to the interface that implements the desired function. If the COM component supports that interface, it will return the appropriate interface pointer and a success code. If the COM component doesn't support the requested interface, it will return an error value. The application will then examine the return code; if successful, it will use the interface pointer to access the desired method. If the QueryInterface failed, the application will take some other action, letting the user know that the desired method is not available.
The following example shows a call to QueryInterface on the PhoneBook component. We are asking this component, "Do you support the ILookup interface?" If the call returns successfully, we know that the COM component supports the ILookup interface and we have a pointer to use to call methods contained in the ILookup interface (either LookupByName or LookupByNumber). If not, we know that the PhoneBook COM component does not implement the ILookup interface.
LPLOOKUP *pLookup;
TCHAR szNumber[64];
HRESULT hRes;
// Call QueryInterface on the COM Component PhoneBook, asking for a pointer
// to the Ilookup interface identified by a unique interface ID.
hRes = pPhoneBook->QueryInterface( IID_ILOOKUP, &pLookup);
if( SUCCEEDED( hRes ) )
{
pLookup->LookupByName("Daffy Duck", &szNumber); // Use Ilookup interface pointer.
pLookup->Release(); // Finished using the IPhoneBook interface pointer.
}
else
{
// Failed to acquire Ilookup interface pointer.
}
[...]
A COM component implements IUnknown to control its lifespan and to provide access to the interfaces it supports. A COM component does not provide direct access to its data. GUIDs provide a unique identifier for each class and interface, thereby preventing naming conflicts. And finally, the COM Library is implemented as part of the operating system, and provides the "legwork" associated with finding and launching COM components.
By holding any COM interface you are controlling its life time just by having a pointer, and the COM object you talk to is essentially a "black box". You "see" a "view" of its functionality by having methods of this particular COM interface available for calling. With QueryInterface you request another "view" with a different set of methods associated with the interface identifier. If you are given this new pointer and it's recognized by the COM object you talk to, those new methods are also available for calling.
I am quite inexperienced with COM objects, following is what I did:
In C++, I created a class which extend IUnknow and implements QueryInterface, AddRef and Release.
Extend IShellIconOverlayIdentifier and implements the 3 methods inside.
Export the class with __declspec(dllexport)
The DLL is compiled with MSVC2015 64 bits. (working on a windows7 64bits).
Now comes the difficult part:
How to make windows to link/use the DLL:
https://msdn.microsoft.com/en-us/library/windows/desktop/hh127455(v=vs.85).aspx Explain how to register the Overlay handler, but not how to register the COM object. I made a few tries, but after windows restart, nothing happens.
How to register and how to check if registered a COM dll?
Is there compilation restrictions like compiler/architecture?
EDITED Simplified:
It seem that I had a very incomplete vision of COM implementation, and in order to register the COM object, 2 directions are possible:
Register manually the object in the register, which is a nightmare.
Implement DllRegisterServer and DllUnregisterServer: which is a nightmare also.
To simplify the problem, I removed everything else up to having only those 2 functions:
STDAPI DllRegisterServer(void)
{
return NOERROR;
}
STDAPI DllUnregisterServer(void)
{
return NOERROR;
}
Unfortunately, this is not exported to the DLL, (as no __declspec(dllexport) is specified). As soon as I try to add this declaration, the compiler complain and no DLL is generated.
SOLUTION STEP 1:
Olectl.h is very very bad and avoid DllRegisterServer to be exported.
In order to create a COM object manually (without wizard), following steps are required:
Implement DllGetClassObject & DllCanUnloadNow
Implement Factory extending IClassFactory
Implement the Target class, extending IUknown
Register the COM in the registry
[optional] implement DllRegisterServer & DllUnregisterServer to make registry easier.
Implements DllGetClassObject & DllCanUnloadNow
Those are the only exported to the DLL functions:
STDAPI DllGetClassObject(_In_ REFCLSID rclsid, _In_ REFIID riid, _Outptr_ LPVOID * ppObject)
STDAPI DllCanUnloadNow(void)
DllGetClassObject, if the provided rclsid is the TargetClassCLSID and riid is the `FactoryCLSID, it creates and return a Factory object.
DllCanUnloadNow return if the DLL is in use (S_OK) or not, in which case the user/client may unload the DLL (S_FALSE).
The problem is, the definition do not allows to add __declspec(dllexport) due probably to some Microsoft monopol directive to make VS "mandatory".
To export them, just add before
#pragma comment( linker, "/export:DllGetClassObject,PRIVATE" )
#pragma comment( linker, "/export:DllCanUnloadNow,PRIVATE" )
//TODO: make this cross-compiler compatible?
Implementing the Factory
Implements AddRef and Release: they maintain how many client/users are currently using this Factory object. AddRef increment it by 1, Release decrements it by 1. As soon as the number is 0, Releasemay auto delete itself.
Implements QueryInterface which return if riidis the FactoryCLSID.
Implements CreateInstance, which return an instance of the TargetObject if riid match TargetClassCLSID
Implementing the Target Class
Again, we have to implements AddRef, Release and QueryInterface, but for the final class this time.
In my case, this Target class extends IShellIconOverlayIdentifier.
Register the COM object into the OS registry
After generating the dll, several registry need to be set. Some of them are linked together, so adding one may automatically add another.
HKEY_CLASS_ROOT::CLSID::<Add Here>
HKEY_CLASS_ROOT::Wow6432Node::CLSID::<Add Here>
HKEY_LOCAL_MACHINE::SOFTWARE::Classes::CLSID::<Add Here>
HKEY_LOCAL_MACHINE::SOFTWARE::Classes::Wow6432Node::CLSID::<Add Here>
HKEY_LOCAL_MACHINE::SOFTWARE::Wow6432Node::Classes::CLSID::<Add Here>
Content to add is:
Create a key: name={TargetClassCLSID}, value=<Any app name>
Create a sub-key: name=InProcServer32, value=<link to your dll>
Create a sub-string of InProcServer32: name=ThreadingModel, value=Apartment
//TODO: probably there are other alternatives, to better understand what that mean.
In my case, I had to register some more registers for the IconHandler:
HKEY_LOCAL_MACHINE::SOFTWARE::Microsoft::Windows::CurrentVersion::Explorer::ShellIconOverlayIdentifier::<Overlay here>
HKEY_LOCAL_MACHINE::SOFTWARE::Microsoft::Windows::CurrentVersion::ShellExtensions::Aproved::<new entry here>
Where the overlay entry is a key: name=<Any app name>, value=<TargetClassCLSID>
The new entry is a string value which name=<TargetClassCLSID>and value=Any app name
Those values need also to be in:
HKEY_LOCAL_MACHINE::SOFTWARE::Wow6432Node::Microsoft::Windows....
I'm trying to create out-of-proc com server in some exe file and a client, which will access to functions through proxy\stub mechanism.
I have my .idl file:
[
object,
uuid(eaa27f4f-ad6b-4a52-90f3-6028507751a1),
dual,
nonextensible,
helpstring("IConfig Interface"),
pointer_default(unique)
]
interface IInterractionInterface : IDispatch
{
[id(1), helpstring("Testing function")] HRESULT Test([in] long param);
};
[
uuid(6fde5037-3034-4ae1-8aa7-2ad45e5716e4),
version(1.0),
helpstring("Some lib")
]
library SomeLib
{
importlib("stdole32.tlb");
importlib("stdole2.tlb");
[
uuid(86feabe4-a0a7-45b5-bcd4-f4f7085d6b1f),
helpstring("Some lib")
]
coclass Interraction
{
[default] interface IInterractionInterface;
};
}
I am generated using midl compiler _p.c, _i.c files, created proxy\stub dll using .def:
LIBRARY proxy_stub.dll
DESCRIPTION 'generic proxy/stub DLL'
EXPORTS DllGetClassObject #1 PRIVATE
DllCanUnloadNow #2 PRIVATE
DllRegisterServer #4 PRIVATE
DllUnregisterServer #5 PRIVATE
Then i am registered this dll using regsrv32, and in win registry i have this:
In my server i am created factory:
CoRegisterClassObject(CLSID_InterractionInterfaceFactory, (IClassFactory*) &factory, CLSCTX_LOCAL_SERVER, REGCLS_MULTIPLEUSE, ®ID);
And it waits for client calls.
In client i am calling my factory using CreateInstance:
result = CoGetClassObject(CLSID_InterractionInterfaceFactory, CLSCTX_LOCAL_SERVER, NULL, IID_IClassFactory, (void**)&factory);
if (S_OK == result)
{
IInterractionInterface* iface = NULL;
result = factory->CreateInstance(NULL, IID_InterractionInterface, (void**)&iface);
if (S_OK == result)
{
}
}
And client receives null iface and result is E_UNEXPECTED, but in factory it creates successfuly and i am returning S_OK from Factory::CreateInstance().
I can't understand is PS mechanism using my .dll or not? Maybe i forgot some steps? Why my object can't pass through process borders?
Edit:
I've tried to replace the client code and now it is:
result = CoCreateInstance(CLSID_InterractionInterfaceFactory, NULL, CLSCTX_LOCAL_SERVER, IID_InterractionInterface, (void**)&iface);
iface->Test(1);
And when i am trying to call Test(1) it throws an error, that this is pure virtual function. And in factory in CreateInstance i am receiving requirement of Unkonown interface.
If you want to use proxy/stub DLL, define the interface outside of the library block. Only stuff defined outside of library goes into the code generated for proxy/stub DLL. Stuff defined, or referenced, inside library block goes into the generated type library. A typical IDL file defines interfaces outside of library, then mentions them inside, in coclass blocks; this way interface definitions end up in both proxy/stub and TLB, for maximum flexibility.
Your interface is automation compatible (well, almost; change the parameter type from int to long). In this case, you may prefer to use so called "universal" marshalling, based on a type library. Simply register the MIDL-generated TLB file with regtlib tool or programmatically with LoadTypeLibEx, and you would have marshaling support in place.
I have a com dll that gets used in a c++ project. I have not registered the dll but its in the same solution. My application continuously breaks and I found that it breaks here
hr = CoCreateInstance(rclsid, pOuter, dwClsContext, __uuidof(IUnknown), reinterpret_cast<void**>(&pIUnknown));
because I don't have a com class with that guid (rclsid). I did more digging and I found that it gets the guid from a .tlh file in a temp directory but the guid differs from my code.
.tlh file
struct __declspec(uuid("b10a18c8-7109-3f48-94d9-e48b526fc928"))
DocMapEntry;
// [ default ] interface _DocMapEntry
// interface _Object
// interface IDocMapEntry
c# code (com visable dll)
[ComVisible(true)]
[Guid("E98676B9-1965-4248-A9B6-74EC5EE5385A")]
[InterfaceType(ComInterfaceType.InterfaceIsDual)]
public interface IDocMapEntry
{
int Offset { get; set; }
int PageOffset { get; set; }
int PageNumber { get; set; }
}
I tried changing the value to the correct value but it continuously gets changed and visual studio asks me to reload.
When I look at the class using com explorer I get a different clsid, and when I try to unregister it, it completes successfully but the item is still there and the file location is mscoree.
my c++ application uses the guid in the .tlh file but doesn't find the file.
public class DocMapEntry : IDocMapEntry
{
...
}
My questions are.
Do I need to register my com dll for this to work?
how can I set the guid for the class?
Why would it create a temp .tlh file with the wrong guid? (optional)
why can't I change this file? (optional)
Thank you.
public interface IDocMapEntry
COM strongly distinguishes between a CLSID and a IID. The IID, the interface identifier, identifies an interface implemented by a COM object. We can see that one, you specified it with the [Guid] attribute. You'd pass it as the 4th argument of CoCreateInstance(), the one where you now pass the IID of IUnknown.
What you need is the CLSID, the class identifier. We can't see it in your snippet, it is the one for whatever class implements your IDocMapEntry. With some odds that it is named DocMapEntry. And additional odds that you didn't give it a [Guid] so the CLR will auto-generate one for you. Note that it will change when you modify the class, one possible reason for CoCreateObject() to fail.
Forgetting to register the .NET assembly with Regasm.exe /codebase is another reason, you stated as much in your question. This is required or COM will not be able to find the DLL and fails the call with error code 0x80040154, "Class not registered". Having it in the same solution is not sufficient, COM search rules are not anything like those used by .NET.
Also note that you are exposing the internals of the class, avoid that by applying the [ClassInterface(ClassInterfaceType.None)] attribute on the class.
No you do not have to register the COM DLL but you do need to register an object responsible for creating the COM objects. Use do this in your application by calling CoRegisterClassObject after the COM subsystem has been initialized (by calling CoInitialize or CoInitializeEx).
IClassFactory *factory = new ObjectFactory();
// Register the factory responsible for creating our COM objects
DWORD classToken;
HRESULT hr = CoRegisterClassObject(
Object_CLSIID,
factory,
CLSCTX_LOCAL_SERVER,
REGCLS_MULTIPLEUSE,
&classToken);
// Now we can create the objects
Object *obj = nullptr;
hr = CoCreateInstance(
Object_CLSIID,
nullptr,
CLSCTX_LOCAL_SERVER,
Object_IID,
reinterpret_cast<void**>(&obj));
Not sure why it's creating a temporary .tlh but I suspect it's something in your build settings - possibly in the build steps.
Is your COM object in a C# dll ? If yes, did you run regasm previously in your system, which could have registered the COM dll.
Guid("E98676B9-1965-4248-A9B6-74EC5EE5385A"): This is your interface GUID, not your component guid.
You should have a GUID declared for COM object in the implementation class, which should be b10a18c8-7109-3f48-94d9-e48b526fc928.
Why would it create a temp .tlh file with the wrong guid?
Are you sure you have not imported the COM dll using #import keyword.
My question is similar to Getting CLSID for a DLL file?, I think.
I have a directory with some DLLs, each one implementing one or more COM interfaces. I would like to get:
1) Each interface name
2) The CLSID of the class implementing the interface
For each DLL. It's important that everything can be done programatically (So I can't use some sort of COM browser and manually look up for that information).
Later I will lookup the CLSID given the interface name and call some methods using IDispatch.
One alternative seems to be scanning the registry trying to match the type, interface and class GUID and the .dll filename. But that seems to be slow and not robust.
Does someone has a clear solution to this problem?
EDIT:
With the response of Ben Voigt, I came with the following code which suit my needs:
ITypeLib *typelib;
ITypeInfo *typeinfo;
LoadTypeLibEx(_T("c:\\mydir\\mycom1"), REGKIND_NONE, &typelib);
for (UINT i = 0;i < typelib->GetTypeInfoCount();++i) {
TYPEKIND typekind;
typelib->GetTypeInfoType(i, &typekind);
if (typekind == TKIND_COCLASS) {
// class!
CComBSTR className;
TYPEATTR *typeattr;
typelib->GetTypeInfo(i, &typeinfo);
typeinfo->GetDocumentation(MEMBERID_NIL, &className, NULL, NULL, NULL);
typeinfo->GetTypeAttr(&typeattr);
GUID classGUID = typeattr->guid;
for (UINT j = 0;j < typeattr->cImplTypes;++j) {
// interface!
CComBSTR interfaceName;
HREFTYPE hreftype;
ITypeInfo *classtypeinfo;
typeinfo->GetRefTypeOfImplType(j, &hreftype);
typeinfo->GetRefTypeInfo(hreftype, &classtypeinfo);
classtypeinfo->GetDocumentation(MEMBERID_NIL, &interfaceName, NULL, NULL, NULL);
// associate interfaceName with classGUID here
}
}
}
You can't get that from the COM DLL, but you can get it from the typelib. I'm pretty sure the MIDL compiler has a switch to decompile a typelib, but parsing IDL wouldn't be as easy as using the TypeLib API.
To complicate matters, the typelib is often stored as a resource inside the DLL. So you'd extract the resource, and open it with the TypeLib API.
Start with LoadTypeLibEx which will return you an ITypeLib* interface pointer (you knew you were going to need COM in order to get information about COM libraries, right?). This will actually do the resource extraction step for you.
Then, call ITypeLib::GetTypeInfoCount to find out how many types there are. Call ITypeLib::GetTypeInfoType for each one to find the interfaces and coclasses. And call ITypeLib::GetTypeInfo followed by ITypeInfo::GetDocumentation to get the name.
It looks like you have all of this so far. Next you need the GUID of the type, which is gotten with ITypeInfo::GetTypeAttr (not ITypeLib::GetLibAttr). That gives you a TYPEATTR structure, which has a guid field.
From the same TYPEATTR structure, you'll need the cImplTypes field. That together with ITypeInfo::GetRefTypeOfImplType will let you match up each coclass to the interfaces it implements.
Note that there's not guaranteed to be a 1:1 relationship between interfaces and implementation coclasses. And the interface can be in a different library from the coclass.
Few caveats to Ben Voigt's answer: not every COM DLL has a typelib. In your case, it seems, it does; but that's not a requirement. The only rock-solid requirement is that the DLL exports the function DllGetClassObject(), where you pass a CLSID and get back an object factory.
You could load the library and call DllGetClassObject for every registered CLSID on the system (scan the registry under HKCR\CLSID for the list of those). The ones where you get a valid object back are the ones that the DLL supports. Now, in theory, it's not even a requirement that the CLSIDs the DLL supports are registered; I can envision a DLL that implements private object classes that only the intended client knows about and no one else should. But this is a very, very exotic scenario; for one thing, the regular COM mechanism of looking up the DLL path by the CLSID will break for those. The client would have to load the DLL directly.
You could also scan the registry for the CLSID's where the DLL under consideration is registered as InprocServer32; this, again, will break in case of private classes. You can do a registry search in regedit, search in data. Also, you'd have to deal with filename case issues, short vs. long names, hardlinks, environment variable substitution and so on. So I would not recommend it.
EDIT: just thought of another way. Download Regmon, run it, and call regsvr32 on the DLL. Then watch what CLSID's are touched.
You might want to look at the source code in WiX's heat utility, which does the same thing:
http://wix.sourceforge.net/manual-wix3/heat.htm