COM object accessing problem in C# - c#

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.

Related

VS2017 Reference Manager does not show Managed COM components on the COM tab

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.

Cannot instantiate 1 out of 2 classes in DLL from COM client

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.

C++ calling C# COM interop error: HRESULT 0x80131509

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.

C# IAttachmentExecute

Im trying to use the IAttachmentExecute interface in my C# application. (Using .NET4 and VS2010).
MSDN says it is in Shdocvw.dll (and its members says that IAttachmentExecute is specified in Shobjidl.h / Shobjidl.idl which leads me to a 73db1241-1e85-4581-8e4f-a81e1d0f8c57 GUID.)
When I add a reference to this DLL in VS I get the generated SHDocVw-Namespace, but it does not contain this interface.
Every other attempt with System.Type.GetTypeFromCLSID and System.Activator.CreateInstance or System.Activator.CreateComInstanceFrom fails with the message that there is no type with that GUID.
How can I use that interface?
(Dont think there are alternatives.)
Thanks in advance.
Edit:
Generated interface definition with:
MIDL.exe msdn.microsoft.com/en-us/library/ms680078.aspx
Tlbimp.exe msdn.microsoft.com/en-us/library/tt0cf3sx.aspx
Edit2:
Independent from interface def.:
Tries with CoCreateInstance fail with REGDB_E_CLASSNOTREG, so how could I make my program find IAttachmentExecute. Or am I wrong with my problem and simply miss the solution / my mistake?
This is exactly what I just investigated, here's the blog post: http://joco.name/2010/12/22/windows-antivirus-api-in-net-and-a-com-interop-crash-course/

C# - EasyHook CoCreateInstance

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.

Categories

Resources