Версия для печати темы
Нажмите сюда для просмотра этой темы в оригинальном формате
Форум программистов > Delphi: WinAPI и системное программирование > Как определить что файл заблокирован LockFile


Автор: denism 16.4.2006, 15:09
Кто с этим имел дело или примерно представляете куда
копать  - подскажите?

Суть следующая: 
файл находится на NT-образном сервере, открыт все время для 
чтения клиентами с разных рабочих станций (win9x, NT-образные),
перед чтением клиенты лочат нужный блок файла ф-цией LoсkFile,
после чтения - разлочивают. Бывают ситуации когда разлочивания
не происходит, в рез-те серверное приложение не может получить
доступ ко всему файлу. Что нужно делать чтобы узнать что файл 
открыт и держится другим процессом и как его отцепить - 
я представляю (начинать с юзания ф-ций NTQuery...).

Вопрос: как узнать что файл залочен ф-цией LockFile и как его
насильно разлочить?

P.S. Сам факт залочивания Computer Management  отображает в
\System Tools\Open Files\ колонка "Locks".

 

Автор: _hunter 17.4.2006, 11:18
а если UnlockFile вызвать? 

Автор: denism 17.4.2006, 13:04
Клиент, который залочил файл - не может этого
сделать, т.к. висит.
На серверном приложении это эффекта на даст,
т.к. оно не может при помощи этой ф-ции
снять чужую блокировку.

Возможно я не корректно сформулировал вопрос, перефразирую:
То что файл залочен кем-то видно по коду возврата из ф-ции LockFile,
как узнать какой процесс его залочил? 

Автор: Snowy 17.4.2006, 13:44
А зачем вообще лочить файл для чтения?
Лочить нужно только для записи.
А читать можно спокойно всем вместе. Главное на чтение открывать с флагом ShareDenyNone. 

Автор: denism 18.4.2006, 10:21
Потому что клиенты под 98-й. когда читают без блокировки 
в определенных ситуациях вместо того чтобы ждать когда блокировка 
по записи снимется возвращают блоки данных забитые нулями.
И не узнать действительно ли эта область файла была забита
нулями или нет. 

Автор: Nickel 18.4.2006, 12:42
Блокировка точно при помощи LockFile? Если да, то щас подумаю что можно сделать... 

Автор: Nickel 18.4.2006, 18:52
Придумал:
Код

type
PSYSTEM_HANDLE_INFORMATION = ^SYSTEM_HANDLE_INFORMATION;
SYSTEM_HANDLE_INFORMATION = packed record
   ProcessId: dword;
   ObjectTypeNumber: byte;
   Flags: byte;
   Handle: word;
   pObject: pointer;
   GrantedAccess: dword;
end;

PSYSTEM_HANDLE_INFORMATION_EX = ^SYSTEM_HANDLE_INFORMATION_EX;
SYSTEM_HANDLE_INFORMATION_EX = packed record
   NumberOfHandles: dword;
   Information: array [0..0] of SYSTEM_HANDLE_INFORMATION;
end;

PUnicodeString = ^TUnicodeString;
  TUnicodeString = packed record
    Length: Word;
    MaximumLength: Word;
    Buffer: PWideChar;
end;

const OB_TYPE_FILE             =  28;
      SystemHandleInformation  =    16;

function ZwQueryObject(ObjectHandle: THandle;
  ObjectInformationClass: integer; ObjectInformation:Pointer;
  Length: ULONG; ReturnLength: PULONG): cardinal; stdcall;
  external 'ntdll.dll';

Function ZwQuerySystemInformation(ASystemInformationClass: dword;
                                  ASystemInformation: Pointer;
                                  ASystemInformationLength: dword;
                                  AReturnLength:PCardinal): cardinal;
                                  stdcall;external 'ntdll.dll';


Function GetInfoTable(ATableType:dword):Pointer;
var
 mSize: dword;
 mPtr: pointer;
 St: cardinal;
begin
 Result := nil;
 mSize := $4000;
 repeat
   mPtr := VirtualAlloc(nil, mSize, MEM_COMMIT or MEM_RESERVE, PAGE_READWRITE);
   if mPtr = nil then Exit;
   St := ZwQuerySystemInformation(ATableType, mPtr, mSize, nil);
   if St = cardinal($C0000004) then
      begin
        VirtualFree(mPtr, 0, MEM_RELEASE);
        mSize := mSize * 2;
      end;
 until St <> cardinal($C0000004);
 if St = 0
   then Result := mPtr
   else VirtualFree(mPtr, 0, MEM_RELEASE);
end;


function Unlock(FileName:string):boolean;
var inf:PSYSTEM_HANDLE_INFORMATION_EX;
    i:integer;
    process,h:cardinal;
    p:pointer;
    buf:array[0..max_path] of char;
    s:string;
begin
result:=false;
p:=GetMemory(max_path);
ZeroMemory(p,max_path);
QueryDosDevice(PChar(ExtractFileDrive('c:\1.txt')),@buf,SizeOf(buf));
s:=FileName;
Delete(s,1,2);
s:=buf+s;
inf:=GetInfoTable(SystemHandleInformation);
for i:=0 to inf.NumberOfHandles-1 do begin
if inf.Information[i].ObjectTypeNumber=OB_TYPE_FILE then begin
process:=OpenProcess(PROCESS_DUP_HANDLE,false,inf.Information[i].ProcessId);
DuplicateHandle(process,inf.Information[i].Handle,getcurrentprocess,
                                  @h,0,false,DUPLICATE_SAME_ACCESS);
ZwQueryObject(h,1,p,max_path,nil);
if s=WideCharToString(TUnicodeString(p^).Buffer) then
  begin
   CloseHandle(h);
   if DuplicateHandle(process,inf.Information[i].Handle,GetCurrentProcess,
                    @h,0,false,DUPLICATE_CLOSE_SOURCE) then result:=true;
  end;
ZeroMemory(p,max_path);
CloseHandle(h);
CloseHandle(process);
end;
end;
end;

Вроде неплохо получилось - надо будет оформить ввиде статейки... Видимо таким же образом действует програмка Unlocker. Код конечно не супер, надо кое-где проверку на ошибки добавить, да и процесс у которого мы закрыли хендл может потом себя не корректно повести. 
ЗЫ извините за второе наподряд сообщение... 

Автор: denism 19.4.2006, 02:31
Спасибо, завтра потестирую код (сегодня - на рыбалку еду).
Программа Unlocker, действительно хорошая программа,
она закрывает файлы, открытые сетевыми клиентами,
другие программы, которые я нашел в Инете с исходным кодом - этого не 
могли делать, т.к. работали через CreateRemoteHandle,
и вызывать эту ф-цию в данном случае приходилось для процесса
System, что, как я понимаю запрещено. 

Автор: denism 21.4.2006, 05:52
Спасибо, работает, только на некоторых процессах на некоторых файлах 
программа зависает в ф-ции QueryObject.
Видимо нужно поразбираться с правами доступа в поле GrantedAccess. 

Автор: denism 26.4.2006, 14:56
С зависаниями разобрался, GrantedAccess не при чем, зависает на pipe, которые 
открыты в блокирующем режиме и находятся в ждущем режиме.
Но вопрос так и остался открытым, прямого отношения к блокировкам через ф-цию LockFile
приведенный код отношения не имеет. С файлами, открытыми и заблокированными локальными
процессами - все ОК, а вот с файлами открытыми клиентами по сети - блокировка остается, 
хотя хэндл, принадлежащий процессу System (т.к. по сети), закрывается. Следовательно
по прежнему не можем его нормально читать.

И еще интересный момент: ф-ция CopyFile благополучно копирует залоченный
файл со всем его содержимым, чихая на блокировку части данных. 
Может кто знает как она это делает? 

Автор: Nickel 28.4.2006, 20:58
Цитата(denism @ 26.4.2006,  14:56)
Но вопрос так и остался открытым, прямого отношения к блокировкам через ф-цию LockFile приведенный код отношения не имеет.

Очень даже имеет: мы закрываем хендл файла, который залочен, (надо сказать довольно необычным способом через DuplicateHandle)  и блокировка пропадает вмсете с хендлом. Кстати ты под какой виндой пробуешь? У меня на XP sp2 system не открывается с PROCESS_DUP_HANDLE а когда беру привелегии отладчика ловлю нехилый подвисон моей проги. Мб всё-таки проблема что хендл не закрывается?
Цитата(denism @ 26.4.2006,  14:56)
И еще интересный момент: ф-ция CopyFile благополучно копирует залоченный файл со всем его содержимым, чихая на блокировку части данных. 
Может кто знает как она это делает? 
 
LockFile блокирует часть файла только на запись. 

Автор: denism 2.5.2006, 12:37
У меня система WinXP SP2, system открывается с PROCESS_DUP_HANDLE 
с привилегиями отладчика, никаких зависаний с этим нет.
Хэндл у залоченного файла успешно закрывается, 
это показывает и прога Unlocker, и монитор 
procexplorers от Sysinternals, но когда серверное приложение 
пытается залочить файл для записи возвращается ошибка о том
что другой процесс залочил файл. Даже в фаре нажимаем F3,
чтобы поглядеть содержимое файла - он показывает пустое окно.

И еще - ф-ция NTQueryObject при попытке получения имени файла
взвисает на pipe'ах так что даже если ее поместить в поток, то поток
потом не убивается, вследствие чего не убивается процесс ничем,
кроме перезагрузки системы. Ф-ция же NTQueryInformationFile,
взвисает на pipe'ах не так смертельно, поток можно убить, но 
имя файла она возвращает без имени диска...  А то что я юзаю
ф-ции NT... вместо Zw... может иметь какое-то значение? 

Автор: Rouse_ 29.5.2006, 21:55
Цитата(denism @  2.5.2006,  13:37 Найти цитируемый пост)
 еще - ф-ция NTQueryObject при попытке получения имени файла
взвисает на pipe'ах так что даже если ее поместить в поток, то поток
потом не убивается, вследствие чего не убивается процесс ничем,
кроме перезагрузки системы

Пайп, это объект ядра и он не принадлежит процессу. Зависание происзходит на синхронных Трубах, которые, имея приоритет выше, чем твое приложение, отпустят поток только после завершения своей операции... 

Powered by Invision Power Board (http://www.invisionboard.com)
© Invision Power Services (http://www.invisionpower.com)