Переводя нативный API в C #, Marshalling создает массивы и делегаты. - PullRequest
1 голос
/ 24 января 2011

Я работал над C # версией C ++ API, но мне не удалось понять это правильно.

Поскольку первый API был слишком большим, я опубликовал его, и я сократил его до некоторых основных функций, для которых мне действительно нужна помощь в создании управляемой версии.

Итак, по сути, это C ++ dll, содержащий экспортированные функции, которые используются для связи с программным обеспечением для технического анализа.

C ++ функция, которую я хотел бы преобразовать

#define PLUGINAPI extern "C" __declspec(dllexport)

PLUGINAPI int GetFunctionTable( FunctionTag **ppFunctionTable )
{
    *ppFunctionTable = gFunctionTable;
    // must return the number of functions in the table
    return gFunctionTableSize;
}

GetFunctionTable вызывается программным обеспечением, которое предоставляет указатель на массив FunctionTag с именем gFunctionTable:

typedef struct FunctionTag
{
    char *Name;
    FunDesc  Descript;
} FunctionTag;

FunctionTag gFunctionTable[] = {"ExampleA",{ VExampleA, 0, 0, 0, 0, NULL },
                             "ExampleB",{ VExampleB, 1, 0, 1, 0, NULL }
                                 };

Структура FunctionTag содержит встроенную структуру с именем Fundesc:

// FunDesc structure holds the pointer to actual
// user-defined function that can be called by AmiBroker.
typedef struct FunDesc
{
    AmiVar (*Function)( int NumArgs, AmiVar *ArgsTable );
    UBYTE   ArrayQty;       // number of Array arguments required   
    UBYTE   StringQty;      // number of String arguments required
    SBYTE   FloatQty;       // number of float args 
    UBYTE   DefaultQty;     // number of default float args
    float   *DefaultValues; // the pointer to defaults table 
} FunDesc;

Наконец, Fundesc содержит тип AmiVar:

#pragma pack( push, 2 )
typedef struct AmiVar
{
    int type;
    union 
    {
        float   val;
        float   *array;
        char    *string;
        void *disp;
    };
} AmiVar;
#pragma pack(pop)

C # преобразование до сих пор

Теперь, это то, что я написал до сих пор в попытке заставить мой C # dll "имитировать" оригинальный C ++ API. Экспортируемая функция GetFunctionTable ():

namespace AmiBrokerFrontDll
{
   internal static class AmiBrokerFrontDll
   {

   [DllExport("GetFunctionTable", CallingConvention = CallingConvention.Cdecl)]
   public static Int32 GetFunctionTable(ref FunctionTag[] ppFunctionTable)
   {
       FillFunction();
       ppFunctionTable=gFunctionTable;
       return gFunctionTableSize;
   }

Затем следует определение структуры FunctionTag и gFunctionTableSize:

   [StructLayoutAttribute(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
   public struct FunctionTag
   {
       [MarshalAs(UnmanagedType.LPStr)]
       public string Name;
       public FunDesc Description;
   }

   public static FunctionTag[] gFunctionTable=new FunctionTag[1];
   public static FunctionTag gfunc;
   static Int32 gFunctionTableSize = Marshal.SizeOf(gFunctionTable) / Marshal.SizeOf(gfunc);
   public static void FillFunction()
   {
        gFunctionTable[0].Name = "VExempleA";
        gFunctionTable[0].Description.Function += VExempleDeMacd;
        //ArrayQty, StringQty, FloatQty, DefaultQty, DefaultTablePtr
        gFunctionTable[0].Description.ArrayQty = 0;
        gFunctionTable[0].Description.StringQty = 0;
        gFunctionTable[0].Description.FloatQty = 2;
        gFunctionTable[0].Description.DefaultQty = 0;
        gFunctionTable[0].Description.DefaultValues = new IntPtr();
   }

Объявление FunDesc включает делегата:

   [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
   public delegate AmiVar FunctionDelegate(int NumArgs, ref AmiVar ArgsTable);

   public struct FunDesc
   {
       [MarshalAs(UnmanagedType.FunctionPtr)]
       public FunctionDelegate Function;
       public byte ArrayQty; // The number of Array arguments required
       public byte StringQty; // The number of String arguments required
       public byte FloatQty; // The number of float args
       public byte DefaultQty; // The number of default float args
       public IntPtr DefaultValues; // The pointer to defaults table
   }

Наконец, у нас есть структура AmiVar:

   [StructLayoutAttribute(LayoutKind.Explicit, Size = 8)]
   public struct AmiVar
   {
       [FieldOffset(0)]
       public Int32 type;
       [FieldOffset(4)]
       public Single val;
       [FieldOffset(4)]
       public IntPtr array;
       [FieldOffset(4)]
       [MarshalAs(UnmanagedType.LPStr)]
       public string name;
       [FieldOffset(4)]
       public IntPtr disp;
   }

Извините, это слишком долго. К сожалению, я не мог сделать небольшой вопрос. Таким образом, этот код скомпилирован (возможно, больше нет, поскольку это выдержка из общей картины), но при загрузке результирующей библиотеки DLL из программного обеспечения технического анализа я получил ОШИБКУ НАРУШЕНИЯ ДОСТУПА. Я считаю, что это означает, что преобразование C # не отображает размер переменных C ++ правильно. С массивами структур и делегатов этот проект стал слишком сложным для решения в одиночку.

Любая помощь будет высоко ценится!

Спасибо, Гийом

1 Ответ

1 голос
/ 25 января 2011

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

  1. Указатели функций, созданные из управляемых делегатов, никогда и никогда не должны храниться в неуправляемом коде. Я не говорю это легко. Есть утверждения, что если вы создадите указатель на функцию из делегата с GetFunctionPointerForDelegate , то будут созданы соответствующие thunks, которые никогда не будут собирать мусор. Это НЕ верно. Я видел, как функциональные указатели, которые были хороши на одном вызове, сгорели на следующем. Самая безопасная ставка - гарантировать, что указатели на функции никогда не будут сохранены после использования неуправляемого вызова.
  2. P / Invoke подходит для некоторых задач, но, безусловно, самый простой способ интегрировать C ++-дружественную библиотеку, не дружественную к C #, - создать лучшую / более подходящую оболочку для нее в C ++ / CLI. См. здесь для описания одного подхода к проблеме.
...