簡體   English   中英

c#p /調用困難編組指針

[英]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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM