可执行文件单实例运行且激活已运行的实例

来源:互联网 发布:驴友用的软件 编辑:程序博客网 时间:2024/05/22 06:15
 
/////某软件的实现方法
 
v5 = CreateMutexA(0, 0, "Mutex_XXXX_object");
  if ( GetLastError() == 183 && (CloseHandle(v5), v6 = GetDesktopWindow(), v7 = GetWindow(v6, 5u), IsWindow(v7)) )//GW_CHILD
  {
    while ( !GetPropA(v7, *(LPCSTR *)(v4 + 140)) )
    {
      v7 = GetWindow(v7, 2u);//GW_HWNDNEXT
      if ( !IsWindow(v7) )
        goto LABEL_5;//继续执行
    }
    if ( IsIconic(v7) )
      ShowWindow(v7, 9);//SW_RESTORE
    SetForegroundWindow(v7);
  }
 
方法一:


::CreateMutex(NULL, FALSE, "MyAPP"); 
if(ERROR_ALREADY_EXISTS == GetLastError()) 

      AfxMessageBox("Already run!"); 
      return FALSE; 
}
 
方法二:

一个程序可以运行多个实例(进程),那我们如何让它仅运行一个实例呢?很简单,使用#pragma编译器指令在进程的地址空间内创建一个“共享节”就可以 达到目的,这个“共享节”里的数据为多个运行的进程所共享,这样我们通过设置一个变量比如:unsigned g_nInstanceCount;作为运行实例的计数,在程序入口处检测一下g_nInstanceCount是否为1,true则禁止运行,否则 g_nInstanceCount++并运行一个实例。
具体如下:
#pragma data_seg("Shared")//这个共享节叫做Shared
unsigned g_nInstanceCount =0;//计数初始为0
#pragma data_seg() //设置完毕
#pragma comment(linker,"/:SECTION:Shared,RWS
")//这句话告诉连接器,我要将Shared设为读写共享


好了,让我们实际动手来做一个小的例子看看,在VC6.0下可以,但在其它IDE(如Code Block)中就有不能达到效果的例子:


#include <iostream>
using namespace std;

#pragma data_seg("Shared")

unsigned g_nInstanceCount = 0;

#pragma data_seg()

#pragma comment(linker,"/SECTION:Shared,RWS")

int main()
{

if(g_nInstanceCount>=1)
{
cout<<"Can only running an instance!"<<endl;
return 0;
}

g_nInstanceCount++; //实例计数加1

cout<<"This is a console program!"<<endl;
system("pause");
return 0;
}
 
方法三:
要使应用程序只运行一个实例,一个简单的方法是在应用程序类中使用互斥量,这可以用VC下的GUIDGEN.EXE程序产生.GUIDGEN.EXE位于VC安装目录CommonTools目录下
实例
1: 新建一基于对话框的工程ex1,采用默认设置
2: 用GUIDGEN.EXE产生一个全局标志,#define one "产生的全局标志"
本例中产生的语句如下:#define one "0xbe8e2ce1, 0xdab6, 0x11d6, 0xad, 0xd0, 0x0, 0xe0, 0x4c, 0x53, 0xf6, 0xe6"
3: 在应用程序类CEx1App::InitInstance()中,用CreateMutex函数创建一个互斥量,后调用函数GetLastError()
如果结果等于ERROR_ALREADY_EXISTS说明已经有一个实例在运行了这时返回FALSE.
BOOL CEx1App::InitInstance()  
handle=::CreateMutex(NULL,FALSE,one);//handle为声明的HANDLE类型的全局变量  
if(GetLastError()==ERROR_ALREADY_EXISTS)  
{
 AfxMessageBox("应用程序已经在运行");  
return FALSE;  
}
4:在CEx1App::ExitInstance()中,删除这个互斥量
int CEx1App::ExitInstance()  
CloseHandle(handle);  
return CWinApp::ExitInstance();  
}

实现程序只运行一次的方法很多,但是原理都是一样的,就是运行第一次的时候设置一个标记,每次运行的时候检查该标记,如果有就说明已经运行了。

具体实现:

1、在程序初始化的时候 (InitInstance()) 枚举所有的窗口,查找本程序的实例是否存在

2、在主窗口初始化的时候在本窗口的属性列表中添加一个标记,以便程序查找.

部分关键代码

1、在App的InitInstance()中枚举所有窗口,查找本程序实例

01.HWND oldHWnd = NULL;
02.EnumWindows(EnumWndProc,(LPARAM)&oldHWnd);    //枚举所有运行的窗口
03.if(oldHWnd != NULL)
04.{
05.AfxMessageBox("本程序已经在运行了");
06.::ShowWindow(oldHWnd,SW_SHOWNORMAL);          //激活找到的前一个程序
07.::SetForegroundWindow(oldHWnd);                //把它设为前景窗口
08.return false;                                  //退出本次运行
09.}

2、添加EnumWndProc窗口过程函数:

01.//添加的标识只运行一次的属性名
02.CString  g_szPropName = "Your Prop Name";   //自己定义一个属性名
03.HANDLE   g_hValue = (HANDLE)1;               //自己定义一个属性值
04. 
05.BOOL CALLBACK EnumWndProc(HWND hwnd,LPARAM lParam)
06.{
07.HANDLE h = GetProp(hwnd,g_szPropName);
08.if( h == g_hValue)
09.{
10.*(HWND*)lParam = hwnd;
11.return false;
12.}
13.return true;
14.}

3、在主窗口的 OnInitDialog()中添加属性

1.//设置窗口属性
2.SetProp(m_hWnd,g_szPropName,g_hValue);

/////////////////////////////////////////////////////////////////////////////////

if (::IsIconic(oldHWnd)) 
{
//第1种 
::ShowWindow(oldHWnd, SW_SHOWNORMAL);  ::SetWindowPos(oldHWnd,HWND_TOPMOST,0,0,0,0,SWP_NOSIZE|SWP_NOMOVE);
::SetWindowPos(oldHWnd,HWND_NOTOPMOST,0,0,0,0,SWP_NOSIZE|SWP_NOMOVE);
//第2种
//::ShowWindow(oldHWnd, SW_RESTORE);//激活找到的前一个程序
//::SetForegroundWindow( (HWND)(((ULONG) oldHWnd) | 0x01) );//把它设为前景窗口
return FALSE;
//第3种
//hForeWnd =  GetForegroundWindow(); 
//dwCurID  =  GetCurrentThreadId(); 
//dwForeID =  GetWindowThreadProcessId( hForeWnd, NULL ); 
//AttachThreadInput( dwCurID, dwForeID, TRUE); 
//ShowWindow( oldHWnd, SW_SHOWNORMAL ); 
//SetWindowPos( oldHWnd, HWND_TOPMOST, 0,0,0,0, SWP_NOSIZE|SWP_NOMOVE ); 
//SetWindowPos( oldHWnd, HWND_NOTOPMOST, 0,0,0,0, SWP_NOSIZE|SWP_NOMOVE ); 
//SetForegroundWindow( oldHWnd ); 
//AttachThreadInput( dwCurID, dwForeID, FALSE); 
 
//第3种解释下设置当前某窗口为当前窗口,有几个步骤要做:
1.得到窗口句柄FindWindow 
2.切换键盘输入焦点AttachThreadInput 
3.显示窗口ShowWindow(有些窗口被最小化/隐藏了) 
4.更改窗口的Zorder,SetWindowPos使之最上,为了不影响后续窗口的Zorder,改完之后,再还原 
5.最后SetForegroundWindow 
//第4种
//GetWindowThreadProcessId( oldHWnd, &dwForeID);
//AllowSetForegroundWindow(dwForeID);
//ShowWindow( oldHWnd, SW_SHOW );
//SetForegroundWindow(oldHWnd);
//第5种只是一个思路
//WM_Self_NOTIFY ,在这儿模拟发出消息
::SendMessage(oldHWnd, WM_ICON_NOTIFY, NULL, WM_LBUTTONDOWN);

/////////////////////////////////////////////////////////////////////////////////

Setforegroundwindow 将窗口置最前

   windows下简单的调用Setforegroundwindow并不能将窗口置最前,我找到三种方法可以实现该功能。1、先置topmost,然后取消
::ShowWindow(oldHWnd, SW_SHOWNORMAL);::SetWindowPos(oldHWnd,HWND_TOPMOST,0,0,0,0,SWP_NOSIZE|SWP_NOMOVE);::SetWindowPos(oldHWnd,HWND_NOTOPMOST,0,0,0,0,SWP_NOSIZE|SWP_NOMOVE);
 ShowWindow( SW_SHOWNORMAL); SetWindowPos(HWND_TOPMOST, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE); SetWindowPos(HWND_NOTOPMOST, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE);2、改变系统环境参数
这些定义winuser.h里都有的
#define SPI_GETFOREGROUNDLOCKTIMEOUT 0x2000#define SPI_SETFOREGROUNDLOCKTIMEOUT 0x2001#define SPIF_UPDATEINIFILE 0x0001#define SPIF_SENDWININICHANGE 0x0002

先设置环境变量
DWORD lockTime = 0;
SystemParametersInfo(SPI_GETFOREGROUNDLOCKTIMEOUT, 0, &lockTime, 0);
SystemParametersInfo(SPI_SETFOREGROUNDLOCKTIMEOUT, 0, 0, 
                     SPIF_SENDWININICHANGE | SPIF_UPDATEINIFILE);

退出程序时还原环境变量

SystemParametersInfo(SPI_SETFOREGROUNDLOCKTIMEOUT, 0, lockTime,SPIF_SENDWININICHANGE | SPIF_UPDATEINIFILE);
3、AllowSetForegroundWindow
 HMODULE hModule = NULL; ASFW_PTR pProcAddress = NULL;  hModule = GetModuleHandle("User32");
 if (hModule != NULL)  pProcAddress = (ASFW_PTR)GetProcAddress(hModule, "AllowSetForegroundWindow");
 if (pProcAddress != NULL) {  nResult = (*pProcAddress)(-1);
  if (nResult == FALSE )   MessageBox(NULL, "Could not set foreground permission", NULL, MB_OK | MB_ICONERROR); }
 

原创粉丝点击