在c#应用程序中使用Win32 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)

我正在开发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);

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
声明为returning
string
,但正如评论员所纠正的,这不是正确的方法。请参阅上的这篇文章,以了解CLR如何处理
retval
s类型的
string
。谢谢你向我指出这一点(也谢谢你)


您还可以考虑将代码从<代码>缓冲区< /代码>复制到在本机代码中通过<代码> COTASKMeMeloLC/分配的一段内存,并返回指向它的指针。然后,您可以简单地将

DllImport
函数声明为返回
string
,并且不需要在托管世界中执行任何进一步的封送。

非常感谢您的回复……但我找不到Marshal.PtrToStringAnsi()在使用System.Runtime.InteropServices时,仅应存在Marshal.PtrToStringUni和Marshal.ptrtostringbstrs。。。听着:我同意你的意见,除了最后你的建议。如果将返回类型声明为字符串,CLR将尝试使用CoTaskMemFree释放返回的内存。如果返回的缓冲区是静态的,那么这显然不合适。将返回类型声明为IntPtr,并使用Marshal.PtrToStringAnsi()检索字符串。非常感谢您就这个问题对我进行的指导。我必须仔细检查我所有的互操作代码,以确保它误用了
字符串
返回类型。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));