[英]c# p/invoke difficulties marshalling pointers
我試圖使用p / invoke從c#調用本機.dll。 我能夠進行調用(沒有崩潰,函數返回一個值),但返回代碼表示“指針參數不指向可訪問的內存”。 為了解決這個問題,我采用了反復試驗,但到目前為止我還沒有取得任何進展。
這是我正在調用的本機函數的簽名:
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
);
這是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
);
這是WFSVERSION的C定義:
typedef struct _wfsversion
{
WORD wVersion;
WORD wLowVersion;
WORD wHighVersion;
CHAR szDescription[257];
CHAR szSystemStatus[257];
} WFSVERSION, * LPWFSVERSION;
這是WFSVersion的C#定義:
[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;
}
這是從C#調用MyFunction:
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);
正如我所說,此調用返回,但返回值是一個錯誤代碼,指示“指針參數不指向可訪問的內存”。 我必須對參數1,3,7,8或9做錯。但是,我已成功調用此.dll中的其他函數,這需要WFSVERSION *作為參數,所以我不認為參數7或8是這里的問題。
如果您對此問題的原因有任何想法,或者對我的代碼有任何建設性的批評,我將不勝感激。 這是我第一次使用P / Invoke,所以我不知道從哪里開始。 有沒有辦法縮小問題范圍,或者試錯是我唯一的選擇?
你有兩個明顯的錯誤。 在你的struct定義中,你應該使用byte []而不是char []用於szDescription和szSystemStatus。
pInvoke調用中的最后一個參數也不是指針。 當你調用MyFunction時,hService為零,因此就函數而言是無效指針。 [Out]是一個Marshaling指令,告訴運行時復制數據的時間和位置,而不是參數是指針的指示符。 你需要的是將[Out]改為out或ref這告訴運行時hService是一個指針:
[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);
一些想法:
C#WFSVersion類應該是一個struct
。 我不知道P / Invoke marshaller是否關心,但我總是看到結構使用。
字符大小可能是個問題。
C的CHAR是8位寬(ANSI),而.Net的System.Char是16位寬(Unicode)。 要為編組提供盡可能多的信息以便進行正確的轉換,請嘗試將“CharSet = CharSet.Ansi”添加到DllImport和StructLayout屬性,並更改WFSVersion中的字符串聲明:
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 257)] public string szDescription; [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 257)] public string szSystemStatus;
另一個問題可能是結構中的數據對齊。 如果在編譯C結構時未指定對齊,則結構中的數據元素可能在一個或兩個字節邊界上對齊。 嘗試在WFSVersion的StructLayout屬性中使用Pack
:
[StructLayout(LayoutKind.Sequential, Pack = 1, CharSet = CharSet.Ansi)] // Try pack values of 2, 4 and 8 if 1 doesn't work.
還有一些問題:
MyFunction是否打算從非C代碼調用? 原作者可能編寫了代碼,假定傳入的數據是使用C運行時內存管理器分配的。
在我的函數返回后,C DLL中的代碼是否使用傳遞給它的指針以便以后處理? 如果是這樣 - 並且假設可能/明智/理智地繼續這種情況 - 可能需要使用fixed
關鍵字“固定”傳遞給MyFunction的結構。 此外,可能還有安全問題需要處理。
我不確定這個問題究竟是什么,但希望這將是一個起點。
嘗試查看DllImport屬性的選項,這可能與字符串的編組有關。
[DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Auto)]
CharSet選項是我認為您可能需要的選項。
我發現AppVerifier可用於追蹤之前的P / Invoke編組問題。 它是免費的,來自微軟。
我的猜測是你的.NET應用程序無法訪問你的一個指針(lpSrvcVersion,lpSPIVersion或lphService)。 你能嘗試修改DLL(如果它是你的),看看你是否可以在沒有指針的情況下使用它? (我知道你必須稍后再添加它們,但至少你可以縮小問題的位置。)
你確定它不是hApp嗎? 這看起來像一個輸入參數,即請求應用程序的句柄。 快速谷歌...是的,有一個功能來創建一個應用程序句柄,你可以使用一個默認參數。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.