-
Доброго времени суток! Возникла проблема при работе с named pipes под win7, не могу понять в чем дело. Ситуация такова: на компе есть программа, которая запускается от имени текущего пользователя и сервис, запускаемый от SYSTEM. Между собой эти два компонента общаются по Named Pipe. Все прекрасно работает на Windows 2000 и XP. Но под Windows 7 сервис стартует без ошибок, но клиент к нему подключиться не может - CreateFile возвращает "Файл не существует"... не пойму в чем может быть дело...???? Хотя бы укажите направление "куда копать" :-) Сервер:
procedure InitializeSecurity(var SA: SECURITY_ATTRIBUTES);
var
SCD: PSECURITY_DESCRIPTOR;
begin
SCD := AllocMem(SECURITY_DESCRIPTOR_MIN_LENGTH);
try
Win32Check(InitializeSecurityDescriptor(SCD, SECURITY_DESCRIPTOR_REVISION));
Win32Check(SetSecurityDescriptorDacl(SCD, True, nil, False));
with SA do
begin
nLength := SizeOf(SECURITY_ATTRIBUTES);
lpSecurityDescriptor := SCD;
bInheritHandle := True;
end;
except
FreeMem(SCD);
raise;
end;
end;
procedure FinalizeSecurity(var SA: SECURITY_ATTRIBUTES);
begin
if Assigned(SA.lpSecurityDescriptor) then
begin
FreeMem(SA.lpSecurityDescriptor);
SA.lpSecurityDescriptor := nil;
end;
end;
function TAuScriptsService.CreatePipeInstance(out NewPipeHandle: THandle; Value: TOverlapped): Boolean;
var
fSA: TSecurityAttributes;
begin
Result := False;
try
try
InitializeSecurity(fSA);
NewPipeHandle := CreateNamedPipe(
SPipeName_AuScriptsService,
PIPE_ACCESS_DUPLEX or FILE_FLAG_OVERLAPPED,
PIPE_TYPE_MESSAGE or
PIPE_READMODE_MESSAGE or
PIPE_WAIT,
PIPE_UNLIMITED_INSTANCES,
SizeOf(TRPipeTaskEvent),
SizeOf(TRPipeTaskCommand),
NMPWAIT_USE_DEFAULT_WAIT,
@fSA);
if NewPipeHandle = INVALID_HANDLE_VALUE then
RaiseSystemError;
Result := ConnectNamedPipe(NewPipeHandle, @Value);
if not Result then
begin
case GetLastError of
ERROR_IO_PENDING:
Result := True;
ERROR_PIPE_CONNECTED:
SetEvent(Value.hEvent);
else
RaiseSystemError;
end;
end;
except
on E: Exception do
begin
if NewPipeHandle <> INVALID_HANDLE_VALUE then
begin
CloseHandle(NewPipeHandle);
NewPipeHandle := INVALID_HANDLE_VALUE;
end;
LockHandleExcept(E, Self, SErrCreatePipeInstance);
end;
end;
finally
FinalizeSecurity(fSA);
end;
end;
Клиент:
fPipe := CreateFile(SPipeName_AuScriptsService,
GENERIC_READ or GENERIC_WRITE, 0, nil, OPEN_EXISTING, 0, 0);
if fPipe = INVALID_HANDLE_VALUE then
RaiseSystemErrorFmt(SErrOpenPipe);
-
Частые причины: 1. Атрибуты безопасности (наличие, недочеты в инициализации, совместимость) 2. Имена (например, префикс Global)
-
MBo © (15.03.13 21:30) [1] Частые причины: 1. Атрибуты безопасности
я тоже подозреваю, что что-то не так в InitializeSecurity... я ее "выдрал" откуда-то, сам не помню... Не подскажете, где в ней может быть проблема?
-
-
Начиная с Windows Vista const
StringSecurityDescriptor = 'D:' + '(A;ID;GRGW;;;WD)' + 'S:' + '(ML;;;;;LW)';
type
TConvertStringSecurityDescriptorToSecurityDescriptorW = function(StringSecurityDescriptor: LPCWSTR;
StringSDRevision: DWORD; var SecurityDescriptor: Pointer;
SecurityDescriptorSize: PULONG): BOOL; stdcall;
var
SecurityDescriptor: PSECURITY_DESCRIPTOR;
SecurityAttributes: TSecurityAttributes;
FillChar(SecurityDescriptor, SizeOf(PSECURITY_DESCRIPTOR), 0);
if ConvertStringSecurityDescriptorToSecurityDescriptorW(StringSecurityDescriptor, 1, SecurityDescriptor, nil) then
begin
SecurityAttributes.nLength := SizeOf(TSecurityAttributes);
SecurityAttributes.lpSecurityDescriptor := SecurityDescriptor;
SecurityAttributes.bInheritHandle := True;
end;
-
-
MBo © Пример отсюда на семерке работает?
нет, не работает... мой вариант на основе него и был сделан. на ХР - на ура, но на 7-ке "не катит"... :-(
> p ©
спасибо, завтра попробую.
Еще вопрос: SECURITY_DESCRIPTOR + SecurityAttributes нужны только для вызова CreateNamedPipe и после вызова CreateNamedPipe их сразу же можно грохнуть из памяти (как в моем примере) или нужно это делать только по завершении канала?
-
Как-то так: function CreateFullAccessSA(var SA: TSecurityAttributes): Boolean;
type
TAceHeader = packed record
AceType: Byte;
AceFlags: Byte;
AceSize: Word;
end;
TAccessAllowedAce = packed record
Header: TAceHeader;
Mask: ACCESS_MASK;
SidStart: DWORD;
end;
const
FILE_READ_DATA = $0001; FILE_LIST_DIRECTORY = $0001; FILE_WRITE_DATA = $0002; FILE_ADD_FILE = $0002; FILE_APPEND_DATA = $0004; FILE_ADD_SUBDIRECTORY = $0004; FILE_CREATE_PIPE_INSTANCE = $0004; FILE_READ_EA = $0008; FILE_WRITE_EA = $0010; FILE_EXECUTE = $0020; FILE_TRAVERSE = $0020; FILE_DELETE_CHILD = $0040; FILE_READ_ATTRIBUTES = $0080; FILE_WRITE_ATTRIBUTES = $0100; FILE_ALL_ACCESS = (STANDARD_RIGHTS_REQUIRED or Windows.SYNCHRONIZE or $1FF);
FILE_GENERIC_READ = (STANDARD_RIGHTS_READ or FILE_READ_DATA or
FILE_READ_ATTRIBUTES or FILE_READ_EA or Windows.SYNCHRONIZE);
FILE_GENERIC_WRITE = (STANDARD_RIGHTS_WRITE or FILE_WRITE_DATA or
FILE_WRITE_ATTRIBUTES or FILE_WRITE_EA or FILE_APPEND_DATA or
Windows.SYNCHRONIZE);
FILE_GENERIC_EXECUTE = (STANDARD_RIGHTS_EXECUTE or FILE_READ_ATTRIBUTES or
FILE_EXECUTE or Windows.SYNCHRONIZE);
HEAP_ZERO_MEMORY = $00000008;
ACL_REVISION = 2;
SECURITY_WORLD_RID = $00000000;
SECURITY_WORLD_SID_AUTHORITY: TSidIdentifierAuthority = (Value: (0, 0, 0, 0,
0, 1));
var
pSD: PSecurityDescriptor;
psidEveryone: PSID;
sidAuth: TSidIdentifierAuthority;
lSDSize, lACLSize: Cardinal;
lpACL: PACL;
begin
Result := False;
pSD := nil;
psidEveryone := nil;
try
SA.nLength := SizeOf(TSecurityAttributes);
SA.lpSecurityDescriptor := nil;
SA.bInheritHandle := False;
sidAuth := SECURITY_WORLD_SID_AUTHORITY;
if not AllocateAndInitializeSid(sidAuth, 1, SECURITY_WORLD_RID, 0, 0, 0, 0,
0, 0, 0, psidEveryone) then
RaiseLastOSError;
lSDSize := SizeOf(TSecurityDescriptor);
lACLSize := GetLengthSID(psidEveryone) + SizeOf(TAccessAllowedACE) +
SizeOf(TACL);
pSD := HeapAlloc(GetProcessHeap, HEAP_ZERO_MEMORY, lSDSize + lACLSize);
if pSD = nil then
RaiseLastOSError;
if not InitializeSecurityDescriptor(pSD, SECURITY_DESCRIPTOR_REVISION) then
RaiseLastOSError;
lpACL := PACL(PRawByte(pSD) + lSDSize);
if not InitializeAcl(lpACL^, lACLSize, ACL_REVISION) then
RaiseLastOSError;
if not AddAccessAllowedAce(lpACL^, ACL_REVISION, FILE_ALL_ACCESS,
psidEveryone) then
RaiseLastOSError;
if not SetSecurityDescriptorDacl(pSD, True, lpACL, False) then
RaiseLastOSError;
SA.lpSecurityDescriptor := pSD;
Result := True;
finally
if Assigned(psidEveryone) then
FreeSID(psidEveryone);
if not Result and Assigned(pSD) then
HeapFree(GetProcessHeap, 0, pSD);
end;
end;
-
> Игорь Шевченко © (17.03.13 22:25) [7]
PRawByte - ???
-
Спасибо за помощь! Ну в принципе все заработало. Только я немного изменил инициализацию SA, "скрестив" метод, предложенный Игорем Шевченко с реализацией, которую я откопал в FWIOCompletionPipes.pas (автор: Александр (Rouse_) Багель). Остался один вопрос - что делать с памятью, выделенной под DACL? Вот что у меня получилось:
function TAuScriptsService.CreatePipeInstance(out NewPipeHandle: THandle; Value: TOverlapped): Boolean;
const
FILE_READ_DATA = $0001; FILE_WRITE_DATA = $0002; FILE_CREATE_PIPE_INSTANCE = $0004; FILE_READ_EA = $0008; FILE_WRITE_EA = $0010; FILE_READ_ATTRIBUTES = $0080; FILE_WRITE_ATTRIBUTES = $0100; FILE_GENERIC_READ = STANDARD_RIGHTS_READ or FILE_READ_DATA or FILE_READ_ATTRIBUTES or FILE_READ_EA or SYNCHRONIZE;
FILE_GENERIC_WRITE = STANDARD_RIGHTS_WRITE or FILE_WRITE_DATA or FILE_WRITE_ATTRIBUTES or FILE_WRITE_EA or SYNCHRONIZE;
PIPE_ALL_ACCESS = FILE_CREATE_PIPE_INSTANCE or FILE_GENERIC_READ or FILE_GENERIC_WRITE;
SECURITY_WORLD_SID_AUTHORITY: TSidIdentifierAuthority = (Value: (0, 0, 0, 0, 0, 1));
SECURITY_WORLD_RID = $00000000;
ACL_REVISION = 2;
var
SecurityAttributes: TSecurityAttributes;
SecurityDescriptor: TSecurityDescriptor;
SIDIdentifierAuthority: TSIDIdentifierAuthority;
SID: PSID;
DACL: PACL;
DaclSize: Cardinal;
begin
Result := False;
SID := nil;
try
SIDIdentifierAuthority := SECURITY_WORLD_SID_AUTHORITY;
if not AllocateAndInitializeSid(SIDIdentifierAuthority, 1,
SECURITY_WORLD_RID, 0, 0, 0, 0, 0, 0, 0, SID) then
RaiseSystemError;
try
DaclSize := SizeOf(TACL) + SizeOf(ACCESS_ALLOWED_ACE) + GetLengthSid(SID);
DACL := AllocMem(DaclSize);
if not InitializeAcl(DACL^, DaclSize, ACL_REVISION) then
RaiseSystemError;
if not AddAccessAllowedAce(DACL^, ACL_REVISION, PIPE_ALL_ACCESS, SID) then
RaiseSystemError;
if not InitializeSecurityDescriptor(@SecurityDescriptor, SECURITY_DESCRIPTOR_REVISION) then
RaiseSystemError;
if not SetSecurityDescriptorDacl(@SecurityDescriptor, True, DACL, False) then
RaiseSystemError;
SecurityAttributes.nLength := SizeOf(TSecurityAttributes);
SecurityAttributes.lpSecurityDescriptor := @SecurityDescriptor;
SecurityAttributes.bInheritHandle := False;
NewPipeHandle := CreateNamedPipe(
SPipeName_AuScriptsService,
PIPE_ACCESS_DUPLEX or FILE_FLAG_OVERLAPPED,
PIPE_TYPE_MESSAGE or
PIPE_READMODE_MESSAGE or
PIPE_WAIT,
PIPE_UNLIMITED_INSTANCES,
SizeOf(TRPipeTaskEvent),
SizeOf(TRPipeTaskCommand),
NMPWAIT_USE_DEFAULT_WAIT,
@SecurityAttributes);
if NewPipeHandle = INVALID_HANDLE_VALUE then
RaiseSystemError;
Result := ConnectNamedPipe(NewPipeHandle, @Value);
if not Result then
begin
case GetLastError of
ERROR_IO_PENDING:
Result := True;
ERROR_PIPE_CONNECTED:
SetEvent(Value.hEvent);
else
RaiseSystemError;
end;
end;
finally
if SID <> nil then
FreeSID(SID);
end;
except
on E: Exception do
begin
if NewPipeHandle <> INVALID_HANDLE_VALUE then
begin
CloseHandle(NewPipeHandle);
NewPipeHandle := INVALID_HANDLE_VALUE;
end;
LockHandleExcept(E, Self, SErrCreatePipeInstance);
end;
end;
end;
Этот код работает и на XP и на 7-ке. Одно но!!!!! Меня смущает
DACL := AllocMem(DaclSize);
В этом коде память из-под DACL никак не освобождается. При этом любая попытка ее освободить, даже при обработке ошибок
DACL := AllocMem(DaclSize);
try
....
except
FreeMem(DACL);
....
end;
приводит к странному результату: первое подключение к каналу не удается, зато все последющие проходят успешно (хотя исключение не генерируется). Если все оставить все как есть (т.е. не освобожать память, выделенную под DACL, все работает и память, занимаемая сервисом не растет при последующих попдыках доступа к каналу). Может все так и оставить?
-
> kotyara12 (18.03.13 14:55) [9]
Если речь идет от этом примере: http://rouse.drkb.ru/network.php#fwiocompletionpipe...то, в моем варианте ничего подправлять не нужно, он сразу готов к работе можно сказать "из коробки" :) Собственно именно этот класс и используется в большей части времени как раз для обеспечения связи между сервисом и утилитой конфигурирования. А память выделенную конечно нужно освобождать.
-
> Rouse_ © (18.03.13 16:44) [10] Attributes: SECURITY_ATTRIBUTES;
Именно об этом. К сожалению не прокатило. Я вначале так и сделал - просто тупо скопировал кусок кода, который отвечает за инициализацию Attributes: SECURITY_ATTRIBUTES. Сервис заработал, но с одной особенностью - на ХР все по прежнему работало без проблем, а на семерке - только со второй попытки. Т.е. первый раз клиент получал отказ "нет такого файла", а во 2-ой, 3-ий и т.д. раз все уже работало нормально. Причем если закомментарить FreeMem(ACL) то все работает с первого раза. Сам удивлен :-(
> А память выделенную конечно нужно освобождать.
вот и я про то же...
может я еще где накосячил :-( чем так принципиально 7-ка от ХР отличается ?
-
Попробуй без экспериментов целиком мой модуль использовать...
-
Кажется я нашел, в чем собственно проблема...
Спасибо за помощь!
-
> Rouse_ © (18.03.13 19:39) [12] > Попробуй без экспериментов целиком мой модуль использовать. > ..
Я может быть что-то не понял, но я не нашел в TFWPipeClient генерации события OnRead (FRead: TOnClientReadEvent); дело в том, что мне нужно отправить "серверу" запись -команду и в ответ клиент получает не один ответ, а некоторое множество... Одним SendData не обойдешся. Поэтому и не понимаю пока как его использовать. Поясните плиз.
-
Это безсобытийный компонент. Грубо сервер всегда висит (в прямом смысле) на чтении, а клиент шлет ему SendData на каждый из которых сразу получает ответ. Собственно это именно то поведение которое обычно реализует сервис - ждет команд извне.
-
> Rouse_ © (18.03.13 20:26) [15]
У меня это работает так: сервер висит на чтении (ждет команду), в idle обрабатывает сообщения главного потока сервиса. При подключении клиента создается новый поток и ему передается handle канала. Далее вся работа с каналом происходит в уже новом потоке. Поток читает из канала команду, компилит ее и начинает ее выполнять. В процессе выполнения он в "одностороннем порядке" отправляет клиенту результаты выполнения - данные о позициях индикаторов, тексты логов и сообщения, выводимые на экран и т.д. Поэтому двухсторонний режим обмена категорически не подходит :-(
Конечно же мне было бы проще использовать готовый модуль. Самое интересное, этот код уже проработал больше двух лет больше чем на 300 компах без проблем. До тех пор пока на 7-ку переходить не начали.
-
Тут тебе тогда обычные сокеты подошли бы :) Пайпы уже излишество...
-
Сокеты клиент использует для связи с сервером приложений, котором они все и управляются . Вот такой многосвязный клиент получился :-)
И все-таки. Я так и не понял главного. SECURITY_ATTRIBUTES нужны только при СОЗДАНИИ канала или на протяжении ВСЕГО ВРЕМЕНИ его существования? Во втором случае тогда мне нужно их сохранить в новом потоке, а уже при завершении потока удалять из памяти? Тогда все логично получается.
-
> SECURITY_ATTRIBUTES нужны только при СОЗДАНИИ канала или > на протяжении ВСЕГО ВРЕМЕНИ его существования?
Первое.
-
УРА! ВСЕ ЗАРАБОТАЛО НА 100%! Проблему с освобождением памяти из-под DACL все же удалось решить. И причина оказалась весьма необычной. Для тех, кому интересно: Во всех примерах на Delphi, что я шашел, размер DACL вычисляется следующим образом: SizeOf(ACL) + SizeOf(ACCESS_ALLOWED_ACE) + GetLengthSid(SID);
DaclSize := SizeOf(ACL) + SizeOf(ACCESS_ALLOWED_ACE) + GetLengthSid(SID);
ACL := AllocMem(DaclSize);
try
Win32Check(InitializeAcl(ACL^, DaclSize, ACL_REVISION));
...
finally
FreeMem(ACL);
end
Между тем, в Win32 Developer's Reference по функции InitializeAcl сказано: When calculating the size of an ACL, note that each ACE in an ACL gets the SID specified by its SidStart member copied to the ACE structure, starting at the ACE's SidStart member. Thus, each ACE added to the ACL requires room for the ACE plus room for its SID minus the size of the SidStart member (a DWORD). For example, the size of an ACL buffer large enough to contain a single ACCESS_ALLOWED_ACE is : cbAcl = sizeof(ACL) + sizeof(ACCESS_ALLOWED_ACE) + GetLengthSid(pSid) - sizeof(DWORD); !!!! Я попробовал сегодня код исправить в соответствии с Help-ом и ВСЕ ЗАРАБОТАЛО!!! Без глюков!!!.
DaclSize := SizeOf(ACL) + SizeOf(ACCESS_ALLOWED_ACE) + GetLengthSid(SID) - SizeOf(DWORD);
ACL := AllocMem(DaclSize);
try
Win32Check(InitializeAcl(ACL^, DaclSize, ACL_REVISION));
...
finally
FreeMem(ACL);
end
И еще мелочь... нашел сегодня в FWIOCompletionPipes:
const
SECURITY_WORLD_SID_AUTHORITY: TSidIdentifierAuthority = (Value: (0, 0, 0, 0, 0, 1));
...
var
SIA: SID_IDENTIFIER_AUTHORITY;
...
begin
Result := False;
try
SIA := SECURITY_WORLD_SID_AUTHORITY;
try
Win32Check(InitializeSid(SID, SECURITY_WORLD_SID_AUTHORITY, 1)); ...
SIA никак не используется. Может SIA нужен для чего-то, но я у себя убрал и все работает. Мелочь конечно. :-) Еще раз огромное спасибо всем откликнувшимся.
|