【问题标题】:C3374: can't take address of 'function' unless creating delegate instanceC3374:除非创建委托实例,否则无法获取“函数”的地址
【发布时间】:2009-05-04 23:18:20
【问题描述】:

我在使用第三方库注册函数注册回调时遇到了困难。我正在用 C++ CLI 编写,并访问一个用 C 或 C++ 编写的库。

上面的编译错误是什么意思?

这是供应商定义的注册功能:

MYCO int WINAPI MyCo_Device_Register_CallbackFunc(LPVOID func, LPVOID lpParam, DWORD mask);

这是供应商定义的回调:

typedef void (CALLBACK* MyCo_Device_CallbackProc)(DWORD ddStatus, LPVOID lpParam);

我的回调函数:

public ref class Device 
{
    void CallBackFunc(DWORD ddStatus, LPVOID lpParam) {};
}

我的电话(失败):

MyCo_Device_Register_CallbackFunc((LPVOID) Device::CallBackFunc, nullptr, 0xFFFF);

【问题讨论】:

标签: .net c++-cli clr


【解决方案1】:

如果我不得不猜测一下,您会尝试获取 MANAGED 函数的地址并将其作为 NATIVE 函数指针传递。那根本行不通。要么写一个原生桥(更难而且可能没有优势),或者(这是更简单的方法),创建一个委托并使用Marshal.GetFunctionPointerForDelegate。不要忘记,委托必须在本机代码期望回调的时间内保持活动状态,因此您很可能需要将其存储在某个地方。

哦,引用类或值类中的所有函数都是托管函数,以防不清楚。

[编辑] 扩展答案以涵盖 cmets 中的问题。第一步是创建一个匹配的委托。对于您提供的功能,它将如下所示:

public delegate void CallbackFuncDelegate(DWORD ddStatus, LPVOID lpParam);

然后,您需要创建一个:

CallbackFuncDelegate del = gcnew CallbackFuncDelegate(CallbackFunc);

如前所述,您应该将该类存储为成员,以确保它不会消失。最后,您需要获取函数指针并将其传递下去:

MyCo_Device_Register_CallbackFunc((MyCo_Device_CallbackProc) Marshal::GetFunctionPointerForDelegate(del).ToPointer(), 0, 0);

我认为这涵盖了所有内容。您只需要使用命名空间系统;你应该没事。

【讨论】:

  • 我认为你一针见血。您建议的代码是什么样的,我需要哪些内容?
  • 以及如何确保我保持代理存活,以免它被收集(生命周期管理)
  • 我进行了以下更改:1. ^ 在委托创建中丢失 2. & 在 gcnew-ing 委托时丢失 CallbackFuncDelegate^ del = gcnew CallbackFuncDelegate(&H8Device::BtnCallBackFunc);
  • 还有一个我无法解决的问题:void Device:CallbackFunc(DWORD, LPVOID): the specified function does not match the delegate type 'void (DWORD,LPVOID)' Alexy 建议将其设为静态(允许它编译),但使用静态,我无权访问任何类的数据。
  • 由于 msdn 上此错误的示例显示 msdn.microsoft.com/en-us/library/5408cs95(VS.80).aspx ,您可以在创建委托时传入对设备的引用: CallbackFuncDelegate ^ del = gcnew CallbackFuncDelegate(device, &Device::CallbackFunc);那么它就不需要是静态方法了。
【解决方案2】:

你的回调函数是一个成员——指向成员的指针和指向函数的指针是不同的东西。

即使在 Promit 的解决方案中:

CallbackFuncDelegate(CallbackFunc)

CallbackFunc 不应该是类成员

你可以简单地写

public ref class Device 
{
    static void CallBackFunc(DWORD ddStatus, LPVOID lpParam) {};
}

然后使用

Device::CallBackFunc

【讨论】:

  • 您的建议确实解决了编译问题,但奇怪的是我现在有一个无法访问私有数据的静态方法(不能做任何事情)。我想知道这是否可行
  • 首先——它可以访问静态私有数据。第二——你可以创建某种单例 8)。直接使用此模式 - 或创建具有静态函数和指向对象的静态指针的委托。只是为了澄清指向成员的问题:你可以在这里阅读一些介绍informit.com/guides/content.aspx?g=cplusplus&seqNum=142 长话短说——你的初始(非静态)回调函数需要“this”指针——这就是为什么指针—— to-member 不同——只有当你有一个对象时才能使用它
  • 感谢您的建议。将我的类更改为单例以便以某种方式使用回调感觉不太正确。
  • 单例解决方案适用于您只能拥有一台设备的情况。以防万一——无论如何,因为你需要静态指针——你只能让一个设备获得回调(直到你可以传递将被传递回你的回调的用户数据)
  • 刚刚又看了一遍代码 MYCO int WINAPI MyCo_Device_Register_CallbackFunc(LPVOID func, LPVOID lpParam, DWORD mask); lpParam 可能正是您所需要的。查看文档——如果这与传递给回调的 lpParam 相同,则可以插入指向所需设备的指针;-)
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-12-04
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-01-02
相关资源
最近更新 更多