Net5 вызывает метод внешней библиотеки DLL и возвращает значение const char *, C # приведет к прямому сбою

#c# #c #string #pinvoke

#c# #c #строка #pinvoke

Вопрос:

Код на C :

 __declspec(dllexport) const char* Get() {
    return "hello word!";
}
 

C # код:

 [DllImport("TestLink.dll")]
public static extern string Get();
 

Программа завершает работу сразу после вызова

Комментарии:

1. Но с использованием IntPtr проблем нет, я могу использовать следующий код для нормальной работы « [DllImport(«TestLink.dll «)] публичный статический extern IntPtr Get(); «

2. Я уже вижу отсутствие __stdcall в коде C … И есть, по крайней мере, еще одна проблема… C # попытается освободить память для строки C (которая должна быть выделена, Marshal.AllocCoTaskMem а не обязательно буквальной строкой). И вы должны сказать C #, что строка будет ANSI, а не Unicode. В конце концов, вы идете в направлении, противоположном «правильному».

3. внутренняя небезопасная статическая строка ConvertToManaged(IntPtr cstr) { if (IntPtr.Zero == cstr) { возвращает null; } возвращает новую строку((sbyte *)((void *)cstr)); } Я могу использовать приведенный выше код, я вижу. Net framework, похоже, является тем же преобразованием

4. «Классический» способ возврата строк из C — это использование StringBuilder() C #-стороны. В противном случае существуют различные решения

Ответ №1:

В любом случае, когда вы выделяете что-либо с C / C / родной стороны, вы должны использовать распределитель COM, который .NET понимает. Итак, есть много способов вернуть строку, например:

C :

 extern "C" __declspec(dllexport) void* __stdcall GetBSTR() {
    return SysAllocString(L"hello world"); // uses CoTaskMemAlloc underneath
}

extern "C" __declspec(dllexport) void* __stdcall GetLPSTR() {

    const char* p = "hello world";
    int size = lstrlenA(p)   1;
    void* lp = CoTaskMemAlloc(size);
    if (lp)
    {
        CopyMemory(lp, p, size);
    }
    return lp;
}

extern "C" __declspec(dllexport) void* __stdcall GetLPWSTR() {

    const wchar_t* p = L"hello world";
    int size = (lstrlenW(p)   1) * sizeof(wchar_t);
    void* lp = CoTaskMemAlloc(size);
    if (lp)
    {
        CopyMemory(lp, p, size);
    }
    return lp;
}
 

И C#

 [DllImport("MyDll")]
[return: MarshalAs(UnmanagedType.BStr)]
public static extern string GetBSTR();

[DllImport("MyDll")]
[return: MarshalAs(UnmanagedType.LPWStr)]
public static extern string GetLPWSTR();

[DllImport("MyDll")]
[return: MarshalAs(UnmanagedType.LPStr)]
public static extern string GetLPSTR();
 

Комментарии:

1. Я думаю, вам следует указать соглашение о вызовах… Я думаю, что код, как есть, использует cdecl на стороне C и stdcall на стороне C #. На x64 это, вероятно, не является большой проблемой, потому что stdcall и cdecl одинаковы, но на x86 это так.

2. @xanatos — да.

3. Это перебор для строки const. Просто объявите возвращаемый тип as IntPtr и вызовите Marshal.PtrToStringAnsi() or Marshal.PtrToStringUTF8() .

4. Стоит отметить, что причина сбоя кода в Q заключается в том, что маршалер вызывает CoTaskMemFree возвращаемый указатель.