c ++ DLL中导出函数的定义是
int func1(const char* input,char** output)
{
int returnCode = 0;
std::stringstream xmlInputStream;
xmlInputStream<< std::string(input);
std::stringstream xmlOutputStream;
returnCode = doRequest(xmlInputStream,xmlOutputStream);
std::string xmlOutputString =xmlOutputStream.str();
*output=const_cast<char *> (xmlOutputString.c_str());
cout<<xmlOutputString;
return returnCode ;
}
我尝试从c#导入函数,就像这样......
==================================================================
[DllImport("sample.dll")]
public static extern int func1(String str1,out String str2);
string str1="hello";
string str2=String.Empty;
MyClass.func1(str1,out str2);
Console.writeln(str2);
====================================================================
输出是垃圾值......
为什么会这样以及如何从c#导入此函数?
答案 0 :(得分:3)
这就是简单的,尽量使用自定义编组(尤其是如果你想使用字符**指针)。
我发现,你是没有得到“内存屁股哑”的字符串,因为一些实现doRequest(字符*,字符**)的不当,以及分配给结果处理不当;
首先,如果您分配在非托管进程的内存并把它传递到管理过程中,您将需要申报的一些机制来释放非托管内存。托管的GC对这个内存一无所知,这个内存将会丢失。
Secondy,需要对结果分配内存,并将它们传递到非托管过程中,由于原来的存储位置可以在任何时刻被重写。
最后,你只是因为你没有为结果分配内存而只获得输入的第一个字符,实际上只将一个内存指针传递给第一个输出字符的内存位置,比如&amp; array [0] < / p>
这是竞争代码(MS VC ++ / MS C#),它修复了所有问题:
lib.cpp:
#include "stdafx.h"
#include "lib.h"
using namespace std;
int func1(char* input, char** output)
{
stringstream xmlInputStream, xmlOutputStream;
xmlInputStream << string(input);
int returnCode = doRequest(&xmlInputStream, &xmlOutputStream);
string xmlOutputString = xmlOutputStream.str();
//*output=const_cast<char *> (xmlOutputString.c_str());
long length = sizeof(char) * xmlOutputString.length();
char* src = const_cast<char *>(xmlOutputString.c_str());
char* dst = (char*)malloc(length+1);
memcpy_s(dst, length, src, length);
dst[length]=0; // 0 byte always ends up given ANSI string
*output = dst;
//cout << xmlOutputString;
return returnCode;
}
int func1_cleanup(char* memptr)
{
free(memptr);
return 0;
}
int doRequest(stringstream* xmlInputStream, stringstream* xmlOutputStream)
{
*xmlOutputStream << "Memory ass dumb";
return 0;
}
的Program.cs:
using System;
using System.Runtime.InteropServices;
namespace test
{
class Program
{
[DllImport("lib.dll", EntryPoint = "func1", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)]
public static extern unsafe int func1(char* input, char** data);
[DllImport("lib.dll", EntryPoint = "func1_cleanup", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)]
public static extern unsafe int func1_cleanup(char* data);
static void Main(string[] args)
{
string input = "Hello, World!";
string output;
int result = func1(input, out output);
}
private const int S_OK = 0;
public static int func1(string input, out string output)
{
unsafe
{
output = null;
int result = -1;
fixed (char* parray1 = &input.ToCharArray()[0])
{
//
// if you allocating memory in a managed process, you can use this
//
//char[] array = new char[0xffffff];
//fixed(char* parray = &array[0])
{
//
// if you allocating memory in unmanaged process do not forget to cleanup the prevously allocated resources
//
char* array = (char*)0;
char** parray2 = &array;
result = func1(parray1, parray2);
if (result == S_OK)
{
//
// if your C++ code returns the ANSI string, you can skip this extraction code block (it can be useful in Unicode, UTF-8, UTF-7, UTF-32, all C# supported encodings)
//
//byte* self = (byte*)*((int*)parray2);
//byte* ptr = self;
//List<byte> bytes = new List<byte>();
//do
//{
// bytes.Add(*ptr++);
//}
//while (*ptr != (byte)0);
//output = Encoding.ASCII.GetString(bytes.ToArray());
output = Marshal.PtrToStringAnsi(new IntPtr(*parray2));
}
func1_cleanup(array);
}
}
return result;
}
}
}
}
答案 1 :(得分:1)
我相信这应该有用
[DllImport("sample.dll")]
public static extern int func1(
[MarhsalAs(UnmanagedType.LPStr)] String str1,
[MarshalAs(UnmanagedType.LPStr)] out String str2);
MarshalAs属性对于控制互操作非常强大。
编辑(基于使用C ++代码更新的帖子):
也许更像这样的东西应该可行(我在这台计算机上没有VC ++来构建一个假的C ++ DLL来检查)
[DllImport("sample.dll")]
public static extern int func1(
[MarshalAs(UnmanagedType.LPStr)] String str1,
[MarshalAs(UnmanagedType.LPArray, ArraySubType=UnmanagedType.LPStr, SizeConst=1), Out] out String[] str2);
注意我将[Out]添加到str2,我不确定这是好还是必要,所以尝试使用和不使用(再次,对不起,我不能在这里尝试)。此外,您可能需要使用SizeParamIndex而不是SizeConst,我不记得 - 如果它需要是SizeParamIndex,您需要更改C ++代码以传回1作为数组元素的数量。
这将str2作为String []传回,但是你可以只使用该数组的第一个元素来获取字符串。
也许有更简洁的方法可以做到这一点--C ++中的__deref_out参数不能很好地转换为我记得的C#,但我可能会在这里忘记一些奥秘。