Маршалинг с помощью C-структуры как возвращаемого значения в C # - PullRequest
4 голосов
/ 30 января 2011

У меня неуправляемый код:

...
typedef struct foo  
{  
 int  a;  
 bool b
 int  c;  
} FOO,*LPFOO;
....
__declspec(dllexport) FOO __stdcall GetFoo()  
{  
   FOO f;  
   <some work>  
   return f;   
}  
....

Я объявил прототип C # для функции GetFoo:

    [StructLayout(LayoutKind.Sequential, Pack = 1)]
    private struct Foo
    {
      public int  a;  
      public bool b
      public int  c; 
    };

    [DllImport("foo.dll", CallingConvention = CallingConvention.StdCall, CharSet = CharSet.Ansi)]
    [return:MarshalAs( UnmanagedType.Struct)]        
    private static extern Foo GetFoo();

Но когда я вызываю GetFoo из кода C #, у меня всегда есть MarshalDirectiveException - подпись типа метода не совместима с PInvoke. Как мне объявить прототип C #?

1 Ответ

9 голосов
/ 30 января 2011

Да, функции, которые возвращают структуру, как правило, трудны для взаимодействия.Такая структура должна быть blittable , чтобы маршаллер pinvoke мог передать указатель на функцию, готовый записать возвращаемое значение.Быть «легкомысленным» означает, что структура структуры в управляемом коде должна быть идентична неуправляемой структуре структуры.Если это не так, то копия должна быть сделана, маршаллер pinvoke не хочет делать эту копию в конкретном случае возвращаемого значения.

Тип bool является проблемой взаимодействия, выполняются разные среды выполненияразные варианты.Обычно он составляет 4 байта в C (сравните с типом Windows BOOL, также по умолчанию для pinvoke), 2 байта в COM-взаимодействии (он же VARIANT_BOOL), 1 байт в C ++, 1 байт в CLR.Поскольку целевое время выполнения неизвестно, CLR не может угадать, какой выбор правильный.BOOL по умолчанию, 4 байта.

Даже использование [MarshalAs(UnmanagedType.U1)] для принудительного точного совпадения не делает его слепым.Что довольно странно, я считаю это ошибкой CLR.Хороший обходной путь - заменить его на byte, вы можете использовать свойство, чтобы обернуть его обратно в bool.Помните, что в опубликованном фрагменте было много ошибок, я заставил эту версию работать:

using System;
using System.Runtime.InteropServices;

class Program {
    static void Main(string[] args) {
        Foo value = GetFoo();
    }

    [StructLayout(LayoutKind.Sequential)]
    private struct Foo {
        public int a;
        private byte _b;
        public bool b {
            get { return _b != 0; }
        }
        public int c;
    };

    [DllImport(@"c:\projects\consoleapplication3\debug\cpptemp10.dll", CallingConvention = CallingConvention.StdCall, CharSet = CharSet.Ansi, EntryPoint = "_GetFoo@0")]
    private static extern Foo GetFoo(/*int CoreIndex*/);
}

typedef struct foo  
{  
    int  a;  
    bool b;
    int  c;  
} FOO,*LPFOO;

extern "C" __declspec(dllexport) 
FOO __stdcall GetFoo()  
{  
    FOO f;  
    f.a = 42;
    f.b = true;
    f.c = 101;
    return f;   
}  
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...