1. 概述
1.1. Miranda IM运行以后的主界面:
1.2. 用spy++可以看到窗口的构成:
从上图可以看出miranda的窗口的基本构成,下面对比较重要的几个窗口进行具体的分析.
1.3. 关于GWL_USERDATA
Miranda的窗口里经常会有一些全局的数据需要保存, Miranda的做法是在处理
WM_INITDIALOG时动态创建一个对象并初始化, 然后以GWL_USERDATA为参数调用SetWindowLong将之作为该窗口的用户数据,这样通过调用GetWindowLong就可以返回该用户数据的指针.
在WM_DESTROY消息处理里释放该动态创建的对象,并将窗口的用户数据设置为空.
这种做法估计在SDK编程里是很常用的手法. 在MFC程序里每个对应窗口(指桌面上看到的窗口)都会有一个从CWnd派生类的实例与之对应,所以与该窗口相关的数据尽管作为类的一个成员变量就好了,不用象sdk里这么麻烦.
BOOL CALLBACK DlgProcMessage(HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam)
{
struct MessageWindowData *dat;
dat = (struct MessageWindowData *) GetWindowLong(hwndDlg, GWL_USERDATA);
switch (msg)
{
case WM_INITDIALOG:
{
struct NewMessageWindowLParam *newData = (struct NewMessageWindowLParam *) lParam;
TranslateDialogDefault(hwndDlg);
dat = (struct MessageWindowData *) calloc(sizeof(struct MessageWindowData),1);
SetWindowLong(hwndDlg, GWL_USERDATA, (LONG) dat);
……
}
……
case WM_DESTROY:
{
……
free(dat);
SetWindowLong(hwndDlg, GWL_USERDATA, 0);
……
}
}
……
}
1.4. MessageWindowData
struct MessageWindowData
{
HANDLE hContact;
HANDLE hDbEventFirst, hDbEventLast;
HANDLE hSendId;
int sendCount;
HBRUSH hBkgBrush;
int splitterPos, originalSplitterPos;
char *sendBuffer;
SIZE minEditBoxSize;
RECT minEditInit;
int lineHeight;
int windowWasCascaded;
int nFlash;
int nFlashMax;
int nLabelRight;
int nTypeSecs;
int nTypeMode;
int avatarWidth;
int avatarHeight;
int limitAvatarH;
HBITMAP avatarPic;
DWORD nLastTyping;
int showTyping;
HWND hwndStatus;
DWORD lastMessage;
char *szProto;
WORD wStatus;
WORD wOldStatus;
TCmdList *cmdList;
TCmdList *cmdListCurrent;
int bIsRtl, bIsFirstAppend, bIsAutoRTL;
int lastEventType;
};
2. 主窗口
2.1. WindowClass:
#define MIRANDACLASS "Miranda"
2.2. 窗口过程:
ContactListWndProc
2.3. 主窗口句柄:
cli.hwndContactList
2.4. 窗口创建过程:
2.4.1. 代码位置:
/miranda/src/modules/clist, cl 是contact list的缩写
2.4.2. CLIST_INTERFACE
CLIST_INTERFACE 是一个关键的struct,里面登记了主窗口,联系人列表窗口里各种api函数指针,程序里有一个全局的变量
CLIST_INTERFACE cli;
cli在加载模块时在函数srvRetrieveInterface里被初始化
模块加载完毕以后,WinMain会激发ModulesLoaded事件
NotifyEventHooks(hModulesLoadedEvent,0,0);
2.4.3. 注册主窗口类
clui.c中的LoadCLUIModule被调用
在int LoadCLUIModule(void) 里注册主窗口类:
wndclass.style = CS_HREDRAW | CS_VREDRAW | (IsWinVerXPPlus() && DBGetContactSettingByte(NULL, "CList", "WindowShadow", 0) == 1 ? CS_DROPSHADOW : 0);
wndclass.lpfnWndProc = ContactListWndProc;
wndclass.cbClsExtra = 0;
wndclass.cbWndExtra = 0;
wndclass.hInstance = cli.hInst;
wndclass.hIcon = LoadSkinnedIcon(SKINICON_OTHER_MIRANDA);
wndclass.hCursor = LoadCursor(NULL, IDC_ARROW);
wndclass.hbrBackground = (HBRUSH) (COLOR_3DFACE + 1);
wndclass.lpszMenuName = MAKEINTRESOURCE(IDR_CLISTMENU);
wndclass.lpszClassName = _T(MIRANDACLASS);//
RegisterClass(&wndclass);
2.4.4. 创建主窗口:
//创建主窗口
cli.hwndContactList = CreateWindowEx(
DBGetContactSettingByte(NULL, "CList", "ToolWindow", SETTING_TOOLWINDOW_DEFAULT) ? WS_EX_TOOLWINDOW : 0,
_T(MIRANDACLASS),//主窗口类
titleText,
(DBGetContactSettingByte(NULL, "CLUI", "ShowCaption", SETTING_SHOWCAPTION_DEFAULT) ?
WS_CAPTION | WS_SYSMENU | WS_MINIMIZEBOX : 0) | WS_POPUPWINDOW | WS_THICKFRAME | WS_CLIPCHILDREN,
(int) DBGetContactSettingDword(NULL, "CList", "x", 700),
(int) DBGetContactSettingDword(NULL, "CList", "y", 221),
(int) DBGetContactSettingDword(NULL, "CList", "Width", 108),
(int) DBGetContactSettingDword(NULL, "CList", "Height", 310),
NULL, NULL, cli.hInst, NULL);
2.4.5. 创建子窗口
主窗口创建以后, 调用cli.pfnOnCreateClc(); 依次创建出其他字窗口