2010-12-13 58 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; 

而且应该这样做

相关问题