繁体   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