MIDL changes the Interface name - c++

I have a COM dll , which is consumed by .NET applications using COM Inter-op.
In one of the CoClasses , there is an Interface called IT6TrackData and it has one get property called TrackData
From the IDL file:
Interface IT6TrackData
{
[propget, id(1)] HRESULT TrackData([out, retval] SAFEARRAY(BYTE) *pVal);
}
When the TLB file is viewed for the above IDL file, it shows the property as trackData ( t in lower case)
For some reason the Client application were referring to this property as trackData and everything was working fine until now.
As part of enhancement the above Interface was upgraded to have a put property
Interface IT6TrackData
{
[propget, id(1)] HRESULT TrackData([out, retval] SAFEARRAY(BYTE) *pVal);
[propput, id(1)] HRESULT TrackData([in]SAFEARRAY(BYTE) pVal);
}
Now when the TLB file is viewed for the above IDL File, it show the property as TrackData ( t is in upper case), this is breaking the old .NET clients who continue to refer to the
trackData with the “t” in lower case.
I have gone through this KB article
http://support2.microsoft.com/kb/220137/en-gb
but is there a way out, does anyone know of a fix for this issue.
your attention is appreciated.
The IDL File
import "oaidl.idl";
import "ocidl.idl";
[
object,
uuid(72867CE8-41B6-459E-A258-C7A162A26D5E),
dual,
nonextensible,
helpstring("ITFST6TrackData Interface"),
pointer_default(unique)
]
interface ITFST6TrackData : IDispatch{
[propget, id(1), helpstring("property TrackData")] HRESULT TrackData([out, retval] SAFEARRAY(BYTE) *pVal);
[propput, id(1), helpstring("property TrackData")] HRESULT TrackData([in]SAFEARRAY(BYTE) pVal);
};
[
uuid(1D7ABC17-2738-4373-9B6B-239E344DBD21),
version(1.0),
helpstring("SampleCom 1.0 Type Library")
]
library SampleComLib
{
importlib("stdole2.tlb");
[
uuid(2013CC98-47A7-468F-912A-2A2CAE25E327),
helpstring("TFST6TrackData Class")
]
coclass TFST6TrackData
{
[default] interface ITFST6TrackData;
};
};

This is the side-effect of a hack in the type library generator built into Windows. It has a workaround for trouble caused by a case-insensitive language. Which might declare a type in one casing but refer to it elsewhere in another casing. Visual Basic is the prime example of such a language.
The hack is very crude, it takes the casing of the first identifier it encounters and then changes the casing of any subsequent identifier to match. The most typical cause of an unexpected casing change is the name of a parameter, typically spelled with a lower-case first letter. So you probably had a "trackData" method parameter in a previous version of the code.
And in your revision, either the order of the identifiers changed or you renamed or removed that parameter. Now getting "TrackData" instead.
If you have existing client code around that depends on the original casing then there's little you can do but change the casing in your source. Fugly fix, but unsurprising to your clients since they can't tell the difference :)

Related

C++ COM out-of-proc marshalling data through proxy\stub

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, &regID);
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.

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.

CoCreateInstance: Create a COM instance defined in a .NET library?

I've created a COM library defined in an IDL, it has an "entrypoint" called IFoo:
[
object,
uuid(789b4d46-4028-4196-8412-4c5c8ef86caa),
nonextensible,
pointer_default(unique)
]
interface IFoo: IUnknown
{
HRESULT HelloWorld();
};
I've implemented this in my C# library like so:
[ComVisible(true)]
[Guid("45b50f1e-d551-4be0-b52a-7ec075840114")]
[ClassInterface(ClassInterfaceType.None)]
[ComDefaultInterface(typeof(IFoo))]
class Foo: IFoo
{
public void HelloWorld()
{
Console.WriteLine("Hello world");
}
}
I compiled this, and registered it with:
regasm.exe foo.dll
Everything looks good thus far.
Now, how do I create an instance of this in my C++ program? I think the right function is ::CoCreateInstance:
::CoInitializeEx(NULL, COINIT_MULTITHREADED);
IUnknown *pUnk = 0;
HRESULT hr = ::CoCreateInstance(???,
NULL,
CLSCTX_INPROC_SERVER,
__uuidof(IFoo),
(void**)&pUnk);
Is this the right way to create an instance of this class? If so, what goes in "???" above?
I think the class's UUID ("45b50f1e-d551-4be0-b52a-7ec075840114" above) should go there, but I can't figure out how to manually create an IID from this GUID string.
It turns out, I was right, the class's UUID goes there, but getting it was something I didn't expect:
struct __declspec(uuid("45b50f1e-d551-4be0-b52a-7ec075840114")) Cls;
Then:
HRESULT hr = ::CoCreateInstance(__uuidof(Cls),
NULL,
CLSCTX_INPROC_SERVER,
__uuidof(IFoo),
(void**)&pUnk);
This fixed the problem and worked correctly.
Just use MIDL to compile your IDL file into set of C++ files and include them into your C++ program. Have in mind that interface is not the "entry point" and you are going to need the class object in your IDL too.
Another way to create a COM client in C++ is Microsoft specific #import directive, but I am not sure if it is compatible with .NET-based components. If you can create a .tlb file for your .NET component, you can just add the needed stuff like this:
#import "MyLibrary.tlb"
This directive has lots of options. Check this MSDN article for details.

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