I have an managed .NET 2.0 dll that provides an IDispatch inteface and calls an IDispatch interface. Reading this question this question:
When to use ReleaseComObject vs FinalReleaseComObject?
It sounds as though I should be waiting for Garbage Collection to release my COM object references rather than attempting to count them myself. However when I do this, the unmanaged code calling my DLL throws an error (I don't have the source code to debug what the error is) but it indicates it's an unauthorized virtual memory access problem. Adding the FinalReleaseCOMObject stops this error.
Should I be using FinalReleaseCOMObject then?
EDIT: forgot to mention when the managed code is replaced by an unmanaged dll it does not exhibit the error.
Related
I try to build a little tensorflow application with TensorFlowSharp and sometimes I recieve this exception:
Managed Debugging Assistant 'CallbackOnCollectedDelegate'
For the function TensorFlowSharp!TensorFlow.TFBuffer+BufferReleaseFunc::Invoke
I tried to find out what it means but I didn't fully understand the explanations. This is the part of the code where the exception is thrown:
var graph = new TFGraph();
var model = File.ReadAllBytes(ModelsFile);
graph.Import(model, "");
Does somebody know what I should do to prevent this exception?
Bruno
I assume this is a bug in TensorflowSharp.
The error looks like a usually inconsistent access violation in the CLR code (occurs usually only under heavy load or on a random number of attempts). Citing from Microsoft docs:
The callbackOnCollectedDelegate managed debugging assistant (MDA) is
activated if a delegate is marshaled from managed to unmanaged code as
a function pointer and a callback is placed on that function pointer
after the delegate has been garbage collected.
This type of error occurs when a delegate from which the function pointer was created and exposed to unmanaged code was garbage collected. When the unmanaged component tries to call on the function pointer, it generates an access violation. The failure appears random because it depends on when garbage collection occurs.
The resolution can be difficult, since once a delegate has been marshaled out as an unmanaged function pointer, the garbage collector cannot track its lifetime. Instead, it is required to keep a reference to the delegate for the lifetime of the unmanaged function pointer. In order to do this, the faulty delegate that was collected, has to be identified in TensorFlowShapr's code (or your code).
You can also enable the gcUnmanagedToManaged MDA to force a garbage
collection before every callback into the runtime. This will remove
the uncertainty introduced by the garbage collection by ensuring that
a garbage collection always occurs before the callback. Once you know
what delegate was collected, change your code to keep a reference to
that delegate on the managed side for the lifetime of the marshaled
unmanaged function pointer.
So, I guess it's best to report this to the maker of the library.
I see only one good candidate for this bug, a delegate in Buffer.cs. But Miguel already fixed this bug on July 27th, the diff is here. So just make sure to update your copy. If you obtained it from Nuget then ensure you have at least version 1.30
I just found out I could unload DLL which is implicit linking with the function FreeLibrary() in C#. I remember I couldn't do this in C++, but it works well in my simple test project. I wonder if this would be okay in my real projects too. Is it safe to use this method?
Fairly vague, I'll have to assume you talk about DLLs that got loaded through pinvoke. Yes, there is no protection against calling FreeLibrary() twice. Works in C++ as well btw for a DLL that's loaded explicitly. Not for implicitly loaded DLLs, they get a reference count of "infinity".
The pinvoke marshaller uses LoadLibrary() under the hood, happens when the very first [DllImport] function gets executed. The OS loader simply keeps a reference count, every LoadLibrary() call increments it and FreeLibrary() decrements. When it reaches 0 then it gets unloaded. So if you pinvoke LoadLibrary() yourself and call FreeLibrary() twice then the DLL does get unloaded. The virtual address space formerly used by the memory-mapped file that maps the code in the DLL is released and can be used again by subsequent allocations.
Safe, no, that's not a word that jumps to mind. When you accidentally call an entrypoint in the DLL then your program is going to behave very poorly. The pinvoke marshaller cannot do anything about it, the stub for the native method was already generated. Odds for an AccessViolationException are decent but not guaranteed. Arbitrary code execution is technically possible.
The only truly safe way to do this is to ensure that the AppDomain that contains the pinvoke code is unloaded. You get no help with this, just a rule you have to implement yourself.
I've a class in my .NET 3.5 C# WinForms application which has five methods.
Each method uses different sets of C++ COM interfaces.
Am using Marshal.FinalReleaseCOMObject for cleaning up these COM objects. This code works fine on this .NET platform without any issues.
But when I move this application to .NET 4.0, I start getting this error in one of these methods at a line where I cast a variable from ICOMInterface1 to ICOMInterface2, i.e.:
ICOMInterface1 myVar= obj as ICOMInterface2;
COM object that has been separated from its underlying RCW cannot be
used.
And if I remove the line where am using Marshal.FinalReleaseCOMObject, I don't get this error.
What am I missing here? And how do I clean up these unmanaged COM objects from the memory on .NET 4.0 platform?
The simple answer is to never use Marshal.FinalReleaseComObject unless you absolutely must. And if you do, there are some additional rules you must follow.
When a COM object is used in .NET, the runtime creates what's known as a "RCW" or "runtime callable wrapper" for that object. This RCW is just a normal object that holds a COM reference on the object. When this object is garbage collected, it will call IUnknown::Release() on the COM object, as you'd expect. What this means is unless your COM object requires that the last Release() is done at a very certain moment in time, just let the garbage collector take care of it. Many COM objects fall into this case, so absolutely verify that you must manage the call to Release() carefully.
So when you're calling FinalReleaseComObject, that is essentially decrementing the reference the RCW has on the COM object until it hits zero and the RCW then releases the COM object. At this point, this RCW is now zombied, and any use of it will give the exception you've seen. The CLR (by default) only creates a single RCW for any underlying COM object, so this means if the COM API you're using returned the same object twice, it's just going to have a single RCW. Calling FinalReleaseComObjectwould mean that suddenly all uses of that RCW are toast.
The only way to guarantee you have a unique Marshal.GetUniqueObjectForIUnknown, which prevents any RCW sharing. But like I said earlier, in most COM APIs this isn't neccessary to do in the first place, so just don't do it.
Paul Harrington wrote up a good blog post about [Final]ReleaseComObject and it's evils. It's a dangerous weapon that unless needed will only hurt you. Since you're asking this question, I'd suspect you don't actually need to be calling it at all. :-)
My managed application consumes legacy functionality exposed by a native COM dll via a managed wrapper. I cannot change the COM dll nor its managed wrapper.
The managed wrapper is in essence automatically generated by a script, given the COM type declarations as input.
The COM layer will internally access network, file system, graphics and other unmanaged resources.
Most managed types in the wrapper are created using factory-like methods.
Most managed types in the wrapper has no way of manually trigger clean-up or resource-release.
No managed type in the wrapper implements IDisposable.
No managed type in the wrapper explicitly implements a finalizer.
I feel that I'm missing a lot in my application layer from this API that I'd like to see in order to ensure that unmanaged resources are properly being released.
The question is if I'm right in my understanding of this:
Given that the application layer no longer references an object from the managed wrapper, there's no way that underlying native objects used by that managed object can ever be released until an exposed cleanup method explicitly does so.
Am I correct in that?
No that is not quite correct. When the garbage collector collects managed types that are referencing the COM types those references will be dereferenced using the COM Release call. At ths point the unmanaged resources will be released assuming nothing else is reference the COM object instance.
However unless you are calling GC.Collect() (a fairly draconian thing to do) then it is possible for those unmanaged resource are held onto for far, far longer than necessary.
Will using GetComInterfaceForObject and passing the returned IntPtr to unmanaged code keep the managed object from being moved in memory? Or does the clr somehow maintain that ptr? Note that the unmanaged code will use this for the lifetime of the program, and I need to make sure the managed object is not being moved by the GC.(At least I think that's right?)
EDIT - Alright I found some info and I am thinking that this may be the answer. It deals with delegates, but I would have to believe calling GetComInterfaceForObject does something along the same lines.
Source of the Following text
"Managed Delegates can be marshaled to unmanaged code,
where they are exposed as unmanaged function pointers. Calls on those
pointers will perform an unmanaged to managed transition; a change in
calling convention; entry into the correct AppDomain; and any necessary
argument marshaling. Clearly the unmanaged function pointer must refer to a
fixed address. It would be a disaster if the GC were relocating that! This
leads many applications to create a pinning handle for the delegate. This
is completely unnecessary. The unmanaged function pointer actually refers
to a native code stub that we dynamically generate to perform the transition
& marshaling. This stub exists in fixed memory outside of the GC heap.
However, the application is responsible for somehow extending the lifetime
of the delegate until no more calls will occur from unmanaged code. The
lifetime of the native code stub is directly related to the lifetime of the
delegate. Once the delegate is collected, subsequent calls via the
unmanaged function pointer will crash or otherwise corrupt the process. In
our recent release, we added a Customer Debug Probe which allows you to
cleanly detect this all too common bug in your code. If you havent
started using Customer Debug Probes during development, please take a look!"
As your edit states (about delegates), your managed object doesn't need to be pinned, since GetComInterfaceForObject returns a "pinned" pointer that calls through to the correct managed object. However, you will need to make sure that the managed object lives for as long as the COM clients are using the unmanaged pointer to it.