Как экспортировать функцию указателя из C ++ в C #? - PullRequest
2 голосов
/ 13 марта 2012

Я использую 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 # вызывает функции с типом возвращаемого значения в качестве указателя?

1 Ответ

3 голосов
/ 13 марта 2012

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 ++, которая предоставляет управляемые функции, которые выполняют вызовы и выполняют преобразование в * 1020.* (используя marshal_as) примерно так:

// compile with: /clr
#include <stdlib.h>
#include <string.h>
#include <memory>
#include <msclr\marshal.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;
}

Это лучшие варианты, чем обрабатывать его вполностью управляемый код, так как вам не нужно беспокоиться о непрерывном распределении между управляемым и неуправляемым для обработки указателей на память, выделенную в неуправляемом пространстве.

...