Как маршалировать указатель на массив указателей на структуры из управляемого в неуправляемый код? - PullRequest
2 голосов
/ 13 декабря 2010

Я пытаюсь написать плагин DLL, который должен реализовать следующую функцию:

int GetFunctionTable(FuncDescStruct **ppFunctionTable);

Итак, мой код плагина в C # объявляет это:

 public static unsafe int GetFunctionTable(IntPtr functionTablePtr);

Эта функция будет вызвана и ожидается, что она заполнит functionTablePtr указателем на массив структур, описывающих набор функций обратного вызова.

В простом C / C ++ это выглядит примерно так:

// declare func table  
// VExampleF1, VExampleF2 - are function pointers
FuncDescStruct funcTable[] = { 
    "ExampleF1",    { VExampleF1, 0, 0, 0, 0, NULL }, //filling descriptions.
    "ExampleF2",    { VExampleF2, 1, 0, 1, 0, NULL }
    };

int GetFunctionTable(FuncDescStruct **ppFunctionTable)
{
*ppFunctionTable = funcTable; // how to do this correctly in C#?

// must return the number of functions in the table
return funcTableSize;
}

Я пытаюсь сделать следующее:

    static unsafe FunctionTag[] funcTable;
    static List<IntPtr> allocatedMemory;
    public static unsafe int GetFunctionTable(IntPtr functionTablePtr)
    {

        //create just one test callback description
        funcTable = new FunctionTag[1];

        funcTable[0].Name = "VExampleF1";
        funcTable[0].Description.Function = VExampleF1;
        funcTable[0].Description.ArrayQty = 0;
        funcTable[0].Description.FloatQty = 0;
        funcTable[0].Description.StringQty = 0;
        funcTable[0].Description.DefaultQty = 0;
        funcTable[0].Description.DefaultValues = null;

        // saving allocated memory for further cleanup
        allocatedMemory = new List<IntPtr>(); 

        int intPtrSize = Marshal.SizeOf(typeof(IntPtr));
        IntPtr nativeArray = Marshal.AllocHGlobal(intPtrSize * funcTable.Length);
        for (int i = 0; i < funcTable.Length; i++)
        {
            IntPtr nativeFD = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(FunctionTag)));
            allocatedMemory.Add(nativeFD);
            Marshal.StructureToPtr(funcTable[i], nativeFD, false);

            Marshal.WriteIntPtr(nativeArray, i * intPtrSize, nativeFD);
        }

        Marshal.WriteIntPtr(functionTablePtr, nativeArray);

        return funcTable.Length;
    }

Такой код не работает, и вопрос в том, как отправить указатель на массив управляемых структур для использования неуправляемым кодом? В каком направлении мне идти?

Ответы [ 3 ]

2 голосов
/ 13 декабря 2010

За последние пару лет мы много чего такого делали. Мы используем сборочный мост в смешанном режиме для управления вызовами функций отображения и сортировки данных между управляемыми и неуправляемыми средами. Мы часто используем класс «смешанного режима», чтобы обернуть управляемый класс, предоставляя собственный интерфейс для вызова его функциональности.

Давайте рассмотрим вашу проблему. Вы можете написать GetFunctionTable на управляемом C ++. Он может вызвать некоторый управляемый код C #, чтобы получить информацию о функции в управляемой структуре, а затем «маршалировать» ее в собственную структуру.

В (c #) управляемой версии функции GetFunctionTable:

delegate void FunctionDelegate();

public struct FuncDescStructManager
{
    public string Name;
    public FunctionDelegate Function;
    //...
}   

public static List<FuncDescStructManager> GetFunctionTableManaged()
{
    List<FuncDescStructManager> list = new List<FuncDescStructManager>();
    list.Add(new FuncDescStructManaged () {"ExampleF1", VExampleF1});
    return list;
}

В сборке моста смешанного режима вы можете реализовать встроенную функцию GetFunctionTable, вызов управляемой функции и сортировка данных:

int GetFunctionTable(FuncDescStruct **ppFunctionTable)
{
    // Call the managed function
    List<FuncDescStructManaged>^ managedList = GetFunctionTableManaged();

    nativeArray = malloc(managedList.Length * sizeof(FuncDescStruct));
    int i=0;
    foreach (FuncDescStructManaged managedFunc in managedList)
    {
        // Marshall the managed string to native string could look like this:
        stringPtr = Marshal::StringToHGlobalUni(managedFunc.Name);
        nativeArray[i].Name = ((wchar_t*)stringPtr.ToPointer());
        Marshal::FreeHGlobal(stringPtr);

        // Marshall a delegate into a native function pointer using a 
        // wrapper class:
        WrapDelegateAsPtr funcPtr = new WrapDelegateAsPtr(managedFunc.Function);
        // funcPtr will need to be deleted by caller
        nativeArray[i].Function = funcPtr.NativeFunction;
        i++;
    }
    return i;
}

// Mixed mode wrapper class
// Member is a reference to a managed delegate.
// Because we need to reference this from native code, the wrapped 
// delegate will be stored as a void*.
class WrapDelegateAsFPtr
{
    public:
    WrapDelegateAsNativeFunctionPointer(FunctionDelegate _delegate)
    {
        delegate = _delegate;
        // Tell the garbage handler not to remove the delegate object yet
        GCHandle gch = GCHandle::Alloc(svgContents);
        managedDelegatePtr = GCHandle::ToIntPtr(gch).ToPointer();       
    }

    ~WrapDelegateAsNativeFunctionPointer
    {
        // Tell the garbage collector we are finished with the managed object
        IntPtr temp(managedDelegatePtr;);
        GCHandle gch = static_cast<GCHandle>(temp);
        gch.Free();
    }

    void NativeFunction()
    {
    }

    private:
        void* managedDelegatePtr;
}

Надеюсь, это поможет - любые вопросы просто задавайте!

1 голос
/ 16 апреля 2012

Это довольно запоздалый ответ, но я столкнулся с точно такой же проблемой. Я внедрил DATA PLUGIN с помощью фреймворка Kostya и заставил его работать отлично, затем, пытаясь внедрить AFL-плагин, я столкнулся с проблемой, как указано выше. Мне удалось заставить его работать без использования рэпперского класса C ++. Проблема заключается в указателе функции / ссылке / адресе, который передается из-под FUnctionTable. Поскольку эти функции были объявлены как STATIC для целей EXPORT, они несовместимы с имплементацией делегата C ++ в методе GETFUNCTIONTABLE. Если вы делаете следующее, оно должно работать: -

Добавьте 2 подписи: -

[DllImport("kernel32", CharSet = CharSet.Ansi, ExactSpelling = true, SetLastError = true)]
static extern IntPtr GetProcAddress(IntPtr hModule, string procName);
[DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
internal static extern IntPtr LoadLibrary(string lpFileName);

[DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
internal static extern bool SetDllDirectory(string lpPathName); 
  • определение ссылки на функцию в структуре GetFunctionTable должно быть изменено на: Функция IntPtr;

  • Добавьте следующие операторы, чтобы получить адрес экспортируемой функции: -

    IntPtr dllHandle = LoadLibrary(fullPath);
    IntPtr fptr = GetProcAddress(dllHandle, "VfmExample1");
    

и, наконец, инициализировать переменную функции в структуре GetFunctionTable, например,

functable[0].Description.function = fptr;

и это должно сделать

0 голосов
/ 13 декабря 2010

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

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