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.
Related
I am not sure the best way to explain this so please leave comments if you do not understand.
Basically, I have a few libraries for various tasks to work with different programs - notification is just one example.
Now, I am building a new program, and I want it to be as lightweight as possible. Whilst I would like to include my notification engine, I do not think many people would actually use its functionality, so, I would rather not include it by default - just as an optional download.
How would I program this?
With unmanaged Dlls and P/Invoke, I can basically wrap the whole lot in a try/catch loop, but I am not sure about the managed version.
So far, the best way I can think of is to check if the DLL file exists upon startup then set a field bool or similar, and every time I would like a notification to be fired, I could do an if/check the bool and fire...
I have seen from the debug window that DLL files are only loaded as they are needed. The program would obviously compile as all components will be visible to the project, but would it run on the end users machine without the DLL?
More importantly, is there a better way of doing this?
I would ideally like to have nothing about notifications in my application and somehow have it so that if the DLL file is downloaded, it adds this functionality externally. It really is not the end of the world to have a few extra bytes calling notification("blabla"); (or similar), but I am thinking a lot further down the line when I have much bigger intentions and just want to know best practices for this sort of thing.
I do not think many people would
actually use its functionality, so, I
would rather not include it by default
- just as an optional download.
Such things are typically described as plugins (or add-ons, or extensions).
Since .NET 4, the standard way to do that is with the Managed Exensibility Framework. It is included in the framework as the System.ComponentModel.Composition assembly and namespace. To get started, it is best to read the MSDN article and the MEF programming guide.
You can use System.Reflection.Assembly and its LoadFile method to dynamically load a DLL. You can then use the methods in Assembly to get Classes, types etc. embedded in the DLL and call them.
If you just check if the .dll exists or load every .dll in a plugin directory you can get what you want.
To your question if the program will run on the user's machine without the dlls already being present - yes , the program would run. As long as you dont do something that needs the runtime to load the classes defined in the dll , it does not matter if the dll is missing from the machine. To the aspect you are looking for regarding loading the dll on demand , I think you are well of using some sort of a configuration and Reflection ( either directly or by some IoC strategy. )
Try to load the plugin at startup.
Instead of checking a boolean all over the place, you can create a delegate field for the notification and initialize it to a no-op function. If loading the plugin succeeds, assign the delegate to the plugin implementation. Then everywhere the event occurs can just call the delegate, without worrying about the fact that the plugin might or might not be available.
Let me clarify:
I have built a class library to be used in several projects. As part of this DLL I want to add a few different custom providers for Owin Cookies by extending CookieAuthenticationProvider so I need to include a reference to Microsoft.Owin.Security.Cookies. This is safe because the newer projects that will use my library also use Microsoft.Owin.Security.Cookies.
However some of the projects are older and dont use Owin etc... Will they blow up if I include the library for other use? Or will they only blow up if I try to use the provider (which I wouldn't since they cant use it).
I want to put some commonly used things in my library without having to reference every one of it's dependent DLL's to every project that uses them. I'm pretty sure what I'm doing is ok but I was hoping somone could tell me before I waste many hours going forward with this. Also if there is a better way I'm all ears.
The rules:
All types which are visible to a given assembly must be declared in assemblies referenced by that assembly.As long as your class library does not actually expose in its public API the types found in the Microsoft.Owin.Security.Cookies assembly, then other assemblies can safely compile with your DLL without referencing that assembly.
A referenced assembly need not be present at runtime, except when code in that assembly is actually needed, i.e. some other code attempts to call that code.
In general, this means that as long as other assemblies which are referencing your assembly and which don't reference Microsoft.Owin.Security.Cookies also don't call any code in your assembly that would then in turn attempt to call code in Microsoft.Owin.Security.Cookies, that assembly need not be present at runtime.
The tricky part on that second point is that what constitutes "calling code in Microsoft.Owin.Security.Cookies" is not always clear. Typically, as long as you don't access the types in the assembly at all, .NET won't try to execute any code in that assembly. But it's not hard to accidently access the types even when they are not necessarily needed (e.g. in initializers, static or otherwise, code that checks for interface implementations, etc.).
If you really want your clients to be able to use your DLL, which references Microsoft.Owin.Security.Cookies, without necessarily needing that DLL to be present at runtime, you will need to be very careful to ensure you've fully supported that scenario. It is possible to do, but it's also not hard to make a mistake.
(I have to admit, I'm surprised that this useful question hasn't already been addressed on Stack Overflow. Seems like it would have come up before by now. But I was unable to find a duplicate, hence the answer above. If anyone is aware of a duplicate I've overlooked, I welcome any suitable notice of that.)
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.
I want to create a single dll that is merged with a 3rd party dll. This means end consumers will only have to deal with 1 dll instead of 2.
For augments sake lets say that the 3rd party dll is nLog. How do I deal with cases where the consumer of the merged dll already has NLog as a reference in their project?
Ideally what I would like to be able to do is change NLog namespace within my project to "XyzNLog", meaning that the user wouldn't need to do any aliasing... Any idea how I might do this?
Now I know I can add aliases to my project for NLog so that I have to refer to it as XyzNLog, but I want the same to carry over to consumers of the merged dll so that there is never a conflict.
UPDATE - Solution
http://blog.mattbrailsford.com/2010/12/10/avoiding-dependency-conflicts-using-ilmerge/
Bingo! So by using ILMerge, it becomes
possible to merge the third-party
libraries DLLs in with the Providers
own DLL, meaning we will only have one
DLL to deploy. But that’s not all, we
can actually go one step further, and
tell ILMerge to internalize all
dependencies. What this does it
converts all the third party classes
to be declared as internal, meaning
they can only be used from within the
final DLL. Woo hoo! problem solved =)
Given this the problem where the consumer of my dll could also have NLog goes away... as my referenced NLog shifts to being all internal! This is exactly what I want.
Does anyone have any feedback or thoughts on this?
I agree with Hans, I would strongly suggest releasing with registering the DLLs separately.
Otherwise, you could be in DLL hell which would drive your consumers away.
You could then devise some clever deploy methods to detect if the DLL is already registered, etc.
I have to agree with #Hans Passant (and here's some info about the oft-discussed DLL hell), but since you've asked the question, I'll try to answer it.
You can bundle the third-party DLL as a resource. Please see this question for details.
As far as your other questions, I'd just expose the relevant classes from a third-party DLL under your own namespace, and maybe use extension methods to provide whatever additional functionality you want.
For instance, you can provide access to NLog's Log() method using a static method in your class, say XyzNLog.Logger.Log(), taking care of initialization, and whatever else internally, inside your code (static constructor or whatever else you fancy up).
Since you load the NLog assembly using the method above, you'll be the only one having access to the embedded NLog assembly directly and the user won't be able to access it. Now, you don't get the benefit of having all classes autoexposed from NLog, you still have to expose them manually in this case.
EDIT: Another approach would be to try to use ILMerge with /internalize flag as described here. You may not be able to completely resolve the issue, but look at this article to see if you can avoid the pitfalls the author described. Spoiler alert: it's not all peaches'n'cream on this one either, but it may work, with enough extra effort.
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.