IPMsg
簡單介紹:
IP Messenger :?http://ipmsg.org?(p.s.:該網站的右上角有英文版網頁鏈接)
島國H.Shirouzu寫的跨平臺局域網通信開源軟件,基于TCP/IP,不需要服務器。
國內大家用的Feiq(飛秋)就是作者基于IPMsg寫的,目前更新到r3.42。
初學tcp/ip的盆友不妨把source code抓下來讀一讀,ipmsg自己定義了一套應用層協議,消息的收發基于udp協議,文件的收發基于tcp。
我記得學校里最初學tcp/ip時,教的是c/s模型的socket編程,先寫個server在那兒一直while(1),這邊再啟動幾個client,如此,一個簡單的局域網通信軟件就寫好了。
ipmsg source code里面有對ipmsg protocol作說明,基于日文有其它程序員翻譯成了英文的prot-eng.txt。 如,啟動或退出時通過向(255.255.255.255)廣播的方式把消息發出去。
話說此文不會針對IPMsg協議來寫,主要還是Win32流程的東西。上一稿《Win32 application (1) Begin》,講到vs創建win32 app后,就沒有下文了,今天接著寫我的。關于Win32應用的流程介紹蜘蛛網上有很多高質量的網文,請大家自行使用搜索引擎。
關于ipmsg的source code的話,有幾點需要說明:
1.作者貌似是用vs2005寫的,我把代碼資源些都放到vs2012面,直接是編不過的,至于怎么解決,我也不知道,有誰知道的話不妨留言分享一下,thanks。
2.Source code里面的很多注釋都是日文寫的,我用VS2012沒有亂碼出現,如果你用Source Insight或其它,可能需要再配置一下字體什么的,讓它支持日文顯示。
3.external:
使用了libpng & zlib.
4. src:
install是面的source code及resource file都是用于安裝ipmsg時的GUI顯示及邏輯處理;
uninst則反之;
TLib則是ipmsg軟件的主要基類:
用VS查看類圖:
1)TApp
2) TWin
按照實現Win32 applicaiton的流程來看ipmsg中Win32部分的流程:
1. WinMain入口
2.?Registers the window class. --> 注冊需要掛一個callback function(To processes messages for the main window.)
3. Application initialization: saves instance handle and creates main window
4.?Main message loop
ipmsg.cpp最后定義了WinMain:
int WINAPI WinMain(HINSTANCE hI, HINSTANCE, LPSTR cmdLine, int nCmdShow) {if (IsWin95()) {MessageBox(0, "Please use old version (v2.06 or earlier)","Win95/98/Me is not supported", MB_OK);::ExitProcess(0xffffffff);return 0;}TMsgApp app(hI, cmdLine, nCmdShow);return app.Run(); } 定義了一個TMsgApp類的對象,從前面的類圖知道TMsgApp是從TApp繼承過來的。TMsgApp沒有重新定義Run(),所以多態地直接調用父類的Run(): int TApp::Run(void) {MSG msg;InitApp();InitWindow();while (::GetMessage(&msg, NULL, 0, 0)){if (PreProcMsg(&msg))continue;::TranslateMessage(&msg);::DispatchMessage(&msg);}return (int)msg.wParam; } 現在可以看出,Run()和我們用VS直接自動生成的WinMain函數:
1) InitApp注冊窗口類;
2) InitWindow實例化窗口;
3) 最后消息處理循環。
1)?InitApp注冊窗口類,并掛載WinProc處理Window Message
BOOL TApp::InitApp(void) // reference kwc {WNDCLASSW wc;memset(&wc, 0, sizeof(wc));wc.style = (CS_BYTEALIGNCLIENT | CS_BYTEALIGNWINDOW | CS_DBLCLKS);wc.lpfnWndProc = WinProc;wc.cbClsExtra = 0;wc.cbWndExtra = 0;wc.hInstance = hI;wc.hIcon = NULL;wc.hCursor = LoadCursor(NULL, IDC_ARROW);wc.hbrBackground = NULL;wc.lpszMenuName = NULL;wc.lpszClassName = (LPCWSTR)defaultClassV;if (::FindWindowV(defaultClassV, NULL) == NULL){if (::RegisterClassV(&wc) == 0)return FALSE;}return TRUE; } TApp類實現的WinProc():LRESULT CALLBACK TApp::WinProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) {TApp *app = TApp::GetApp();TWin *win = app->SearchWnd(hWnd);if (win)return win->WinProc(uMsg, wParam, lParam);if ((win = app->preWnd)){app->preWnd = NULL;app->AddWinByWnd(win, hWnd);return win->WinProc(uMsg, wParam, lParam);}return ::DefWindowProc(hWnd, uMsg, wParam, lParam); } 可以看出,TApp作為父類,通過傳遞進來的窗口HANDLE找到對應的Window,并調用Window的消息處理函數。
這里會調用到TWin類的WinProc():
LRESULT TWin::WinProc(UINT uMsg, WPARAM wParam, LPARAM lParam) {BOOL done = FALSE;LRESULT result = 0;switch(uMsg){case WM_CREATE:GetWindowRect(&orgRect);done = EvCreate(lParam);break;case WM_CLOSE:done = EvClose();break;case WM_COMMAND:done = EvCommand(HIWORD(wParam), LOWORD(wParam), lParam);break;case WM_SYSCOMMAND:done = EvSysCommand(wParam, MAKEPOINTS(lParam));break;case WM_TIMER:done = EvTimer(wParam, (TIMERPROC)lParam);break;case WM_DESTROY:done = EvDestroy();break;/* other case */ }TWin作為主窗口類,ipmsg從TWin繼承了很多的子類,TMainWin是其中一個。
2) InitWindow實例化窗口TApp將InitWindow()聲明為virtual,需要子類來實現,下面是
TMsgApp類實現的InitWindow():
從后面可以看到mainWnd被實例化為一個TMainWin類的對象,并調用TMainWin類的create函數來創建main window;繼續跟下去會發現它最終調用到了TWin的CreateV(): BOOL TWin::CreateV(const void *className, const void *title, DWORD style, DWORD exStyle,HMENU hMenu) {if (className == NULL) {className = TApp::GetApp()->GetDefaultClassV();}TApp::GetApp()->AddWin(this);if ((hWnd = ::CreateWindowExV(exStyle, className, title, style,rect.left, rect.top, rect.right - rect.left, rect.bottom - rect.top,parent ? parent->hWnd : NULL, hMenu, TApp::GetInstance(), NULL)) == NULL)return TApp::GetApp()->DelWin(this), FALSE;elsereturn TRUE; }
reateWindowExV()被不是Windows SDK提供的API,而只是TLib里定義的一個函數指針:
HWND (WINAPI *CreateWindowExV)(DWORD exStyle, const void *className, const void *title,DWORD style, int x, int y, int nw, int nh, HWND hParent, HMENU hMenu, HINSTANCE hI,void *param);
如果是在Windows系統中跑,它最終會指向CreateWindowExW這個Windows API。
至此,實例化窗口順序調用就完成了,不過并沒有看到主窗口是怎么設計的,它只不過創建了一個窗口,僅此而已,并且,沒有看到Show&Update。當然這是在WinProc里收到WM_CREATE里面來做的,主窗口的EvCreate:
LRESULT TWin::WinProc(UINT uMsg, WPARAM wParam, LPARAM lParam) {BOOL done = FALSE;LRESULT result = 0;switch(uMsg){case WM_CREATE:GetWindowRect(&orgRect);done = EvCreate(lParam);break;/* other case */} }從TWin類繼承來的TMainWin重新定義了WinProc函數:
BOOL TMainWin::EvCreate(LPARAM lParam) {hMainWnd = hWnd;mainWin = this;if (IsWinVista() && TIsUserAnAdmin() && TIsEnableUAC()) {TChangeWindowMessageFilter(WM_DROPFILES, 1);TChangeWindowMessageFilter(WM_COPYDATA, 1);TChangeWindowMessageFilter(WM_COPYGLOBALDATA, 1);TChangeWindowMessageFilter(WM_CLOSE, 1);}if (!msgMng->GetStatus()) return TRUE;if (cfg->TaskbarUI) {Show(SW_MINIMIZE);} else {Show(SW_HIDE);}while (!TaskTray(NIM_ADD, hMainIcon, IP_MSG)) {Sleep(1000); // for logon script}TaskBarCreateMsg = ::RegisterWindowMessage("TaskbarCreated");TaskBarButtonMsg = ::RegisterWindowMessage("TaskbarButtonCreated");TaskBarNotifyMsg = ::RegisterWindowMessage(IP_MSG);SetIcon(cfg->AbsenceCheck ? hRevIcon : hMainIcon);SetCaption();if (!SetupCryptAPI(cfg, msgMng)) MessageBoxU8("CryptoAPI can't be used. Setup New version IE");msgMng->AsyncSelectRegister(hWnd);SetHotKey(cfg);if (msgMng->GetStatus()) {EntryHost();}if (IsWin7()) { // for TaskbarUI::CoInitializeEx(NULL, COINIT_APARTMENTTHREADED); if (cfg->TaskbarUI) {CreateJumpList(className);}else { // DeleteJumpList();}}::SetTimer(hWnd, IPMSG_CLEANUP_TIMER, 60000, NULL); // 1minreturn TRUE; }
Show Window: Show(SW_HIDE); 調用Windows API。IPMsg啟動后會自動最小化,所以你不會看到有一個主窗口界面出現,不過它的確已經創建了一個main window,當然嘗試點擊右下角的圖標來打開IPMsg時,會觸發BUTTON事件,WinProc會去處理WM_LBUTTONUP消息, 用來Send Msg的窗口就會打開。
void TWin::Show(int mode) {::ShowWindow(hWnd, mode);::UpdateWindow(hWnd); }
3) 最后消息處理循環
while (::GetMessage(&msg, NULL, 0, 0)){if (PreProcMsg(&msg))continue;::TranslateMessage(&msg);::DispatchMessage(&msg);}
其中,PreProcMsg()是為了將msg傳遞給對應的窗口去處理,自己的娃自己管好,你不管,就要交給父輩來管。最后最后,就是編寫各種消息處理函數了,當然Win32主流程當中也有不少細節在這里沒有說,大家不妨自己抓份source code來讀一讀。上一個post中提到Win32 application開發,各種蛋疼,其中一個原因就是Win32沒有像WinForm或WPF那樣可以直接拖動控件來布局應用的圖形界面,其實Win32是有的,Windows SDK已經提供了一些基本的控件給Win32開發人員。
用這些控件前需要調用?InitCommonControls()或者InitCommonControlsEx()來初始化一下,這個函數在Commctrl.h里聲明的。
這是IPMsg用來發消息的Dialog窗口,當然啰,要做出Tencent QQ那樣的漂亮界面,還需要自己去定制一些界面庫來實現。
總結
- 上一篇: ogre for wind7的自我娱乐
- 下一篇: 献给大学生,未来程序猿的前端学习网站