窗口类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
窗口和类
---------------------------------

原文地址:https://www.cnblogs.com/xinyuyuanm/p/3112913.html