首页 > 编程语言 >多用户、多会话 Server版操作系统下 程序唯一实例运行

多用户、多会话 Server版操作系统下 程序唯一实例运行

时间:2023-09-19 10:35:56浏览次数:49  
标签:多会话 多用户 HWND pt32 Server 实例 pHwnd lpName hMutex


很多时候我们需要做这样的事:只允许当前程序只有唯一一个实例运行。

这样的情况包括串口程序、网络socket等。

这时我们有多种解决方法。

常见的像用互斥、信号量等比运行实例生存期更长的内核对象,或者使用内存映射(点击打开链接)等。

如使用互斥内核对象:

HANDLE hmute=CreateMutex(NULL,FALSE,"BCB");
                 if(GetLastError()==ERROR_ALREADY_EXISTS)
                 {
                     Application->MessageBoxA("程序已运行","提示",MB_OK);//此处你可以做其他处理 如激活已运行的实例
                     return 1;
                 }//记得CloseHandle:)

当时如果在多用户操作系统,如Window Server 2003之类的操作系统,上述做法就不奏效了,这时我们看看MSDN:

点击打开链接

注意这段:



HANDLE WINAPI CreateMutex(
  __in_opt  LPSECURITY_ATTRIBUTES lpMutexAttributes,
  __in      BOOL bInitialOwner,
  __in_opt  LPCTSTR lpName
);
lpName:



The name can have a "Global\" or "Local\" prefix to explicitly create the object in the global or session name space. The remainder of the name can contain any character except the backslash character (\). For more information, see Kernel Object Namespaces. Fast user switching is implemented using Terminal Services sessions. Kernel object names must follow the guidelines outlined for Terminal Services so that applications can support multiple users.

Windows 2000:  If Terminal Services is not running, the "Global\" and "Local\" prefixes are ignored. The remainder of the name can contain any character except the backslash character.

The object can be created in a private namespace. For more information, see Object Namespaces.


该名称可以由一个“Global\”或“Local\”前缀在全局或会话名称空间的对象显式地创建。其余的名称可以包含反斜杠字符(\)以外的任何字符。欲了解更多信息,请参阅内核对象的命名空间。快速用户切换使用终端服务会话。内核对象名称必须遵循的准则,概述终端服务,使应用程序可以支持多个用户。



由MSDN解释,我们知道了如何让互斥内核对象在多会话之间可见,即将lpName参数设置为如下

const char *lpName="Global\\XXXXXXX";

既然知道是这样了,于是我们开始编码:

//BCB记得 #define STRICT
#include <tlhelp32.h>

HANDLE hMutex=NULL;
HWND g_Hwnd=NULL;
DWORD g_dwProcessId=0;
String g_strExeName;
const char *lpName="Global\\lhy-1989-03-23";
//
void  GetProcessID()
{
  HANDLE hSnap=CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS,0);
  PROCESSENTRY32 pt32={0};
  pt32.dwSize=sizeof pt32;
  if(Process32First(hSnap,&pt32))
  {
    do
    {
      String strExe=pt32.szExeFile;
      if(strExe==g_strExeName)
      {
        g_dwProcessId=pt32.th32ProcessID;
        break;
      } 
    }while(Process32Next(hSnap,&pt32));
  }
  CloseHandle(hSnap);
}
BOOL CALLBACK EnumWindowByProcessId(HWND hwnd,LPARAM lParam)
{
    DWORD dwProcessID=0;
    GetWindowThreadProcessId(hwnd,&dwProcessID);
    if(dwProcessID==g_dwProcessId)
    {
       HWND pHwnd=GetParent(hwnd);
       while(GetParent(pHwnd))
       pHwnd=GetParent(pHwnd);
       if(pHwnd)
       g_Hwnd=pHwnd;
    }
    return 1;
}
int WINAPI WinMain(HINSTANCE,HINSTANCE,LPSTR,int)
{
     g_strExeName=ExtractFileName(Application->ExeName);
     
     hMutex=CreateMutex(NULL,FALSE,lpName);
     if(GetLastError()==ERROR_ALREADY_EXISTS)
     {
                    CloseHandle(hMutex);
                    GetProcessID();
                    if(g_dwProcessId)
                    {
                        EnumWindows(EnumWindowByProcessId,0);
                        if(g_Hwnd)
                        {
                            ShowWindow(g_Hwnd,SW_RESTORE);//如果已经运行了实例,Show已运行实例,然后退出。
               return 1;                          
                        }  
       } 
   //....
  CloseHandle(hMutex);
}

如果大家仔细瞧过这段代码的思路,再放到2003下多会话运行,我们可以通过任务管理员查看到相应进程,会发现根本没有达到我们的目的! 即实例唯一允许。


既然上面的代码达不到效果,我们来找找原因,

看看我们的思路:

先通过检测发现如果已经有同名互斥对象存在,那么就对比实例名称获取进程ID,再使用EnumWindows枚举主窗口句柄,显示已运行窗口然后退出。

仔细瞧上述思路,发现我们依赖HWND来提醒用户程序已运行,那么是不是HWND的作用域不足以跨会话呢?:(没找到相关确切资料。单步下,发现在另外一个会话中无法获取目标HWND!

好吧,那我们就试试使用进程句柄来作为提示。


hMutex=CreateMutex(NULL,FALSE,lpName);
                     if(GetLastError()==ERROR_ALREADY_EXISTS)
                     {
                        GetProcessID();
                        if(g_dwProcessId)
                        {
                                if(Application->MessageBox("另一个客户会话正在运行该程序,是否结束已运行的程序重新从本会话启动?",
                                "提示",MB_ICONQUESTION|MB_YESNO)==IDYES)
                                {
                                  HANDLE hProcess=OpenProcess(PROCESS_ALL_ACCESS,False,g_dwProcessId);
                                  HANDLE hFront=OpenMutex(MUTEX_ALL_ACCESS,False,lpName);
                                  if(hFront)
                                  CloseHandle(hFront);
                                 
                                  TerminateProcess(hProcess,1);//无奈。。。
                                  WaitForSingleObject(hProcess,2000);
                                }
                                else
                                {
                                 CloseHandle(hMutex);
                                 return 1;
                                }
                        }
                     }



哈,成功了!由此我们可以得出HWND的作用域只限于当前会话。





标签:多会话,多用户,HWND,pt32,Server,实例,pHwnd,lpName,hMutex
From: https://blog.51cto.com/u_15487030/7522112

相关文章

  • SQL Server备份/还原 SQL注入
    SQL还原目标数据库 注:不能在目标数据库会话中执行alterdatabasetestsetsingle_userwithrollbackimmediate--(这里也可以延迟几秒回滚你的操作)restoredatabasetestfromdisk='d:\test.bak'alterdatabasetestsetmulti_user无意中看到的,有意思的SQL注入:--完整备份......
  • SQL设置SQLServer最大连接数
    设置最大连接数下面的T-SQL语句可以配置SQLServer允许的并发用户连接的最大数目。execsp_configure'showadvancedoptions',1execsp_configure'userconnections',100第一句用以表示显示sp_configure系统存储过程高级选项,使用userconnections时,要求......
  • 【漏洞复现】JumpServer未授权访问漏洞(CVE-2023-42442)
    1、简介JumpServer是一款符合4A规范的开源堡垒机,帮助企业以更安全的方式管控和登录各种类型的资产,实现事前授权、事中监察、事后审计,满足等保合规要求。2、漏洞描述JumpServer在3.0.0-3.6.3版本存在未授权访问漏洞。由于系统权限配置存在不合理,导致未授权攻击者可以直接访问......
  • 通过Sysmon+Nxlogs收集Windows Server 2012服务器日志-并以Syslog形式发送Json格式数
    0x01环境介绍WindowsServer2012已经安装部署好了域控,目的除了收集Windows服务器本身的日志外还收集域控环境下的各种日志。0x02Nxlog配置和使用使用社区版本即可,下载地址:https://nxlog.co/downloads/nxlog-ce#nxlog-community-edition使用的版本是当前最新版本安装过程就省略,......
  • windows server 原创用户启动后,弹窗--启动监听过程错误
    描述:弹出应用程序:服务控制管理器:在系统启动时至少有一个服务或驱动程序产生错误。详细信息,请使用事件查看器查看事件日志。server2003/xp/2000每次弹出服务控制管理器“系统启动时至少有一个服务或驱动程序产生错误”解决方法。解决方法:打开注册表,找到HKEY_LOCAL_MACHINE\S......
  • SQLServer递归触发器在KES中的一次改造分析
    文章概要:某项目将数据从SQLSERVER迁移到KES。其中SQLSERVER中触发器用到了TRIGGER_NESTLEVEL()函数,KES并不能直接支持该函数。起初在分析该问题时想复杂了本文做了一次记录。实际上在kes兼容sqlsevrer基础语法,直接简单使用SYS_TRIGGER_DEPTH()替换TRIGGER_NESTLEVEL()函......
  • Navicat连接SQLServer提示:未发现数据源名并且未指定默认驱动程序
    问题:Navicat连接SQLServer提示:未发现数据源名并且未指定默认驱动程序解决方式一:可以直接安装Navicat根目录(安装目录)下的sqlncli.msi(32位)或sqlncli_x64.msi(64位) 双击开始安装,一直下一步,正常安装完成的话,重启下Navicat就没有问题了,可以正常连接了。解决方式一:在官......
  • 今天安装了SqlServer2005
    手头早就有了一套sqlserver2005,今天上午看了一会儿《sap德国造》,有些累了。想歇息一会儿。在随便浏览电脑的时候,无疑中看到了存储在硬盘上的sqlserver2005安装程序。就猛地萌生了安装它的想法。 虽然sqlserver2005与2000已经区别很大了。但是基于桌面操作系统的应用程序的安装工作......
  • Mac专用投屏工具AirServer 7 .27 for Mac中文免费激活版
    AirServer7.27forMac中文免费激活版是一款Mac专用投屏工具,能够通过本地网络将音频、照片、视频以及支持AirPlay功能的第三方App,从iOS设备无线传送到Mac电脑的屏幕上,把Mac变成一个AirPlay终端的实用工具。目前最新的AirServer7.2.7版本,支持macOSHighSierra和iOS11,通......
  • 从0开始搭建SQL Server AlwaysOn
    从0开始搭建SQLServerAlwaysOn   第一篇http://www.cnblogs.com/lyhabc/p/4678330.html第二篇http://www.cnblogs.com/lyhabc/p/4682028.html第三篇http://www.cnblogs.com/lyhabc/p/4682986.html第四篇http://www.cnblogs.com/lyhabc/p/6136227.html搭建非域AlwaysOnwin2......