Именованные каналы из приложения Windows в клиентское приложение
Моя история заключается в том, что я разрабатываю новое приложение, которое должно взаимодействовать с сервисом Windows. После долгих исследований я пришел к выводу, что Named Pipes являются рекомендуемым методом (Как отправить строку из одного экземпляра моей программы Delphi другому?) кажется, что я не могу использовать SendMessage или Named Pipes в Win7 из-за проблем с безопасностью... сообщения никогда не попадают за пределы службы в приложение.
Я использую компоненты Russell Libby с именем Pipe, которые работают без сучка и затыкания между обычными настольными приложениями, но служба Windows, похоже, бросает ключ в решение. Дальнейшие исследования говорят мне, что может быть возможно открыть защиту с обеих сторон, чтобы они могли общаться, однако мой уровень знаний на этом минимален в лучшем случае, и я не смог сделать головы или хвосты возможных вызовов API.
Основываясь на компоненте Delphi pipe.pas, что нужно сделать, чтобы открыть этого ребенка, чтобы обе стороны могли начать говорить? Я уверен, что следующие две функции из файла pipe.pas идентифицируют атрибуты безопасности, может ли кто-нибудь помочь мне здесь?
Спасибо!
procedure InitializeSecurity(var SA: TSecurityAttributes);
var
sd: PSecurityDescriptor;
begin
// Allocate memory for the security descriptor
sd := AllocMem(SECURITY_DESCRIPTOR_MIN_LENGTH);
// Initialize the new security descriptor
if InitializeSecurityDescriptor(sd, SECURITY_DESCRIPTOR_REVISION) then
begin
// Add a NULL descriptor ACL to the security descriptor
if SetSecurityDescriptorDacl(sd, True, nil, False) then
begin
// Set up the security attributes structure
SA.nLength := SizeOf(TSecurityAttributes);
SA.lpSecurityDescriptor := sd;
SA.bInheritHandle := True;
end
else
// Failed to init the sec descriptor
RaiseWindowsError;
end
else
// Failed to init the sec descriptor
RaiseWindowsError;
end;
procedure FinalizeSecurity(var SA: TSecurityAttributes);
begin
// Release memory that was assigned to security descriptor
if Assigned(SA.lpSecurityDescriptor) then
begin
// Reource protection
try
// Free memory
FreeMem(SA.lpSecurityDescriptor);
finally
// Clear pointer
SA.lpSecurityDescriptor := nil;
end;
end;
end;
Ответы
Ответ 1
Windows Vista, Seven и 2008 обеспечивают более безопасное использование именованных каналов, см., например, http://blogs.technet.com/b/nettracer/archive/2010/07/23/why-does-anonymous-pipe-access-fail-on-windows-vista-2008-windows-7-or-windows-2008-r2.aspx
Ответ 2
Я попытался реализовать это:
function GetUserSid(var SID: PSID; var Token: THandle): boolean;
var TokenUserSize: DWORD;
TokenUserP: PSIDAndAttributes;
begin
result := false;
if not OpenThreadToken(GetCurrentThread, TOKEN_QUERY, True, Token) then
if (GetLastError <> ERROR_NO_TOKEN) or
not OpenProcessToken(GetCurrentProcess, TOKEN_QUERY, Token) then
Exit;
TokenUserP := nil;
TokenUserSize := 0;
try
if not GetTokenInformation(Token, TokenUser, nil, 0, TokenUserSize) and
(GetLastError <> ERROR_INSUFFICIENT_BUFFER) then
Exit;
TokenUserP := AllocMem(TokenUserSize);
if not GetTokenInformation(Token, TokenUser, TokenUserP,
TokenUserSize, TokenUserSize) then
Exit;
SID := TokenUserP^.Sid;
result := true;
finally
FreeMem(TokenUserP);
end;
end;
function ConvertSidToStringSidA(aSID: PSID; var aStr: PAnsiChar): BOOL; stdcall; external advapi32;
function ConvertStringSecurityDescriptorToSecurityDescriptorA(
StringSecurityDescriptor: PAnsiChar; StringSDRevision: DWORD;
SecurityDescriptor: pointer; SecurityDescriptorSize: Pointer): BOOL; stdcall; external advapi32;
const
SDDL_REVISION_1 = 1;
procedure InitializeSecurity(var SA: TSecurityAttributes; var SD; Client: boolean);
var OK: boolean;
Token: THandle;
pSidOwner: PSID;
pSid: PAnsiChar;
SACL: AnsiString;
begin
fillchar(SD,SECURITY_DESCRIPTOR_MIN_LENGTH,0);
// Initialize the new security descriptor
OK := false;
if InitializeSecurityDescriptor(@SD, SECURITY_DESCRIPTOR_REVISION) then begin
if Client or (OSVersionInfo.dwMajorVersion<6) then
// before Vista: add a NULL descriptor ACL to the security descriptor
OK := SetSecurityDescriptorDacl(@SD, true, nil, false)
else begin
// since Vista: need to specify special ACL
if GetUserSid(pSidOwner,Token) then
try
if ConvertSidToStringSidA(pSidOwner,pSid) then
try
SACL := 'D:(A;;GA;;;'+pSID+')(A;;GWGR;;;AN)(A;;GWGR;;;WD)S:(ML;;NW;;;S-1-16-0)';
OK := ConvertStringSecurityDescriptorToSecurityDescriptorA(
pointer(SACL),SDDL_REVISION_1,@SD,nil);
finally
LocalFree(PtrUInt(pSid));
end;
finally
FreeSid(pSidOwner);
CloseHandle(Token);
end;
end;
end;
if OK then begin
// Set up the security attributes structure
SA.nLength := sizeof(TSecurityAttributes);
SA.bInheritHandle := true;
SA.lpSecurityDescriptor := @SD;
end else
fillchar(SA,sizeof(SA),0); // mark error: no security
end;
Кажется, что он работает на стороне сервера (т.е. атрибуты безопасности создаются, как ожидалось), и вам придется писать код на стороне клиента, не забывая добавлять имя канала в SYSTEM\CurrentControlSet\Services\lanmanserver\parameters\NullSessionPipes, как и ожидалось.
Ответ 3
Когда мы перенесли наш продукт с Win 2K на Win7, мы запустили работу Named Pipes. После 2 недель общения с MS (и $275) мы обнаружили, что это вызвано настройками файла "Использовать общие папки". Отмена этой функции позволила нам продолжить работу с каналами.
Ответ 4
Кажется, я помню, что RemObjects имеет именованный канал клиент/серверный элемент управления в своем пакете. Если вы не используете бюджет, я настоятельно рекомендую вам посмотреть на готовые компоненты для таких вещей. Это требует много времени и хитрости, чтобы получить право.
В качестве альтернативы, Justin Smyth имеет статью о именованных каналах прямо сейчас. Посмотрите его блог на эту тему здесь: http://smythconsulting.blogspot.com/2011/07/smartmediaplayer-pipes-part4.html
Удачи!
Ответ 5
У меня была такая же проблема, и я просто решил ее. Для меня причина, по которой это не сработало, состояла в том, что реализация Russels TPipe
имела проверку идентификатора потоков непосредственно перед созданием трубы: if not(Sync.SyncBaseTID = FNotifyThread) then..
Оказалось, что я создал TPipeServer
в неправильном месте в моем сервисе. (Я переопределил DoStart
и т.д. Вместо использования события OnStart
... не делайте этого!)
Я создаю экземпляр TPipeServer
в том же потоке, который я позже активирую.