IPMsg

来源:互联网 发布:广州万户网络怎么样 编辑:程序博客网 时间:2024/05/21 17:00

简单介绍:

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);return0;}TMsgAppapp(hI, cmdLine, nCmdShow);returnapp.Run();}
定义了一个TMsgApp类的对象,从前面的类图知道TMsgApp是从TApp继承过来的。TMsgApp没有重新定义Run(),所以多态地直接调用父类的Run():

int TApp::Run(void){MSGmsg;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;}returnTRUE;}
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)returnwin->WinProc(uMsg, wParam, lParam);if ((win = app->preWnd)){app->preWnd = NULL;app->AddWinByWnd(win, hWnd);returnwin->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){BOOLdone = FALSE;LRESULTresult = 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():

void TMsgApp::InitWindow(void){    HWND        hWnd;    char        class_name[MAX_PATH_U8] = IPMSG_CLASS, *tok, *msg, *p;    char        *class_ptr = NULL;    ULONG       nicAddr = 0;    int         port_no = atoi(cmdLine);    BOOL        show_history = FALSE;    enum Stat { ST_NORMAL, ST_TASKBARUI_MSG, ST_EXIT, ST_ERR } status = ST_NORMAL;    int         taskbar_msg = 0;    int         taskbar_cmd = 0;    /* ... ... something ... ... */    HANDLE  hMutex = ::CreateMutex(NULL, FALSE, class_name);    ::WaitForSingleObject(hMutex, INFINITE);    if ((hWnd = FindWindowU8(class_name)) ||        !TRegisterClassU8(class_name, CS_DBLCLKS, ::LoadIcon(hI, (LPCSTR)IPMSG_ICON),                        ::LoadCursor(NULL, IDC_ARROW))) {        if (hWnd) ::SetForegroundWindow(hWnd);        ::ExitProcess(0xffffffff);        return;    }    mainWnd = new TMainWin(nicAddr, port_no);    mainWnd->Create(class_name);    ::ReleaseMutex(hMutex);    ::CloseHandle(hMutex);    if (show_history) mainWnd->SendMessage(WM_COMMAND, MENU_HELP_HISTORY, 0);}

从后面可以看到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;    else        return  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); // 1min    return  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那样的漂亮界面,还需要自己去定制一些界面库来实现。




原创粉丝点击