I have a unmanaged C++/ATL in-process COM object (in Unmanaged.dll) that I'm trying to use from a managed C# DLL (Managed.dll). However, I want I want to use registration free COM. I have it down to these steps:
Register the COM object on the development machine. The in-process server must have a properly registered type library.
Add a reference to the COM object within the C# project, and then set the Reference Properties to Isolated = True.
This produces Unmanaged.dll, Managed.dll, and Native.Managed.manifest. Opening the manifest, it's pretty clear how the system uses it to load the COM object in a registration-free way.
Here's the rub. I have a managed EXE (Managed.exe) which dynamically loads Managed.dll to access public types. What I mean by "dynamically" is, it uses Assembly.LoadFrom("Managed.dll"). When the code inside Managed.dll tries to create the COM object, it gets a "class not registered" exception. It appears the activation context doesn't get setup correctly when Managed.dll gets loaded.
Is there a way to get registration free COM to work in this scenario?
Two days without an answer, so here's what I've come up with in that time...
It does indeed look like the activation context is setup by the OS at process launch based on the manifest associated with the main EXE. That means all registration-free COM related elements must be in the Main.exe.manifest at the time the process is started. This breaks the isolation between EXE and DLLs. If a DLL is responsible for creating COM objects, you wouldn't expect the EXE manifest to have to contain the reg-free COM information. You might have expected the manifest associated with the DLL to be merged into the process activation context at the time the DLL gets loaded, but it does not.
To work around this, the DLL must configure a new activation context before creating the COM object. To make things worse, there is currently (as of .NET 4.0) no managed way to do this. So, the DLL will have to PInvoke the following Win32 functions:
CreateActCtx
ActivateActCtx
DeactivateActCtx
ReleaseActCtx
I wrapped these calls with a managed class that calls CreateActCtx and ActivationActCtx in the constructor and DeativateActCtx and ReleaseActCtx in IDisposable::Dispose.
Related
My question is whether there are certain limitations to accessing COM objects when you're dealing with an MSIX-packaged app.
I've created a blank WinUI 3 C++ project, using the Visual Studio project template. In the project, I've created a traditional COM class that's registered at runtime using CoRegisterClassObject. I do not otherwise register the object in the registry. It's a singleton that's supposed to be accessible only when the .exe is already launched.
Now when I modify the project to be unpackaged, and the .exe is launched, I'm able to access the singeton class from another process, by calling CoCreateInstance. Everything works as designed.
But when I change the MSIX project back to the packaged setup, the call to CoCreateInstance from the unpackaged process now fails, with code REGDB_E_CLASSNOTREG.
It's like trying to connect to a COM object when there's an unelevated/elevated mismatch. Same error.
So are unpackaged processes unable to access COM objects registered with CoRegisterClassObject in MSIX-packaged apps? Is there a workaround or anything? Thanks in advance.
http://s4.postimg.org/bkprrelpp/6210.gif
Can someone explain this to me? I set my MCMS.Primary output as instructed by this page.
http://mdc.custhelp.com/app/answers/detail/a_id/19889/~/using-visual-studio-2013-professional-to-build-a-microsoft-visual-basic
I set the registration type to Extract COM information, the Scan At Build to Dependencies and Properties, and checked the COM Interop. But I'm getting an error and a warning after trying to build the project. I tried the help page that pops out after clicking the error message. But I can't find the components view it was referring to. What I want to know is how much important this COM Interop is. And also why sometimes when I build the project the error does not appear. But mostly it does.
One suggestion of that help page told me to register the item mentioned in the error message. An .exe file.
COM extraction is useful for turning self-registration routines into Windows Installer table data. COM Interop is the equivalent for registering managed assemblies for use via COM Interop. In both cases, the resulting registry keys are used to allow other DLLs to instantiate your COM server.
So the fundamental question you have to answer here is whether MCMS.exe is a COM server and registers any such data. If it is and does, then something is wrong with the extraction, and this is a problem. However if MCMS.exe is not a COM server, or does not actually register any relevant COM data, you may need to disable COM extraction.
I'm analyzing a rather old COM server project in order to reuse some stuff from it and noticed a strange thing - in order to expose itself to the registry it implements a separate function different from DllRegisterServer() and that function accepts the path to the typelib and loads the typelib from that path and the typelib is shipped separately and can be located anywhere.
The typical solution is to just use ATL CComModule::RegisterServer() which will happily load the typelib from the same DLL if it is embedded as a resource. The DLL already uses ATL and this should be the most direct way.
I tried to find why the typelib is not embedded as a resource but looks like it was a very old design decision and noone can explain it.
I can see a reason for shipping the typelib separately - this way it's easier for COM server developers to #import it. But why not both ship it as a separate file and embed as a resource into the COM server DLL? I could imagine that the typelib increases the DLL size by several hundred kilobytes but can't find any other serious reason.
What could be the reasons for shipping the typelib as a separate file only?
I finally found a serious reason for shipping a typelib separately only. If two versions - standalone and embedded - are shipped the installer could screw something up and those two typelibs could come out of sync and this would cause all kinds of weird problems for customers. Shipping exactly one version of typelib is safer in this aspect.
A change needs to be made in a DLL. The DLL was originally coded in VB6 (not by me), and the source code lost.
It is very simple in its functionality, so I recreated it from scratch, but I only have access to VB Express 2008.
I created it first as a normal DLL then realized it had to be a COM DLL. Fortunately, an excellent article at http://www.codeproject.com/KB/COM/nettocom.aspx tells me how.
But, I don't know anything about GUIDs...
Should I use the same GUID as the original DLL or not? Does it make any difference?
Edit: Does it really matter since it's a COM DLL? It is called into by an Active X control & I can see no reference in the web page to the GUI ... (but I'm just a n00b, so what do I know? ;-)
If you want this library to be a direct replacement of the original and it is fully binary compatible (all interfaces are unchanged) - then yes, you should use the same GUIDs for the class ids and interface ids. If you don't do that users of the original library will not be able to use yours without recompiling their programs.
Beware that binary compatibility is a must for reusing the GUIDs. If you break any interface - change its id and the id of the class implementing it and recompile the client.
The GUIDs acts as a kind of identifier for your COM object and DLL. If you use the same GUID you need to register your new DLL so that the location is updated (i.e. if you don't place it exactly in the same spot and have recreated all interfaces the old DLL previously had registered).
The cleaner approach is to generate a new GUID and modify the caller to use the new GUID/DLL instead.
I have a COM .dll registered successfully with regsvr32 but somehow CoCreateInstance() fails to create one of its interfaces. Is there a freeware tool which can determine the reason for the failure?
First of all, check the return value of the CoCreateInstance() call. Second, you can use a tool like Regmon or Process Monitor to see what registry lookup fails. This way, you can quickly determine what exactly wasn't registered the way you'd expect it to be.
If your com dll is implemented in C++ & has debug info, you could also try debugging with MSVC to step into CoCreateInstance.
My guess is that you missed associating your class with one of the interfaces properly. I've done that a number of times by mistake. If you're using ATL you need to make sure your implementing class derives from the interface & also you have added COM_INTERFACE_ENTRY(I____) for your interface in the COM_MAP:
BEGIN_COM_MAP(CFileHelper)
COM_INTERFACE_ENTRY(IFileHelper)
COM_INTERFACE_ENTRY(IDispatch)
COM_INTERFACE_ENTRY(IStream)
COM_INTERFACE_ENTRY(ISupportErrorInfo)
END_COM_MAP()
Or maybe the GUID differs between the IDL file and what's in the implementing C++ file. MSVC6 has an annoying bug where if the wizard fails to create a new class because of file permissions (e.g. some of the files it wants to change are read-only) but it has already added a GUID to the IDL file, and you fix the file permissions and go to do it again, there will be an inconsistency in the GUIDs and it's a pain to catch this. In an ideal world, the GUID definitions would reside ONLY in one file and then you wouldn't have to worry about this.
If not that, sometimes there are weird errors regarding marshaling & apartments but that only pops up if you've got multiple threads and are sending interface pointers across thread or interface boundaries.