VB6:跨所有用户会话的单实例应用程序

VB6:跨所有用户会话的单实例应用程序,vb6,mutex,single-instance,Vb6,Mutex,Single Instance,我有一个应用程序,需要在Windows PC上的所有用户会话中都是一个单实例应用程序。到目前为止,我的研究主要围绕使用互斥来实现这一点,但我有一个问题,我不确定这是否真的是一个问题,我相信这是一个最佳实践问题 以下是代码: Private Const AppVer = "Global\UNIQUENAME" ' This is not what i am using but the name is unique Public Sub Main() Dim mutexValue As

我有一个应用程序,需要在Windows PC上的所有用户会话中都是一个单实例应用程序。到目前为止,我的研究主要围绕使用互斥来实现这一点,但我有一个问题,我不确定这是否真的是一个问题,我相信这是一个最佳实践问题

以下是代码:

Private Const AppVer = "Global\UNIQUENAME" ' This is not what i am using but the name is unique

Public Sub Main()

    Dim mutexValue As Long

    mutexValue = CreateMutex(ByVal 0&, 1, AppVer)
    If (Err.LastDllError = ERROR_ALREADY_EXISTS) Then
        SaveTitle$ = App.Title
        App.Title = "... duplicate instance."
        MsgBox "A duplicate instance of this program exists."
        CloseHandle mutexValue
        Exit Sub
    End If
    ' Else keep on truckin'
现在,根据这篇文章,我相信我理解了,通过向上面的CreateMutex函数传递NULL指针,我基本上是在分配与当前登录用户关联的任何安全描述符

如果这意味着我认为它会告诉我,其他登录用户将无法“看到”在原始用户会话下创建的互斥,也无法创建同名互斥


现在,证据似乎支持这一点。我使用一个消息框弹出我收到的“LastDLLError”,当另一个用户尝试启动应用程序时(当它已经在另一个用户帐户下运行时),我会收到一个错误\u访问\u拒绝代码。我可以针对这个错误进行测试,错误代码已经存在,并且只是在/or上退出。然而,这感觉有点像黑客,我想知道是否有人可以提出一个替代方案。“正确”的做法似乎是将适当的指针传递给CreateMutex函数,这样任何用户都有适当的权限查看任何现有的互斥体(mutices?),但我不确定如果当前登录的用户不是管理员(这是不可接受的),这是可能的。非常感谢您的任何帮助/指导。提前谢谢

我认为你的直觉完全正确。我不知道为什么从错误中推断出其他进程有互斥锁是不安全的,所以实际上它与错误已经存在(在这个上下文中)是一样的。但同时,它感觉不太正确

正如您所建议的,设置适当的安全描述符确实是正确的方法。MSDN说授予互斥访问权限会增加用户成为管理员的风险,我认为您确实需要互斥访问权限。但根据我的经验,它对非管理员很有效

你的问题吸引了我足够多做一个快速测试。这意味着我有一些源代码,所以这里是:

int wmain(int argc, wchar_t* argv[])
{
    ACL *existing_dacl = NULL;
    ACL *new_dacl = NULL;
    PSECURITY_DESCRIPTOR security_descriptor = NULL;

    bool owner = false;
    HANDLE mutex = CreateMutex(NULL,FALSE,L"Global\\blah");
    if(mutex == NULL)
        wprintf(L"CreateMutex failed: 0x%08x\r\n",GetLastError());
    if(GetLastError() == ERROR_ALREADY_EXISTS)
        wprintf(L"Got handle to existing mutex\r\n");
    else
    {
        wprintf(L"Created new mutex\r\n");
        owner = true;
    }

    if(owner)
    {
        //  Get the DACL on the mutex
        HRESULT hr = GetSecurityInfo(mutex,SE_KERNEL_OBJECT,
                                    DACL_SECURITY_INFORMATION,NULL,NULL,
                                    &existing_dacl,NULL,
                                    &security_descriptor);
        if(hr != S_OK)
            wprintf(L"GetSecurityInfo failed: 0x%08x\r\n",hr);

        //  Add an ACE to the ACL
        EXPLICIT_ACCESSW ace;
        memset(&ace,0,sizeof(ace));
        ace.grfAccessPermissions = MUTEX_ALL_ACCESS;
        ace.grfAccessMode = GRANT_ACCESS;
        ace.grfInheritance = NO_INHERITANCE;
        ace.Trustee.pMultipleTrustee = NULL;
        ace.Trustee.MultipleTrusteeOperation = NO_MULTIPLE_TRUSTEE;
        ace.Trustee.TrusteeForm = TRUSTEE_IS_NAME;
        ace.Trustee.TrusteeType = TRUSTEE_IS_WELL_KNOWN_GROUP;
        ace.Trustee.ptstrName = L"EVERYONE";
        hr = SetEntriesInAcl(1,&ace,existing_dacl,&new_dacl);
        if(hr != S_OK)
            wprintf(L"SetEntriesInAcl failed: 0x%08x\r\n",hr);

        //  Set the modified DACL on the mutex
        hr = SetSecurityInfo(mutex,SE_KERNEL_OBJECT,
                            DACL_SECURITY_INFORMATION,NULL,NULL,new_dacl,NULL);
        if(hr != S_OK)
            wprintf(L"SetSecurityInfo failed: 0x%08x\r\n",hr);
        else
            wprintf(L"Changed ACL\r\n");

        LocalFree(existing_dacl);
        LocalFree(new_dacl);
        LocalFree(security_descriptor);
    }

    wprintf(L"Press any key...");
    _getch();
    CloseHandle(mutex);
    return 0;
}

去年年底,我在VB6中寻找类似的解决方案。当时,我找不到任何VB6应用程序跨用户边界通信的示例,因此我不得不编写自己的应用程序

见:


您可以使用该类创建并检查全局信号量,该信号量将告诉您应用程序是否已在任何用户下运行。我没有看互斥API,但它们的用法非常相似。如果您已经编写了一些互斥体代码,那么您将需要转置GetSecurityDescriptor函数。

您不需要管理员权限来设置自己互斥体的安全性。这是一个简单的演示应用程序,基本上让每个人/完全控制互斥

Option Explicit

Private Const STANDARD_RIGHTS_REQUIRED              As Long = &HF0000
Private Const SYNCHRONIZE                           As Long = &H100000
Private Const MUTANT_QUERY_STATE                    As Long = &H1
Private Const MUTANT_ALL_ACCESS                     As Long = (STANDARD_RIGHTS_REQUIRED Or SYNCHRONIZE Or MUTANT_QUERY_STATE)
Private Const SECURITY_DESCRIPTOR_REVISION          As Long = 1
Private Const DACL_SECURITY_INFORMATION             As Long = 4

Private Declare Function CreateMutex Lib "kernel32" Alias "CreateMutexA" (lpMutexAttributes As Any, ByVal bInitialOwner As Long, ByVal lpName As String) As Long
Private Declare Function OpenMutex Lib "kernel32" Alias "OpenMutexA" (ByVal dwDesiredAccess As Long, ByVal bInheritHandle As Long, ByVal lpName As String) As Long
Private Declare Function CloseHandle Lib "kernel32" (ByVal hObject As Long) As Long
Private Declare Function InitializeSecurityDescriptor Lib "advapi32.dll" (pSecurityDescriptor As Any, ByVal dwRevision As Long) As Long
Private Declare Function SetSecurityDescriptorDacl Lib "advapi32.dll" (pSecurityDescriptor As Any, ByVal bDaclPresent As Long, pDacl As Any, ByVal bDaclDefaulted As Long) As Long
Private Declare Function SetKernelObjectSecurity Lib "advapi32.dll" (ByVal Handle As Long, ByVal SecurityInformation As Long, pSecurityDescriptor As SECURITY_DESCRIPTOR) As Long

Private Type SECURITY_DESCRIPTOR
    Revision            As Byte
    Sbz1                As Byte
    Control             As Long
    Owner               As Long
    Group               As Long
    pSacl               As Long
    pDacl               As Long
End Type

Private Const MUTEX_NAME            As String = "Global\20b70e57-1c2e-4de9-99e5-20f3961e6812"

Private m_hCurrentMutex         As Long

Private Sub Form_Load()
    Dim hMutex          As Long
    Dim uSec            As SECURITY_DESCRIPTOR

    hMutex = OpenMutex(MUTANT_ALL_ACCESS, 0, MUTEX_NAME)
    If hMutex <> 0 Then
        Call CloseHandle(hMutex)
        MsgBox "Already running", vbExclamation
        Unload Me
        Exit Sub
    End If
    m_hCurrentMutex = CreateMutex(ByVal 0&, 1, MUTEX_NAME)
    Call InitializeSecurityDescriptor(uSec, SECURITY_DESCRIPTOR_REVISION)
    Call SetSecurityDescriptorDacl(uSec, 1, ByVal 0, 0)
    Call SetKernelObjectSecurity(m_hCurrentMutex, DACL_SECURITY_INFORMATION, uSec)
End Sub

Private Sub Form_Unload(Cancel As Integer)
    If m_hCurrentMutex <> 0 Then
        Call CloseHandle(m_hCurrentMutex)
        m_hCurrentMutex = 0
    End If
End Sub
选项显式
私人康斯特标准权利要求,长度=&HF0000
私有常量同步长度=&H100000
私有常量变量\u查询\u状态为Long=&H1
Private Const MUTANT_ALL_ACCESS As Long=(需要标准权限或同步或MUTANT_QUERY_状态)
私有常量安全描述符修订版(长=1)
专用Const DACL\u安全信息长度=4
私有声明函数CreateMutex Lib“kernel32”别名“CreateMutexA”(lpMutexAttributes为Any,ByVal binitilowner为Long,ByVal lpName为String)为Long
私有声明函数OpenMutex Lib“kernel32”别名“OpenMutexA”(ByVal dwDesiredAccess为Long,ByVal bInheritHandle为Long,ByVal lpName为String)为Long
私有声明函数CloseHandle Lib“kernel32”(ByVal hObject As Long)为Long
私有声明函数InitializeSecurityDescriptor Lib“advapi32.dll”(pSecurityDescriptor如有,ByVal dwRevision如长)如长
私有声明函数SetSecurityDescriptorDacl Lib“advapi32.dll”(pSecurityDescriptor为任意值,ByVal bDaclPresent为任意值,pDacl为任意值,ByVal bDaclDefaulted为任意值)为任意值
私有声明函数SetKernelObjectSecurity Lib“advapi32.dll”(ByVal句柄为Long,ByVal SecurityInformation为Long,pSecurityDescriptor为SECURITY_描述符)为Long
私有类型安全描述符
修订为字节
Sbz1作为字节
控制尽可能长
所有者只要
组长
pSacl尽可能长
pDacl尽可能长
端型
Private Const MUTEX_NAME As String=“全局\20b70e57-1c2e-4de9-99e5-20f3961e6812”
专用m_hCurrentMutex,长度为
专用子表单_加载()
暗淡的hMutex和长的一样
Dim uSec作为安全描述符
hMutex=OpenMutex(变种\u所有\u访问,0,互斥\u名称)
如果hMutex为0,则
调用CloseHandle(hMutex)
MsgBox“已在运行”,请感叹
卸下我
出口接头
如果结束
m_hCurrentMutex=CreateMutex(ByVal 0&,1,MUTEX_NAME)
调用InitializeSecurityDescriptor(uSec,安全描述符修订版)
调用SetSecurityDescriptorDacl(uSec,1,ByVal 0,0)
调用SetKernelObject安全性(m_hCurrentMutex、DACL_安全信息、uSec)
端接头
私有子表单_卸载(取消为整数)
如果m_hCurrentMutex为0,则
调用CloseHandle(m_hCurrentMutex)
m_hCurrentMutex=0
如果结束
端接头

感谢大家的反馈。我将修改我的代码,使其行为类似于Joe的类中的代码,它附带的测试应用程序确认我应该能够使其工作。把这个关了。