我们以minigui-1.6.10的industrialsample为例来讨论这个问题,问题的答案是在GetWndProc函数中发现的
static inline WNDPROC GetWndProc (HWND hWnd)
{
return ((PMAINWIN)hWnd)->MainWindowProc;
// 读取hWnd地址开始处以PMAINWIN为结构体的MainWindowProc偏移出的数据,
// 而CONTROL控件类结构体的ControlProc和PMAINWIN为结构体的MainWindowProc偏移位置相同,因为
// 这2个结构体的前面定义的所有数据完全一致(如果完全一致,完全可以将共有的内容抽象到一个独立结构体里边去,
// 像minigui这种做法,很是缺乏代码结构性,完全在拼凑代码嘛,不敢苟同这种做法[luther.gliethttp])
}
1.首先注册class控件类"ctrl_rotate",该控件类的回调函数为RotateProc
#define CTRL_ROTATE "ctrl_rotate"
BOOL
RegisterRotate(void)
{
WNDCLASS Class;
Class.spClassName = CTRL_ROTATE; // 将要注册的控件类名称"ctrl_rotate"
Class.dwStyle = WS_NONE;
Class.dwExStyle = WS_EX_NONE;
Class.hCursor = GetSystemCursor(IDC_ARROW);
Class.iBkColor = COLOR_lightwhite;
Class.WinProc = RotateProc; // 控件回调函数
return RegisterWindowClass(&Class);
}
2.在RadarWinProc窗口创建时生成"ctrl_rotate"控件类的实例对象.
==>
case MSG_CREATE:
radar_rotate.hwnd =
CreateWindow(CTRL_ROTATE, "", WS_VISIBLE, IDC_RADAR_CTRL,
RADAR_X, RADAR_Y, RADAR_W, RADAR_H, hWnd,
(int)&radar_rotate); // 创建一个窗口,该窗口回调函数参数为&radar_rotate
细节就在CreateWindow()中,展开它看看,
#define CreateWindow(class_name, caption, style, \
id, x, y, w, h, parent, add_data) \
CreateWindowEx(class_name, caption, style, 0, \
id, x, y, w, h, parent, add_data)
HWND GUIAPI CreateWindowEx (const char* spClassName, const char* spCaption,
DWORD dwStyle, DWORD dwExStyle, int id,
int x, int y, int w, int h,
HWND hParentWnd, DWORD dwAddData)
{
......
// MSG_GETCTRLCLASSINFO消息调用
// GetControlClassInfo (const char* szClassName)处理函数
// 获取RegisterWindowClass注册的类,以该类名命名的控件将全部使用该类定义的控件统一方法,即:Class.WinProc = RotateProc;
cci = (PCTRLCLASSINFO)SendMessage (HWND_DESKTOP, // 以同步方式直接回调HWND_DESKTOP桌面窗口的回调函数处理消息[luther.gliethttp]
MSG_GETCTRLCLASSINFO, 0, (LPARAM)spClassName); // 源码见后
pNewCtrl->ControlProc = cci->ControlProc; // 就是Class.WinProc = RotateProc;
pNewCtrl->pcci = cci; // 但是随后
SendMessage (HWND_DESKTOP, MSG_NEWCTRLINSTANCE, (WPARAM)hParentWnd, (LPARAM)pNewCtrl); // 将该pNewCtrl添加到hParentWnd父窗口中.
SendMessage ((HWND)pNewCtrl, MSG_NCCREATE, 0, (LPARAM)pNewCtrl);
// 向pNewCtrl发送MSG_NCCREATE消息, 调用的pNewCtrl窗口回调函数MainWindowProc()就是控件的ControlProc
// 函数,我们这里就是RotateProc回调函数,传给该回调函数的参数为pNewCtrl,即该控件类的一个具体实例对象指针[luther.gliethttp].
......
}
DesktopWinProc
==>
case MSG_GETCTRLCLASSINFO:
return (int)GetControlClassInfo ((const char*)lParam); // 获取RegisterRotate注册的"ctrl_rotate"控件类信息 case MSG_NEWCTRLINSTANCE:
dskOnNewCtrlInstance ((PCONTROL)wParam, (PCONTROL)lParam); // 源码见后
break;
// 将一个calloc内存空间的新控件登记到pParent父窗口中[lutehr.gliethttp]
static void dskOnNewCtrlInstance (PCONTROL pParent, PCONTROL pNewCtrl)
{
PCONTROL pFirstCtrl, pLastCtrl;
pFirstCtrl = pParent->children; // 取出child链表头
pNewCtrl->next = NULL;
if (!pFirstCtrl) {
// parent还没有一个child,那么pNewCtrl成为第一个
pParent->children = pNewCtrl;
pNewCtrl->prev = NULL;
}
else {
pLastCtrl = pFirstCtrl;
while (pLastCtrl->next) // 找到结尾,之所以需要将pNewCtrl添加到结尾,涉及到GUI图形的Z序知识,
// 位于一个平面desktop上的多个windows窗体,是有上下层次感的,位于最上层的窗体可以覆盖剪切所有位于它下层的
// 窗体,根据添加顺序先后,该windows窗体内部多个控件也就有了控件们的Z序,后添加的控件永远可以覆盖先前添加的
// 控件[luther.gliethttp]
pLastCtrl = pLastCtrl->next;
pLastCtrl->next = pNewCtrl; // 将pNewCtrl置于Z序最上层[luther.gliethttp]
pNewCtrl->prev = pLastCtrl;
}
init_invrgn ((PMAINWIN)pNewCtrl); // 初始化该控件窗口剪切计算单元
if (pNewCtrl->dwExStyle & WS_EX_CTRLASMAINWIN) {
// The control can be displayed out of the main window which contains the control.
PZORDERNODE pNode = pNewCtrl->pZOrderNode;
// Init Global Clip Region info.
init_gcrinfo ((PMAINWIN)pNewCtrl);
// Add to Z-Order list.
pNode->hWnd = (HWND)pNewCtrl;
add_new_window ((PMAINWIN)pNewCtrl, pNode);
// Update others' GCRInfo.
if (pNewCtrl->dwStyle & WS_VISIBLE ) {
dskUpdateGCRInfoOnShowNewMainWin ((PMAINWIN)pNewCtrl);
}
}
pNewCtrl->pcci->nUseCount ++;
// 该pcci引用计数加1,只有当该控件类引用计数nUseCount为0时,UnregisterWindowClass才会真正释放掉
// 由RegisterWindowClass登记到ccitable[]全局控件类管理变量中的控件类空间free(cci);
}
int GUIAPI SendMessage (HWND hWnd, int iMsg, WPARAM wParam, LPARAM lParam)
{
WNDPROC WndProc;
MG_CHECK_RET (MG_IS_WINDOW(hWnd), -1);
#ifndef _LITE_VERSION
//该发起者因为调用SendMessage,发现向其他thread[比如Desktop线程发送消息]而被阻塞),将停留在 SendSyncMessage函数sem_wait (SyncMsg.sem_handle)中,等待其他线程处理完自己发过去的消息之后,发送sem_post(pSyncMsg->sem_handle);唤醒信号.[luther.gliethttp]
if (!BE_THIS_THREAD(hWnd))
return SendSyncMessage (hWnd, iMsg, wParam, lParam);
#endif /* !_LITE_VERSION */
if ( !(WndProc = GetWndProc(hWnd)) ) // 和ucgui不同的是,hWnd就是指针,所以省去了检索指针的操作,
// 但这也体现了minigui中1并不见得是1,让维护的人概念模糊,文字上说不清,我看像是混世型的[luther.gliethttp].
// 读取hWnd地址开始处以PMAINWIN为结构体的MainWindowProc偏移出的数据,
// 而CONTROL控件类结构体的ControlProc和PMAINWIN为结构体的MainWindowProc偏移位置相同,因为
// 这2个结构体的前面定义的所有数据完全一致(如果完全一致,完全可以将共有的内容抽象到一个独立结构体里边去,
// 像minigui这种做法,很是缺乏代码结构性,完全在拼凑代码嘛,不敢苟同这种做法,trick[luther.gliethttp])
return ERR_INV_HWND;
return (*WndProc)(hWnd, iMsg, wParam, lParam);
}
|