Именованные каналы из приложения 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;

Ответ 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 в том же потоке, который я позже активирую.