C#编组(C#调用C ++ DLL)

时间:2017-08-16 02:43:52

标签: c# c++ dll marshalling

你能帮我解决一下这个问题吗?我有一个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 ++函数?如果不可能,有任何解决方法吗?

谢谢。

2 个答案:

答案 0 :(得分:1)

MSVC中的

unsigned long是一个32位无符号整数,因此您应该将其映射到System.UInt32 .NET类型,对应于C#中的uint关键字。

C#ulong是无符号的64位整数,对应于MSVC的unsigned __int64unsigned long long

我应该在C#PInvoke声明中使用unsigned long *recvArrLen映射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
);

答案 1 :(得分:0)

你的'test.dll'在哪里?我认为这是一个路径问题...

该文件必须位于以下目录之一..

[%SystemRoot%] (Windows directory)
[%SystemRoot%]\system32\(32 bit)   or 
[%SystemRoot%]\sysWOW64\(64 bit)
The same location with your executable file
PATH variable

或者它可能是类型不匹配...请参阅 [site]

我在桌面上的c / c ++中将ulong类型的csharp与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=======>");


        }
    }
}

enter image description here