c#调用C、C++编写的dll

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了c#调用C、C++编写的dll相关的知识,希望对你有一定的参考价值。

用c++编写了一个dll,现在要供c#调用,

在c++中调用的格式为:

typedef void (*CALLBACKF) (char **, int, double, char **);

typedef bool (*FUN)(char *, double , double , double , CALLBACKF, int);

dll=LoadLibrary(dllLocation);
FUN fun1=(FUN)GetProcAddress(dll, "fun");

在c#中如何实现?谢谢~
我需要的是如何输入输出 数组,题中的是char数组,包括一维和多维,我是这样写的

c中的callback在c#中如何实现?

参考技术A 此示例使用 C 程序创建一个 DLL,在下一示例中将从 C# 程序调用该 DLL。
// cmdll.c
// compile with: /LD
int __declspec(dllexport) SampleMethod(int i)

return i*10;


该示例使用两个文件 CM.cs 和 Cmdll.c 来说明 extern。C 文件是示例 2 中创建的外部 DLL,它从 C# 程序内调用。
// cm.cs
using System;
using System.Runtime.InteropServices;
public class MainClass

[DllImport("Cmdll.dll")]
public static extern int SampleMethod(int x);

static void Main()

Console.WriteLine("SampleMethod() returns 0.", SampleMethod(5));

参考资料:MSDN

参考技术B 将该DLL文件复制到C#的BIN下面就可以了撒 参考技术C 回调函数我用过,

定义这个typedef void (*CALLBACKF) (char **, int, double, char **);
回调函数
using System.Runtime.InteropServices;
class API

[UnmanagedFunctionPointerAttribute(CallingConvention.Cdecl)]
//上面这个可能不要 可能要,具体看你这个回调函数的调用方式是cdecl调用还是其他。
public delegate void ChCallback(参数自己一一对应);

[DllImport("Cmdll.dll", CallingConvention = CallingConvention.StdCall, CharSet = CharSet.Ansi)]
public static extern bool FUN(char *, double , double , double ,ChCallback CallBack,int );
参数自己对应写,我的意思是自己定义一个delegate 对应C++ 中的CALLBACKF 然后再放到 fun参数里


然后其他类里实例api这个类后
pcallback = new API.ChCallback(MTEvent);
MTEvent为事件处理函数 意思就是typedef void (*CALLBACKF) (char **, int, double, char **);
这个在C#的具体回调实例!
public MTEvent(char **, int, double, char **)'

char 数组用 string接受

char *,这样定义

int len=128;
IntPtr cha = Marshal.AllocHGlobal(len);
len中间的len你自己看分配多少内存!cha 这就是一个指针。就是指向你分配的一块内存,然后把这个指针传递进去!本回答被提问者采纳

C++ dll 和 C# 调用

【中文标题】C++ dll 和 C# 调用【英文标题】:C++ dll and C# call 【发布时间】:2011-10-24 07:20:33 【问题描述】:

我有一个用 C++ 编写的函数,它调用 COM 接口的函数 它的签名:

BOOL func(LPWSTR strIn, __out LPWSTR strOut)

  //initcom
  //do something
  // release pointers

在 C# 中:

[DllImport("funcdll.dll")]
static extern bool func(String strIn, ref String strOut);

// use it

for(int i=0;i<10;i++)

   if(func(strin, strout))
   
      //do something with strout
   

我在 C++ 控制台应用程序中测试了我的 dll,它可以工作,但在 C# 中它会因未知错误而崩溃。

【问题讨论】:

您是否尝试过添加一些调试输出或日志记录并查看您的函数在哪里崩溃? 【参考方案1】:

我可以看到你有三个问题。

    调用约定不匹配。您的 C++ 代码是 cdecl,您的 C# 代码是 stdcall。 C++ 代码使用宽字符串,但 C# 代码编组 ANSI 字符串。 第二个参数不匹配。您的 C# 代码假定 C++ 代码返回一个指向 C 字符串的新指针,然后 C# 代码使用 COM 分配器释放该指针。您的 C++ 代码不会这样做。

现在,更详细地处理这些问题。

调用约定

这很容易解决。只需将 C++ 代码更改为 stdcall,或将 C# 代码更改为 cdecl。但不要两者都做。我会更改 C# 代码:

[DllImport("funcdll.dll"), CallingConvention=CallingConvention.Cdecl]

Unicode/ANSI 字符串

我认为您想使用 Unicode 字符串,因为您已在 C++ 代码中明确选择了它们。但是 P/invoke 默认编组 ANSI 字符串。您可以在DllImport 中再次更改此设置,如下所示:

[DllImport("funcdll.dll"), CallingConvention=CallingConvention.Cdecl, 
    CharSet=CharSet.Unicode]

将字符串从 C++ 返回到 C#

你当前的 C++ 函数声明是这样的:

BOOL func(LPWSTR strIn, __out LPWSTR strOut)

__out 装饰器没有实际作用,除了记录您要修改 strOut 指向的缓冲区并将这些修改返回给调用者之外。

您的 C# 声明是:

static extern bool func(String strIn, ref String strOut);

现在,ref String strOut 根本不匹配。 ref 字符串参数在 C++ 中与此匹配:

BOOL func(LPWSTR strIn, LPWSTR *strOut)

换句话说,C# 代码期望您返回一个新指针。事实上,它会通过调用CoTaskMemFree 继续释放您在strOut 中返回的缓冲区。我相信这不是你想要的。

您的原始 C++ 代码只能通过修改传递给它的缓冲区将字符串返回给 C# 代码。该代码如下所示:

BOOL func(LPWSTR strIn, __out LPWSTR strOut)

    ...
    wcscpy(strOut, L"the returned string");
    ...

如果这是您想要的,那么您应该在 C# 中的 StringBuilder 对象中分配足够的缓冲区。

[DllImport("funcdll.dll"), CallingConvention=CallingConvention.Cdecl, 
    CharSet=CharSet.Unicode]
static extern bool func(string strIn, StringBuilder strOut);
...
StringBuilder strOutBuffer = new StringBuilder(128);
bool res = func("input string", strOutBuffer);
string strOut = StringBuilder.ToString();

如果您根本无法在 C# 代码中确定需要多大的缓冲区,那么最好的办法是使用 BSTR 来编组 strOut。有关详细信息,请参阅此answer。

【讨论】:

谢谢,我无法想象你在第三点中写了什么。我希望 C++ 函数返回一个布尔值和一个对参数的引用,该参数在被调用后传入。如果它不这样做,那么我会发出一条警报消息,表示无法获得我想要的东西。我现在正在寻找另一种解决方案。 既然我在一台真机上,我将用一些代码进行详细说明。但基本上函数签名根本不匹配。【参考方案2】:

如果没有看到您的 C++ 方法的详细信息,我很难说出来......但是,我从来没有运气好使用带有 P/Invoke 的 String

尝试使用IntPtr而不是String,并使用Marshal.PtrToStringUni作为传出字符串,并使用Marshal.StringToHGlobalUni将托管字符串编组到非托管区域,并在函数调用后确保释放非托管字符串Marshal.FreeHGlobal

另外,来自 C99 领域,我的布尔值不适用于 .NET 布尔值...我不知道为什么。我必须使用字节并检查 == 1。不知道您是否会在使用 C++ 时遇到此问题...但是,如果您希望我的建议从我的经验中似乎最不易损坏的开始,这里是去:

[DllImport("funcdll.dll")]
static extern byte func(IntPtr strIn, out IntPtr strOut);

// use it
string myString = "Testing";

IntPtr stringOut;
IntPtr stringIn = Marshal.StringToHGlobalUni(myString);

   if(func(stringIn, out stringOut) == 1)
   
      //do something with strout
      string stringOutValue = Marshal.PtrToStringUni(stringOut);
      // Depending on how you dealt with stringOut in your
      // unmanaged code, you may have to: Marshal.FreeCoTaskMem(stringOut);
      // or Marshal.FreeHGlobal(stringOut) if you're returning
      // an owning reference to a copied string.
   

Marshal.FreeHGlobal(stringIn);

【讨论】:

这在很大程度上是个坏建议。在这里使用 IntPtr 只会在 C# 端产生不必要的复杂性。这种复杂性很容易避免。 Hrm... 它对我来说一直有效.. 并且来自不受管理的土地很容易理解正在发生的事情,而不是其他一些“神奇”的可能性。我认为称其为“坏建议”可能有点过分。 当然可以,但它是不必要的复杂性。没有 IntPtr 很容易编组字符串。您将其称为魔术这一事实仅表明您尚未掌握这样做。那是你的。 如果它有效,那么它绝对不是“坏建议”。 “次优”也许......但远非“坏”。你怎么这么暴躁? 不,我认为这是个坏建议。要正确编写代码,您确实需要尝试/最终您错过了。您对第二个参数的处理非常不完整。根本不需要这种复杂性。你所说的bool 是错误的。 C# bool 的默认编组为 BOOL。这里没有 C99 bool。我不暴躁。我只是相信尽可能提供最好的建议很重要。我不认为这个答案能做到这一点。【参考方案3】:

我相信您的调用约定不匹配。 C++ 中的默认调用约定是 cdecl,而对于 .NET,它是 stdcall。试试

[DllImport("funcdll.dll", CallingConvention = CallingConvention.Cdecl)] 
static extern bool func(String strIn, ref String strOut); 

此外,您可能还必须使用 [MarshalAs(UnmanagedType.LPWStr)] 属性专门告诉编组员您想将字符串编组为 LPWSTR。

[DllImport("funcdll.dll", CallingConvention = CallingConvention.Cdecl)] 
static extern bool func([MarshalAs(UnmanagedType.LPWStr)]String strIn
                       ,[MarshalAs(UnmanagedType.LPWStr)]ref String strOut);

看看http://msdn.microsoft.com/en-us/library/s9ts558h.aspx

【讨论】:

在 DllImport 属性中使用 CharSet 比使用单个 MarshalAs 简单得多。 我不认为两者相等,将 Charset 设置为 Unicode 只会告诉它字符串是 Unicode,Bstr 和 LPWStr 都是 unicode,你仍然不知道它是哪一个。使用 MarshalAs 显式设置它更好恕我直言 这两个选项是等价的。仅使用 CharSet 时默认为 LPTSTR。更重要的是,一个 ref LPWSTR 实际上是一个 BSTR,因为这就是编组器对返回的内存所做的事情。这与代码中的 C++ 声明不匹配。 msdn.microsoft.com/en-us/library/aa719692(v=VS.71).aspx 没有提到任何关于 CharSet 默认为任何内容的内容。在没有任何明确的文档的情况下,我不相信隐含的。 至于第二部分,是的,这是一个问题,当我第一次阅读这个问题时,我没有看到。但是,从什么时候开始 LPWSTR(可能以 null 终止,可能以长度为前缀)和 BStr(始终以 null 终止并以长度为前缀)相等?【参考方案4】:

在 C++ 中确保你有这样的东西(至少对于第二个参数)

extern "C" BOOL __stdcall func( BSTR * pBstr )

    *pBstr = SysAllocString( L"Foobar" );
    return 0;

在 C# 中编写类似这样的内容(用于第二个参数):

static extern bool func( [MarshalAs(UnmanagedType.BStr)] ref String strOut); 

【讨论】:

+1 BSTR 是第二个参数的绝佳选择,尤其是在调用代码中无法确定缓冲区大小的情况下。【参考方案5】:

对不起,我没有什么大的答案,只是从我的经验中记住一些事情。你是否尝试使用 StringBuilder,例如将你的 c# 导入函数签名更改为

   [System.Runtime.InteropServices.DllImport("funcdll.dll")]
static extern bool func(String strIn, System.Text.StringBuilder strOut);

【讨论】:

以上是关于c#调用C、C++编写的dll的主要内容,如果未能解决你的问题,请参考以下文章

C++ dll 和 C# 调用

C#调用C++ DLL类方法

从 C# 调用 C++ dll 方法/类

C++编写动态库(.DLL)给C#调用方法

如何从 c++ 项目中调用用 c# 创建的 dll 文件? [复制]

调试从 C++ 调用的 C# dll(嵌入 Mono)