2017-08-16 1 views
1

Pourriez-vous les gars s'il vous plaît aidez-moi à résoudre le problème suivant? J'ai une fonction C++ dll, et elle sera appelée par une autre application C#. L'une des fonctions dont je avais besoin est la suivante:C# Marshalling (appel C# DLL C++)

unsigned long makeArray(unsigned char* sendArr, unsigned long sendArrLen, unsigned char *recvArr, unsigned long *recvArrLen); 

j'ai écrit le code suivant dans 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; 
    } 

Malheureusement, le code ci-dessus ne fonctionne pas ... Puis-je savoir comment puis-je passer un pointeur vers un pointeur vers la fonction C++? Si ce n'est pas possible, y a-t-il une solution?

Merci.

Répondre

0

Où est votre 'test.dll'? Je pense qu'il est un problème de chemin ...

Le fichier doit se trouver à l'un des répertoires suivants ..

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

Ou il peut être une incompatibilité de type ... reportez-vous à la [site].

Je correspondais au type ulong de csharp à __int64 non signé dans c/C++ sur windows.

La déclaration du code C# est légèrement modifiée.

[DllImport(@"testdll.dll", CallingConvention = CallingConvention.Cdecl)] 
    static extern ulong makeArray 
    (
    byte[] sendArr, 
    ulong sendArrLen, 
    [Out] byte[] recvArr, 
    ref ulong recvArrLen 
    ); 

Voici les testdll.cpp abd testdll.h i testé

#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; 
} 

code 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 
); 

Enfin, le code C# d'application de la console comme suit, appelez la fonction native dll en C++ - Testdll.dll éléments d'impression sur la console.

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

+0

l'erreur est "System.AccessViolationException". pas "EntryPointNotFoundException". –

+0

Aviez-vous un mot-clé extern dans votre en-tête, n'est-ce pas? comme extern "C" __declspec (dllexport) – tommybee

1

unsigned long dans MSVC est un entier non signé 32 bits, vous devez mapper au type System.UInt32 .NET, ce qui correspond au mot-clé uint en C#.

C# ulong est un nombre entier non signé de 64 bits, ce qui correspond à unsigned __int64 ou unsigned long long de MSVC.

Le paramètre unsigned long *recvArrLen doit être mappé à l'aide de ref dans la déclaration C# PInvoke, car vous disposez d'un niveau d'indirection via le pointeur.

Il semble également que le paramètre de tableau arrRecv doit être alloué par l'appelant (C# dans votre cas) et rempli par la fonction DLL. Si la fonction DLL alloue le tampon, vous devez ajouter un autre niveau d'indirection (unsigned char **recvArr) et fournir un moyen de libérer la mémoire allouée (par exemple, la DLL doit également exporter une fonction de libération).

Je voudrais essayer quelque chose comme ça pour PInvoke:

[DllImport("test.dll", CallingConvention = CallingConvention.Cdecl)] 
public static extern uint makeArray(
    byte[] sendArr, 
    uint sendArrLen, 
    [Out] byte[] recvArr, 
    ref uint recvArrLen 
);