2010-12-13 59 views
2

我試圖寫一個必須實現以下功能插件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; 
    } 

這樣的代碼不工作,問題是如何發送一個指向託管結構數組的指針,供非託管代碼使用?我應該往哪個方向走?

回答

0

您必須使用修復構造來固定緩衝區,因爲如果您不這樣做,C#GC保留移動它的權利,使指針無效。我不知道如何無限期修復緩衝區。你將不得不擔心內存的管理方式。

2

在過去幾年中,我們一直在做很多這類事情。 我們使用混合模式「橋」組件來管理映射函數調用和編組託管和非託管環境之間的數據。我們經常使用'混合模式'類來包裝託管類,爲調用它的功能提供本地接口。

讓我們考慮你的問題。您可以在託管的C++中編寫GetFunctionTable。它可以調用一些託管的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

這是一個相當遲鈍的答案,但我已經提出了完全相同的問題。我用Kostya的框架實現了一個數據插件,並使它完美工作,然後試圖實現一個AFL插件,我遇到了上述問題。我設法在不使用C++ rapper類的情況下實現它的工作。 問題在於使用FUnctionTable傳遞的函數指針/引用/地址。由於這些函數被聲明爲STATIC用於EXPORT目的,它們與GETFUNCTIONTABLE方法中的C++委託實現不兼容。如果您DDO以下,它應該工作: -

添加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; 

而且應該這樣做

相關問題