I have a 3rd party component, lets say FIPreviewHandler to handle preview, which implements IPreviewHandler. FIPreviewHandler is implemented as a Managed Component, and uses the IPreviewHandler interface and related interfaces through means of an interop. FIPreviewHandler is registered using regasm.exe as COM.
I have a client application which is also Managed. I want to create an instance of FIPreviewHandler as a COM component in my application.
I have an interop assembly that defines IPreviewHandler and related interfaces.
When I create an instance of FIPreviewHandler, using Activator.CreateInstance(), on a type returned by GetTypeByCLSID(), which uses the correct CLSID for FIPreviewHandler, it returns me a managed instance, as it has the actual assembly available, and skips COM. When I try to QI/cast this instance as any of the interfaces, IPreviewHandler for example, it returns null because, it is loaded as a managed object, and although the IPreviewHandler interface implemented by FIPreviewHandler is the same interface as I have in my interop, but its in a difference namespace/assembly, hence null. If it were to return me a COM instance/RCW (System.__ComObject), it would not take namespace into account, and would cast fine, and return a valid instance.
FIPreviewHandler is a 32 bit component, and on a 64bit Win7 machine, if I compile my client application as "Any CPU", Activator.CreateInstance() returns a COM instance/RCW (System.__ComObject), as it cudnt find a 64bit implementation of FIPreviewHandler, hence returns a proxy. In this scenario, my application works fine. But when I compile it for x86, it gets the 32bit implementation, and returns a managed instance of the actual managed class, and not a COM instance, hence fails.
I cannot use the interfaces defined in FIPreviewHandler's assembly, as I have to write a generic client for IPreviewHandler, and my application will work with any component implementing IPreviewHandler, which would work great for C++ based clients accessing FIPreviewHandler as a COM object, but is failing for Managed clients.
I hope I make sense and I would be really grateful for any help.
So clearly this a fail on .NET's part as I'm finding there is no way to use a COM wrapper around a managed COM object.
The "solution" (and I use that term very loosely) is the use of a PIA or 'Primary Interop Assembly'. The PIA provides a single strong-named assembly imported with TlbImp.exe that is GAC registered. Basically I guess we then must rely on GAC publisher policies to force clients to the correct interface assembly.
see also
http://social.msdn.microsoft.com/Forums/en/csharpgeneral/thread/b11a0f90-fcc5-487a-b057-632f5415bfc2
http://www.codeproject.com/KB/COM/BuildCOMServersInDotNet.aspx
Man, if I were you, I would just make the wrapper myself, only when the type is NOT a COM type. To know if the type created is a COM type, use the IsCOMObject from the Type of the object:
myObject.GetType().IsCOMObject
If this is FALSE create a wrapper, that uses reflection to call the managed type. Reflection is slow, but you can cache the MethodInfo objects that you get... otherwise you can generate IL code, and create a wrapper that will have no reflection at all.
Of course there are other methods, of doing it... this is up to you.
As I am in love with dynamic run-time IL generation, I can provide you with a code that does this... if you are interested!
My experience is that Microsoft did it this way by design. They don't want your two managed code assemblies to communicate via COM. If you try to add an assembly which supports COM as a COM reference in your project, the error says as much.
If the COM interfaces are the only way you can get at the functionality you need, a non-managed wrapper should do the trick. Write a new COM server in C++ or VB6 (or any non-managed language which is COM friendly) which wraps you third party managed COM server. Then add this new wrapper DLL to your managed code project as a COM server.
I've tried to do a similar thing with no success. (In my case the existing defined COM interface had a bug that meant it couldn't be implemented in .Net, so I re-defined the COM interface in a separate namespace myself and implemented this interface instead. The resulting object implemented the correct COM interfaces, however I couldn't get a hold of a COM wrapper to cast back to original broken interface).
As far as I am aware there are only 2 solutions:
Declare all COM interfaces in a Primary Interop Assembly, (either using TLBimp or by hand) so that all interfaces are defined in a common namespace. Note that this assembly normally doesn't contain any implementation, and so shouldn't need to reference other assemblies (unless those other assemblies are also interop assemblies that declare dependent COM interfaces).
Create a wrapper (for example in Managed C++) that performs the calls through "traditional" COM, rather than through the .Net COM interop.
Option 1. definitely strikes me as your best option - note that there is no need to register this assembly or place it in the GAC, and the implementation can be in an entirely separate assembly as long as the common interop assembly is referenced.
I can't think of many legitimate situations where a Primary Interop Assembly is not feasible.
It sounds like there are two problems:
Access to the Interfaces
Bitness of the component.
First, why reference it as a COM object in the first place if you know it is managed? Why not make a direct reference to the DLL instead of through interop and in that way, you should have access to the interfaces in the same way the the library has access to them. Thus, you wouldn't need to call Activator.CreateInstance. Instead, you would called var foo = new FIPreviewHandler();. However, that does not solve the bitness problem so the point is moot. Instead...
Coming to the second problem, one solution is to put the COM component into a COM+ Server Application. COM+ applications determine their bitness when they are first loaded. If all libraries in the COM+ application are 32-bit, when it is loaded, it will load into a WOW32 and you can call it from a 64-bit application. I've used this trick for COM libraries that only existed in 32-bit form but I needed the use them on a 64-bit server. The downside is that you are loading the library into a separate process and incurring the marshalling costs due to execution being out of process to your application but once instantiated, it should perform well enough.
Use PInvoke to call the COM function CoCreateInstance(Ex) and pass it the CLSID defined in your interop assembly and the IID of IPreviewHandler. This way .NET gets no opportunity to interfere.
You say you have an interop assembly. It's best if this is a PIA distributed by the author of the third-party assembly, but it's fine if you have to build it yourself. (PIAs do not have to be registered in the GAC, although they often are.) You can get the CLSID by loading the interop assembly in the IL disassembler and looking up the System.Runtime.InteropServices.GuidAttribute attribute on the class.
If I'm reading this right, you need to force it to use COM. Two ideas:
Does the behavior of Activator.CreateInstance() change, if you get the type via GetTypeFromProgID() instead of GetTypeByCLSID()?
How does Microsoft.VisualBasic.Interaction.CreateObject() behave? Yes, I hate to bring VB into this, but I am curious if it returns the COM object instead of the .NET class.
Related
I call into a COM component from a C# project, via an interop assembly that I generate from the COM DLL. The COM interface has DispIds defined and I have verified that these appear in the generated interop assembly.
Empirically, if I upgrade the COM component to a newer version, the interop calls go horribly wrong (as if it's calling the wrong COM methods).
Is this expected, i.e. that the interop assembly is tightly bound to the specific version of the COM interface that it was generated for? I had naively assumed that as long as the DispIds and function prototypes matched up in the new COM component, which they do, it would all work OK.
Is there a way of telling the CLR to use the DispIds when calling into the COM component via the interop assembly, i.e. a late binding of sorts? (I know it is possible to use late binding using reflection-style C# code, but this would be less convenient than an interop assembly.)
I found Brian Long's article .NET Interoperability: COM Interop:
The most common requirement will be to use early binding to get compile-time type checking and direct (well, as direct as it gets) vtable calls to the COM objects. The [interop assembly] example above took this approach.
An interop assembly resulting in "direct vtable calls" sounds like it would not work with a new version of the interface (unless, perhaps, new methods were only added to the end of the interface, i.e. to the end of the vtable?).
Perhaps someone can corroborate or provide a more complete answer?
DispIds are only used when the client programmer uses late binding. If that's what you want him to do then you have to enforce it. That's very easy to do, apply the [InterfaceType(ComInterfaceType.InterfaceIsDispatch)] attribute on the interface. If you didn't write an interface but only a class then give it the [ClassInterface(ClassInterfceType.AutoDispatch)] attribute.
Any attempt by the client programmer to use the dangerous early binding will now fail, he must write late-bound code. Don't expect a thank-you note.
The only other way is to remove the [Guid] attribute you now use. It is very dangerous, let the CLR auto-generate the guid so it will automatically be different when you change the interface. And the client programmer gets a decent E_NOINTERFACE error instead of calling the completely wrong method. Well, don't expect a thank-you note :)
I've used COM for some years now but I keep learning new (and strange) things.
Recently I've realized that COM interfaces didn't had to be registered in the registry for components implementing them to work.
I've come to this conclusion after analysing the registry of a workstation where COM DLLs (implemented in .Net/C#) were registered with .reg files created by RegAsm because the user was not an administrator. And RegAsm only generates registry keys for COM classes and not interfaces.
If that's true my guess is that interfaces are important for early binding and have only to be present in TLB files. On the contrary registering implementations (classes) is essential because they are backed by physical code on the file-system that need to be referenced.
1) So am I crazy, missing something, or interfaces can be omitted?
2) If they can be omitted what are the consequences if any?
There are a lot things that you can't do without the interface being registered. Many of the features of COM -- marshaling, proxying, asynchronous calling -- have standard implementations that prevent you from having to roll this stuff yourself. For example, CoMarshalInterface is a standard way of taking any COM object interface and marshaling that interface into a stream so that it can be unmarshaled in another thread, process or machine. The interface information is critical in this -- without the interface metadata, the standard COM implementations of things like this won't work, as the infrastructure simply doesn't know enough about your interfaces to do what it needs to do in a generic way that works for all COM objects.
Additionally, while most automation clients (like VBA, C# and C++) can reference a type library file directly for purposes of early-binding, there are still limitations. For example, suppose you're working with a type library that contains some classes that implement interfaces from a different type library, or maybe the interfaces in the first type library accept parameters or return values that are defined by interfaces/enums/etc in another type library. In order for an automation client to work with these interfaces which contain cross-references, the cross-referenced type library must be discoverable somehow. Registration is the way this is accomplished.
Worth noting: In my experience, pretty much everything that works when a COM object is registered machine-wide (registered in HKLM) works exactly the same when registered per-user (in HKCU). This often makes COM registration more palatable in situations where machine-wide registration can't be performed (e.g. the user is not an admin). However, there are some significant gotchas, most notably https://techcommunity.microsoft.com/t5/Windows-Blog-Archive/Per-User-COM-Registrations-and-Elevated-Processes-with-UAC-on/ba-p/228531
Pretty vague, not sure I could read all the words between the bold ones. There is in general more than one way to skin this cat. COM requires using a class factory to get an object created, the generic work-horse one is CoCreateInstance(). CreateObject() is popular in scripting environments. You give it a number and it spits an interface pointer back. With the COM runtime taking care of the job to locate the executable file that contains the coclass, loading it and finding the proper class factory implementation.
Finding the executable is the tricky part, this is commonly done by info in the registry. Entered there when the component was registered. Not exclusively, a manifest can also be the source of this info. It needs to be embedded in the client app, one reason it is not a universal solution. More modern is the package manifest in a Windows Store/Phone/Universal application. Required, only very privileged components can still use the registry to let themselves be found. Microsoft components.
A completely different tack is having custom class factories. The way it is done in DirectX for example, it doesn't depend on the registry at all. You call CreateDevice() instead. Still calling this COM is a bit of a stretch, it is a more general technique called interface-based programming.
This all applies to objects, interfaces are different. You call IUnknown::QueryInterface() to obtain an interface pointer. No registration required, it is the coclass that handles it.
Nevertheless, you'll find lots and lots of registered interfaces with Regedit.exe in the HKLM\Software\Classes\Interface registry key. They take care of another COM detail, if the component does not live in the same machine or same process or the same thread as the client code then extra work must be done to get the call serialized across the machine/process/thread boundary. Same kind of thing that happens in .NET Remoting, it requires a proxy. An object that also implements the same interface but doesn't execute the method directly, passing the arguments to the stub instead so it can make the call.
Simple to do in .NET, Reflection makes it very easy. Not simple in COM, an extra component is required that knows how to serialize the arguments into an interop packet. And get the return value back the same way. Proxy/stubs are normally automatically built from the IDL. Or very common in .NET since it doesn't use IDL, you use the marshaller that digs out method details from the type library. A mechanism that's highly comparable to .NET Reflection, the type library plays the exact same role as .NET metadata does.
The ProxyStubClsId32 registry key inside the Interface key contains the CLSID of that component. You'll very commonly find {00000320-0000-0000-C000-000000000046} there, that's the system provided marshaller that uses the type library.
Regasm doesn't write the interface keys, it sets the ThreadingModel key for a .NET [ComVisible] class to "Both". So that the methods can be called both from an STA as well as an MTA thread without having to be marshaled. That's very optimistic and very rarely tested, writing thread-safe .NET code isn't that easy.
Regarding your first question, if the interface is not supposed to be used across COM contexts, or if the interface derives from IDispatch and you only use late-binding, you don't need to register it.
However, if you use early-binding, or if the interface is supposed to be used across COM contexts, you need to register it.
Just registering an interface doesn't enable marshaling, all argument types and return types must be marshalable too, i.e. not HANDLE or alike.
Regarding your second question, my hope is that you can answer yourself after reading the answer thus far. If not,
if you don't register an interface, you can't use it directly across COM contexts. If it derives from some registered interface, you can use that interface, such as the case of IDispatch-based interfaces.
However, very few interfaces are as general as IDispatch, so for any other base interface, you won't be able to use your derived interface's new methods.
In type libraries, if you don't register event dispinterfaces, then development tools (typically IDEs) won't be able to show you which events can be fired, or any event at all. The only other option is to implement the dispinterfaces by hand, if your programming language has that option, which requires documentation equivalent to the missing IDL in the first place.
One common extreme of this is to have all objects simply implement IDispatch and no other interface, but again this will hinder any effort a development tool might do towards method listing, code completion and/or argument choice (e.g. IntelliSense). Note that sometimes this is enough, such as when implementing a window.external object for IE's JScript, but it's a bit of lazyness when done in more general objects.
In general, if you're required very few extra effort to have interfaces registered, given you're already targeting COM, do so.
When I try to Convert HTML to XHTML Tag I'm getting the following error...
Error: Retrieving the COM class factory for component with CLSID
{59939390-0E6A-4F1B-A742-20C5459501F7} failed due to the following
error: 80040154.
After googling I found few solutions:
Registering the DLL into regsvr32 "E:Source
Code\bin\Interop.HTML2XHTMLLib.dll"
I'm just tried to register the dll. But E:Source Code\bin\Interop.HTML2XHTMLLib.dll was loaded. But the DllRegisterServer entry point was not found this error message was displayed. Why..?
Recompiled my project for x86 and x64.. no use..
VB.NET Code:
Dim xhtmlUtil As New XHTMLUtilities // Here itself im getting the above error.
sFormattedOutput = xhtmlUtil.convertToXHTML(sInputline) //Send it for conversion
My Operating system is Windows XP 32-bit Service pack 3. My application was done in VS2008. currently I'm working with VS2010.
Here what I'm missing. Could any one help me to figure out this problem?
Thanks in advance.
i'm just tried to register the dll. But E:Source Code\bin\Interop.HTML2XHTMLLib.dll was loaded. But the DllRegisterServer entry point was not found this error message was displayed. why?
The Interop.HTML2XHTMLLib.dll file isn't the library you want to register using regsvr32. It is only the managed interop assembly, generated make COM objects accessable for your .NET application. You actually need to register the type library for the HTML2XHTMLLib.dll.
To do this, you have two options:
Find the redistributeable package, that contains the library and install it together with your application.
On your development system, open the "Add references" dialog of Visual Studio. Choose the COM tab and search for the library (just like you did when you've added the reference). There you will find the absolute path to the library. Copy the library to the client system and register it using regsvr32.
Since I do not know the source of the HTML2XHTMLLib, I can only suggest those ways. You should prefer the first one.
Since you've started a bounty on this, I want to go a little bit more into detail on COM and the InterOp.
Differences between COM and .NET assemblies
There are two types of servers in COM: InProc-servers and OutProc-servers. InProc (In Process) are servers we usually know als DLL. OutProc (Out of Process) servers are standing alone, running in their own process. We know them as EXEcutables.
You want to consume an InProc-server. Your COM-server (HTML2XHTMLLib) consists out of two parts:
A type library (.tlb), that contains meta-information about the server, it's contained objects and their accessability.
A library, containing the code where all the objects are implemented. The library also exports the following static functions:
DllGetClassObject – Tries to create an instance of an object, defined inside the server
DllCanUnloadNow – Tells the COM environment, whether or not the server can be released, because it isn't used by any other process any more.
DllRegisterServer – Called by regsvr32 to register the previously mentioned type library in the Windows Registry, to make it visible to clients and the COM environment.
DllUnregisterServer – Does the exact opposite, when called through regsvr32 -u.
The type library can also a resource of the DLL or EXE file, so that there's only one file. For C# developers this seems somehow confusing, since meta-information is directly compiled into a.NET assembly and accessable through reflection.
The InterOp: A wrapper between .NET and COM
So basicly type libraries describe everything that is needed by the .NET reflection to access the objects exposed through COM. But the problem is, that COM-components are stored in a different format:
Usually they are directly compiled into machine code: You cannot link a .NET assembly, compiled with AnyCPU against a COM-server. COM-servers are directly compiled to either x86-assembler, or x86-64-assembler. They have fixed pointer sizes and thus are only compatible with one of the compilation-models.
COM defines rules for memory management. Each COM-object must implement the IUnknown-interface. This interface defines three methods. The methods AddRef and Release are for memory management purposes. Whenever a client accesses an COM object it needs to call AddRef. This increases a counter by one. When the client does not need the object anymore, it calls Release instead of deleting the object, resulting in a counter decrement. If the pointer reaches 0, the object delete's itself. This is different from how .NET manages memory. In .NET the garbage collector visits each object on the heap in a non-deterministic manner (you cannot determinate the exact point of time an object get's deleted) and releases the object, when there are no references left to it.
COM defines rules for identity. Whenever you only want to access an base interface of an object, you have to call the QueryInterface method, defined by IUnknown. This method is guaranteed to allways return the same pointer, when a specific interface get's queried. This might be also true for .NET (besides you are overloading some operators), but the way .NET ensures object identity is different.
COM defines rules for object relations. Crazy stuff like Aggregation and Containment, which do also exist in .NET, but are implemented differently.
COM defines different multithreading rules, like Single Threaded Appartments and Multi Threaded Appartments. Those threading models define how objects interact, when they are coexisting in different manners. In .NET you have to perform each synchronisation process manually.
This list may not be complete, neither I want to go into detail any further, because it is only incidental for your question, but you see, that there are some big differences between .NET and COM. And to manage those differences there is a layer between both worlds: the COM InterOp.
If you are calling a COM server from .NET, the InterOp is nothing more than a .NET assembly, that does all the hard work under the hood. It get's created using the tlbimp.exe tool. Visual Studio typically calls it for you whenever you are referencing a library from the COM tab. The result is the library you wanted to register: InterOp.Libary.dll. This library redefines all types of the type library of the COM server, implements the rules required by COM and performs the actual calls for you. However it is a managed .NET library which does not define the methods, described earlier. This is why regsvr32 cannot find the DllRegisterServer entry point.
The way described above is only a one-way with an unmanaged COM server and a managed .NET client. There is also the other way, with the counterparts tlbexp.exe and regasm.
I'm using this exe com server:
https://cfx.svn.codeplex.com/svn/Visual%20Studio%202008/CSExeCOMServer/ExeCOMServer.cs
my prog is a com app
my com method which take another com object is void Init(AppsScriptRunningContext rc);
in this method I try to read out an property and get this error
Unable to cast COM object of type
'AppsScriptLib.AppsScriptRunningContextClass' to interface type
'AppsScriptLib.IAppsScriptRunningContext'. This operation failed
because the QueryInterface call on the COM component for the interface
with IID '{4D2E5723-87C2-49C1-AA28-ED2D88275100}' failed due to the
following error: No such interface supported (Exception from HRESULT:
0x80004002 (E_NOINTERFACE))
If my app is not a com server but a normal com app there is no error.
Thats why I think the error is produced by the exe com server.
https://cfx.svn.codeplex.com/svn/Visual%20Studio%202008/CSExeCOMServer/ExeCOMServer.cs
Regards, Chris
That's a very regrettable code sample, the odds of having success with it are very close to zero. It is a product of the MSDN forums support group and their work isn't being peer-reviewed at Microsoft. One team member later acknowledged that this is not the correct way to do it.
An issue with that code is that it completely ignores the Remark in the MSDN documentation for RegistrationServices.RegisterTypeForComClients() method:
Note that using platform invoke to call the unmanaged CoRegisterClassObject and CoDisconnectObject methods for registration and unregistration of COM objects is not supported.
Unfortunately without explaining why it isn't supported. The key problem is that a COM interface always needs to be marshaled in an out-of-process activation case. This is done by creating a proxy at the client side, an instance of the interface that has all the same methods as the original interface but whose implemented methods send the arguments of the method to the other process through RPC. At the server side, a stub performs the same but opposite role, building a stack frame with the method arguments and making the actual method call.
Creating the proxy and the stub is pretty easy in .NET, reflection makes it simple. Done by the Remoting plumbing built into the framework. It is however not easy in COM, it doesn't have anything similar to reflection. There are two basic ways it gets done, firstly by writing the COM interface definitions in the IDL language and compiling it with midl.exe. Which can automatically generate the C source code that does the marshaling from which you can build a DLL. That DLL then needs to be registered properly in the HKCR\Interface registry key so that COM can find and load the DLL whenever the interface needs to be marshaled. A second way is available if your interface is derived from IDispatch and limits itself to OLE Automation compatible argument types. You can then generate a type library and use the standard marshaller. This requires the type library to be registered, along with appropriate keys in HKCR\Interface so COM knows about it.
That's the part that doesn't happen in a .NET out-of-process server. You don't have midl.exe to help you generate the proxy/stub DLL and you don't get the registration help to use the standard marshaller. Which is what the error message is really saying, E_NOINTERFACE here really means that it couldn't find any way to marshal the interface. Yes, crappy error message.
The officially supported way in .NET to create an out-of-process COM server is to register it with COM+. Using the System.EnterpriseServices.ServicedComponent class as a base class. The MSDN library article is here.
I got an advise to make a change in main thread and it helped me!
Find in the main thread (mostly in "Program.cs") the line [STAThread] and change it to [MTAThread].
I'm working the open source library EasyHook.
What I'm trying to do, is hook into when a VB6 app calls CoCreateInstance from ole32.dll on a particular CLSID, return my own C# implementation for the object rather than the true COM object. My C# implementation derives from the same interface that tlbimp.exe spits out for the COM object I want to replace.
My hook works, and I'm able to hook into calls, log data about the call, and then p/invoke CoCreateInstance from C# to allow the VB6 application to run as normal.
I'm noticing that the COM object that I want to replace isn't passed through my hook.
Does anyone know how VB6 loads ocx files under the hood? Am I hooking into the proper native api call?
Or is what I'm trying to do impossible due to the nature of .Net?
UPDATE: An alternate solution is to write a COM object to replace the old one, but we cannot get this to work. Here is an old post that I closed on the subject: Replace COM object
UPDATE: After further inspection, we are able to regsvr32 /u the old ocx file and use regasm to register our .Net dll. We put a MessageBox in the constructor of our COM object and the VB6 app loads and pops the box, but it crashes as soon as it makes the first method call on the object.
I suspect that we have some method signatures wrong, also we are using what tlbimp.exe gave us when we ran it on the target ocx we want to replace. Is it possible that tlbimp is making changes to the signatures that is preventing VB6 apps from loading our assembly?
For example sometimes COM signature will look like:
HRESULT MyMethod(IUnknown* ppv);
And tlbimp.exe will give C# something like:
IUnknown MyMethod();
Which looks much cleaner to a C# developer. Does anyone know about this, or a good article that could explain how to write a "binary compatible" COM assembly from C# to replace an ocx file?
Couple of comments: First, VB6 does not use CoCreateInstance on "local" classes, i.e. classes from the same project -- it calls "constructor" directly. Second, you have to hook CoCreateInstance on the import section of every dll/ocx the CLSID can be cocreated from.
A better way is just to register you "upgraded" COM component with the same coclass CLSID. This way it will be automagically used by the client app.
Edit: Or take a look at CoTreatAsClass function.
If you have the source code for the original component, apparently VBMigration Partner can upgrade a VB6 COM component to a VB.Net component that has binary compatibility with the original VB6 component. I don't know whether it supports OCXs.