C#编组(C#调用C++ DLL)
Posted
技术标签:
【中文标题】C#编组(C#调用C++ DLL)【英文标题】:C# Marshalling (C# call C++ DLL) 【发布时间】:2017-08-16 02:43:51 【问题描述】:你们能帮我解决以下问题吗?我有一个 C++ 函数 dll,它将被另一个 C# 应用程序调用。我需要的功能之一如下:
unsigned long makeArray(unsigned char* sendArr, unsigned long sendArrLen, unsigned char *recvArr, unsigned long *recvArrLen);
我用 C# 编写了以下代码:
[DllImport("test.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern ulong makeArray(byte[] sendArr, ulong sendArrLen, byte[] recvArr, ulong recvArrLen);
private byte[] MakeArray()
byte[] arrSend = new byte[] 0x00, 0x12, 0x34 ;
ulong nRecvArrLen = 0;
byte[] arrRecv = null; // assign in c++ dll function (variable size)
if(makeArray(arrSend, (ulong)arrSend.Length, arrRecv, nRecvArrLen) == 1)
return arrRecv;
return null;
很遗憾,上面的代码不起作用... 我可以知道如何将指向指针的指针传递给 C++ 函数吗?如果不可能,是否有任何解决方法?
谢谢。
【问题讨论】:
【参考方案1】:MSVC中的unsigned long
是一个32位无符号整数,所以你应该把它映射到System.UInt32
.NET类型,对应C#中的uint
关键字。
C#ulong
是一个无符号的64位整数,对应MSVC的unsigned __int64
或unsigned long long
。
unsigned long *recvArrLen
参数应该在 C# PInvoke 声明中使用 ref
进行映射,因为您可以通过指针进行间接级别。
似乎arrRecv
数组参数应该由调用者(在你的情况下为 C#)分配,并由 DLL 函数填充。
如果 DLL 函数分配了缓冲区,您应该添加另一个间接级别 (unsigned char **recvArr
),并且您应该提供一种方法来释放分配的内存(例如 DLL 也应该导出一个释放函数)。
我会为 PInvoke 尝试类似的方法:
[DllImport("test.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern uint makeArray(
byte[] sendArr,
uint sendArrLen,
[Out] byte[] recvArr,
ref uint recvArrLen
);
【讨论】:
【参考方案2】:您的“test.dll”在哪里?我认为这是一个路径问题...
该文件必须位于以下目录之一..
[%SystemRoot%] (Windows directory)
[%SystemRoot%]\system32\(32 bit) or
[%SystemRoot%]\sysWOW64\(64 bit)
The same location with your executable file
PATH variable
也可能是类型不匹配...参考 [site]。
我将 csharp 的 ulong 类型与 windows 上 c/c++ 中的 unsigned __int64 匹配。
C# 代码的声明略有改动。
[DllImport(@"testdll.dll", CallingConvention = CallingConvention.Cdecl)]
static extern ulong makeArray
(
byte[] sendArr,
ulong sendArrLen,
[Out] byte[] recvArr,
ref ulong recvArrLen
);
这是我测试的 testdll.cpp abd testdll.h
#include "testdll.h"
unsigned __int64 makeArray(
unsigned char* sendArr,
unsigned __int64 sendArrLen,
unsigned char *recvArr,
unsigned __int64 *recvArrLen
)
int i;
for(i=0; i < sendArrLen; i++)
recvArr[i] = sendArr[i];
memcpy(recvArrLen, &sendArrLen, sizeof(unsigned __int64));
return i;
testdll.h 代码。
#pragma once
#ifdef EXPORT_TESTDLL
#define TESTDLL_API __declspec(dllexport)
#else
#define TESTDLL_API __declspec(dllimport)
#endif
extern "C" TESTDLL_API unsigned __int64 makeArray(
unsigned char* sendArr,
unsigned __int64 sendArrLen,
unsigned char *recvArr,
unsigned __int64 *recvArrLen
);
最后,控制台应用的C#代码如下,调用c++中的原生dll函数——testdll.dll 在控制台上打印项目。
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading.Tasks;
namespace ConsoleApplication2
class Program
[DllImport(@"testdll.dll", CallingConvention = CallingConvention.Cdecl)]
static extern ulong makeArray(byte[] sendArr, ulong sendArrLen, [Out] byte[] recvArr, ref ulong recvArrLen);
static byte[] MakeArray()
byte[] arrSend = new byte[] 0x00, 0x12, 0x34 ;
ulong nRecvArrLen = 0;
ulong ret = 0;
byte[] arrRecv = new byte[3]; // assign in c++ dll function (variable size)
try
if ((ret = makeArray(arrSend, (ulong)arrSend.Length, arrRecv, ref nRecvArrLen)) > 0)
if(arrRecv != null)
Console.WriteLine("nRecvArrLen2============>" + arrRecv.Length);
return arrRecv;
catch (DllNotFoundException dne)
Console.WriteLine("============> dll not found....");
return null;
static void Main(string[] args)
byte[] retbytes = MakeArray();
if (retbytes != null)
Console.WriteLine("=====LEN=======>" + retbytes.Length);
for (int i = 0; i < retbytes.Length; i++)
Console.WriteLine("====ITEM========>" + retbytes[i]);
else
Console.WriteLine("=====NULL=======>");
【讨论】:
错误是“System.AccessViolationException”。不是“EntryPointNotFoundException”。 你的标题中是否有 extern 关键字?像 extern "C" __declspec(dllexport)以上是关于C#编组(C#调用C++ DLL)的主要内容,如果未能解决你的问题,请参考以下文章
通过非默认 AppDomain 中的 C# 函数调用编组 C++ 指针接口