Make COM-visible .NET assembly release COM reference - c#

I have a C# assembly that is COM-visible. It in turn communicates with a 3rd-party COM application. My problem is that when I release the reference to my C# assembly from VBA, the C# assembly keeps the 3-rd party application open.
' Call assembly from VBA
Dim asm : Set asm = CreateObject("MyCSharpAssembly")
' Get a managed object exposed by the assembly
' managedObject communicates with a 3-rd party COM application
Dim managedObject : Set managedObject = asm.GetManagedObject()
' When I release managedObject from VBA, the 3rd party application stays open.
Set managedObject = Nothing
I have tried implementing the IDisposable pattern in the managedObject and explicitly release the COM object. That works, but I still have to explicitly call the Dispose method from VBA. Just setting the reference to managedObject to Nothing is not enough. This is easily forgotten by VBA coders, who may assume setting the object to Nothing is enough.
Is there any way I can code around that from the C# perspective, or do I have to stick with the explicit Dispose?

You say you implemented the disposal pattern but did you implement the full pattern, not just the public Dispose method, in particular the one with the finalizer, e.g. something similar to below?
~ComplexResourceHolder(){
Dispose(false);
}
Dispose pattern
Please note that this is still non-deterministic; the finalizer may run much much later than the point when VBA consumers sets it to Nothing, but at least it will eventually release the object. If you MUST have a deterministic release, then we need to rethink the approach.
One possible but simplistic option that may simplify the operation is to only take reference to the 3rd party COM component within each of your public method and release it at the end. That might work as long the typical usage don't normally call several methods on same object in succession.
The other simple option is to allow the VBA consumer assign the 3rd party COM object to the managed object so VBA can then manage the lifetime of that component, rather than the .NET.
There may be other options but they will be quite challenging to implement and won't be as foolproof.

Related

Marshaling unmanaged dll in C# through ComInterop without registering DLL

I have an unmanaged DLL which I'm currently calling from C# using a COM Class Wrapper.
[ComImport(), Guid("75E81043-CAD5-11D3-800D-00105A5E2FA0")]
public class MyObject { }
[ComImport(), Guid("75E81042-CAD5-11D3-800D-00105A5E2FA0"),
InterfaceType(ComInterfaceType.InterfaceIsDual)]
public interface MyInterface
{
string EncryptString([In, MarshalAs(UnmanagedType.BStr)] string bstrOrginal);
}
Then to call:
MyInterface obj = (MyInterface)new MyObject();
string crypt = obj.EncryptString("something");
This works, the return value is as I expect. However, it requires that the dll is registered with regsvr32.
I'm looking for a way to do this without the requirement of needing to regsvr32. Preferably, by just having a copy of the dll available. It is worth noting, I have the source for the unmanaged dll, and the ability to modify it if necessary.
A shove in the right direction would be greatly appreciated.
I have wanted to do the same thing myself. As Jim mentions it is possible to get the address of DllRegisterServer and call it, but that still modifies your registry with the various entries. If you don't want to do this (for example, you might not have the necessary privileges to write to the Registry), then there is another way.
Any DLL that houses one or more in-process COM objects must expose the DllGetClassObject function. This function is used to acquire an instance of the COM class factory that is used to create a COM object. What you need to do is:
Load the library (DLL) that houses the desired COM object
Locate the DllGetClassObject function
Call DllGetClassObject, passing it the CLSID of the desired COM object, this will return an IClassFactory instance.
Call the CreateInstance method on the class factory to get an instance of the COM object.
Cast the returned object to the interface you wish to use.
Note that there be dragons with this approach -- it is fairly low level. If you get anything wrong you will experience access violation exceptions or worse. (For instance, your interface declaration has to exactly match the COM interface).
I have included some sample code at gist which you might like to use if you want to go this way.
Using this code would look something like this:
// Load the library. You dispose this after you are finished with
// all of your COM objects.
var library = new LibraryModule();
library.Load("mylibrary.dll"); or whatever your dll is called
var clsid = new Guid("75E81043-CAD5-11D3-800D-00105A5E2FA0");
var myObject = (MyInterface)ComHelper.CreateInstance(library, clsid);
Just note that if you dispose of the LibraryModule object, then this will unload the DLL. Depending on your needs, you might just assign the value to a static field so that it exists for the lifetime of the program.
You need to set up your unmanaged DLL for registration-free COM. There's a pretty complete walkthrough here, with plenty of examples. Among other things, it involves using manifests to point to the file, side-by-side assemblies, and some interop.
An important note in the walkthrough is that registration is required for the initial setup of the client, but not later.
If your DLL has to be a COM object, then it must be registered. This registration does not need to be performed by regsvr32. All regsvr32 does is load the DLL, get the address of DllRegisterServer and call it it. DllRegisterServer adds the necessary entries to the registry for an application to use the object.
If your DLL does not need to be a COM object, you can modify it to simply export the function(s) you need and p-invoke it(them).

Force .NET type to instantiate as COM

Is it possible to force a COM Visible .NET assembly to instantiate as System.__ComObject, rather than it's .NET type? The reason I ask is, part of my app uses 3rd party COM objects, but some of those, when written in .NET, get instantiated as their .NET types and break the object handlers I've created. I have no control over methods used to create the COM objects. Currently I'm using the following to create the objects.
Type comType = Type.GetTypeFromCLSID(objectGUID);
comObject = Activator.CreateInstance(comType);
Thanks!
Actually, no.
The COM activation of managed objects is done in the same AppDomain as the caller. IMHO, This is a nasty issue with COM interop in .NET. There are several questions here on SO with all sorts of attempts at making this work. I can attest that I've tried most of these to no avail. Your COM API provider SHOULD be producing a PIA (Primary Interop Assembly) to ensure forward type compatibility. If they are not, you are going to have some nasty issues.
The only workaround I'm aware of as a client is to only use the 'object' type. Everything would need to go through reflection. Some things you still can't do. For example, you would not be able to implement an interface.
I havn't a clue why this works this way. It's caused me no end of problems producing a viable api for both unmanaged and managed code.

C# .Net4: Properly disposing (dynamic) COM objects

I need to automate office documents (Word & Excel) from my .Net4 app.
Since I can't really force my users to use a specific Office version I don't use interop assemblies or tlbimp, so my project doesn't contain any additional references nor will the whole app fail if Office is not installed (feature just won't be available).
Instead I ask the system which COM server can handle "Word.Application" or "Excel.Application":
dynamic app = Activator.CreateInstance(Type.GetTypeFromProgID("Word.Application"));
app.Foo();
I'm concerned how to properly dispose of the "app" object when I'm done with it, since now I have two internal management systems to worry about (COM reference counting and .Net reference tracking). Ideally I should be able to wrap the dynamic app object into a disposable wrapper class and to be sure that underlying COM object is unreferenced when the wrapper is disposed of.
EDIT: Also I'd like to know how to properly make the COM object "live on" when I'm done with it, since Word is in a separate process. I should be able to instantiate Word app, automate it and then free all my references but the Word app should stay open.
I would definitely implement IDisposable and call Marshal.ReleaseComObject within it. Make sure you correctly implement the disposable pattern to avoid releasing the COM object more than once and reducing the reference count further than you should, although this may not matter if you are only going to create one instance of the COM object. I have implemented this pattern successfully in the past so you are definitely heading in the right direction.
I think Word will remain open even when the last COM object is reference counted to 0 in your app, unless you explicitly ask it to exit. ( Word.Application.Close() ?)

Replacing a C++ ActiveX component with a .NET implementation?

I have existing managed and unmanaged software using an ActiveX component supplied by a third party to perform some communications, but it is now required that this communication be routed through my application.
Ideally I'd be able to install a .NET component which will expose exactly the same interface, and will be usable as a drop-in replacement.
However, I am running into the limits of my understanding of COM, which admittedly is quite minimal.
How best to ensure that my implementation of the interface is 100% binary compatible with the existing object?
How do I ensure that applications use my implementation of the interface instead of the legacy implementation? Is it simply a matter of registering my implementation, and unregistering the legacy one?
How do I ensure it's a "drop-in" replacement, and requires no changes to existing software?
How do I ensure unmanaged code can use it without issue?
Note: I am able to require that .NET 4.0 be used, if that will make things simpler.
Edit: Bounty will be moved here How to debug why a VB6 application using my .NET ActiveX control does not register for events? after 2 days.
Use the type library of the ActiveX component. Import it with Tlbimp.exe to get the interop library, you probably already have it if you use this component yourself. Implement your own code by inheriting the interfaces in that type library.
Your implementation must use the exact same GUIDs and ProgIDs as the ActiveX component. Use OleView.exe, File + View Typelib and select the ActiveX DLL to see the GUIDs. The ProgIDs are more difficult, best thing to do is to watch how the registry is modified with the SysInternals' ProcMon utility when you register the ActiveX DLL with Regsvr32.exe. Ultimately, the exact same changes need to be made by Regasm.exe when you register your replacement.
As point 2.
Same, the registration gets unmanaged code to use yours instead.
To make this work out well, you really have to know what the interfaces do. You cannot make this work if the ActiveX component is actually an out-of-process server (an EXE).
Well, I've gotten a lot further along with this, but I seem to have encountered an intractable problem.
The object I am replacing uses COM events. When one of the client applications (VB6 I believe, as depends.exe tells me it uses msvbvm60.dll) instantiates and uses my replacement, it does not register for any of the events, and unfortunately, the way it works is that after a particular method call has completed, the client application does nothing until an event fires.
Note: My replacement ActiveX control inherits from System.Windows.Forms.Control, and sets MiscOptions of 131457 on the coclass registry entries as suggested by http://ondotnet.com/pub/a/dotnet/2003/01/20/winformshosting.html, the reason being that the thing I am replacing was an honest to goodness ActiveX control, and I could not get these existing clients to instantiate my object successfully without any code changes at all until I inherited from WinForms control.
I have tried the approach where my coclass declares public events with the same name as the interface specified by ComSourceInterfaces, this works 100% from a C# app that uses AxHost, events are triggered.
I have also tried instead to implement IConnectionPointContainer and all supporting interfaces on my replacement control, and this works 100% from a C# app, but in the VB app, it never actually attempts to Advise() the connection point of the client sink interface to call, it only calls Unadvise() with an invalid cookie value of 0.
One issue with the typelib that I have noticed is that I cannot get tlbexp.exe to export one of the properties on the coclass interface as OLE_HANDLE, it just ends up being a long in the TLB generated from the assembly (this TLB is referenced by the TypeLib entry in the registry). Could this cause issues with eventing?
Any ideas how to debug this?

How to marshal an object and its content (also objects)

I have a question for which I suspect the answer is a bit complex. At this moment I am programming a DLL (class library) in C#. This DLL uses a 3rd party library and therefore deals with 3rd party objects of which I do not have the source code. Now I am planning to create another DLL, which is going to be used in a later stadium in my application. This second DLL should use the 3rd party objects (with corresponding object states) created by the first DLL.
Luckily the 3rd party objects extend the MarshalByRefObject class. I can marshal the objects using System.Runtime.Remoting.Marshal(...). I then serialize the objects using a BinaryFormatter and store the objects as a byte[] array. All goes well. I can deserialize and unmarshal in a the opposite way and end up with my original 3rd party objects...so it appears...
Nevertheless, when calling methods on my 3rd party deserialized objects I get object internal exceptions. Normally these methods return other 3rd party objects, but (obviously - I guess) now these objects are missing because they weren't serialized.
Now my global question: how would I go about marshalling/serializing all the objects which my 3rd party objects reference...and cascade down the "reference tree" to obtain a full and complete serialized object? Right now my guess is to preprocess: obtain all the objects and build my own custom object and serialize it. But I'm hoping there is some other way...
It's a bit unclear to me why you need two C# assemblies to deal with the third party DLL, isn't the first class library you created already interfacing your third party DLL? Here are some general answers when dealing with native libraries. It would help if the third party library is publicly available, to see what interfaces it uses.
If the native DLL exposes its functions you can use P/Invoke calls and in most cases the marshaling will be done for you;
If the native DLL exposes its methods as COM interfaces, you can create COM wrappers;
If you must do everything by hand, you might need to use the LayoutKind and FieldOffsetAttribute or the StructLayoutAttribute, these attribute help you tell the compiler how the internal memory layout of the object is;
Have a look at MarshalAsAttribute and UnmanagedType, it may be just what you need.

Categories