5

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'

さて、この記事に基づいて、上記のように NULL ポインターを CreateMutex 関数に渡すことで、現在ログインしているユーザーに関連付けられているセキュリティ記述子を基本的に割り当てていることを理解していると思います。

それが私が思うことを意味する場合 (ここでさらにガイダンスが必要な場合があります)、ログインする他のユーザーは、元のユーザーのセッションで作成されたミューテックスを「見る」ことができず、作成することもできません。同じ名前のミューテックス。

現在、経験的証拠がこれを裏付けているようです。メッセージ ボックスを使用して受け取った "LastDLLError" をポップしました。別のユーザーが (別のユーザー アカウントで既に実行されているときに) アプリケーションを起動しようとすると、ERROR_ACCESS_DENIED コードを受け取りました。ERROR_ALREADY_EXISTS コードと一緒にこれに対してテストし、どちらかまたは両方で終了しても問題ありません。しかし、これは一種のハックのように感じられ、誰かが代替案を提案できるかどうか疑問に思っています. 「正しい」ことは、適切なポインターを CreateMutex 関数に渡して、すべてのユーザーが既存のミューテックス (mutices?) を表示するための適切なアクセス許可を持つようにすることですが、現在ログインしているユーザーが管理者であること (これは受け入れられません)。どんな支援/指導も大歓迎です。

4

3 に答える 3

4

独自のミューテックスにセキュリティを設定するために管理者権限は必要ありません。これは基本的にEveryone/Fullコントロールをミューテックスに与える簡単なデモアプリです。

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
于 2011-01-26T13:50:32.383 に答える
1

昨年末、VB6 で同様のソリューションを探していました。当時、ユーザー境界を越えて通信する VB6 アプリの例を見つけることができなかったため、自分で作成する必要がありました。

参照:セマフォによるプロセス間通信

このクラスを使用して、アプリが既に任意のユーザーの下で実行されているかどうかを通知するグローバル セマフォを作成および確認できます。Mutex API については調べていませんが、使い方は非常に似ています。GetSecurityDescriptor関数は、Mutex コードを既に記述している場合に置き換えたいものです。

于 2011-01-26T04:21:57.963 に答える
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;
}
于 2011-01-26T00:34:39.127 に答える