无法防止来自不同用户的同一程序的多个实例[重复]

Posted

技术标签:

【中文标题】无法防止来自不同用户的同一程序的多个实例[重复]【英文标题】:Not able to prevent multiple instances of the same program from different users [duplicate] 【发布时间】:2019-04-03 18:46:55 【问题描述】:

我有以下代码。它可以防止同一用户下同一应用程序的多个实例。但是,当另一个用户已经在运行应用程序时,我还想防止同一台机器上的不同用户运行应用程序。即使应用程序已由其他用户运行,该代码也成功创建了互斥锁。无法弄清楚我在这里做错了什么。任何帮助表示赞赏。

m_hMutex = ::CreateMutex(NULL, FALSE, PREDEFINED_UNIQUE_ID);
if (::GetLastError() == ERROR_ALREADY_EXISTS)

    if (m_hMutex)
        ::CloseHandle(m_hMutex);

    //in case the previous instance of APP is still in the process of closing
    ::Sleep(5*1000);
    m_hMutex = ::CreateMutex(NULL, FALSE, PREDEFINED_UNIQUE_ID);
    if (::GetLastError() == ERROR_ALREADY_EXISTS)
    
        EndSplashWindow();
        AfxMessageBox( _T("An instance of APPis already running."), MB_OK | MB_ICONERROR | MB_SYSTEMMODAL );
        return FALSE;
    

【问题讨论】:

【参考方案1】:

您没有显示PREDEFINED_UNIQUE_ID 的实际值,但我猜它不是在Global kernel namespace 中创建互斥锁,而是在每个会话命名空间中创建。跨越用户会话需要使用 Global 命名空间:

单独的客户端会话命名空间允许多个客户端运行相同的应用程序不会相互干扰。对于在客户端会话下启动的进程,系统默认使用会话命名空间。但是,这些进程可以通过在对象名称前添加“Global\”前缀来使用全局命名空间。例如,以下代码调用CreateEvent,并在全局命名空间中创建一个名为CSAPP的事件对象:

CreateEvent( NULL, FALSE, FALSE, "Global\\CSAPP" );

...

全局命名空间的另一个用途是应用程序使用命名对象来检测系统中是否已经有一个应用程序实例在所有会话中运行。这个命名对象必须在全局命名空间而不是每个会话命名空间中创建或打开。默认支持每个会话运行一次应用程序的更常见情况,因为命名对象是在每个会话命名空间中创建的.

更新:您还需要处理CreateMutex() 由于安全权限而无法在另一个会话中访问互斥锁的情况。你需要处理GetLastError()返回ERROR_ACCESS_DENIED的情况,这本身就意味着互斥体存在。

试试这样的:

#define PREDEFINED_UNIQUE_ID _T("Global\\MyUniqueMutexName");

m_hMutex = ::CreateMutex(NULL, FALSE, PREDEFINED_UNIQUE_ID);
// or: m_hMutex = ::CreateMutexEx(&sa, PREDEFINED_UNIQUE_ID, 0, SYNCHRONIZE);
DWORD dwErrorCode = ::GetLastError();

if ((m_hMutex) && (dwErrorCode == ERROR_ALREADY_EXISTS))

    ::CloseHandle(m_hMutex);

    //in case the previous instance of APP is still in the process of closing
    ::Sleep(5*1000);
    // consider using WaitForSingleObject() instead, in case the app takes less than 5 seconds to close...

    m_hMutex = ::CreateMutex(NULL, FALSE, PREDEFINED_UNIQUE_ID);
    // or: m_hMutex = ::CreateMutexEx(&sa, PREDEFINED_UNIQUE_ID, 0, SYNCHRONIZE);
    dwErrorCode = ::GetLastError();


if (dwErrorCode != 0)

    EndSplashWindow();

    switch (dwErrorCode)
    
        case ERROR_ALREADY_EXISTS:
        case ERROR_ACCESS_DENIED:
            AfxMessageBox( _T("An instance of APP is already running."), MB_OK | MB_ICONERROR | MB_SYSTEMMODAL );
            break;

        default:
            AfxMessageBox( _T("Error checking if an instance of APP is already running."), MB_OK | MB_ICONERROR | MB_SYSTEMMODAL );
            break;
    

    return FALSE;


// OK to run now!
return TRUE;

或者,为了尽量减少ERROR_ACCESS_DENIED 被报告的可能性,您可以向CreateMutex() 提供一个NULL DACL:

PSECURITY_DESCRIPTOR pSD = (PSECURITY_DESCRIPTOR) LocalAlloc(LPTR, SECURITY_DESCRIPTOR_MIN_LENGTH); 
if (!pSD) 
 
    EndSplashWindow();
    AfxMessageBox( _T("Error allocating security descriptor."), MB_OK | MB_ICONERROR | MB_SYSTEMMODAL );
    return FALSE;


if (!InitializeSecurityDescriptor(pSD, SECURITY_DESCRIPTOR_REVISION)) 
 
    LocalFree(pSD); 
    EndSplashWindow();
    AfxMessageBox( _T("Error initializing security descriptor."), MB_OK | MB_ICONERROR | MB_SYSTEMMODAL );
    return FALSE;


if (!SetSecurityDescriptorDacl(pSD, TRUE, NULL, FALSE))
 
    LocalFree(pSD); 
    EndSplashWindow();
    AfxMessageBox( _T("Error setting access for security descriptor."), MB_OK | MB_ICONERROR | MB_SYSTEMMODAL );
    return FALSE;


SECURITY_ATTRIBUTES sa = ;
sa.nLength = sizeof(sa);
sa.lpSecurityDescriptor = pSD;
sa.bInheritHandle = FALSE;

m_hMutex = ::CreateMutex(&sa, FALSE, PREDEFINED_UNIQUE_ID);
...

LocalFree(pSD);

【讨论】:

我将 PREDEFINED_UNIQUE_ID 的值更改为 "Global\\CSAPP" 我仍然遇到同样的问题?!​​ 重点是简单地将Global\ 前缀添加到您已经使用的任何名称中,您不必将名称更改为CSAPP,这只是MSDN示例中使用的名称.但无论哪种方式,一旦互斥锁位于全局命名空间中,您就不会遇到同样的问题。 您需要为CreateMutex() 提供包含NULL DACL 的SECURITY_ATTRIBUTES,以便不同会话中的进程可以共享同一个互斥对象,否则CreateMutex() 可能会失败并出现ERROR_ACCESS_DENIED 错误,这你没有处理,因为你没有处理 CreateMutex() 返回 NULL 互斥句柄的可能性。虽然,ERROR_ACCESS_DENIED 错误本身会告诉您互斥体已经存在,即使您无权访问它。

以上是关于无法防止来自不同用户的同一程序的多个实例[重复]的主要内容,如果未能解决你的问题,请参考以下文章

如何防止在 PHP 中重复提交表单(跨多个窗口进行保护)?

MySQL5.7 多实例

GCM 多设备令牌

MySQL数据库多实例介绍及安装

在同一会话中执行来自不同活动的多个请求

如何防止两个用户在 Sharepoint 中编辑同一个列表