IPMsg

简单介绍:

IP Messenger : http://ipmsg.org (p.s.:该网站的右上角有英文版网页链接)

岛国H.Shirouzu写的跨平台局域网通信开源软件,基于TCP/IP,不需要服务器。
国内大家用的Feiq(飞秋)就是作者基于IPMsg写的,目前更新到r3.42。


IPMsg_第1张图片


IPMsg_第2张图片

初学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:

IPMsg_第3张图片

install是面的source code及resource file都是用于安装ipmsg时的GUI显示及逻辑处理;

uninst则反之;

TLib则是ipmsg软件的主要基类:

IPMsg_第4张图片


用VS查看类图:

1)TApp

IPMsg_第5张图片

2) TWin

IPMsg_第6张图片


按照实现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开发人员。


IPMsg_第7张图片

用这些控件前需要调用 InitCommonControls()或者InitCommonControlsEx()来初始化一下,这个函数在Commctrl.h里声明的。


这是IPMsg用来发消息的Dialog窗口,当然啰,要做出Tencent QQ那样的漂亮界面,还需要自己去定制一些界面库来实现。




你可能感兴趣的:(IPMsg)