c# p/invoke Schwierigkeiten marshalling Zeiger
Ich bin versucht zu rufen, in eine native .dll aus c# mit p/invoke. Ich bin in der Lage, um den Anruf zu machen (kein Absturz, der Funktion einen Wert zurückgibt), aber der return-code bedeutet "Ein-Zeiger-parameter nicht zugänglich Gedächtnis." Ich habe gegriffen zu Versuch und Irrtum um dieses Problem zu lösen, aber ich habe nicht alle Fortschritte so weit.
Hier ist die Signatur der nativen Funktion rufe ich:
LONG extern WINAPI MyFunction ( LPSTR lpszLogicalName, //input
HANDLE hApp, //input
LPSTR lpszAppID, //input
DWORD dwTraceLevel, //input
DWORD dwTimeOut, //input
DWORD dwSrvcVersionsRequired, //input
LPWFSVERSION lpSrvcVersion, //WFSVERSION*, output
LPWFSVERSION lpSPIVersion, //WFSVERSION*, output
LPHSERVICE lphService //unsigned short*, output
);
Hier ist die importierte Signatur in C#:
[DllImport("my.dll")]
public static extern int MyFunction( [MarshalAs(UnmanagedType.LPStr)]
string logicalName,
IntPtr hApp,
[MarshalAs(UnmanagedType.LPStr)]
string appID,
int traceLevel,
int timeout,
int srvcVersionsRequired,
[Out] WFSVersion srvcVersion,
[Out] WFSVersion spiVersion,
[Out] UInt16 hService
);
Hier ist die C-definition der WFSVERSION:
typedef struct _wfsversion
{
WORD wVersion;
WORD wLowVersion;
WORD wHighVersion;
CHAR szDescription[257];
CHAR szSystemStatus[257];
} WFSVERSION, * LPWFSVERSION;
Hier ist das C# definition der WFSVersion:
[StructLayout(LayoutKind.Sequential)]
public class WFSVersion
{
public Int16 wVersion;
public Int16 wLowVersion;
public Int16 wHighVersion;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 257)]
public char[] szDescription;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 257)]
public char[] szSystemStatus;
}
Hier ist der Aufruf von MyFunction von C#:
WFSVersion srvcVersionInfo = new WFSVersion();
WFSVersion spiVersionInfo = new WFSVersion();
UInt16 hService = 0;
IntPtr hApp = IntPtr.Zero;
string logicalServiceName = tbServiceName.Text;
int openResult = MyFunction(logicalServiceName, hApp, null, 0,
XFSConstants.WFS_INDEFINITE_WAIT,
0x00000004, srvcVersionInfo, spiVersionInfo,
hService);
Wie gesagt, dieser Aufruf gibt, aber der Rückgabewert ist ein Fehlercode, der angibt, "Einen Zeiger-parameter nicht zugänglich Gedächtnis." Ich muss etwas falsch mit Parameter 1,3,7,8, oder 9. Allerdings hab ich gemacht, erfolgreiche Gespräche, um weitere Funktionen in diesem .dll, die erforderlich WFSVERSION* als Parameter, also ich glaube nicht, Parameter 7 oder 8 sind das Problem hier.
Ich würde schätzen alle Gedanken, die Sie möglicherweise über die Ursache dieses Problems, oder jede Konstruktive Kritik an meinem code. Dies ist meine erste Erfahrung mit P/Invoke, also ich bin mir nicht sicher, wo zu beginnen. Gibt es eine Möglichkeit, um das Problem einzugrenzen, oder Versuch ein Fehler meine einzige option?
- pinvoke.net kann hilfreich sein
- Sie haben einen null-Zeiger im Dritten argument - ist, dass der richtige code?
- Ja, ein null-Zeiger ist gültig als drittes argument.
Du musst angemeldet sein, um einen Kommentar abzugeben.
Haben Sie zwei offensichtliche Fehler hier. In deinem struct definition, die Sie verwenden sollten, byte[] statt char[] für szDescription und szSystemStatus.
Auch die letzten parameter in den pInvoke-Aufruf ist nicht ein Zeiger. Wenn Sie Ihren Aufruf Meinefunktion hService ist null und daher einen ungültigen Zeiger so weit wie die Funktion betroffen ist. [Aus] ist ein Marshalling-Richtlinie, erzählt die Laufzeit, Wann und wo das kopieren von Daten kein Indikator, den der parameter ist ein Zeiger. Was Sie brauchen, ist zu ändern Sie [Aus], um out-oder ref-dieser erzählt die Laufzeit, die hService ist ein Zeiger:
Einige Ideen:
C# WFSVersion Klasse muss wohl ein
struct
. Ich weiß nicht, ob die P/Invoke-marshaller interessiert, aber ich habe immer gesehen, structs verwendet.Charakter Größe könnte ein Problem sein.
C s CHAR ist 8 bit breit (ANSI), und .Net-System.Char ist 16 bit breit (Unicode). Geben Sie den marshaller so viel info wie möglich, damit es nicht die korrekte Konvertierung, versuchen Sie, "CharSet = CharSet.Ansi" auf die DllImport und Attribute StructLayout und ändern Sie den string-Deklarationen in WFSVersion:
Weiteres Problem könnte sein, die Daten Ausrichtung in der structs. Wenn keine Ausrichtung angegeben wird, wenn die C-struct kompiliert wurde, werden die Datenelemente in die Struktur wahrscheinlich waren ausgerichtet auf eine ein-oder zwei-byte-Grenze. Versuchen Sie es mit
Pack
im WFSVersion das StructLayout-Attribut:Und einige Fragen:
War MyFunction aufgerufen, die von nicht-C-code? Der ursprüngliche Autor geschrieben haben könnte-code, übernimmt die übergebenen Daten zugeordnet wird, die mit der C-Laufzeit-Speicher-manager.
Macht-code in der C-DLL verwenden, die Zeiger übergeben, um es für die spätere Verarbeitung nach MyFunction zurückgegeben hat? Wenn dem so ist - und vorausgesetzt, es ist möglich/ratsam/sane, vorwärts zu gehen mit solch einer situation - es kann notwendig sein, um "pin" die Strukturen übergeben MyFunction mit dem
fixed
Schlüsselwort. Plus gibt es wahrscheinlich Sicherheits-Probleme zu behandeln.Ich bin mir nicht sicher, was genau das Problem mit dieser ist, aber hoffentlich wird dies ein Ausgangspunkt sein.
Versuchen, in den Optionen auf die DllImport-Attribut, das könnte zu tun mit der Anordnung der Saiten.
CharSet-option ist die ich denke, Sie brauchen können.
Habe ich gefunden AppVerifier nützlich für das aufspüren von P/Invoke-Marschall Themen vor. Es ist kostenlos und von Microsoft.
Meine Vermutung wäre, dass einer der Zeiger (lpSrvcVersion, lpSPIVersion oder lphService) ist nicht erreichbar von Ihr .NET-Anwendung. Können Sie versuchen, ändern Sie die DLL (wenn es dir gehört) und sehen, ob Sie es zu arbeiten, ohne die Zeiger? (Ich weiß, du wirst haben, um Sie später wieder hinzufügen, aber man kann zumindest eingrenzen, wo das problem.)
Sind Sie sicher, dass es kein hApp? Das sieht aus wie ein input-parameter, das handle der anfragenden Anwendung. Schnelle google...ja, es gibt eine Funktion um eine app zu erstellen Griff, und ein default-parameter, die Sie verwenden können.