回调函数可以是成员函数吗?

rchu 2001-01-18 01:50:00
我在用DirectPlay编程的时候,有类似下面的回调函数
BOOL FAR PASCAL EnumConnectionsCallback(LPCGUID lpguidSP,LPVOID lpConnection,DWORD dwConnectionSize, LPCDPNAME lpName,DWORD wFlags,LPVOID lpContext)

为了方便,我想把它改成类成员函数而不是全局函数,应该怎么办?
我怎么改都编译不通过

另外:新装的VC 6.0没有自动提示(类成员,参数)功能,怎么办?
...全文
361 10 打赏 收藏 转发到动态 举报
写回复
用AI写文章
10 条回复
切换为时间正序
请发表友善的回复…
发表回复
y_pro 2001-01-18
  • 打赏
  • 举报
回复
将成员函数声明为static FAR PASCAL
不过一般不要将回调函数放在类里面,这样会反而会破坏程序的结构,而且效率也较低。
通常回调函数要么什么都不做,要么就是实现一个完整的算法,应该把它独立在你的对象结
构之外,便于调试和修改。
zjsowin 2001-01-18
  • 打赏
  • 举报
回复
可以做到的,<Using windows Hooks with MFC> 里边有说明(英文的,不过不难):
不过 m_mov 为什么要用 0xB9 我就不明白,你要是看明白了,千万要告诉我 :)
template <class T> class HOOKDLL_API CHKThunk
{
BYTE m_mov; // mov ecx, <0xB9>
....
}

Callback thunking
As good C++ programmers, our intention is to provide a generic wrapper class to encapsulate all the windows hooks-related issues. In the optimal case, the programmer will only have to derive from my CHook class, and to override (if necessary) a member function, namely the hook procedure. Unfortunately, the hook procedures, as any callback functions, are prototyped as C functions, which do not associate data with operations on that data. Therefore, one cannot use as callback a normal member function, which needs the hidden “this” pointer.

Sometimes, it is possible to handle callbacks with member functions, by keeping a pointer in a collection indexed by a return value or passing a pointer in a parameter for application-supplied data. (See Davide Marcato's article, “Encapsulating Windows Timers in MFC,” in the January 98 issue of VCDJ.) However, these techniques are dependent on the particularities of the callback functions and not suitable for our case.

The solution appears from a deeper observation of the ATL source code, which ships with VC++ 6.0. The ATL team used thunks to give WindowProc access to the this pointer of the CWindowImpl<> class (See also Richard Grimes' article, “ATL and Win32 Applications” in the February 99 issue of VCDJ.). Generally, a thunk is a stubroutine, in an overlay-programming environment, which loads and jumps to the correct overlay.

In our case, we have to simulate the function call mechanism by putting the this value in the ECX CPU's register, and forcing a jump to the member function's code. As in the ATL code, I defined a helper class CThunk to "wrap" the method to be used as a callback. The difference between my example and the ATL code is in the CPU's instructions, which are completely different because we are using the thiscall calling convention, i.e. the this pointer is stored in ECX.

Here is the complete code for the class, which has only two methods:

#pragma pack(push, 1) // Force byte alignment

template <class T> class HOOKDLL_API CHKThunk
{
BYTE m_mov; // mov ecx, <0xB9>
DWORD m_this; // pThis
BYTE m_jmp; // jmp func <0xE9>
DWORD m_relproc; // jmp is relative
public:
typedef void (T::*TMPFN)();
void InitThunk(TMPFN method, const T* pThis)
{
union {
DWORD func;
TMPFN method;
} addr;

addr.method = method;
m_mov = 0xB9;
m_this = (DWORD)pThis;
m_jmp = 0xE9;
m_relproc = addr.func - (DWORD)(this+1);
FlushInstructionCache(GetCurrentProcess(), this, sizeof(*this));
}
FARPROC GetThunk() const {
_ASSERTE(m_mov == 0xB9);
return (FARPROC)this; }
};

#pragma pack(pop) // CHKThunk

Now, we simply derive the hook class from CHKThunk. The SetWindowsHookEx() function expects a callback function as a second argument, which normally cannot be a member in any class. After initializing the thunk in CHook's constructor with the address of our HKHookProc() member function, we simply pass to SetWindowsHookEx() the pointer returned by GetThunk(). This pointer can be interpreted as the address of a piece of code, which finally calls the HKHookProc() member function:

class HOOKDLL_API CHook: public CHKThunk<CHook>
{
public:
HHOOK m_hhook;

CHook(HHOOK hHook = NULL): m_hhook(hHook)
{
InitThunk((TMPFN)HKHookProc, this);
}
LRESULT HKHookProc(int nCode, WPARAM wParam, LPARAM lParam);
BOOL Hook();
BOOL UnHook();
};

Of course, this implementation is characteristic of the Intel X86 architecture, but the same technique can be used for any platform. For example, the window thunk used by ATL (see the file atlcom.h, which ships with VC++ 6.0) is implemented for both machines based on Intel and Alpha architectures.
ab 2001-01-18
  • 打赏
  • 举报
回复
static
无我无人 2001-01-18
  • 打赏
  • 举报
回复
全局函数或静态成员函数(因为静态成员函数本质上就是全局函数)。
类成员函数是隐含带有this指针工作的(由编译器实现),所以即使成员函数和回调函数的声明看上去一模一样,但实际却是不同的。
wishyouluck 2001-01-18
  • 打赏
  • 举报
回复
回调函数最好用全局函数。如果要是必须用类成员函数的话,这个成员函数一定要是static类型的,也就是静态成员函数。
因为回调函数必须具有独立于任何对象实例的地址。普通成员函数的地址是通过类中的this指针计算出来的,所以不是独立地址。而静态成员函数的地址是与this指针无关的,具有全局函数的性质。
taolei 2001-01-18
  • 打赏
  • 举报
回复
没有自动提示,你要把.h文件加到你的"Header Files"里。
taolei 2001-01-18
  • 打赏
  • 举报
回复
写成static函数即可。
Rechard_G 2001-01-18
  • 打赏
  • 举报
回复
静态成员函数可以做回调函数!
没有自动提示可以试试将项目目录下的.ncb删除,再重新大开该项目。(别担心!VC会重新生成一个新的.ncb文件)
znsoft 2001-01-18
  • 打赏
  • 举报
回复
应该在安装回调时函数已经存在(包含它的对象已经建立),拆除时函数还在(包含它的对象还在)这样才用(理论上,我一般用全局的),切记不要在构造函数中安装本类成员作为callback函数否则你会死得难看的!
nononono 2001-01-18
  • 打赏
  • 举报
回复
没这样用过,自认为:
回调函数应该可以是成员函数。但这个函数,应该是一个已存在的对象的成员函数。


16,472

社区成员

发帖
与我相关
我的任务
社区描述
VC/MFC相关问题讨论
社区管理员
  • 基础类社区
  • Web++
  • encoderlee
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告

        VC/MFC社区版块或许是CSDN最“古老”的版块了,记忆之中,与CSDN的年龄几乎差不多。随着时间的推移,MFC技术渐渐的偏离了开发主流,若干年之后的今天,当我们面对着微软的这个经典之笔,内心充满着敬意,那些曾经的记忆,可以说代表着二十年前曾经的辉煌……
        向经典致敬,或许是老一代程序员内心里面难以释怀的感受。互联网大行其道的今天,我们期待着MFC技术能够恢复其曾经的辉煌,或许这个期待会永远成为一种“梦想”,或许一切皆有可能……
        我们希望这个版块可以很好的适配Web时代,期待更好的互联网技术能够使得MFC技术框架得以重现活力,……

试试用AI创作助手写篇文章吧