Ipmsg源码分析(二)
Ipmsg.cpp为其主要的实现文件,其中调用系统的
int WINAPI WinMain(HINSTANCE hI, HINSTANCE, LPSTR cmdLine, int nCmdShow)作为入口函数,在函数中只是简单的调用了自己写的app类
TMsgApp app(hI, cmdLine, nCmdShow); // 调用其构造函数 创建对象
return app.Run(); // 由于TMsgApp没有自己的run
// 因此会调用其父类TApp的Run // 函数
在TMsgApp的构造函数的实现中,种下随机数字种子,只调用了一个系统函数
srand((UINT)Time());
TMsgApp 简单地public继承自TApp
在其继承中,添加了一个自己的函数virtual void InitWindow(void);
// 系统启动的时候调用的是其父类TApp的Run函数,此 函数如下。易知函数主要调用两个函数,随后进入消息循环。
- int TApp::Run(void)
- {
- MSG msg;
- InitApp(); // 实现注册窗口类, 设置窗口处理过程
- InitWindow(); // initWindow()在为一个纯虚函数,此处调用应该 // 是子类TMsgApp的initWindow()函数
- // 注意:由于是子类调用的父类的run成员函数
- // 而父类调用了一个纯虚函数,通过虚函数表,
- // 此调用为调用子类的initWindow函数。
- while (::GetMessage(&msg, NULL, 0, 0))
- {
- if (PreProcMsg(&msg))
- continue;
- ::TranslateMessage(&msg);
- ::DispatchMessage(&msg);
- }
- return msg.wParam;
- }
下面看子类实现的initWindow()主要完成了什么功能:
下面是子类TMsgApp的initWindow函数
- void TMsgApp::InitWindow(void)
- {
- WNDCLASS wc;
- HWND hWnd;
- char class_name[MAX_PATH] = IPMSG_CLASS, *tok, *msg, *p;
- ULONG nicAddr = 0;
- int port_no = atoi(cmdLine);
- if (port_no == 0) 、、端口处理,是否设置了端口
- port_no = IPMSG_DEFAULT_PORT;
- 、、处理命令行消息,如果是命令行方式启动
- if ((tok = strchr(cmdLine, '/')) && separate_token(tok, ' ', &p))
- {
- BOOL diag = TRUE;
- DWORD status = 0xffffffff;
- if (stricmp(tok, "/NIC") == 0)
- {
- if (tok = separate_token(NULL, ' ', &p))
- nicAddr = ResolveAddr(tok);
- }
- else if (stricmp(tok, "/MSG") == 0)
- {
- MsgMng msgMng(nicAddr, port_no);
- ULONG command = IPMSG_SENDMSG|IPMSG_NOADDLISTOPT|IPMSG_NOLOGOPT, destAddr;
- while ((tok = separate_token(NULL, ' ', &p)) != NULL && *tok == '/') {
- if (stricmp(tok, "/LOG") == 0)
- command &= ~IPMSG_NOLOGOPT;
- else if (stricmp(tok, "/SEAL") == 0)
- command |= IPMSG_SECRETOPT;
- }
- if ((msg = separate_token(NULL, 0, &p)) != NULL)
- {
- diag = FALSE;
- if ((destAddr = ResolveAddr(tok)) != NULL)
- status = msgMng.Send(destAddr, htons(port_no), command, msg) ? 0 : -1;
- }
- }
- 、、显示出错信息,给用户以提示。此种错误仅仅在命令行方式下产生
- if (nicAddr == 0)
- {
- if (diag)
- MessageBox(0, "ipmsg.exe [portno] [/MSG [/LOG] [/SEAL] <hostname or IP addr> <message>]/r/nipmsg.exe [portno] [/NIC nic_addr]", MSG_STR, MB_OK);
- ::ExitProcess(status);
- return;
- }
- }
- if (port_no != IPMSG_DEFAULT_PORT || nicAddr)
- wsprintf(class_name, nicAddr ? "%s_%d_%s" : "%s_%d", IPMSG_CLASS, port_no, inet_ntoa(*(in_addr *)&nicAddr));
- memset(&wc, 0, sizeof(wc));
- wc.style = CS_DBLCLKS;
- wc.lpfnWndProc = TApp::WinProc; 、、窗口处理函数为一个静态成员函数
- wc.cbClsExtra = 0;
- wc.cbWndExtra = 0;
- wc.hInstance = hI;
- wc.hIcon = ::LoadIcon(hI, (LPCSTR)IPMSG_ICON);
- wc.hCursor = ::LoadCursor(NULL, IDC_ARROW);
- wc.hbrBackground = NULL;
- wc.lpszMenuName = NULL;
- wc.lpszClassName = class_name;
- // 创建互斥mutex来保护共享资源,但是由于创建此mutex的线程不拥有其使用权。// WaitForSingleObject函数什么时候返回呢?
- HANDLE hMutex = ::CreateMutex(NULL, FALSE, class_name);
- ::WaitForSingleObject(hMutex, INFINITE);
- if ((hWnd = ::FindWindow(class_name, NULL)) != NULL || ::RegisterClass(&wc) == 0)
- {
- if (hWnd != NULL)
- ::SetForegroundWindow(hWnd);
- ::ExitProcess(0xffffffff);
- return;
- }
- // 生成主窗口,并且将其显示
- mainWnd = new TMainWin(nicAddr, port_no);
- mainWnd->Create(class_name, IP_MSG, WS_OVERLAPPEDWINDOW | (IsNewShell() ? WS_MINIMIZE : 0));
- ::ReleaseMutex(hMutex);
- ::CloseHandle(hMutex);
- }
主程序的窗口处理过程为TApp类的一个静态成员函数,由此可知。回调函数可以封装在类中,但是注意必须作为类的静态成员函数。
- LRESULT CALLBACK TApp::WinProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
- {
- TWin *win = SearchWnd(hWnd);
- // 调用子类的窗口处理函数 由于其父类窗口处理过程定义的都为虚函数,而且基本没有对// 消息进行处理,因此主要在子类中实现消息处理过程,这就是调用虚函数的好处
- if (win)
- return win->WinProc(uMsg, wParam, lParam);
- if ((win = preWnd) != NULL)
- {
- preWnd = NULL;
- AddWinByWnd(win, hWnd);
- return win->WinProc(uMsg, wParam, lParam);
- }
- return DefWindowProc(hWnd, uMsg, wParam, lParam);
- }
此程序将创建的所有的窗口句柄放到一个句柄数组中,以便查询。此窗口数组是TWin的静态数据成员。此静态成员变量操作了静态成员函数
如果找到了接受消息的窗口句柄,调用窗口句柄的消息处理函数进行处理
LRESULT TWin::WinProc(UINT uMsg, WPARAM wParam, LPARAM lParam)
此窗口处理过程处理以下消息:
WM_CREATE、WM_CLOSE、WM_COMMAND、WM_SYSCOMMAND、WM_TIMER、WM_NCDESTROY、WM_QUERYENDSESSION、WM_ENDSESSION、WM_QUERYOPENWM_PAINT、WM_NCPAINT、WM_SIZE、WM_GETMINMAXINFO、WM_SETCURSOR、WM_MOUSEMOVE、WM_NCHITTEST、
WM_MEASUREITEM、WM_DRAWITEM、WM_NOTIFY、WM_CONTEXTMENU、WM_HOTKEY、WM_VSCROLL
此语句作用:如果done为真,即消息已经被处理,返回result,否则调用默认消息处理函数。
return done ? result : DefWindowProc(uMsg, wParam, lParam);
下面主要完成对TMainWin类中函数的分析,此函数中完成发送到此窗口的消息的处理。
例如TWin中成员函数WinProc调用了函数EvCreate();
由于在父类和子类中都存在EvCreate()函数。用基类的指针调用函数的时候,基类指针得到对象类型的确定化。调用父类中的WinProc函数(子类中没有对此函数进行重载),在WinProc函数中调用EvCreate(),此时应该调用子类的EvCreate()函数。可以理解为在调用函数的时候始终存在this指针,根据this指针指向的类型和类型中的成员函数是否是虚函数,是否重新定义来确定到底调用哪一个函数。
分析EvTimer函数处理过程。
此函数处理定时器消息。根据TimeID的不同分别进行处理
取得在线列表,是udp方式发送一个广播,此局域网中所有用户都可以接受到此消息,由于此消息有特定的格式,标识符等信息,由于是对特定端口进行的广播,故此局域网是哪个的所有用户都可以接受到此消息。
未完待续。。。