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

问题描述 投票:0回答:3

我有一个应用程序需要成为 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'

现在,基于this文章,我相信我明白,通过将 NULL 指针传递给上面的 CreateMutex 函数,我基本上是在分配与当前登录用户关联的任何安全描述符。

如果这意味着我认为它的作用(我可能需要更多指导),这告诉我登录的其他用户将无法“看到”在原始用户会话下创建的互斥体,也无法创建具有相同名称的互斥锁。

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

vb6 mutex single-instance
3个回答
4
投票

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

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

2
投票

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

参见:通过信号量进行进程间通信

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


1
投票

我认为你的直觉是完全正确的。我不知道为什么从 ERROR_ACCESS_DENIED 推断其他进程具有互斥体是不安全的,因此实际上它与 ERROR_ALREADY_EXISTS 相同(在这种情况下)。但同时,它感觉不非常正确。

正如您所建议的,设置适当的安全描述符确实是正确的方法。 MSDN 表示,授予 MUTEX_ALL_ACCESS 权限会增加用户必须成为管理员的风险,我认为您确实需要 MUTEX_ALL_ACCESS。但根据我的经验,它对于非管理员来说效果很好。

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

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;
}
© www.soinside.com 2019 - 2024. All rights reserved.