VB6: приложение с одним средством во всех пользовательских сеансах
-
24-10-2019 - |
Вопрос
У меня есть приложение, которое должно быть приложением для одного инстанса во всех пользовательских сеансах на ПК Windows. Мое исследование до сих пор было сосредоточено на использовании мутекса для достижения этого, но у меня есть проблема, которую я не уверен, действительно проблема, я верю, что это действительно лучший вопрос.
Вот код в первую очередь:
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, так как я выше, я в основном назначаю любой дескриптор безопасности, связанный с в настоящее время регистрированным в пользователе.
Если это означает, что я думаю, что это делает (мне может понадобиться больше рекомендаций здесь), которое говорит мне, что другие пользователи, которые входят Мутекс с тем же именем.
Теперь, кажется, что эмпиционные данные подтверждают это. Я использовал окно сообщений, чтобы разместить «LastDllerror», который я получал, и когда другой пользователь попытался запустить приложение (в то время как оно уже работало под другой учетной записью пользователя), я получу код error_access_dyed. Я в порядке с тестированием против этого вместе с кодом error_already_exists и просто выхожу на либо/или. Тем не менее, это кажется немного хакерским, и мне интересно, может ли кто -то предложить альтернативу. Похоже, что «правильная» предназначена для передачи правильного указателя функции CreateMutex, так что у любого пользователя есть правильные разрешения на просмотр любых существующих мутекс (mitices?), Но я не так уверен, что это возможно без в настоящее время Зарегистрированный пользователем является администратором (что неприемлемо). Любая помощь/руководство высоко ценится. Заранее спасибо!
Решение
Я искал подобное решение в VB6 в конце прошлого года. В то время я не смог найти примеры приложений VB6, общающихся по границе пользователя, поэтому мне пришлось написать свой собственный.
Видеть: Межпроцессная связь с помощью семафоров
Вы можете использовать класс для создания и проверки глобального семафора, который сообщит вам, работает ли ваше приложение при любом пользователе. Я не смотрел на API Mutex, но их использование очень похоже. А GetSecurityDescriptor Функция - это то, что вы захотите перенести, если у вас уже есть какой -то код Mutex.
Другие советы
Вам не нужны администраторы Priveleges, чтобы установить безопасность на собственных мутекс. Вот простое демонстрационное приложение, которое в основном дает всем/полный контроль Mutex.
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
Я думаю, что ваши инстинкты точно правы. Я не знаю никакой причины, по которой не было бы безопасно сделать вывод из ошибки_акульс_акул_, что у некоторых других процессов есть мутекс, так что это то же самое, что 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;
}