I create a C# COM interop for c++ to invoke.
I have registered the dll and tlb file by regasm.
everything goes well till one day i changed code of C# part (i didn't change the definition of interface, just implementation changed). one interface in COM returns an error 0x80131509. the strange thing is, it is only happened in some computers (my develop PC is works well so i can't debug this problem).
I'm not really clear on how the C# COM worked with C++, after i registered them, i just know they create key value in window registry.(like what regasm /regfile generated). how c++ knows where the COM dll is(search path environment variables)? and what the use of tlb file in run time?
any suggestion?
I got the same error as soon as I introduced a simple inheritance hierarchy to my COM library. A quick resolution was to set the ComVisible attribute to true on the base class. This fixed my problem immediately.
It does make a lot of sense when you think about it - the compiler doesn't allow you to build a hierarchy where the base class is less visible than the inheriting class. So it being the same for COM should come as no surprise - the only difference being, that it is failing at run-time instead of compile-time.
I would venture that the true reason for the error is a broken constructor chain, but I put no further research into it.
I think that your problem is related to the registry... You should try to unregister and register (using regasm) your dll in the computers that are having this problem.
If that doesn't work unregister the dll in those computers, than use regedit to search and delete any missing registry keys that refer to it, after that register your dll again. You could also use one of those registry cleaner programs after deleting the missing keys to guarantee that you didn't miss anything.
Remember that you should allways register a dll in the directory that it will be used by your application and this should happen only once. If you need to unregister a dll, then you should allways do it in the same directory that you used for the registration. In other words, once a dll is registered do not move it.
Note: if your dll is not on the same path as your C++ application it should be in a directory that is referenced in the PATH environment variable.
I got the same error message when I was calling a .Net4 C# COM object from Visual FoxPro.
The method returns object, type of which may be one of several. All the types are derived from an abstract class which implemented an interface with the common stuff for these types.
Eventually I decided to remove the abstract-modifier from the base class and just make it public and ComVisible. This solved the problem for me, even though I would like the base class to be abstract.
Related
I have a number of C# Assemblies which have been built and registered as COM components. They work perfectly fine when invoked from unmanaged code (Fujitsu NetCOBOL for Windows) and I also have no difficulty when invoking COBOL COM components from C#. The unmanaged COM component appears in the VS 2017 Reference Manager COM tab, VS 2017 generates the Interop and it is all excellent.
But if I try to use one of the managed COM components with managed code (a C# .exe), the VS 2017 Reference Manager doesn't show an entry for the .DLL. Instead, it presents the DESCRIPTION of the component instead of the NAME, and it is pointing to the .TLB which was generated when I built the original component. If I select that reference, this happens:
If I go and Browse for the .DLL the reference is added OK, but it forces me to "Copy Local" and I get a copy of the .DLL deployed with my executable.
If I try and make it false, and embed the types, it fails to build and I get a message about some obscure attributes not being set...
If I build it as shown, it deploys and works perfectly, but I am adding a copy of the COM component .dll to every executable I deploy. That kind of defeats the object of COM components where one copy of the code is shared between users. I used COM for years with unmanaged code and never had any problems. Is a different understanding required for use with managed code?
I have spent a number of hours reading the docs on COM and interOP (although InterOP is not applicable here I think, as both the client and the server are managed code.)
Is there something I need to set when I build the component? (I use "Make COM Visible" and it certainly seems to register it OK. However, I noticed that if I unregister it with Regasm it does not clear the registry entries...
Is there a VS 2017 setting I am missing or something in the build? Any help would be gratefully received.
Pete.
After some intensive reading and experimentation I finally solved the problem.
SUMMARY:
To be able to used managed COM components in the same way as we used unmanaged ones (one copy, registered and shared by all users), you SHOULD NOT try and add the .DLL as a reference to your project. Instead, you late bind the component and use reflection to implement its interfaces, methods, and properties.
This means doing the following:
Getting an instance of the component dynamically at run time.
using reflection on that instance to access properties and methods in the normal way.
DETAILS and CODE SAMPLES (to hopefully save someone else from having to do the background I did...):
First thing: DON'T add a reference to the .DLL to your Project.
I set up some global variables as follows:
Now I needed to consider what actions I wanted. I wanted a general solution so that I could use it on ANY COM reference and what I'm posting here is close, but things like
specific error messages have not been replaced with variables yet.
The COM object supports Getting and Setting Properties and invoking Methods. I also need to be able to instantiate it easily.
The methods I have written here return a dynamic value in retVal.
They have been tested and all work very well, so I can continue to use the managed COM components just as I did the unmanaged ones, by simply adding the code samples here to any project that deals with them, and invoking these methods to deal with a given managed COM component. I do not claim to be an expert in C# and I'm sure that more experienced people will find better ways to code than I have, but it does work and that is always a good thing... :-)
I was a little concerned that using reflection might slow everything down, but in practice it is no "slower" than late binding any COM object is.
Here are the methods:
(Sorry for code misalignment; I'm new to this...)
The method to instantiate the COM object. The example uses something called "RAVDesktop.dll"
Now the "Action" methods for properties and methods in the COM object...
Here's an example of instantiating the component, then setting a Property in it called "WebServiceURL", and, if the setting succeeded, retrieving it so it can be checked:
Grateful thanks to all who contributed. Stack Overflow rocks!
Pete.
ActiveX controls or COM components written in .NET languages cannot be referenced by .NET
applications in the form of interop assemblies.
If you "add reference" to such a TLB, or drag & drop such an ActiveX control to your .NET application, you will get an error "The ActiveX type library 'XXXXX.tlb' was exported from a .NET assembly and cannot be added as a reference."Support: Can not add reference a COM in COM client?
The correct way to solve this problem is to add the reference.As follows:Right-click Reference->Add Reference->Browse.
I’m working with a .NET 4 application written mostly in C#. The application has a user interface, but it also has an automation interface that allows the features of the application to be exploited directly from a .NET client. It also supports automation via COM, and for this there are “COM adapter” DLLs that present the classes/methods in the “real” DLLs in a COM-friendly way.
For example, let’s say the API for the bulk of the functionality is in a DLL called “Alpha.DLL”: a .NET client can simply reference that DLL directly, but a separate DLL called “Alpha.Com.DLL” is provided for use by COM clients (e.g. VBA).
There are 3 such COM adapter DLLs, and while two work fine, I simply cannot get the last one to work correctly.
The problem DLL only has two classes defined within it, and while I can instantiate one of them from a COM client such as VBScript, I get an error when I try to instantiate the other. The error I get is:
-2146234304 (0x80131040) Automation Error
I can instantiate the same class from .NET code, just not from a COM client.
I’ve tried using FUSLOGVW.EXE to look for assembly-loading errors, but there don’t seem to be any (and in any case, the fact that I can instantiate the other class from the same DLL suggests that it’s not the DLL itself that can’t be found/loaded?).
I’ve tried attaching a debugger and putting a breakpoint inside the constructor for the offending class, but it doesn’t get hit when I try to instantiate the class from VBScript. (A breakpoint in the constructor of the class that works does get hit).
I’ve checked the registry entries for the class I’m trying to instantiate, and I can’t see any problem. The GUIDs and version numbers all seem to match up.
I’m all out of ideas, and at the end of my tether, and I’d be extremely grateful for some help…
-2146234304 (0x80131040) Automation Error
The common problem with using .NET code from a COM client like VBA is that .NET exceptions get rather difficult to diagnose. You have to make do with an often cryptic HRESULT error code, you don't get the Holy Stack trace to see how code blew up. This exception is a doozy like that, it is FUSION_E_REF_DEF_MISMATCH, you can find these HRESULT codes in the CorError.h SDK include file.
You'd normally get the easier to interpret exception message "The located assembly's manifest definition does not match the assembly reference". And the stack trace to tell you what type caused this exception so you'll know what assembly is the problem. Nothing like that when this failed when called from VBA.
It is otherwise an every-day .NET mishap, the CLR found your assembly but its [AssemblyVersion] does not match the reference assembly's version that your code was compiled with. COM certainly increases the odds that this can go wrong, the version is recorded in the registry when you register the assembly with Regasm.exe. Forgetting to re-register if you do it by hand instead of letting the build system take care of it is a very easy oversight. Also very easy to copy dependent DLLs in the client EXE's directory, so the CLR can find them, and forgetting to update them.
Fuslogvw.exe does show this kind of mishap, hard to guess why you don't see anything. The backup plan is to use SysInternals' Process Monitor. It also shows you how the client is reading the registry, another thing that often goes wrong in COM. And you'll see it locating the DLL from the registry key so you'll have a shot at guessing why it found an old one.
Stay out of trouble by using the GAC, often necessary anyway to help the CLR to find dependent assemblies and to solve COM's rather severe DLL Hell problem. And strongly consider using the .NET 4 AppDomain.FirstChanceException event. Good to log exceptions before they turn undiagnosable in the COM client.
please check first
your com dll is placed into GAC
you dont't forget about regasm
http://www.jagjot.com/2014/01/register-c-vb-net-dll-regasm-gacutil/
check cpu architecture
does your com dll depends on anything outside GAC?
Aaargh. I found the problem. I said in my question:
I’ve checked the registry entries for the class I’m trying to instantiate, and I can’t see any problem. The GUIDs and version numbers all seem to match up.
...which was true. However, what I had not noticed was that in the registry definition of one of my classes, the public key token was wrong.
This explains why one class could be instantiated while the other could not, and possibly why there was nothing in the FUSLOGVW log (because the assembly was loaded OK when an instance of the "good" class was created).
Thanks for your help, Hans and Dimzon.
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 am having a hard time figuring out what I'm doing wrong, so I thought I would ask this at SO. I am trying to automate a measurement task (Qualcomm QXDM), hence would like to access the COM interface exposed by a measurement tool. I wrote the following python code with works perfectly:
from comtypes.client import CreateObject
QXDM = CreateObject("QXDM.Application")
IQXDM2 = QXDM.GetIQXDM2
...
Now, I'm trying to rewrite this is C# because of some specific requirements I have. Here's what I tried:
using QXDM;
QXDM2Class IQXDM = new QXDM2Class();
But when I try to run this, I get:
Retrieving the COM class factory for component with CLSID {6777AAE0-D9D2-4EA7-996B-0EECC68F97D8} failed due to the following error: 80040154.
What am I doing wrong? I can see all the methods and interfaces provided by QXDM in the object browser in Visual Studio.
Edit: Seems like late binding is the only way to do this as Hans suggested. I modified the code to the following:
Type QXDM = Type.GetTypeFromProgID("QXDM.Application");
Object QXDMObject = Activator.CreateInstance(QXDM);
This works. The only trouble is that I need to know what methods and classes are exposed by QXDM, which I think I could figure out using the object browser. Thanks all!
Your code just isn't the same. In the Python code you are clearly using the common "Application" object. In many automation object models, that's the object from which you create other ones. Like IQXDM2 from the GetIQXDM2() method.
Your C# code seems to be trying to create the class that implements IQXDM2 directly. Cannot work, you have to go through the Application interface. It's just like the error message says, there's no "class factory" for that object, the Application interface creates it. Whatever it is called, it is almost always has "application" in the name. Use Object Browser on the interop reference to have a look-see. Look for the one that has the GetIQXDM2 method.
If you don't see anything resembling it then you may have added the wrong DLL. Look in the registry for the name of the right one. Start Regedit.exe and look at HKCR\QXDM.Application. You'll find a CLSID key with a guid. Then look at HKCR\Clsid\{guid} where {guid} is the guid you found. The InprocServer32 key has the DLL name. An out-of-process server uses the LocalServer32 key.
If that doesn't pan out then maybe the COM server was only meant to be used by scripting languages. Very unusual but it can happen. In which case you'll have to use it late-bound in your C# code. That's only easy to do with the C# 4.0 dynamic keyword or VB.NET
It could be the fact that the DLL are dependent on other DLL's.. Debug with ProcExp (http://www.sysinternals.com/) might give some light
I think the COM objects are registered OK, or your Python code would fail and you would not see them on the machine. If this is the this case, your problem is accessing the COM objects from C#. Try the approach specified here.
If the library is already registered,
you can perform the following steps to
have Visual Studio generate an interop
assembly for you
HRESULT 0x80040154 means that the COM class is not registered. Try running regsvr32 on the COM dll.
It could be that you are building for 64 bit. Check your platform target. Make sure it's 32 bit.
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.