Как импортировать C ++ DLL из C # с помощью PInvoke? - PullRequest
2 голосов
/ 24 января 2011

Определение экспортируемой функции в c ++ DLL:

int func1(const char* input,char** output)
{
 int returnCode = 0;

 std::stringstream xmlInputStream;
 xmlInputStream<< std::string(input);
 std::stringstream xmlOutputStream;
 returnCode = doRequest(xmlInputStream,xmlOutputStream);
 std::string xmlOutputString =xmlOutputStream.str();
 *output=const_cast<char *> (xmlOutputString.c_str());

cout<<xmlOutputString;

 return returnCode ;
}

Я пытался импортировать функцию из c # следующим образом ...

==================================================================

[DllImport("sample.dll")]
    public static extern int func1(String str1,out String str2);


string str1="hello";
string str2=String.Empty;

MyClass.func1(str1,out str2);
Console.writeln(str2);

====================================================================

Вывод является значением мусора ...

Почему так и как импортировать эту функцию из c #?

Ответы [ 2 ]

3 голосов
/ 24 января 2011

Это просто, попробуйте использовать пользовательский маршалинг (особенно, если вы хотите использовать указатели char **).

Я обнаружил, что вы не получаете строку "Memory ass dumb" из-за неправильной реализацииdoRequest (char *, char **), а также присвоение результата не обрабатываются должным образом;

Во-первых, если вы выделяете память в неуправляемом процессе и передаете ее управляемому процессу, вам потребуется объявить некоторый механизм для освобождения неуправляемой памяти.Управляемый GC ничего не знает об этой памяти, которая будет потеряна при чрезмерном размере.

Секунда, вам необходимо выделить память для результатов и передать их неуправляемому процессу, поскольку исходные области памяти можно переписать вв любое время.

Наконец, вы получаете только первую характеристику ввода только потому, что вам не выделена память для результатов, фактически передавая только указатель памяти на ячейку памяти первого выходного символа, скажем, & массив[0]

Вот код соревнования (MS VC ++ / MS C #), который устраняет все проблемы:

lib.cpp:

#include "stdafx.h"
#include "lib.h"
using namespace std;

int func1(char* input, char** output)
{
    stringstream xmlInputStream, xmlOutputStream;

    xmlInputStream << string(input);    
    int returnCode = doRequest(&xmlInputStream, &xmlOutputStream);
    string xmlOutputString = xmlOutputStream.str();

    //*output=const_cast<char *> (xmlOutputString.c_str());
    long length = sizeof(char) * xmlOutputString.length();
    char* src = const_cast<char *>(xmlOutputString.c_str());
    char* dst = (char*)malloc(length+1);
    memcpy_s(dst, length, src, length);
    dst[length]=0; // 0 byte always ends up given ANSI string
    *output = dst;

    //cout << xmlOutputString;
    return returnCode;
}

int func1_cleanup(char* memptr)
{
    free(memptr);
    return 0;
}


int doRequest(stringstream* xmlInputStream, stringstream* xmlOutputStream)
{
    *xmlOutputStream << "Memory ass dumb";
    return 0;
}

Program.cs:

using System;
using System.Runtime.InteropServices;
namespace test
{
    class Program
    {
        [DllImport("lib.dll", EntryPoint = "func1", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)]
        public static extern unsafe int func1(char* input, char** data);

        [DllImport("lib.dll", EntryPoint = "func1_cleanup", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)]
        public static extern unsafe int func1_cleanup(char* data);

        static void Main(string[] args)
        {
            string input = "Hello, World!";
            string output;
            int result = func1(input, out output);
        }

        private const int S_OK = 0;

        public static int func1(string input, out string output)
        {
            unsafe
            {
                output = null;
                int result = -1;
                fixed (char* parray1 = &input.ToCharArray()[0])
                {
                    //
                    // if you allocating memory in a managed process, you can use this
                    //
                    //char[] array = new char[0xffffff];
                    //fixed(char* parray = &array[0])
                    {
                        //
                        // if you allocating memory in unmanaged process do not forget to cleanup the prevously allocated resources
                        //
                        char* array = (char*)0; 
                        char** parray2 = &array;
                        result = func1(parray1, parray2);
                        if (result == S_OK)
                        {
                            //
                            // if your C++ code returns the ANSI string, you can skip this extraction code block (it can be useful in Unicode, UTF-8, UTF-7, UTF-32, all C# supported encodings)
                            //
                            //byte* self = (byte*)*((int*)parray2);
                            //byte* ptr = self;
                            //List<byte> bytes = new List<byte>();
                            //do
                            //{
                            //    bytes.Add(*ptr++);
                            //}
                            //while (*ptr != (byte)0);
                            //output = Encoding.ASCII.GetString(bytes.ToArray());
                            output = Marshal.PtrToStringAnsi(new IntPtr(*parray2));
                        }
                        func1_cleanup(array);
                    }
                }
                return result;
            }
        }
    }
}
1 голос
/ 24 января 2011

Я считаю, что это должно работать

[DllImport("sample.dll")]
public static extern int func1(
    [MarhsalAs(UnmanagedType.LPStr)] String str1,
    [MarshalAs(UnmanagedType.LPStr)] out String str2);

Атрибут MarshalAs является чрезвычайно мощным для управления взаимодействием.

РЕДАКТИРОВАТЬ (на основе обновленного сообщения с кодом C ++):

Может быть, что-то более подобное должно работать (у меня нет VC ++ на этом компьютере, чтобы создать поддельную C ++ DLL для проверки)

[DllImport("sample.dll")]
public static extern int func1(
    [MarshalAs(UnmanagedType.LPStr)] String str1,
    [MarshalAs(UnmanagedType.LPArray, ArraySubType=UnmanagedType.LPStr, SizeConst=1), Out] out String[] str2);

Примечание. Я добавил [Out] в str2, я не уверен, что это хорошо или необходимо, поэтому попробуйте с этим и без него (опять же, извините, я не могу попробовать здесь). Кроме того, вам может понадобиться использовать SizeParamIndex вместо SizeConst, я не помню - если это должен быть SizeParamIndex, вам нужно изменить код C ++, чтобы он возвращал 1 в качестве числа элементов массива.

Это возвращает str2 как String [], но затем вы можете просто использовать первый элемент этого массива, чтобы получить строку.

Могут быть и более чистые способы сделать это - параметр __deref_out в C ++ не очень хорошо транслируется в C #, что я помню, но я могу забыть некоторые тайны здесь.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...