How to load registered COM DLL in C++ - c++

I have a C++ COM dll and I have register it with regsvr32. I want tho use the functions and class of the dll Inside my code. Unfortunatly I dont possess any .h and it doesnt come with a .tlb file. I have the documentation how to use the functions and class but there is no information about how to link the dll to my project so I could use it. I am new with using external COM interface so i'm not quite sur where I could find this information.
I have tried #import "example.dll" (dll inserted in the project folder but it looks like it doesn't work I have an unable to load dll error. My program is mixed CLR / unmanaged C++.
Any suggestions?
Thanks in advance

If enough information is provided, you can define the interfaces in a header file yourself. I would recommend using #import to import an existing COM type library and investigate the generated .tlh file for ideas. For a simple interface with functions, for example, the code looks something like this:
struct __declspec(uuid("Interface-Guid-with-Dashes")) IInterfaceName : IUnknown
{
virtual HRESULT __stdcall get_Value (/*[out,retval]*/ long * result) = 0;
virtual HRESULT __stdcall Execute (/*[in]*/ int value) = 0;
};

Related

Test COM DLL without exporting methods explicitly

I created an ATL COM DLL. I now have to unit test the methods defined in the COM interface of the DLL. I created a console application "tester.exe" and wrote some google tests, which would talk to the COM DLL and its method. Problem is, the tester.exe only compiles if I explicitly export the COM method like:
__declspec(dllexport) STDMETHOD(Add)(DOUBLE Input1, DOUBLE Input2, DOUBLE* pOutput);
I want to test the COM interface directly and not rely on a DLL export. Is there any way my tester/com client can compile and run the dll without needing the dllexport, such that the method definition stays:
STDMETHOD(Add)(DOUBLE Input1, DOUBLE Input2, DOUBLE* pOutput);
Am I missing something very obvious? I tried looking at many resources and tutorials but none were very helpful.
I was able to resolve this issue by directly setting the CLSID and IDD in the test class:
const CLSID CLSID_CTest = { 0x809a77g7,0x2624,0x453e,{0xb3,0xc6,0x2c,0x68,0x15,0xff,0xe3,0x23} };
const IID IID_ICTest = { 0x0c832b41,0xbfe9,0x4a16,{0xb9,0x34,0xff,0x7f,0x8b,0x77,0x30,0x44} };
and then using these to create a CoCreateInstance
hr = CoCreateInstance(CLSID_CTest , NULL, CLSCTX_INPROC_SERVER, IID_ICTest , (LPVOID*)&ptrICTest);
With this, I neither need the lib of the COM server when compiling, nor any header.

c++ application cannot find com dll because compiler generating .tlh file with incorrect guids

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.

What is a good way to get a C++ and WPF app to communicate

I have a C++ dll which is a plug-in to ADOBE Acrobat. It needs to talk often ( to and fro) and with a fair amount of complicated data-structures to a WPF process.
Any thoughts of what might be the best way to go . Need something that is a little long term and maintainable, in other words would love some ideas around something that lets both process make what looks like methods calls and some infrastructure piece does the marshaling and dispatch . I've tried Windows messages but ran into some conflict issues on ADOBE, also not really interested in anything that causes the dll to get adobe to load the CLR. Only other things that come to my mind are named pipes or http.
Thanks in Advance
Named pipes could do but you won't get a feeling of just calling functions. Named pipe are quite low-level IPC. Other IPC options are:
Windows RPC, you definitely get a feeling of just calling functions.
What about hosting a COM object in WPF application and calling it from the Adobe plugin?
I would go with COM:
Implement an interface in WPF app
generate a typelib (e.g by using regasm)
import the typelib into C++ dll
communicate
if you need bidirectional communication, C++ dll can also implement a COM interface which is then accessed from WPF application.
This is what I have used to connect legacy C++ app with new .NET service, and it works great. The biggest issue is to find people who know COM, but fortunately this doesn't require a deep understanding of COM.
your hint with COM is very intersting. I tried to implement this concept.
I have created an interface in my WPF CallDllFromWpf3Interface project:
using System.Runtime.InteropServices;
namespace CallDllFromWpf3Interface
{
[Guid("F6E0E2E8-CCC6-487B-8BF1-261265061E6A")]
public interface SetValueInterface
{
void SetValue(int value);
}
}
Then I have generated the typelib with the regasm tool:
regasm CallDllFromWpf3Interface.exe /tlb
With the "oleview" tool I can see the typelib and the interface.
The next step was to create a c++ dll project called "CallSetValueInterface".
In my CallSetValueInterface.cpp file I wrote this lines:
#import "D:\Thomas\Programming\WPF\Basics\CallDllFromWpf\CallDllFromWpf3Interface\CallDllFromWpf3Interface\bin\Debug\CallDllFromWpf3Interface.tlb"
void
CallSetValueInterface::startAcq(void)
{
CoInitializeEx(NULL, COINIT_APARTMENTTHREADED);
CallDllFromWpf3Interface::SetValueInterfacePtr Svip("f6e0e2e8-ccc6-487b-8bf1-261265061e6a");
Svip->SetValue(55);
Svip = NULL;
CoUninitialize();
}
After a successful build of the dll project I copied "CallSetValueInterface.dll" to the "CallDllFromWpf3Interface" project.
Finally I changed my WPF code to:
#region SetValueInterface Members
public void SetValue(int value)
{
MyValue = value;
}
#endregion
[DllImport("CallSetValueInterface.dll", EntryPoint = "startAcq", ExactSpelling = true, SetLastError = true)]
public static extern void StartAcqFromDll();
private void Button_Click(object sender, RoutedEventArgs e)
{
StartAcqFromDll();
}
And when the debugger came to StartAcqFromDll() there occurred an error dialog "An unhandled exception of type 'System.Runtime.InteropServices.SEHException' occurred in CallDllFromWpf3Interface.exe".
Does anybody know whats going wrong?
Regards,
ThomasL.

Why do my C# and C++ dlls exhibit different behavior?

I am working on a project that involves the creation of a dll that honours a certain interface in order to plug into some software in order to add functionality to it. This is done by a dll that calls my dll (I do not have the source code for the dll that does the calling). Originally I was given an interface and a C# implementation that created a COM visible dll. However after using this for a while I found I wanted to make use of some large C++ libraries and as creating wrappers would take a long time I thought about creating a C++ ATL COM dll instead. I did this and the methods of my class appear to be called correctly (I register my dll, run the program and the methods appear to be called in the correct order), however I have found some of the behavior to be different.
I am not sure how to go about explaining this as my code relates to a closed source API but perhaps if I describe an example someone might have some ideas as to where I might want look.
For instance, in the C# dll I attempt to open a file by doing this:
FMANFileControl fileControl = new FMANFileControl();
FMANFile wFile = null;
const string filePath = #"C:\Data\April 4\Data_IDA.wiff";
wFile = fileControl.GetFileObject(filePath, 1);
long numSamples = wFile.GetNumberOfSamples();
I get the correct number of samples.
In my C++ dll I have this (with some of the HRESULT checks removed in order to keep the code shorter):
std::string filePath = "C:\\Data\\April 4\\Data_IDA.wiff";
_bstr_t fileName(filePath.c_str());
IFMANFilePtr ipFMANFile;
IFMANFileControlPtr ipFMANFileControl;
hr = ipFMANFileControl.CreateInstance(__uuidof(FMANFileControl));
hr = ipFMANFile.CreateInstance(__uuidof(FMANFile));
ipFMANFile = ipFMANFileControl->GetFileObject(fileName, 1);
long numSamples = ipFMANFile->GetNumberOfSamples();
but the files does not open correctly, resulting is zero samples.
Using oleview I looked at the typelib and it says this for the function:
[id(0x00000001), helpstring("method GetWiffFileObject")]
IFMANWiffFile* GetWiffFileObject( [in] BSTR WiffFileName, [in] long sample);
The file I get information from is one that is being written to during an experiment and just before it obtains more data it calls my method and I should be able to obtain the newest file. In the C# dll this is possible, but in the C++ dll this is not. While I realize the specifics of this is hidden, I am wondering is anyone has any idea why a C++ COM dll and a C#, comvisible dll that make use of the same interface would exhibit different behavour when being called by the same dll.
I am pretty stumped at this moment so any ideas at all would be appreciated, even if they turn out to be way off base. I can share my source code if anyone thinks they might be able to help.
EDIT:
I tried the solution to answer 1, however I could not compile my code. When reading about this I found this post:
Differences between [in, out] and [out, retval] in COM IDL definitions
that seems to suggest that since the FMANFile pointer is marked [out, retval] that the method becomes:
IFMANFilePtr ExploreData::IFMANFileControl(BSTR filename, long sample);
or am I misinterpreting that article?
EDIT 2:
Got it working though I am not really sure why.
Originally I had the variables declared in the header as private member variables of the class, like this:
class ATL_NO_VTABLE CUserIDA :
public CComObjectRootEx<CComSingleThreadModel>,
public CComCoClass<CUserIDA, &CLSID_UserIDAObject>,
public IUserIDA
{
.
.
.
public:
STDMETHOD(GetSwitchCriteria)(DOUBLE* intensity, DOUBLE* minMass, DOUBLE* maxMass, VARIANT_BOOL *selectIntensity, LONG* numOfDepCycles);
.
.
.
private:
ExploreDataObjects::IFMANWiffFilePtr ipFMANWiffFile;
ExploreDataObjects::IFMANWiffFile2Ptr ipFMANWiffFile2;
};
Just to try it I moved them to the top of the class delcaration like this:
class ATL_NO_VTABLE CUserIDA :
public CComObjectRootEx<CComSingleThreadModel>,
public CComCoClass<CUserIDA, &CLSID_UserIDAObject>,
public IUserIDA
{
ExploreDataObjects::IFMANWiffFilePtr ipFMANWiffFile;
ExploreDataObjects::IFMANWiffFile2Ptr ipFMANWiffFile2;
I thought that by default these would also be private members and the same as before so I am at a loss to explain why this seemed to work. Can someone explain this?
Your C++ code is correct, except for the following line:
hr = ipFMANFile.CreateInstance(__uuidof(FMANFile));
It doesn't make any sens, because ipFMANFile is initialized once again in the next statement.
Unfortunately, this IDL declaration:
IFMANWiffFile* GetWiffFileObject([in] BSTR WiffFileName, [in] long sample);
is limited for debugging purposes since it doesn't support the native COM mechanisme for the exception reporting via HRESULT. The COM compliant declaration would be:
HRESULT GetWiffFileObject([in] BSTR WiffFileName, [in] long sample, [out, retval] IFMANWiffFile** fileInstance);
I believe that you are unable to change the library's code so I suggest you to try some external debugging tools like 'procmon.exe' and 'dbgview.exe' to inspect the application events when you run the CPP test case. Look for all failed actions.
I hope this will help you somehow

Given a COM DLL, extract all classes CLSID and corresponding interface name

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