为什么要让进程再操作系统上只保存一份实例?
因为某些情况,只需要运行一份实例。比如病毒木马,又或者开启多进程会导致程序出问题等。
如何确保一个进程只运行一个实例?
方法很多,可以通过扫描进程列表来实现,可以枚举程序窗口来实现,也可以通过共享全局变量来实现,也可以通过创建系统命名互斥对象的方式来实现。
编码实现:BOOL IsAlreadyRun()
{
HANDLE hMutex = NULL;
hMutex = ::CreateMutex(NULL, FALSE, "TEST");
if (hMutex)
{
if (ERROR_ALREADY_EXISTS == ::GetLastError())
{
return TRUE;
}
}
return FALSE;
}
小结:
关键是熟悉CreateMutex的使用,在调用CreateMutex函数创建互斥的命名对象时,注意不要与已有的事件、信号量或者文件映射等对象相同。且特别注意不要调用CloseHandle来关闭互斥对象的句柄,不然就会释放资源,无法起到单一实例的效果了。
2 DLL延时加载 原因:为什么要使用DLL延时加载?
因为再开发程序的时候通常会使用第三方库,有些第三方库不提供静态库LIB,只提供动态库DLL,这样的话,程序需要依赖相应的DLL才能加载。通过DLL延时加载技术可以实现,所依赖的DLL再正式调用时再加载进来。这样做的好处是把DLL以资源的形式插入到exe中,这样就不用附加DLL文件了,一个exe搞定。
实现原理:本程序以加载第三方库——skin++ SkinPPWTL.dll为例,首先编译不延时加载的exe,发现运行这个exe报错。
观察该exe的导入表可以发现包含SkinPPWTL.dll,所以程序加载运行的时候,SkinPPWTL.dll必须存在。

{
// 获取指定模块里的指定资源
HRSRC hRsrc = ::FindResource(NULL, MAKEINTRESOURCE(uiResouceName), lpszResourceType);
if (NULL == hRsrc)
{
FreeRes_ShowError("FindResource");
return FALSE;
}
// 获取资源的大小 NULL表示当前进程
DWORD dwSize = ::SizeofResource(NULL, hRsrc);
if (0 >= dwSize)
{
FreeRes_ShowError("SizeofResource");
return FALSE;
}
// 将资源加载到内存里 NULL表示当前进程
HGLOBAL hGlobal = ::LoadResource(NULL, hRsrc);
if (NULL == hGlobal)
{
FreeRes_ShowError("LoadResource");
return FALSE;
}
// 锁定资源
LPVOID lpVoid = ::LockResource(hGlobal);
if (NULL == lpVoid)
{
FreeRes_ShowError("LockResource");
return FALSE;
}
// 保存资源为文件
FILE *fp = NULL;
fopen_s(&fp, lpszSaveFileName, "wb+");
if (NULL == fp)
{
FreeRes_ShowError("LockResource");
return FALSE;
}
fwrite(lpVoid, sizeof(char), dwSize, fp);
fclose(fp);
return TRUE;
}
小结:
资源释放技术主要是理解相关的API的调用关系,注意一点的就是,明确资源所在模块,如果资源包含在DLL文件中,则需要获取DLL模块的句柄去读取文件。



