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