如何编组指向从托管代码到非托管代码的结构指针数组的指针?

时间:2010-12-13 10:39:06

标签: c# c++ arrays interop

我正在尝试编写必须实现以下功能的插件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 个答案:

答案 0 :(得分: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 :(得分:1)

这是一个相当迟钝的答案,但我遇到了完全相同的问题。我已经使用Kostya的框架实现了一个DATA PLUGIN,并且让它完美地工作,然后试图破坏AFL插件,我按照上面的说法遇到了问题。我已经设法在不使用C ++说唱歌手类的情况下进行工作。 问题在于使用FUnctionTable传递的函数指针/引用/地址。由于这些函数已被STLA用于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;

那应该这样做

答案 2 :(得分:0)

您必须使用修复构造来修复缓冲区,因为如果不这样做,C#GC保留移动它的权利,使指针无效。我不知道如何无限期地修复缓冲区。您将不得不担心如何管理内存。