65.9K
CodeProject 正在变化。 阅读更多。
Home

将委托转换为函数指针以实现回调函数

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.71/5 (13投票s)

2003年2月3日

2分钟阅读

viewsIcon

154383

downloadIcon

978

将委托转换为函数指针以实现回调函数,用于混合托管 C++ 和非托管 C++ 编码,以及 DLL 调用。

引言

这些天,我读了 Nishant S. 的文章 使用 IJW 实现回调函数(避免 DllImport),从中受益良多。 我想知道是否有某种方法可以将委托转换为函数指针,我终于找到了。

当您调用 DLL 中的一个函数,并且该函数需要一个回调函数指针时。 首先,您可以使用 DllImport(如果您愿意),这是一个简单的解决方案。 但我不喜欢 DllImport。 更重要的是,有时我想要调用的函数不在 DLL 中。 也就是说,如果我正在编写混合的托管 C++ 和非托管 C++。 即使 DllImport 也将无法工作。 有几种方法可以在非托管 C++ 代码中调用托管 C++ 函数。

  1. 使用静态函数
     //managed class
    __gc class classA1 {
        public: static void func1(int nArg)    {
                     Console::WriteLine(nArg.ToString());
                }
    };
    //unmanaged class
    class classB1 {
        public: classB1(int nArg)    {
            m_nCount = nArg;
        }
        public: void func1()  {
            classA1::func1(m_nCount);
        }
        protected: int m_nCount;
    };

    当需要调用非静态函数时,有第二个解决方案。

  2. 使用托管对象作为参数。
     //managed class
    __gc class classA1 {
        public : void func2(int nArg)  {
                     Console::WriteLine(nArg.ToString());
                 }
    };
    //unmanaged class
    class classB1 {
        public: classB1(int nArg) {
            m_nCount = nArg;
        }
        public : void func2(classA1* pClassA1)     {
             pClassA1->func2(m_nCount);
         }
        protected: int m_nCount;
    };

    它似乎运行良好,但是当调用 pClassA1->func2(…) 的不是函数 classB1:: func2(…)] 时。 也许 classB1:: func2(…) 调用其他对象的成员函数,并且该函数调用其他函数,最后遗憾的是调用了 pClassA1->func2(…)。 您必须长时间保留 pClassA1 作为参数才能运行,幸好没有虚函数。 虚函数不能将托管对象作为参数。 并且一个非托管对象不能有成员变量来保存托管对象。

  3. 创建一个指向实例成员函数的函数指针。

    我还没有在 .NET framework SDK 中找到任何可以直接将委托转换为函数指针的方法,但我注意到 Marshal::StructToPtr() 宣布能够将托管 struct 转换为非托管内存块。 它的作用是将委托包装在托管 struct 中,然后将 struct 转换为非托管块。 起初,我使用了一个非托管 struct,它有一个函数指针作为非托管内存块,像这样

    typedef void (*PFUNC)(int)
    
    Struct PFUNC_Wrapper
    {
        PFUNC thepfunc;
    };

    像这样使用托管 struct

    __delegate void MyDelegate(int nArg);
    [StructLayoutAttribute( LayoutKind::Sequential, 
                           CharSet = CharSet::Ansi )]
    __gc struct Delegate_Wrapper
    {
        [MarshalAsAttribute(UnmanagedType::FunctionPtr)]
        MyDelegate*  _theDelegate;
    };

    然后我将托管 struct 转换为非托管 struct。 在 PFUNC thepfunc 字段中,我得到了函数指针。 喔,我得到了! 但很快我发现我也可以将非托管 struct 转换为函数指针,而不是访问 PFUNC thepfunc 的成员字段。 像这样:*(PFUNC*)(&theUnmanagedStruct)。 在这里,您一定已经看到非托管 struct 没用了。 所以我只是使用一个 PFUNC 来接收数据作为非托管内存块。

源代码

 __gc class classA1 {    
    public : void func2(int nArg) {
                 Console::WriteLine(nArg.ToString());
             }
};
typedef void (*PFUNC)(int);

class classB1 {
    public: classB1(int nArg) {
        m_nCount = nArg;
    }
    public: virtual void func3(PFUNC pCallback){
        if(pCallback != NULL)    {
            pCallback(m_nCount);
        }
    }
    protected: int m_nCount;
};
__delegate void MyDelegate(int nArg);

[StructLayoutAttribute( LayoutKind::Sequential, CharSet = CharSet::Ansi )]
__gc struct Delegate_Wrapper
{
    [MarshalAsAttribute(UnmanagedType::FunctionPtr)]
    MyDelegate*  _theDelegate;
};

// This is the entry point for this application
int _tmain(void)
{
    // TODO: Please replace the sample code below with your own.
    Console::WriteLine(S"Hello World");

    //create instance of managed classA1
    classA1* theA1 = new classA1();

    //create instance of Delegate_Wrapper
    Delegate_Wrapper* theWrapper = new Delegate_Wrapper();
    theWrapper->_theDelegate = new MyDelegate(theA1,&classA1::func2);
    
    //Convert the Delegate to Function Pointer.
    PFUNC pFnc;
    Marshal::StructureToPtr(theWrapper,&pFnc,false);
    
    //create instance of unmaged classB1
    classB1 theB1(3);

    //use the function pointer as a callback function
    theB1.func3(pFnc);
    
    Console::ReadLine();
    return 0;
}

确保垃圾回收器在回调函数完成其工作之前不会回收委托。

结论

可以通过函数指针使用托管类的成员函数。 即使它是一个非静态函数!!

电子邮件:peiweny@hotmail.com

© . All rights reserved.