C ++ DLL и C # вызов - PullRequest
       13

C ++ DLL и C # вызов

4 голосов
/ 24 октября 2011

У меня есть функция, созданная в C ++, которая вызывает функцию интерфейса COM Его подпись:

BOOL func(LPWSTR strIn, __out LPWSTR strOut)
{
  //initcom
  //do something
  // release pointers
}

В C #:

[DllImport("funcdll.dll")]
static extern bool func(String strIn, ref String strOut);

// use it

for(int i=0;i<10;i++)
{
   if(func(strin, strout))
   {
      //do something with strout
   }
}

Я протестировал свою dll в консольном приложении C ++, она работает, но в C # она вылетает с неизвестной ошибкой.

Ответы [ 5 ]

6 голосов
/ 24 октября 2011

У меня есть три проблемы, которые я вижу.

  1. Соглашения о вызовах не совпадают.Ваш код C ++ cdecl, а код C # stdcall.
  2. В коде C ++ используются широкие строки, но в коде C # используются строки ANSI.
  3. Второй параметр не соответствует,Ваш код C # предполагает, что код C ++ возвращает новый указатель на строку C, которую код C # затем освобождает с помощью COM-распределителя.Ваш код на C ++ этого не делает.

Теперь рассмотрим их более подробно.

Соглашения о вызовах

Это довольнолегко исправитьПросто измените код C ++ на stdcall или код C # на cdecl.Но не делай и того, и другого.Я бы изменил код C #:

[DllImport("funcdll.dll"), CallingConvention=CallingConvention.Cdecl]

Unicode / ANSI-строки

Я предполагаю, что вы хотите использовать Unicode-строки, поскольку вы явно выбрали их вC ++ код.Но по умолчанию P / invoke используется для сортировки строк ANSI.Вы можете изменить это снова в DllImport следующим образом:

[DllImport("funcdll.dll"), CallingConvention=CallingConvention.Cdecl, 
    CharSet=CharSet.Unicode]

Возвращение строки из C ++ в C #

Ваше текущее объявление функции C ++ выглядит так:

BOOL func(LPWSTR strIn, __out LPWSTR strOut)

Декоратор __out не имеет никакого реального эффекта, кроме документирования того, что вы хотите изменить буфер, на который указывает strOut, и вернуть эти изменения вызывающей стороне.

ВашОбъявление C #:

static extern bool func(String strIn, ref String strOut);

Теперь ref String strOut просто не совпадает.Строковый параметр ref соответствует этому в C ++:

BOOL func(LPWSTR strIn, LPWSTR *strOut)

Другими словами, код C # ожидает, что вы вернете новый указатель.На самом деле он затем приступит к освобождению буфера, который вы вернули в strOut, вызвав CoTaskMemFree.Я уверен, что это не то, что вам нужно.

Ваш исходный код C ++ может возвращать строку в код C # только путем изменения буфера, который был ему передан.Этот код будет выглядеть следующим образом:

BOOL func(LPWSTR strIn, __out LPWSTR strOut)
{
    ...
    wcscpy(strOut, L"the returned string");
    ...
}

Если это то, что вам нужно, вам следует выделить достаточный буфер в C # для объекта StringBuilder.

[DllImport("funcdll.dll"), CallingConvention=CallingConvention.Cdecl, 
    CharSet=CharSet.Unicode]
static extern bool func(string strIn, StringBuilder strOut);
...
StringBuilder strOutBuffer = new StringBuilder(128);
bool res = func("input string", strOutBuffer);
string strOut = StringBuilder.ToString();

Если вы простов коде C # не можете решить, какой размер буфера вам нужен, поэтому лучше всего использовать BSTR для маршалинга strOut.См. ответ для деталей.

3 голосов
/ 24 октября 2011

Мне трудно сказать, не видя деталей вашего метода C ++ ... но мне никогда не удавалось использовать String с P / Invoke.

Попробуйте использовать IntPtr вместо String, и используйте Marshal.PtrToStringUni для исходящей строки, и перенаправьте вашу управляемую строку в неуправляемую землю с помощью Marshal.StringToHGlobalUni и, после вызова функции, обязательно освободитенеуправляемая строка с Marshal.FreeHGlobal.

Кроме того, из земли C99 мои bools не работают с .NET bools ... Я не знаю почему.Я должен использовать байт и проверить == 1. Не знаю, столкнетесь ли вы с этим на C ++ ... но, если вы хотите, чтобы мой совет о том, с чего начать, который кажется наименее разрушаемым в моем опыте, вот вамgo:

[DllImport("funcdll.dll")]
static extern byte func(IntPtr strIn, out IntPtr strOut);

// use it
string myString = "Testing";

IntPtr stringOut;
IntPtr stringIn = Marshal.StringToHGlobalUni(myString);

   if(func(stringIn, out stringOut) == 1)
   {
      //do something with strout
      string stringOutValue = Marshal.PtrToStringUni(stringOut);
      // Depending on how you dealt with stringOut in your
      // unmanaged code, you may have to: Marshal.FreeCoTaskMem(stringOut);
      // or Marshal.FreeHGlobal(stringOut) if you're returning
      // an owning reference to a copied string.
   }

Marshal.FreeHGlobal(stringIn);
2 голосов
/ 24 октября 2011

В C ++ убедитесь, что у вас есть что-то вроде этого (по крайней мере, для второго аргумента)

extern "C" BOOL __stdcall func( BSTR * pBstr )
{
    *pBstr = SysAllocString( L"Foobar" );
    return 0;
}

В C # напишите что-то вроде этого (для второго аргумента):

static extern bool func( [MarshalAs(UnmanagedType.BStr)] ref String strOut); 
2 голосов
/ 24 октября 2011

Я полагаю, что у вас несоответствие соглашения о вызовах.Соглашение о вызовах по умолчанию в C ++ - cdecl, а для .NET - stdcall.try

[DllImport("funcdll.dll", CallingConvention = CallingConvention.Cdecl)] 
static extern bool func(String strIn, ref String strOut); 

Также вам может потребоваться указать маршаллару, что вы хотите упорядочить строки как LPWSTR с использованием атрибутов [MarshalAs (UnmanagedType.LPWStr)].

[DllImport("funcdll.dll", CallingConvention = CallingConvention.Cdecl)] 
static extern bool func([MarshalAs(UnmanagedType.LPWStr)]String strIn
                       ,[MarshalAs(UnmanagedType.LPWStr)]ref String strOut);

имеетпосмотрите на http://msdn.microsoft.com/en-us/library/s9ts558h.aspx

1 голос
/ 24 октября 2011

Извините, у меня нет большого ответа, но я просто запомнил кое-что из моего опыта ... Вы пытались использовать StringBuilder, например, изменив сигнатуру функции импорта c # как

   [System.Runtime.InteropServices.DllImport("funcdll.dll")]
static extern bool func(String strIn, System.Text.StringBuilder strOut);
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...