很多人在程序实现时可能会遇到一个问题:我只想让程序实现时只能打开一个程序进程,在已经有运行进程的情况下,如最小化到托盘的情况下,点击打开程序时,不是再次打开新进程,而是把原来进程的窗口显示出来.
这个问题有两个细节需要处理:
1:保证只打开一个进程。这个的方式有很多种.我这里用的是在程序开始时,查找到所有进程名字相同的进程id,然后通过与当前进程Id来进行处理.
2:进程已经存在时,显示存在进程的窗口。通过SendMessage向原进程窗口发送一个用户消息。程序中实现nativeEvent的重载,接收到这个消息时,把窗口显示出来。
保证只有一个进程,并发送消息
//SingleProcess.h
#pragma once
bool MakeSingle(const char* winTitle);
//SingleProcess.cpp
#include <string>
#include <map>
#include <vector>
#include <Windows.h>
#include <iostream>
#include <TlHelp32.h>
#include <algorithm>
#include <Psapi.h>
#include "SingleProcess.h"
#pragma comment(lib,"psapi.lib")
#define LogSave printf
void CloseByProcHandle(HANDLE procHandle);
HANDLE GetProcTerminateHandleByProcId(DWORD procId);
bool GetProcessName( char* processName, int maxLen);
void CloseByProcId(DWORD& procID);
std::string GetProcName(DWORD procID);
std::vector<DWORD> GetProcIDList( const char* proName );
char* wchar2char(const wchar_t* wstr, char* retStr, int retMaxLen);
wchar_t* char2wchar(const char* cstr, wchar_t* retWStr, int retMaxLen);
bool MakeSingle(const char* winTitle )
{
char processName[MAX_PATH]={0};
bool ret = GetProcessName(processName, MAX_PATH);
if(!ret)
{
return false;
}
std::vector<DWORD> tList = GetProcIDList(processName);
DWORD processId = GetCurrentProcessId();
int size = tList.size();
if (size == 2)
{
DWORD pi0 = tList[0];
DWORD pi1 = tList[1];
DWORD poid = 0;
DWORD clsid = 0;
if (pi0 == processId)
{
poid = pi1;
clsid = pi0;
}
else if ( pi1 == processId )
{
poid = pi0;
clsid = pi1;
}
if (poid != 0)
{
HWND hwnd = ::FindWindowA(NULL, winTitle);
if (hwnd != NULL || hwnd == INVALID_HANDLE_VALUE)
{
::SendMessage(hwnd, WM_USER + 100, 0, 0);
}
CloseByProcId(clsid);
}
}
//一般不会执行,防异常
else if( size > 1)
{
for( int i = 0; i < size; ++i )
{
DWORD ck = tList[i];
if (ck == processId)
{
CloseByProcId(ck);
return false;
}
}
}
return true;
}
//
HANDLE GetProcTerminateHandleByProcId(DWORD procId)
{
if (procId == 0)
{
return NULL;
}
HANDLE procHandle = OpenProcess(SYNCHRONIZE | PROCESS_TERMINATE, FALSE, procId);
return procHandle;
}
void CloseByProcHandle(HANDLE procHandle)
{
UINT exitCode = 0;
TerminateProcess(procHandle, exitCode);
CloseHandle(procHandle);
}
void CloseByProcId(DWORD& procID)
{
HANDLE procHandle = GetProcTerminateHandleByProcId(procID);
CloseByProcHandle(procHandle);
procID = 0;
}
std::string GetProcName(DWORD procID)
{
TCHAR szProcessName[1024] = TEXT("<unknown>");
HANDLE procHandle = OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, procID);
if (procHandle)
{
wchar_t tExeName[MAX_PATH] = { 0 };
GetProcessImageFileName(procHandle, tExeName, MAX_PATH);
CloseHandle(procHandle);
if (tExeName[0] == 0)
{
return "";
}
char cExeName[MAX_PATH] = { 0 };
wchar2char(tExeName, cExeName, MAX_PATH);
std::string name = cExeName;
std::string stdPath = cExeName;
int index = stdPath.find_last_of('/');
if (index > 0)
{
name = stdPath.substr(index + 1, stdPath.size() - index - 1);
}
else
{
index = stdPath.find_last_of('\\');
if (index > 0)
{
name = stdPath.substr(index + 1, stdPath.size() - index - 1);
}
}
return name;
}
return "";
}
bool GetProcessName(char* processName, int maxLen)
{
char szFileFullPath[MAX_PATH] = { 0 };
char szProcessName[MAX_PATH] = { 0 };
::GetModuleFileNameA(NULL, szFileFullPath, MAX_PATH);//获取文件路径
int length = ::strlen(szFileFullPath);
int index = -1;
for (int i = length - 1; i >= 0; i--)//从路径后面开始找\,即倒着找右斜杠
{
char ck = szFileFullPath[i];
if (ck == '\\' || ck == '/')//找到第一个\,就可以马上获取进程名称了
{
index = i + 1;
break;
}
}
if (index >= 0)
{
memcpy(szProcessName, szFileFullPath + index, length - index);
strcpy(processName, szProcessName);
return true;
}
return false;
}
std::vector<DWORD> GetProcIDList( const char* proName )
{
DWORD procIDList[1024];
DWORD retByteLen=0;
DWORD retNumProc;
std::vector<DWORD> tList;
if( !EnumProcesses(procIDList, sizeof(DWORD)*1024, &retByteLen) )
{
return tList;
}
retNumProc = retByteLen/sizeof(DWORD);
std::string srcName = proName;
std::transform(srcName.begin(), srcName.end(), srcName.begin(), ::tolower);
std::string ckName;
int find_i = -1;
for( int i = 0; i < retNumProc; ++i )
{
ckName = GetProcName( procIDList[i] );
if( ckName.empty() )
{
continue;
}
std::transform(ckName.begin(),ckName.end(), ckName.begin(), ::tolower);
find_i = ckName.find(srcName);
if( find_i >= 0 )
{
tList.push_back(procIDList[i]);
}
}
return tList;
}
char* wchar2char(const wchar_t* wstr, char* cstr, int retMaxLen)
{
if( !cstr )
{
return cstr;
}
memset(cstr, 0, sizeof(char)*retMaxLen);
if( !wstr )
{
return cstr;
}
int nlength=wcslen(wstr);
//获取转换后的长度
int nbytes = WideCharToMultiByte( 0, // specify the code page used to perform the conversion
0, // no special flags to handle unmapped characters
wstr, // wide character string to convert
nlength, // the number of wide characters in that string
NULL, // no output buffer given, we just want to know how long it needs to be
0,
NULL, // no replacement character given
NULL ); // we don't want to know if a character didn't make it through the translation
// make sure the buffer is big enough for this, making it larger if necessary
if(nbytes>retMaxLen) nbytes=retMaxLen-1;
// 通过以上得到的结果,转换unicode 字符为ascii 字符
WideCharToMultiByte( 0, // specify the code page used to perform the conversion
0, // no special flags to handle unmapped characters
wstr, // wide character string to convert
nlength, // the number of wide characters in that string
cstr, // put the output ascii characters at the end of the buffer
nbytes, // there is at least this much space there
NULL, // no replacement character given
NULL );
return cstr;
}
wchar_t* char2wchar(const char* cstr, wchar_t* wstr, int retMaxLen)
{
if( !wstr )
{
return wstr;
}
memset(wstr, 0, sizeof(wchar_t)*retMaxLen);
if( !cstr )
{
return wstr;
}
size_t nu = strlen(cstr);
size_t n =(size_t)MultiByteToWideChar(CP_ACP,0,cstr,(int)nu,NULL,0);
if(n>=retMaxLen)n=retMaxLen-1;
MultiByteToWideChar(CP_ACP,0,cstr,(int)nu,wstr,(int)n);
return wstr;
}
接收消息:
//main.cpp中修改添加
const char* winTitle = "MyQTWindown";
MakeSingle(winTitle.c_str());
//Qt窗口设置窗口名字
RealTrade w;
w.setWindowTitle(winTitle);
//w.show();
int ret = a.exec();
重载nativeEvent
bool RealTrade::nativeEvent(const QByteArray &eventType, void *message, long *result)
{
//LogSave("接收到外部消息");
MSG* msg = reinterpret_cast<MSG*>(message);
if (msg->message == WM_USER+100)
{
LogSave("WindowShow");
show();
}
return false;
}