Domanda

this is the source of the issue. My answer there was deleted with the hint to start a new question. So, here we go:

I want to pass the managed reference of this to unmanaged code. And then call the managed callback out of the unmanaged callback.

public ref class CReader

with a private field

private:
    [...]
    void *m_pTag;
    [...]

In the constructor of the managed class I initialize the m_pTag like this:

m_pTag = new gcroot<CReader ^>(this);

Later, I pass this void *m_pTag to the unmanaged code. If the unmanaged callback is called, I'm casting the void *m_pTag back to managed reference and call the managed callback

(*(gcroot<CReader ^>*)pTag)->MANAGEDCALLBACKFUNCTION

and there is an exception thrown, if the DLL is used under another AppDomain. The Debugger stops in gcroot.h on line

// don't return T& here because & to gc pointer not yet implemented
// (T should be a pointer anyway).
T operator->() const {
    // gcroot is typesafe, so use static_cast
    return static_cast<T>(__VOIDPTR_TO_GCHANDLE(_handle).Target);
}

with Cannot pass a GCHandle across AppDomains.

My question is, what should I do?

Sincerly,

Sebastian

==================== EDIT ====================

I am now able to reproduce the problem. I've took some screenshots, to show the issue.

1st screenshot: constructor

snd screenshot: callback

The problem is, that the value-member of the struct gcroot in the callback is empty.

Thank you.

Sebastian

==================== EDIT ====================

Push.

È stato utile?

Soluzione

The gcroot<> C++ class is a wrapper that uses the GCHandle class. The constructor calls GCHandle.ToIntPtr() to turn the handle into an opaque pointer, one that you can safely store as a member of an unmanaged struct or C++ class.

The cast then, later, converts that raw pointer back to the handle with the GCHandle.FromIntPtr() method. The GCHandle.Target property gives you the managed object reference back.

GCHandle.FromIntPtr() can indeed fail, the generic exception message is "Cannot pass a GCHandle across AppDomains". This message only fingers the common reason that this fails, it assumes that GCHandle is used in safe code and simply used incorrectly.

The message does not cover the most common reason that this fails in unsafe code. Code that invariably dies with undiagnosable exceptions due to heap corruption. In other words, the gcroot<> member of the C++ class or struct getting overwritten with an arbitrary value. Which of course dooms GCHandle.FromIntPtr(), the CLR can no longer find the handle back from a junk pointer value.

You diagnose this bug the way you diagnose any heap corruption problem. You first make sure that you get a good repro so you can trip the exception reliably. And you set a data breakpoint on the gcroot member. The debugger automatically breaks when the member is written inappropriately, the call stack gives you good idea why this happened.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top