在c#应用程序中使用Win32 dll(将字符*返回c#问题)
我正在开发c#应用程序(在我的应用程序中使用win 32 dll)…我正在尝试类似的东西 在DLL(test.DLL)中: 在c#应用程序中:在c#应用程序中使用Win32 dll(将字符*返回c#问题),c#,windows,windows-mobile,winapi,C#,Windows,Windows Mobile,Winapi,我正在开发c#应用程序(在我的应用程序中使用win 32 dll)…我正在尝试类似的东西 在DLL(test.DLL)中: 在c#应用程序中: [DllImport("test.dll", EntryPoint = "Connect", CharSet = CharSet.Unicode)] [return: MarshalAs(UnmanagedType.LPWStr)] public static extern string Connect(StringBuilder postdata)
[DllImport("test.dll", EntryPoint = "Connect", CharSet = CharSet.Unicode)]
[return: MarshalAs(UnmanagedType.LPWStr)]
public static extern string Connect(StringBuilder postdata);
string returnedData = Connect(postdata);
但数据的返回并没有正常进行。。。。
请问有谁能告诉我哪里出了问题
提前感谢函数的返回类型应该是
IntPtr
,然后使用Marshal.PtrToStringAnsi
从IntPtr
中提取字符串TCHAR*in告诉我它是unicode输入(unicode在CE下定义),但char*告诉我它可能是字节数组(或ascii字符串)无论是谁创建了这个API,都应该因为两者的混合而受到惩罚,因为这是一个非常非常糟糕的设计
您当然不能将返回作为宽字符串封送,因为它不是宽字符串。在桌面上,您可以使用Tony的建议,但MSDN(和实践)认为CF中没有它(不知道为什么MS认为我们不需要它)
智能设备框架。另一个选项是使用Marshal将返回的指针复制到字节数组,然后使用Encoding.ASCII将该数组转换为字符串。当然,这指出了该API中的另一个明显缺陷,即它首先存在的缺陷
编辑1
既然我看到了关于你应该做什么的其他建议,但我并不完全同意,我想我应该给你举个例子:
您的本机呼叫应该更像这样:
extern "C"
__declspec(dllexport)
const BOOL __cdecl Connect(TCHAR* lpPostData,
TCHAR *returnBuffer,
DWORD *returnSize)
{
// validate returnSize, returnBuffer, etc
// write your data into returnBuffer
TCHAR *data = _T("this is my data");
_tcscpy(returnBuffer, data);
*returnSize = (_tcslen(data) + 1) * sizeof(TCHAR);
return succeeded;
}
请注意,我只是返回一个成功代码。文本数据作为一个指针传入,并带有其长度(因此API知道它可以使用多少空间,并可以返回它使用了多少空间)。另外,我并不是与我的字符串变量数据类型一致,我使用的是TCHAR宏,它将成为CE下的wchar\u t,这与操作系统的其余部分一致(操作系统一开始几乎没有ASCII api)
大多数WIn32 API集的工作方式与此完全相同
您的p/Invoke声明非常简单:
[DllImport("test.dll", SetLastError=true)]
private static extern bool Connect(string postData,
StringBuilder data,
ref int length);
使用它也很简单:
void Foo()
{
int length = 260;
StringBuilder sb = new StringBuilder(length);
if(Connect("Bar", sb, ref length))
{
// do something useful
}
}
请注意,StringBuilder必须初始化为某个大小,该大小是在第三个参数之前传入的。您试图在堆栈上返回一个变量;这是行不通的。不过,您可以将缓冲区声明为静态缓冲区,作为让封送工作正常进行的概念证明。下面是一个示例,说明如何执行Tony建议的操作:
[DllImport("test.dll", EntryPoint="my_test_func",
CharSet=CharSet.Ansi, ExactSpelling=true,
CallingConvention=CallingConvention.Cdecl)]
private static extern IntPtr my_test_func_imported();
public static string my_test_func()
{
return Marshal.PtrToStringAnsi(my_test_func_imported());
}
在非托管端:
const char *my_test_func(void);
(这是从“test.dll”导入C函数)
注意:正如ctacke所指出的,这可能不适用于.NET CF.正如您收到的各种回复中所指出的,有几个缺陷。总而言之,以下是我的建议 在本机端,声明如下内容(假设您使用C++编写代码):
extern“C”
向编译器明确指出,导出函数时不应损坏函数名,而应像对待C函数一样对待它
\uu ceclspec(dllexport)
使函数在DLL中作为导出的入口点可见。然后可以从外部项目引用它
\uu cdecl
确保使用C方式在堆栈上传递参数。如果调用方不采用相同的参数传递方案,您可能会把事情搞得一团糟
一致使用char
:病态到char
(ANSI文本)或wchar\u t
(16位Unicode文本)。而且永远不要像您在示例中所做的那样,返回指向局部变量的指针。一旦函数返回,该内存位置将不再有效,并且可能包含垃圾。此外,如果用户修改存储在那里的数据,可能会导致程序崩溃,因为这会破坏调用堆栈
在托管C#端,我建议如下:
[DllImport("test.dll", EntryPoint="Connect",
CharSet=CharSet.Ansi, ExactSpelling=true,
CallingConvention=CallingConvention.Cdecl)]
private static extern System.IntPtr Connect(string postData);
它将绑定到您的Connect
入口点,使用ANSI字符串约定(每个字符8位,这是char
所期望的),并确保调用者期望调用转换
您必须通过调用以下命令将IntPtr
封送到string
对象:
string value = Marshal.PtrToStringAnsi (Connect (postData));
注意:在我最初的帖子中,我建议将Connect
声明为returningstring
,但正如评论员所纠正的,这不是正确的方法。请参阅上的这篇文章,以了解CLR如何处理retval
s类型的string
。谢谢你向我指出这一点(也谢谢你)
您还可以考虑将代码从<代码>缓冲区< /代码>复制到在本机代码中通过<代码> COTASKMeMeloLC/
函数声明为返回DllImport
,并且不需要在托管世界中执行任何进一步的封送。string
字符串
返回类型。PtrToStringAnsi在CF中不存在,并且您对DllImport进行了过度修饰。@ctacke:显示了为什么我认为标记不是问题的一部分-如果是CF,那么问题最好提及这一点。至于德林波特的装饰——我宁愿直截了当;此外,这是从自动生成的文件复制粘贴的。我不是e
[DllImport("test.dll", EntryPoint="Connect",
CharSet=CharSet.Ansi, ExactSpelling=true,
CallingConvention=CallingConvention.Cdecl)]
private static extern System.IntPtr Connect(string postData);
string value = Marshal.PtrToStringAnsi (Connect (postData));