让你的MFC程序只能开一个例程

暂时有两种方法实现,直接上代码:

⑴将这段代码放到app的InitInstance中:

//  脱壳部分,正式发布需要打开
	HANDLE mutex = NULL;
	mutex=CreateMutex(0,false,"RegServer");//创建互斥体,创建进程希望立即拥有互斥体,则设为TRUE,一个互斥体同时只能由一个线程拥有
	if(GetLastError() == ERROR_ALREADY_EXISTS)
		mutex=OpenMutex(MUTEX_ALL_ACCESS,false,"RegServer");//为现有的一个已命名互斥体对象创建一个新句柄
	
	LPWSTR *szArglist = NULL;
	int nArgs;
	szArglist = CommandLineToArgvW(GetCommandLineW(), &nArgs);//szArglist就是保存参数的数组   
                                                              //nArgs是数组中参数的个数   
                                                              //数组的第一个元素表示进程的path,也就是szArglist[0],其他的元素依次是输入参数。
	if( NULL == szArglist )
	{
		CloseHandle(mutex);
		return FALSE;
	}
	else
	{
		if(nArgs > 1)
			LocalFree(szArglist);//释放局部内存对象并使句柄失效
		else
		{
			char exeFullPath[MAX_PATH];
			char exePath[MAX_PATH];
			char exeTmpFullPath[MAX_PATH];
			char exeTmpName[MAX_PATH];
			memset(exeFullPath,0,sizeof(exeFullPath));
			memset(exePath,0,sizeof(exePath));
			memset(exeTmpFullPath,0,sizeof(exeTmpFullPath));
			strcpy(exeTmpName,"RegServer_bak.exe");
			GetModuleFileNameA(NULL,exeFullPath,MAX_PATH);//得到进程的完全路径
			strcpy(exePath,exeFullPath);
			
			size_t i;
			for(i=strlen(exePath)-1; i>0 && exePath[i]!='\\'; i--);
			exePath[i]='\0';//将文件名去掉
			
			sprintf(exeTmpFullPath,"%s\\RegServer_bak.exe",exePath);//定位RegServer_bak.exe副本程序的绝对位置
			/*WaitForSingleObject函数用来检测hHandle事件的信号状态,
			在某一线程中调用该函数时,线程暂时挂起,如果在挂起的dwMilliseconds毫秒内,
			线程所等待的对象变为有信号状态,则该函数立即返回;如果超时时间已经到达dwMilliseconds毫秒,
			但hHandle所指向的对象还没有变成有信号状态,函数照样返回。参数dwMilliseconds有两个具有特殊意义的值:
			0和INFINITE。若为0,则该函数立即返回;若为INFINITE,则线程一直被挂起,
			直到hHandle所指向的对象变为有信号状态时为止。*/
			WaitForSingleObject(mutex,INFINITE);
			
			int count=0;
			CString strProcessName = exeTmpName;
			//将字符串转换为小写
			strProcessName.MakeLower();
			while(1)
			{
				//KILL Process begin
				//创建进程快照(TH32CS_SNAPPROCESS表示创建所有进程的快照)
				HANDLE hSnapShot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS,0);//获取当前运行的所有进程信息	
				//PROCESSENTRY32进程快照的结构体
				PROCESSENTRY32 pe;
				//实例化后使用Process32First获取第一个快照的进程前必做的初始化操作
				pe.dwSize = sizeof(PROCESSENTRY32);	
				Process32First(hSnapShot,&pe);//得到运行的第一个进程
				//如果句柄有效  则一直获取下一个句柄循环下去
				while (Process32Next(hSnapShot,&pe))
				{
					//pe.szExeFile获取当前进程的可执行文件名称
					CString scTmp = pe.szExeFile;
					//将可执行文件名称所有英文字母修改为小写
					scTmp.MakeLower();	
					//比较当前进程的可执行文件名称和传递进来的文件名称是否相同
					//相同的话Compare返回0
					if(!scTmp.Compare(strProcessName))
					{
						//从快照进程中获取该进程的PID(即任务管理器中的PID)
						DWORD dwProcessID = pe.th32ProcessID;
						HANDLE hProcess = ::OpenProcess(PROCESS_TERMINATE,FALSE,dwProcessID);//OpenProcess 函数用来打开一个已存在的进程对象,并返回进程的句柄。
						::TerminateProcess(hProcess,0);//TerminateProcess函数终止指定进程及其所有线程。
						CloseHandle(hProcess);
						break;
					}
					scTmp.ReleaseBuffer();
				}
				strProcessName.ReleaseBuffer();
				//KILL Process end
				if(!strcmp(exeFullPath,exeTmpFullPath))
					break;
				if(count > 300)
					break;
				CopyFileA(exeFullPath,exeTmpFullPath,FALSE);//复制原文件,制作拷贝
				i=GetLastError();
				if(i == 0)
				{
					break;
				}
				count++;
				::Sleep(1000);
			}
			sprintf(exeTmpFullPath,"\"%s\\RegServer_bak.exe\" -2",exePath);
			WinExec(exeTmpFullPath, SW_HIDE);//打开副本
			LocalFree(szArglist);
			ReleaseMutex(mutex);//释放互斥体同时主线程被杀死,新的线程RegServer_bak.exe拥有互斥对象
			CloseHandle(mutex);
			return FALSE;
		}
		
	}
	CloseHandle(mutex);
	
//end 生成可执行程序的副本  end//
⑵ Windows是多进程操作系统,框架生成的应用程序可以多次运行,形成多个运行实例。
但在有些情况下为保证应用程序的安全运行,要求程序只能运行一个实例,比如程 
序要使用只能被一个进程单独使用的特殊硬件(例如调制解调器)时,必须限制程 
序只运行一个实例。
这里涉及两个基本的问题,一是在程序的第二个实例启动时,如何发现该程序已有 
一个实例在运行,而是如何将第一个实例激活,而第二个实例退出。
对于第一个问题,可以通过给应用程序设置信号量,实例启动时首先检测该信号量, 
如已存在,则说明程序已运行一个实例。
第二个问题的难点是获取第一个实例的主窗对象指针或句柄,然后便可用 
SetForegroundWindow来激活。虽然FindWindow函数能寻找正运行着的窗口,但该函 
数要求指明所寻找窗口的标题或窗口类名,不是实现通用方法的途径。
我们可以用Win32 SDK函数SetProp来给应用程序主窗设置一个特有的标记。
用GetDesktopWindow 可以获取Windows系统主控窗口对象指针或句柄,所有应用程
序主窗都可看成该窗口的子窗口,即可用GetWindow函数来获得它们的对象指针或句
柄。用Win32 SDK函数GetProp查找每一应用程序主窗是否包含有我们设置的特定标
记便可确定它是否我们要寻找的第一个实例主窗。使第二个实例退出很简单,只要
让其应用程序对象的InitInstance函数返回FALSE即可。此外,当主窗口退出时,应
用RemoveProp函数删除我们为其设置的标记。
下面的InitInstance、OnCreate和OnDestroy函数代码将实现上述的操作:

BOOL CEllipseWndApp::InitInstance() 
{ 
// 用应用程序名创建信号量 
HANDLE hSem = CreateSemaphore(NULL, 1, 1, m_pszAppName); 

// 信号量已存在? 
// 信号量存在,则程序已有一个实例运行 
if (GetLastError() == ERROR_ALREADY_EXISTS) 
{ 
// 关闭信号量句柄 
CloseHandle(hSem); 
// 寻找先前实例的主窗口 
HWND hWndPrevious = ::GetWindow(::GetDesktopWindow(),GW_CHILD); 
while (::IsWindow(hWndPrevious)) 
{ 
// 检查窗口是否有预设的标记? 
// 有,则是我们寻找的主窗 
if (::GetProp(hWndPrevious, m_pszAppName)) 
{ 
// 主窗口已最小化,则恢复其大小 
if (::IsIconic(hWndPrevious)) 
::ShowWindow(hWndPrevious,SW_RESTORE); 

// 将主窗激活 
::SetForegroundWindow(hWndPrevious); 

// 将主窗的对话框激活 
::SetForegroundWindow( 
::GetLastActivePopup(hWndPrevious)); 

// 退出本实例 
return FALSE; 
} 
// 继续寻找下一个窗口 
hWndPrevious = ::GetWindow(hWndPrevious,GW_HWNDNEXT);
} 
// 前一实例已存在,但找不到其主窗 
// 可能出错了 
// 退出本实例 
return FALSE; 
} 
AfxEnableControlContainer(); 
// Standard initialization 
// If you are not using these features and wish to reduce the size 
// of your final executable, you should remove from the following 
// the specific initialization routines you do not need. 
#ifdef _AFXDLL 
Enable3dControls(); // Call this when using MFC in a shared DLL 
#else 
Enable3dControlsStatic();// Call this when linking to MFC statically 
#endif 

CEllipseWndDlg dlg; 
m_pMainWnd = &dlg; 
int nResponse = dlg.DoModal(); 
if (nResponse == IDOK) 
{ 
// TODO: Place code here to handle when the dialog is 
// dismissed with OK 
} 
else if (nResponse == IDCANCEL) 
{ 
// TODO: Place code here to handle when the dialog is 
// dismissed with Cancel 
} 
// Since the dialog has been closed, return FALSE so that we exit the 
// application, rather than start the application's message pump. 
return FALSE; 
} 

int CEllipseWndDlg::OnCreate(LPCREATESTRUCT lpCreateStruct)  
{ 
if (CDialog::OnCreate(lpCreateStruct) == -1) 
return -1; 
// 设置寻找标记 
::SetProp(m_hWnd, AfxGetApp()->m_pszAppName, (HANDLE)1); 
return 0; 
} 

void CEllipseWndDlg::OnDestroy()  
{ 
CDialog::OnDestroy(); 
// 删除寻找标记 
::RemoveProp(m_hWnd, AfxGetApp()->m_pszAppName);  
}



  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
以下是一个简单的 MFC 客户端连接池的例程,仅供参考: ```c++ class Connection { public: CString ip; // 服务器 IP 地址 UINT port; // 服务器端口号 CAsyncSocket socket; // 与服务器建立的连接的 Socket 对象 }; class ConnectionPool { public: ConnectionPool() {} ~ConnectionPool() {} // 连接池中连接的最大数量 static const int MAX_CONNECTIONS = 10; // 添加一个连接 bool addConnection(CString ip, UINT port) { if (connections.size() >= MAX_CONNECTIONS) { return false; } // 创建一个 Connection 对象 Connection conn; conn.ip = ip; conn.port = port; // 连接服务器 if (conn.socket.Create() && conn.socket.Connect(ip, port)) { // 连接成功,将 Connection 对象添加到连接池中 connections.push_back(conn); return true; } return false; } // 获取一个连接 Connection* getConnection() { if (connections.empty()) { return nullptr; } // 从连接池中取出第一个 Connection 对象 Connection* conn = &connections.front(); // 将该 Connection 对象移到连接池的末尾 connections.pop_front(); connections.push_back(*conn); return conn; } // 删除一个连接 void removeConnection(Connection* conn) { auto it = std::find(connections.begin(), connections.end(), *conn); if (it != connections.end()) { connections.erase(it); } conn->socket.Close(); } private: std::list<Connection> connections; // 存储 Connection 对象的列表 }; // 在初始化时创建 ConnectionPool 对象,并添加需要连接的服务器 void CMyDialog::OnInitDialog() { CDialogEx::OnInitDialog(); // 创建 ConnectionPool 对象 connectionPool = std::make_unique<ConnectionPool>(); // 添加需要连接的服务器 connectionPool->addConnection(_T("192.168.0.1"), 1234); connectionPool->addConnection(_T("192.168.0.2"), 1234); connectionPool->addConnection(_T("192.168.0.3"), 1234); } // 发送消息给服务器 void CMyDialog::sendMessage(CString message) { // 获取一个连接 Connection* conn = connectionPool->getConnection(); if (conn == nullptr) { // 连接池为空,无法发送消息 return; } // 发送消息 conn->socket.Send(message, message.GetLength()); // 将连接放回连接池 connectionPool->removeConnection(conn); } ``` 需要注意的是,在实际应用中,需要根据具体的需求来设计 Connection 类和 ConnectionPool 类,以及相应的方法,并加入足够的错误处理来保证程序的稳定性和可靠性。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

春阳CYang

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

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

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

打赏作者

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

抵扣说明:

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

余额充值