我正在研究c#应用程序(在我的apllication中使用win 32 dll)......我正在尝试这样的事情 在DLL(test.dll)中:
char* Connect(TCHAR* lpPostData)
{
char buffer[1000];
.....
return buffer;
}
IN c#application:
[DllImport("test.dll", EntryPoint = "Connect", CharSet = CharSet.Unicode)]
[return: MarshalAs(UnmanagedType.LPWStr)]
public static extern string Connect(StringBuilder postdata);
string returnedData = Connect(postdata);
但是数据的返回没有正确发生...... 请任何人都可以告诉我哪里出错了 提前致谢
答案 0 :(得分:2)
TCHAR * in告诉我它是一个unicode输入(UNICODE是在CE下定义的),但是char *告诉我它可能是一个字节数组(或ascii字符串),并且创建这个API的人应该被打耳机混合两者,如这是非常非常糟糕的设计。
你当然不能将返回编组为宽字符串,因为它不是一个。在桌面上你会使用Tony的建议,但MSDN(和练习)clearly shows它在CF中不可用(不知道为什么MS认为我们不需要它)。
智能设备框架does have it。另一种选择是使用Marshal从返回的指针复制到字节数组,然后使用Encoding.ASCII将该数组转换为字符串。当然,这首先指出了这个API中的另一个显而易见的缺陷shouldn't be returning a string。
编辑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_t,这与OS的其余部分(几乎没有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必须初始化为某个大小,并且该大小是您在第三个参数之前传递的大小。
答案 1 :(得分:2)
您正在尝试在堆栈上返回变量;这不会起作用。但是,您可以将缓冲区声明为静态,这只是为了使编组工作正常的概念证明。
答案 2 :(得分:1)
在您收到的各种回复中指出了几个缺陷。总而言之,这就是我的建议。
在本机方面,声明类似这样的内容(假设您使用C ++编写代码):
extern "C"
__declspec(dllexport)
const char* __cdecl Connect(const char* lpPostData)
{
static char buffer[1000];
...
return buffer;
}
extern "C"
使编译器清楚地知道它在导出时不应该破坏函数名称,而应该像处理C函数一样处理它。
__ceclspec(dllexport)
使函数在DLL中可见,作为导出的入口点。然后,您可以从外部项目中引用它。
__cdecl
确保您使用C方式在堆栈上传递参数。如果调用者没有采用相同的参数传递方案,那么你可能会把事情搞得一团糟。
始终使用char
:生病到char
(ANSI文本)或wchar_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
期望的)并确保调用者期望{{1}呼叫对话。
您必须通过调用以下内容将__cdecl
封送到IntPtr
对象:
string
注意:在我的原始帖子中,我建议将string value = Marshal.PtrToStringAnsi (Connect (postData));
声明为返回Connect
,但作为评论者Mattias更正,这不是正确的方法。请参阅CLR Inside Out上的这篇文章,了解CLR如何处理string
类型的retval
。感谢您向我指出(并感谢romkyns)。
您还可以考虑将字符串从string
复制到本机代码中通过buffer
分配的内存块,并返回指向它的指针。然后,您可以简单地将CoTaskMemAlloc
函数声明为返回DllImport
,并且不需要在托管环境中进行任何进一步的编组。
答案 3 :(得分:0)
您的函数的返回类型应为IntPtr
,然后使用Marshal.PtrToStringAnsi
从IntPtr
中获取字符串。
答案 4 :(得分:0)
这是一个如何做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函数)
P.S。正如ctacke所指出的 - 这可能不适合.NET CF。