Rime输入法核心代码分析——WeaselServer文件夹网络化分析(七)

2021SC@SDUSC

今天继续分析代码的后续部分,具体代码如下所示:

hRes = _Module.Init(NULL, hInstance);
	ATLASSERT(SUCCEEDED(hRes));

	if (!wcscmp(L"/userdir", lpstrCmdLine))
	{
		CreateDirectory(WeaselUserDataPath().c_str(), NULL);
		WeaselServerApp::explore(WeaselUserDataPath());
		return 0;
	}
	if (!wcscmp(L"/weaseldir", lpstrCmdLine))
	{
		WeaselServerApp::explore(WeaselServerApp::install_dir());
		return 0;
	}

	// command line option /q stops the running server
	bool quit = !wcscmp(L"/q", lpstrCmdLine) || !wcscmp(L"/quit", lpstrCmdLine);
	// restart if already running
	{
		weasel::Client client;
		if (client.Connect())  // try to connect to running server
		{
			client.ShutdownServer();
		}
		if (quit)
			return 0;
	}

	bool check_updates = !wcscmp(L"/update", lpstrCmdLine);
	if (check_updates)
	{
		WeaselServerApp::check_update();
	}

	CreateDirectory(WeaselUserDataPath().c_str(), NULL);

	int nRet = 0;

	try
	{
		WeaselServerApp app;
		if (IsWindowsVistaOrGreater())
		{
			PRAR RegisterApplicationRestart = (PRAR)::GetProcAddress(::GetModuleHandle(_T("kernel32.dll")), "RegisterApplicationRestart");
			RegisterApplicationRestart(NULL, 0);
		}
		nRet = app.Run();
	}
	catch (...)
	{
		// bad luck...
		nRet = -1;
	}

	_Module.Term();
	::CoUninitialize();

	return nRet;
}

1.CAppModule

可以看到,_Module是CAppModule中的一共实际对象,因此下面将首先介绍CAppModule

1、WTL应用程序入口代码处,_Module开始被使用,如下:

    在这简单的4行代码间,就是你的应用程序代码运行的地方。

2、那么这个_Module是应用程序定义的东西呢?还是WTL库定义的东西呢?

    我们在工程主CPP里找到_Module的定义,所以,_Module定义在工程中,而非WTL库中。

3、这个CAppModule到底是干嘛的呢?它有啥功能呢?

    我们看到CAppModule继承了CComModule,第一眼看上去,以为扯上CComModule了,实际上不然。查阅MSDN文档,CComModule已经obsolete,它被拆分成CAtlWinModule、CAtlComModule、CAtlBaseModule和CAtlModule四类。这里CAppModule继承了CComModule只是为了兼容老代码,WTL初学者可以直接无视CComModule。

    m_dwMainThreadID保存着WTL主线程的线程ID,m_pMsgLoopMap保存着应用程序所有的线程的消息循环(线程号:消息循环映射),m_pSettingChangeNotify用实现广播的一个东西。

    OK,分析完毕。CAppModule实际上是很简单的。

 也就是说,程序中

hRes = _Module.Init(NULL, hInstance);

    ATLASSERT(SUCCEEDED(hRes));

这两行代码标定了这个WeaselServer应用程序的开始执行位置,代码是从这里开始执行的。

2.C++ 中 main函数 wmain函数 _tmain函数 WinMain函数 wWInMain函数 _tWinMain函数的区别

main函数与WinMain函数区别:

前者为控制台程序入口主函数,后者为Windows API窗体程序入口函数,在windef.h文件中定义。


_tmain函数和main函数和wmain函数:

main函数是多字节字符集版本,一般是ASCII编码下使用

wmain函数是main函数的宽字符版本,一般在Unicode编码下使用。

_tmain的定义如下图:

因为此时我的设置的编码是Unicode编码,所以这时候的_tmain也就相当宏定义为wmain

如果设置的编码为Ascii编码,这时候就是如下图:


_tWinMainWinMain函数和wWinMain函数:

_tWinMain函数是wWinMain函数和WinMain函数Unicode版本的别名

跟上面解释的一样,只不过一个是控制台,另一个是应用程序

因为此时我的设置的编码是Unicode编码,所以这时候的_tWinMain也就相当宏定义为wWinMain

如果设置的编码为Ascii编码,这时候就是如下图:


总结:

如果为Unicode编码,_tmain就是wmain,_tWinMain就是wWinMain

如果为Ascii编码,_tmain则为main,_tWinMain就是WinMain

注意:使用这两个函数时要加入<tchar.h>引用

WinMain函数是提供给用户的Windows应用程序入口点,其原型如下:

1

int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR ipCmdLine, int nCmdShow);

函数有四个参数:

hInstancd: 是当前应用长须实例的Handle。

hPrevInstance: 是应用程序上一个实例的Handle。(MSDN:如果你想要知道程序是否有另一个实例,建议使用Mutex来实现,用Mutex可以实现只运行一个实例)

ipCmdLine: 字符串,是命令行参数。

nCmdShow: int型,指明windows应该怎么实现,windows定义了一系列的宏来帮助记忆,以SW开头,如:SW_SHOW。

返回值是一个int型。

如果函数运行成功的话,它会一直运行,知道接收到WM_QUIT消息,它应该返回消息的wParam参数的退出值。如果函数进入消息循环前退出,它应该返回0。

WINAPI 是一个宏定义:

1

#define WINAPI __stdcall

 __stdcall和__cdecl, __pascal, __fastcall都是一些类似的关键字,详细信息如下:

1.它们实际上是关于堆栈的一些说明,包括函数参数压栈顺序和压入堆栈的内容由谁来清除(是调用者还是自己)。而上述这些关键字用来告诉编译器产生什么样的汇编代码。

2.VC由两种函数的调用方式:__stdcall和__cdecl

前者指的是PASCAL调用方式,后者指的时C调用方式。使用PASCAL调用方式,函数在返回到调用者之间将参数从栈中删除;使用C调用方式,参数的删除是调用者完成的。

WinMain函数由系统调用,windows系统规定由系统调用的函数都要遵循PASCAL调用方式,但是VC中函数的缺省调用方式是C调用方式,所以要在WinMain前显式声明WINAPI。在Windows编程中将会遇到很多类似的声明,如:CALLBACK, WINAPI, PASCAL这些在Intel CPU的计算机上都是__stdcall。

3.__cdecl是C/C++和MFC程序默认使用的调用方式,也可以在函数声明时加上__cdecl关键字来手动指定。采用__cdecl调用方式时,函数的参数按照从右到左的顺序入栈,并且由函数调用者把参数弹出栈以清理堆栈,因此,实现可变参数的函数只能使用该调用方式,由于每一个使用__cdecl调用方式的函数都要包含清理堆栈的代码,所以产生的可执行文件的大小会比较大;__stdcall调用方式用于调用win32 API函数,采用__stdcal调用方式时,函数参数按照从右到左的顺序入栈,被调用的函数在返回前清理传送参数的栈,函数参数个数固定,由于函数体本身知道传进来的参数个数,因此被调用的函数可以在返回前用一条ret n指令直接清理传递参数的堆栈;__fastcall调用方式用于对性能要求非常高,__fastcall调用方式将函数的从左边开始的两个大小不大于4个字节(DWORD)的参数分别放在ECX和EDX寄存器,其余的参数仍旧自右向左压栈传送,被调用的函数在返回前清理传送参数的堆栈。

thiscall仅仅用于"C++"成员函数。this指针存放在CX/ECX寄存器中,参数从右到左压。thiscall不是关键字,因此不能被程序员指定。

nakedcall。当采用其他调用方式时,如果必要的话,进入函数时编译器会产生代码来保存ESI,EDI,EBX,EBP寄存器,退出函数时则产生代码恢复这些寄存器的内容。

特别说明:

  1.在默认情况下,采用__cdecl方式,因此可以省略。

  2.WINAPI一般用于修饰动态链接库中导出的函数。

  3.CALLBACK仅用于修饰回调函数。

  4.你可能已经发现,VC下和BCB下对WINAPI的定义不同,那么你至少理解为什么不能直接从BCB下调用VC的DLL的一个原因了。

总体来说,VC默认的是__cdecl方式,Win32 API函数是用__stdcall方式的,他们都是将函数的参数从右到左入栈的,__cdecl方式的每个函数都有清理堆栈的代码,可以实现可变参数列表,但可执行文件比较大。__stdcall方式是调用方式是调用者清理堆栈的。__fastcall的特点是他讲参数左边的两个参数放在寄存器上,比较快。其余参数还是在堆栈中,堆栈还是由函数自己清理。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值