简体   繁体   English

c#p /调用困难编组指针

[英]c# p/invoke difficulties marshalling pointers

I am attempting to call into a native .dll from c# using p/invoke. 我试图使用p / invoke从c#调用本机.dll。 I'm able to make the call (no crash, the function returns a value), but the return code indicates "A pointer parameter does not point to accessible memory." 我能够进行调用(没有崩溃,函数返回一个值),但返回代码表示“指针参数不指向可访问的内存”。 I've resorted to trial and error in order to solve this, but I haven't made any progress so far. 为了解决这个问题,我采用了反复试验,但到目前为止我还没有取得任何进展。

Here's the signature of the native function I'm calling: 这是我正在调用的本机函数的签名:

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
                                 );

Here's the imported signature in C#: 这是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
                                    );

Here's the C definition of WFSVERSION: 这是WFSVERSION的C定义:

typedef struct _wfsversion
{
    WORD            wVersion;
    WORD            wLowVersion;
    WORD            wHighVersion;
    CHAR            szDescription[257];
    CHAR            szSystemStatus[257];
} WFSVERSION, * LPWFSVERSION;

Here's the C# definition of WFSVersion: 这是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;
}

Here's the call to MyFunction from C#: 这是从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);

As I said, this call returns, but the return value is an error code indicating "A pointer parameter does not point to accessible memory." 正如我所说,此调用返回,但返回值是一个错误代码,指示“指针参数不指向可访问的内存”。 I must be doing something wrong with parameters 1,3,7,8, or 9. However, I've made successful calls to other functions in this .dll which required WFSVERSION* as parameters, so I don't think parameters 7 or 8 are the issue here. 我必须对参数1,3,7,8或9做错。但是,我已成功调用此.dll中的其他函数,这需要WFSVERSION *作为参数,所以我不认为参数7或8是这里的问题。

I would appreciate any thoughts you might have about the cause of this issue, or any constructive criticisms of my code. 如果您对此问题的原因有任何想法,或者对我的代码有任何建设性的批评,我将不胜感激。 This is my first experience with P/Invoke, so I'm not sure where to begin. 这是我第一次使用P / Invoke,所以我不知道从哪里开始。 Is there any way to narrow down the issue, or is trial an error my only option? 有没有办法缩小问题范围,或者试错是我唯一的选择?

You have two obvious errors here. 你有两个明显的错误。 In your struct definition you should be using byte[] instead of char[] for szDescription and szSystemStatus. 在你的struct定义中,你应该使用byte []而不是char []用于szDescription和szSystemStatus。

Also the last parameter in your pInvoke call is not a pointer. pInvoke调用中的最后一个参数也不是指针。 When you make your call to MyFunction hService is zero and therefore an invalid pointer as far as the function is concerned. 当你调用MyFunction时,hService为零,因此就函数而言是无效指针。 [Out] is a Marshaling directive telling the runtime when and where to copy data not an indicator that the parameter is a pointer. [Out]是一个Marshaling指令,告诉运行时复制数据的时间和位置,而不是参数是指针的指示符。 What you need is to change [Out] to out or ref this tells the runtime that hService is a pointer: 你需要的是将[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);

Some ideas: 一些想法:

  • The C# WFSVersion class should probably be a struct . C#WFSVersion类应该是一个struct I don't know if the P/Invoke marshaller cares, but I've always seen structs used. 我不知道P / Invoke marshaller是否关心,但我总是看到结构使用。

  • Character size might be an issue. 字符大小可能是个问题。

    C's CHAR is 8 bits wide (ANSI), and .Net's System.Char is 16 bits wide (Unicode). C的CHAR是8位宽(ANSI),而.Net的System.Char是16位宽(Unicode)。 To give the marshaller as much info as possible so it does the correct conversion, try adding "CharSet = CharSet.Ansi" to the DllImport and StructLayout attributes, and changing the string declarations in WFSVersion: 要为编组提供尽可能多的信息以便进行正确的转换,请尝试将“CharSet = CharSet.Ansi”添加到DllImport和StructLayout属性,并更改WFSVersion中的字符串声明:

     [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 257)] public string szDescription; [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 257)] public string szSystemStatus; 
  • Another issue could be data alignment in the structs. 另一个问题可能是结构中的数据对齐。 If no alignment was specified when the C struct was compiled, the data elements in the struct were probably aligned on a one or two byte boundary. 如果在编译C结构时未指定对齐,则结构中的数据元素可能在一个或两个字节边界上对齐。 Try using Pack in WFSVersion's StructLayout attribute: 尝试在WFSVersion的StructLayout属性中使用Pack

     [StructLayout(LayoutKind.Sequential, Pack = 1, CharSet = CharSet.Ansi)] // Try pack values of 2, 4 and 8 if 1 doesn't work. 

And some questions: 还有一些问题:

  • Was MyFunction intended to be called from non-C code? MyFunction是否打算从非C代码调用? The original author may have written code that assumes the data passed in is allocated with the C runtime memory manager. 原作者可能编写了代码,假定传入的数据是使用C运行时内存管理器分配的。

  • Does code in the C DLL use the pointers passed to it for later processing after MyFunction has returned? 在我的函数返回后,C DLL中的代码是否使用传递给它的指针以便以后处理? If so - and assuming it's possible/wise/sane to go forward with such a situation - it may be necessary to "pin" the structs passed to MyFunction using the fixed keyword. 如果是这样 - 并且假设可能/明智/理智地继续这种情况 - 可能需要使用fixed关键字“固定”传递给MyFunction的结构。 Plus there are probably security issues to deal with. 此外,可能还有安全问题需要处理。

I'm not sure exactly what the issue with this one is but hopefully this will be a starting point. 我不确定这个问题究竟是什么,但希望这将是一个起点。

Try looking into the options on the DllImport attribute, this could be to do with the marshalling of the strings. 尝试查看DllImport属性的选项,这可能与字符串的编组有关。

    [DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Auto)]

The CharSet option is the one I think you may need. CharSet选项是我认为您可能需要的选项。

I have found AppVerifier useful for tracking down P/Invoke marshal issues before. 我发现AppVerifier可用于追踪之前的P / Invoke编组问题。 It's free and from Microsoft. 它是免费的,来自微软。

My guess would be that one of your pointers (lpSrvcVersion, lpSPIVersion or lphService) isn't accessible from your .NET application. 我的猜测是你的.NET应用程序无法访问你的一个指针(lpSrvcVersion,lpSPIVersion或lphService)。 Can you try modifying the DLL (if it's yours) and see if you can get it working without the pointers? 你能尝试修改DLL(如果它是你的),看看你是否可以在没有指针的情况下使用它? (I know you'll have to add them back later, but at least you can narrow down the location of the problem.) (我知道你必须稍后再添加它们,但至少你可以缩小问题的位置。)

Are you sure that it is not hApp? 你确定它不是hApp吗? That looks like an input parameter, the handle to the requesting application. 这看起来像一个输入参数,即请求应用程序的句柄。 Quick google...yes, there is a function to create an app handle, and a default parameter you can use. 快速谷歌...是的,有一个功能来创建一个应用程序句柄,你可以使用一个默认参数。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM