• 窗口类Win32 application (2) 看局域网聊天开源软件IPMsg的Win32部分


    废话就不多说了,开始。。。

        简单分析:

        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():

    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那样的漂亮界面,还需要自己去定制一些界面库来实现。

        

        

        

    文章结束给大家分享下程序员的一些笑话语录: 一程序员告老还乡,想安度晚年,于是决定在书法上有所造诣。省略数字……,准备好文房4宝,挥起毛笔在白纸上郑重的写下:Hello World

    --------------------------------- 原创文章 By
    窗口和类
    ---------------------------------

  • 相关阅读:
    不用if/else swich for while实现累加
    1.java数据结构
    青蛙跳台阶问题
    next与nextLine
    9.遗传算法
    Docker镜像管理
    Docker镜像管理
    CentOS 6.7安装Docker
    CentOS 6.7安装Docker
    数字三角形_递归_递推(动态规划)
  • 原文地址:https://www.cnblogs.com/xinyuyuanm/p/3112913.html
Copyright © 2020-2023  润新知