2011-01-25 36 views
5

我有一个应用程序需要在Windows PC上的所有用户会话中成为单实例应用程序。到目前为止,我的研究主要集中在使用互斥体来完成这个任务,但是我有一个问题,我不确定是否真的是一个问题,这是我相信的一个最佳实践问题。VB6:跨所有用户会话的单实例应用程序

这里第一是所有的代码的:

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?),但是我不确定如果没有当前的互斥体登录的用户是管理员(这是不可感知的)。任何援助/指导非常感谢。提前致谢!

回答

1

我在去年年底在VB6寻找类似的解决方案。当时我无法找到VB6应用程序在用户边界进行通信的任何示例,所以我必须自己写。

请参见:Interprocess Communication via Semaphores

可以使用类来创建并检查是否有信号全球,这将告诉你,如果你的应用程序已经被任何用户下运行。我没有看到Mutex API,但它们的用法非常相似。如果你已经有了一些Mutex代码,那么你需要转换的函数是GetSecurityDescriptor

+0

感谢大家的反馈。我将修改我的代码,使其像Joe的代码中的代码一样,随附的测试应用程序证实我应该能够使其工作。结束这一个。 – 2011-01-26 22:19:33

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; 
} 
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 
相关问题