2017-10-18 5 views
-1

Ich bin eine verwaltete DLL für die Verwendung in nicht verwalteten Umgebung (C/C++ App - FreeRDP) erstellen. Interop funktioniert in den meisten Fällen gut, aber in einem bestimmten bin ich nicht in der Lage, einen Zeiger auf struct zu übergeben. In der API habe ich eine Struktur:Übergeben eines Struct-Zeigers in C# -Operop führt zu NULL

typedef struct _IWTSListenerCallback IWTSListenerCallback; 
struct _IWTSListenerCallback 
{ 
    UINT(*OnNewChannelConnection)(IWTSListenerCallback* pListenerCallback, 
           IWTSVirtualChannel* pChannel, 
           BYTE* Data, 
           BOOL* pbAccept, 
           IWTSVirtualChannelCallback** ppCallback); 
}; 

Neben einer Funktion Ich rufe:

UINT(*CreateListener)(IWTSVirtualChannelManager* pChannelMgr, 
         const char* pszChannelName, 
         ULONG ulFlags, 
         IWTSListenerCallback* pListenerCallback); 

Sowohl in C# übersetzt:

[UnmanagedFunctionPointer(CallingConvention.Cdecl)] 
public delegate uint ListenerCallbackNewConnectionDelegate(IntPtr listenerCallback, IntPtr channel, [MarshalAs(UnmanagedType.LPArray)] byte[] data, IntPtr accept, ref IntPtr channelCallback); 

[StructLayout(LayoutKind.Sequential)] 
public struct IWTSListenerCallback 
{ 
    [MarshalAs(UnmanagedType.FunctionPtr)] 
    public ListenerCallbackNewConnectionDelegate OnNewChannelConnection; 
} 

[UnmanagedFunctionPointer(CallingConvention.Cdecl)] 
public delegate uint ChannelManagerCreateListenerDelegate(IntPtr channelManager, [MarshalAs(UnmanagedType.LPStr)] string channelName, ulong flags, IntPtr listenerCallback); 

[MarshalAs(UnmanagedType.FunctionPtr)] 
public ChannelManagerCreateListenerDelegate CreateListener; 

Und Ausführungscode:

var callback = new IWTSListenerCallback(); 
callback.OnNewChannelConnection = NewChannelConnection; 
var pCallback = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(IWTSListenerCallback))); 
Marshal.StructureToPtr(callback, pCallback, false); 
var ret = channelManager.CreateListener(pChannelManager, "TestChannel", 0, pCallback); 

Und während pChannelManager (der ein Zeiger ist, den ich aus nicht verwaltetem Code erhalte, der meine DLL aufruft) und die Zeichenfolge ohne Probleme gesendet werden, wird der hier erstellte Zeiger (pCallback) erfolgreich in C# zugewiesen, aber führt zu NULL in nicht verwaltetem Code .

Ich nehme an, das Problem ist entweder mit, wie ich die Struktur definiert, oder wie ich die Funktion definiert (obwohl die Funktion erfolgreich in nicht verwaltetem Code aufgerufen wird). Ich benutze die Methode, um den Zeiger genau wie in einem anderen Teil der DLL zu erstellen, und es funktioniert tadellos dort, wenn es an die nicht verwaltete Funktion übergeben wird.

EDIT: von @jdweng Vorschlag:

[StructLayout(LayoutKind.Sequential)] 
public struct TestCall 
{ 
    public IntPtr channelManager; 
    [MarshalAs(UnmanagedType.LPStr)] 
    public string channelName; 
    public ulong flags; 
    public IntPtr listenerCallback; 
} 

[UnmanagedFunctionPointer(CallingConvention.Cdecl)] 
public delegate uint ChannelManagerCreateListenerDelegate(IntPtr testStructure); 

var test = new TestCall(); 
test.channelManager = pChannelManager; 
test.channelName = "TestChannel"; 
test.flags = 0; 
test.listenerCallback = pCallback; 
var pTest = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(FreeRDPTypes.TestCall))); 
Marshal.StructureToPtr(test, pTest, false); 
var ret = channelManager.CreateListener(pTest); 

Hat nicht funktioniert.

EDIT2: Workaround! Nur wenn Sie Zugriff auf den ursprünglichen nicht verwalteten Code haben. Ich habe die Funktionsargumente so umgeordnet, dass die Strukturzeiger zuerst lauten:

Und es funktioniert! Wahrscheinlich ein Problem mit Offset.

+0

Sie benötigen eine C# -Klasse für OnNewChannelConnection. Der Rückruf wird einen Zeiger auf die Struktur zurückgeben. Sie sollten c-Parameteraufrufe angeben. Der Standard ist die Windows-Aufrufkonvention. In c-Sprache wird die Parameterliste in umgekehrter Reihenfolge auf den Stack geschoben. Welches wäre rückwärts von der Strukturreihenfolge. Damit Sie Code arbeiten können, müssen Sie die Reihenfolge der Parameterliste ändern. – jdweng

+0

@jdweng Ich bin mir nicht sicher, ob ich das richtig verstanden habe, aber die Methoden sind mit der Aufrufkonvention __cdecl (über die Delegierten) angegeben. Auch alle anderen Parameter durchlaufen ohne Verzerrung nur diesen einen Zeiger. –

+0

Ich habe die Cdecl. Es tut uns leid. Die ListenerCallbackNewConnectionDelegate-Methode ist die einzige, die eine Parameterliste übergibt. Eine Struktur hat die Elemente in der Reihenfolge der Deklarationen. Eine Parameterliste beim Aufrufen einer Funktion erzeugt auch eine Struktur in der Reihenfolge der Parameter. Der Stapel beginnt am oberen Rand des Speichers und bewegt sich nach unten, so dass die Parameter in der gleichen Reihenfolge wie die Struktur angezeigt werden oben im Speicher). Das Problem ist, wenn die Funktion die Werte auf dem Stapel zurückgibt, die verloren gehen.Ich werde arbeiten, wenn ich rufe, aber nicht arbeiten, wenn ich zurückkomme. – jdweng

Antwort

0

Es war eine Frage des Offset. C/C++ ULONG war typedef unsigned long, die ich fälschlicherweise angenommen C# ulong, aber in der Tat der erste ist 4 Bytes in Visual, während der andere ist 8 Bytes, was in 4 Bytes Offset ergab. Fixed durch Ändern ulong zu uint und Hinzufügen [MarshalAs(UnmanagedType.U4)] für ein gutes Maß. Endgültiges Aussehen der Funktion, die ich innerhalb C# angerufen habe:

[UnmanagedFunctionPointer(CallingConvention.Cdecl)] 
public delegate uint ChannelManagerCreateListenerDelegate(IntPtr channelManager, [MarshalAs(UnmanagedType.LPStr)] string channelName, [MarshalAs(UnmanagedType.U4)] uint flags, IntPtr listenerCallback); 
Verwandte Themen