如何将指针函数从C++导出到C#

How to export pointer function from C++ to C#?

本文关键字:C++ 指针 函数      更新时间:2023-10-16

我正在使用DllImport从C#调用C++的函数。

#if defined(__cplusplus)
extern "C" {
#endif
__declspec(dllexport) int __stdcall ABC(int i);
__declspec(dllexport) char* __stdcall C(int i);
#if defined(__cplusplus)
}
#endif
int __stdcall ABC(int i)
{
    return i;
}
char* __stdcall C(int i)
{
    char* n = new char[i];
    memset(n, 9, i);
    return n;
}

C#中的代码为:

using System.Runtime.InteropServices;
using System;
namespace DepartmentStore
{
    class Exercise
    {
        [DllImport("library.dll")]
        public static extern int ABC(int i);
        [DllImport("library.dll")]
        public static extern char* C(int i);
        static int Main()
        {
            int k = ABC(10);
            byte[] b = C(1024);
            return 0;
        }
    }
}

函数ABC(int i)正常,但函数C(int i)在构建时产生以下错误:

"指针和固定大小的缓冲区只能在不安全的上下文中使用"

我想我不知道如何导出函数的指针返回。

有人能告诉我C#调用以返回类型为指针的函数的正确方式吗?

p/Invoke稍后不知道如何将C样式数组从非托管端封送到托管端;与SAFEARRAY不同的是,在C样式数组中没有嵌入信息,P/Invoke稍后可以使用这些信息来确定应该复制多少字节。

因此,您需要声明C函数以返回IntPtr,然后调用Marshal.PtrToStrAnsi方法将指针转换为托管端的字符串,如下所示:

[DllImport("library.dll")]
public static extern IntPtr C(int i);
static int Main()
{
    int k = ABC(10);
    IntPtr b = C(1024);
    string s = Marshal.PtrToStrAnsi(b);
    // Problem: How do you release what is pointed to
    // by IntPtr?
    return 0;
}

此外,您必须将IntPtr传递回非托管端,以解除分配使用new分配的内存;如果你不这样做,你的内存就会泄漏。

一个更简单的选项是在C++中为非托管库创建一个托管包装器,该包装器公开进行调用并执行转换为String^(使用marshal_as)的托管函数,如下所示:

// compile with: /clr
#include <stdlib.h>
#include <string.h>
#include <memory>
#include <msclrmarshal.h>
using namespace System;
using namespace msclr::interop;
String^ WrappedC(int i) {
   // Make the call to the native function.
   // Let's store in an auto_ptr to handle
   // cleanup when the wrapper is exited.
   auto_ptr<char> c(C(i));
   // Convert to a managed string and
   // return.
   return marshal_as<String^>(c.get());
}

如果您不想封送回string,而是封送回一个byte数组,那么我仍然建议使用包装器方法(尽管在这种情况下,它需要您具备一些特定的知识才能创建要返回的托管数组):

// compile with: /clr
#include <stdlib.h>
#include <memory>
using namespace System;
using namespace msclr::interop;
String^ WrappedC(int i) {
   // Make the call to the native function.
   // Let's store in an auto_ptr to handle
   // cleanup when the wrapper is exited.
   auto_ptr<char> c(C(i));
   // Copy the pointer.
   char* p = c.get();
   // The byte array to return.
   // i is the size of the array, as per the call
   // to C.
   array<byte>^ a = gcnew array<byte>(i); 
   // Populate.
   for (int index = 0; index < i; ++index) 
       a[index] = (byte) *p++; 
   // Return the array.
   return a;
}

这些选项比完全在托管代码中处理要好,因为您不必担心在托管和非托管之间连续编组,以处理指向非托管空间中分配的内存的指针。