程序防多开之三:全局对象检测

        在前两章讲了枚举进程和窗口的方式实现对进程多开的检测.

        详情请见:

        程序防多开之一:进程数量检测

        程序防多开之二:程序窗口检测

        如果在这两种方法均无法达到很好检测效果的情况下,那么可以使用全局对象的方式来实现.

        在Windows中典型可用于共享进程数据的全局对象有:

        Event(事件)

        Mutex(互斥体)

        Semaphore(信号量)

        它们均有一个共同点:随进程的生命结束而减少对象的引用或关闭对象.

        基于这种特性,我们刚好可以拿来进行对进程数量的检测.

使用Event检测:

//调用CreateEventW来创建事件
//成功返回TRUE,失败FALSE
BOOL myCreateEvent(LPWSTR pName)
{
	BOOL bRet = FALSE;
	__try
	{

		if (!pName || !wcslen(pName))
			return bRet;

		HANDLE hMutex = CreateEventW(NULL,FALSE, FALSE, pName);
		if (!hMutex)
			return bRet;
		if (GetLastError() == ERROR_ALREADY_EXISTS)
			return bRet;
		bRet = TRUE;
	}
	__except (EXCEPTION_EXECUTE_HANDLER) { ; }

	//发生异常错误,返回NULL
	return bRet;
}



BOOL CheckCountByEvent(LONG nMaxCount)
{
	WCHAR strMoudleName[MAX_PATH] = { 0 };
	GetModuleFileNameW(GetModuleHandleW(NULL), strMoudleName, sizeof(strMoudleName));
	if (!wcslen(strMoudleName))//没取到模块路径,直接返回TRUE,意味可能系统环境异常
		return TRUE;


	WCHAR* strName = wcsrchr(strMoudleName, L'\\');
	if (!strName)//路径异常
		return TRUE;

	BOOL isLimit = TRUE;
	strName += 1;

	for (size_t i = 0; i < nMaxCount; i++)
	{
		WCHAR strMutant[MAX_PATH] = { 0 };
		wsprintfW(strMutant, L"Event_%s_%d", strName, i);
		BOOL isCreate = myCreateEvent(strMutant);
		if (isCreate)
		{
			isLimit = FALSE;
			break;
		}
	}
	return isLimit;
}

代码解释:

        我们直接调用CheckCountByEvent()函数,并且传递最大限制数量即可实现多开限制.

        函数内部通过GetModuleFileNameW()获取进程程序的文件名,并以此作为基础Event名字.

        在函数内实现一个遍历,实现每启动一次程序便起创建一个名字为"Event_程序名字_序号"的Event.

        如果在限制数量内无法创建Event,则代表已经达到了最大限制数量,或者系统环境有异常.

        其实在这里,您可以将事件名字修改为一个常量字符串,例如:"MY_GAME_序号",这种方式可以避免用户通过修改文件名实现躲避检测.

        但我们此处故意使用文件名作为基础名字,是为了以后章节介绍的关于对程序运行期间作弊行为检测而做铺垫(本章不讲).

        您可以理解为它是一种暗桩即可.

        

        在myCreateEvent()函数中的实现非常简单,就是通过传入的名字创建Event,如果GetLastError()的返回值是ERROR_ALREADY_EXISTS则表示Event重复创建,并且返回FALSE

        另外关于对Mutex和Semaphore的检测原理与此相同,而且这三种全局对象可以同时存在,即:您可以同时使用这些全局对象的检测,而不会互相干扰.

        

关于Mutex和Semaphore的检测代码:


//调用CreateMutexW来创建互斥体
//成功返回TRUE,失败FALSE
BOOL myCreateMutex(LPWSTR pName)
{
	BOOL bRet = FALSE;
	__try
	{

		if(!pName || !wcslen(pName))
			return bRet;

		HANDLE hMutex = CreateMutexW(NULL,FALSE, pName);
		if (!hMutex)
			return bRet;
		if(GetLastError() == ERROR_ALREADY_EXISTS)
			return bRet;
		bRet = TRUE;
	}
	__except (EXCEPTION_EXECUTE_HANDLER) { ; }

	//发生异常错误,返回NULL
	return bRet;
}



BOOL CheckCountByMutex(LONG nMaxCount)
{
	WCHAR strMoudleName[MAX_PATH] = { 0 };
	GetModuleFileNameW(GetModuleHandleW(NULL), strMoudleName, sizeof(strMoudleName));
	if (!wcslen(strMoudleName))//没取到模块路径,直接返回TRUE,意味可能系统环境异常
		return TRUE;


	WCHAR* strName = wcsrchr(strMoudleName, L'\\');
	if(!strName)//路径异常
		return TRUE;

	BOOL isLimit = TRUE;
	strName += 1;

	for (size_t i = 0; i < nMaxCount; i++)
	{
		WCHAR strMutant[MAX_PATH] = { 0 };
		wsprintfW(strMutant, L"Mutex_%s_%d", strName, i);
		BOOL isCreate = myCreateMutex(strMutant);
		if (isCreate)
		{
			isLimit = FALSE;
			break;
		}
	}
	return isLimit;
}




//调用OpenSemaphoreW来创建信号量
//成功返回TRUE,失败FALSE
BOOL myCreateSemaphore(LPWSTR pName,DWORD nMaxCount)
{
	BOOL bRet = FALSE;
	__try
	{
		if (!pName || !wcslen(pName))
			return bRet;

		//判断信号量是否存在
		HANDLE hSema = OpenSemaphoreW(SEMAPHORE_ALL_ACCESS, FALSE, pName);
		if (hSema)
			return FALSE;
		
		//创建信号量
		hSema = CreateSemaphoreW(NULL, 0, 1, pName);
		if(!hSema)//创建失败,可能是系统环境异常,直接返回FALSE
			return FALSE;

		bRet = TRUE;
	}
	__except (EXCEPTION_EXECUTE_HANDLER) { ; }

	//发生异常错误,返回NULL
	return bRet;
}

BOOL CheckCountBySemaphore(LONG nMaxCount)
{
	WCHAR strMoudleName[MAX_PATH] = { 0 };
	GetModuleFileNameW(GetModuleHandleW(NULL), strMoudleName, sizeof(strMoudleName));
	if (!wcslen(strMoudleName))//没取到模块路径,直接返回TRUE,意味可能系统环境异常
		return TRUE;


	WCHAR* strName = wcsrchr(strMoudleName, L'\\');
	if (!strName)//路径异常
		return TRUE;

	BOOL isLimit = TRUE;
	strName += 1;

	for (size_t i = 0; i < nMaxCount; i++)
	{
		WCHAR strMutant[MAX_PATH] = { 0 };
		wsprintfW(strMutant, L"Semaphore_%s_%d", strName,i);
		BOOL isCreate = myCreateSemaphore(strMutant, nMaxCount);
		if (isCreate)
		{
			isLimit = FALSE;
			break;
		}
	}
	return isLimit;
}

最后的调用:

    //限制多开数量
    LONG nMaxCount = 2;

    BOOL bEvent, bMutex, bSemaphore;

    //判断进程数量是否超出限制数量
    bEvent = CheckCountByEvent(nMaxCount);
    bMutex = CheckCountByMutex(nMaxCount);
    bSemaphore = CheckCountBySemaphore(nMaxCount);
    if(bEvent || bMutex|| bSemaphore)
        MessageBoxA(0, "客户端开启数量超出限制!", "提示", 0);

运行效果如图:

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

°默然

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值