2011-01-25 71 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