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 __int64unsigned 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)的主要内容,如果未能解决你的问题,请参考以下文章

C++ <--> C# 修改编组的字节数组

将引用类型从 C++ 编组到 C#

从 c# dll 调用/编组字符串到非托管代码

通过非默认 AppDomain 中的 C# 函数调用编组 C++ 指针接口

c#编组尝试将结构从cpp dll返回到c#应用程序,该应用程序被分配为它自己的dll

C#编组来自C++ DLL的无符号字符返回值[重复]