Наши проекты:
Журнал · Discuz!ML · Wiki · DRKB · Помощь проекту |
||
ПРАВИЛА | FAQ | Помощь | Поиск | Участники | Календарь | Избранное | RSS |
[18.118.150.80] |
|
Сообщ.
#1
,
|
|
|
Когда chkdsk.exe начинает проверять диск, он закрывает на нём все открытые файлы. Можно ли самому узнать их Thandle'ы и, если надо, закрыть. Существует ли какая-нибудь функция, наподобе EnumWindows для окон.
|
Сообщ.
#2
,
|
|
|
Вот так:
//////////////////////////////////////////////////////////////////////////////// // // **************************************************************************** // * Unit Name : Unit15 // * Purpose : Перечисление всех открытых файлов в системе // * (до которых получилось достучаться) // * Author : Александр (Rouse_) Багель // * Version : 1.00 // **************************************************************************** // unit Unit15; interface uses Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs, StdCtrls, ComCtrls; type TForm15 = class(TForm) Button1: TButton; Memo1: TMemo; ProgressBar1: TProgressBar; procedure Button1Click(Sender: TObject); public procedure ShowLockedProcess(FileName: String); end; var Form15: TForm15; implementation {$R *.dfm} type NT_STATUS = Cardinal; TFileDirectoryInformation = packed record NextEntryOffset: ULONG; FileIndex: ULONG; CreationTime: LARGE_INTEGER; LastAccessTime: LARGE_INTEGER; LastWriteTime: LARGE_INTEGER; ChangeTime: LARGE_INTEGER; EndOfFile: LARGE_INTEGER; AllocationSize: LARGE_INTEGER; FileAttributes: ULONG; FileNameLength: ULONG; FileName: array[0..0] of WideChar; end; FILE_DIRECTORY_INFORMATION = TFileDirectoryInformation; PFileDirectoryInformation = ^TFileDirectoryInformation; PFILE_DIRECTORY_INFORMATION = PFileDirectoryInformation; PSYSTEM_THREADS = ^SYSTEM_THREADS; SYSTEM_THREADS = packed record KernelTime: LARGE_INTEGER; UserTime: LARGE_INTEGER; CreateTime: LARGE_INTEGER; WaitTime: ULONG; StartAddress: Pointer; UniqueProcess: DWORD; UniqueThread: DWORD; Priority: Integer; BasePriority: Integer; ContextSwitchCount: ULONG; State: Longint; WaitReason: Longint; end; PSYSTEM_PROCESS_INFORMATION = ^SYSTEM_PROCESS_INFORMATION; SYSTEM_PROCESS_INFORMATION = packed record NextOffset: ULONG; ThreadCount: ULONG; Reserved1: array [0..5] of ULONG; CreateTime: FILETIME; UserTime: FILETIME; KernelTime: FILETIME; ModuleNameLength: WORD; ModuleNameMaxLength: WORD; ModuleName: PWideChar; BasePriority: ULONG; ProcessID: ULONG; InheritedFromUniqueProcessID: ULONG; HandleCount: ULONG; Reserved2 : array[0..1] of ULONG; PeakVirtualSize : ULONG; VirtualSize : ULONG; PageFaultCount : ULONG; PeakWorkingSetSize : ULONG; WorkingSetSize : ULONG; QuotaPeakPagedPoolUsage : ULONG; QuotaPagedPoolUsage : ULONG; QuotaPeakNonPagedPoolUsage : ULONG; QuotaNonPagedPoolUsage : ULONG; PageFileUsage : ULONG; PeakPageFileUsage : ULONG; PrivatePageCount : ULONG; ReadOperationCount : LARGE_INTEGER; WriteOperationCount : LARGE_INTEGER; OtherOperationCount : LARGE_INTEGER; ReadTransferCount : LARGE_INTEGER; WriteTransferCount : LARGE_INTEGER; OtherTransferCount : LARGE_INTEGER; ThreadInfo: array [0..0] of SYSTEM_THREADS; end; 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; PFILE_NAME_INFORMATION = ^FILE_NAME_INFORMATION; FILE_NAME_INFORMATION = packed record FileNameLength: ULONG; FileName: array [0..MAX_PATH - 1] of WideChar; end; PUNICODE_STRING = ^TUNICODE_STRING; TUNICODE_STRING = packed record Length : WORD; MaximumLength : WORD; Buffer : array [0..MAX_PATH - 1] of WideChar; end; POBJECT_NAME_INFORMATION = ^TOBJECT_NAME_INFORMATION; TOBJECT_NAME_INFORMATION = packed record Name : TUNICODE_STRING; end; PIO_STATUS_BLOCK = ^IO_STATUS_BLOCK; IO_STATUS_BLOCK = packed record Status: NT_STATUS; Information: DWORD; end; PGetFileNameThreadParam = ^TGetFileNameThreadParam; TGetFileNameThreadParam = packed record hFile: THandle; Data: array [0..MAX_PATH - 1] of Char; Status: NT_STATUS; end; const STATUS_SUCCESS = NT_STATUS($00000000); STATUS_INVALID_INFO_CLASS = NT_STATUS($C0000003); STATUS_INFO_LENGTH_MISMATCH = NT_STATUS($C0000004); STATUS_INVALID_DEVICE_REQUEST = NT_STATUS($C0000010); ObjectNameInformation = 1; FileDirectoryInformation = 1; FileNameInformation = 9; SystemProcessesAndThreadsInformation = 5; SystemHandleInformation = 16; function ZwQuerySystemInformation(ASystemInformationClass: DWORD; ASystemInformation: Pointer; ASystemInformationLength: DWORD; AReturnLength: PDWORD): NT_STATUS; stdcall; external 'ntdll.dll'; function NtQueryInformationFile(FileHandle: THandle; IoStatusBlock: PIO_STATUS_BLOCK; FileInformation: Pointer; Length: DWORD; FileInformationClass: DWORD): NT_STATUS; stdcall; external 'ntdll.dll'; function NtQueryObject(ObjectHandle: THandle; ObjectInformationClass: DWORD; ObjectInformation: Pointer; ObjectInformationLength: ULONG; ReturnLength: PDWORD): NT_STATUS; stdcall; external 'ntdll.dll'; function GetLongPathNameA(lpszShortPath, lpszLongPath: PChar; cchBuffer: DWORD): DWORD; stdcall; external kernel32; procedure TForm15.Button1Click(Sender: TObject); begin ShowLockedProcess(''); end; procedure TForm15.ShowLockedProcess(FileName: String); function GetInfoTable(ATableType: DWORD): Pointer; var dwSize: DWORD; pPtr: Pointer; ntStatus: NT_STATUS; begin Result := nil; dwSize := WORD(-1); GetMem(pPtr, dwSize); ntStatus := ZwQuerySystemInformation(ATableType, pPtr, dwSize, nil); while ntStatus = STATUS_INFO_LENGTH_MISMATCH do begin dwSize := dwSize * 2; ReallocMem(pPtr, dwSize); ntStatus := ZwQuerySystemInformation(ATableType, pPtr, dwSize, nil); end; if ntStatus = STATUS_SUCCESS then Result := pPtr else FreeMem(pPtr); end; function GetFileNameThread(lpParameters: Pointer): DWORD; stdcall; var FileNameInfo: FILE_NAME_INFORMATION; ObjectNameInfo: TOBJECT_NAME_INFORMATION; IoStatusBlock: IO_STATUS_BLOCK; pThreadParam: TGetFileNameThreadParam; dwReturn: DWORD; begin ZeroMemory(@FileNameInfo, SizeOf(FILE_NAME_INFORMATION)); pThreadParam := PGetFileNameThreadParam(lpParameters)^; Result := NtQueryInformationFile(pThreadParam.hFile, @IoStatusBlock, @FileNameInfo, MAX_PATH * 2, FileNameInformation); if Result = STATUS_SUCCESS then begin Result := NtQueryObject(pThreadParam.hFile, ObjectNameInformation, @ObjectNameInfo, MAX_PATH * 2, @dwReturn); if Result = STATUS_SUCCESS then begin pThreadParam.Status := Result; WideCharToMultiByte(CP_ACP, 0, @ObjectNameInfo.Name.Buffer[ObjectNameInfo.Name.MaximumLength - ObjectNameInfo.Name.Length], ObjectNameInfo.Name.Length, @pThreadParam.Data[0], MAX_PATH, nil, nil); end else begin pThreadParam.Status := STATUS_SUCCESS; Result := STATUS_SUCCESS; WideCharToMultiByte(CP_ACP, 0, @FileNameInfo.FileName[0], IoStatusBlock.Information, @pThreadParam.Data[0], MAX_PATH, nil, nil); end; end; PGetFileNameThreadParam(lpParameters)^ := pThreadParam; ExitThread(Result); end; function GetFileNameFromHandle(hFile: THandle): String; var lpExitCode: DWORD; pThreadParam: TGetFileNameThreadParam; hThread: THandle; begin Result := ''; ZeroMemory(@pThreadParam, SizeOf(TGetFileNameThreadParam)); pThreadParam.hFile := hFile; hThread := CreateThread(nil, 0, @GetFileNameThread, @pThreadParam, 0, PDWORD(nil)^); if hThread <> 0 then try case WaitForSingleObject(hThread, 100) of WAIT_OBJECT_0: begin GetExitCodeThread(hThread, lpExitCode); if lpExitCode = STATUS_SUCCESS then Result := pThreadParam.Data; end; WAIT_TIMEOUT: TerminateThread(hThread, 0); end; finally CloseHandle(hThread); end; end; function SetDebugPriv: Boolean; var Token: THandle; tkp: TTokenPrivileges; begin Result := false; if OpenProcessToken(GetCurrentProcess, TOKEN_ADJUST_PRIVILEGES or TOKEN_QUERY, Token) then begin if LookupPrivilegeValue(nil, PChar('SeDebugPrivilege'), tkp.Privileges[0].Luid) then begin tkp.PrivilegeCount := 1; tkp.Privileges[0].Attributes := SE_PRIVILEGE_ENABLED; Result := AdjustTokenPrivileges(Token, False, tkp, 0, PTokenPrivileges(nil)^, PDWord(nil)^); end; end; end; type DriveQueryData = record DiskLabel: String; DiskDosQuery: String; DosQueryLen: Integer; end; var hFile, hProcess: THandle; pHandleInfo: PSYSTEM_HANDLE_INFORMATION_EX; I, Drive: Integer; ObjectTypeNumber: Byte; FileDirectory, FilePath, ProcessName: String; SystemInformation, TempSI: PSYSTEM_PROCESS_INFORMATION; DosDevices: array [0..25] of DriveQueryData; LongFileName, TmpFileName: String; begin SetLength(LongFileName, MAX_PATH); GetLongPathNameA(PChar(FileName), @LongFileName[1], MAX_PATH); for Drive := 0 to 25 do begin DosDevices[Drive].DiskLabel := Chr(Drive + Ord('a')) + ':'; SetLength(DosDevices[Drive].DiskDosQuery, MAXCHAR); ZeroMemory(@DosDevices[Drive].DiskDosQuery[1], MAXCHAR); QueryDosDevice(PChar(DosDevices[Drive].DiskLabel), @DosDevices[Drive].DiskDosQuery[1], MAXCHAR); DosDevices[Drive].DosQueryLen := Length(PChar(DosDevices[Drive].DiskDosQuery)); SetLength(DosDevices[Drive].DiskDosQuery, DosDevices[Drive].DosQueryLen); end; ObjectTypeNumber := 0; SetDebugPriv; hFile := CreateFile('NUL', GENERIC_READ, 0, nil, OPEN_EXISTING, 0, 0); if hFile = INVALID_HANDLE_VALUE then RaiseLastOSError; try pHandleInfo := GetInfoTable(SystemHandleInformation); if pHandleInfo = nil then RaiseLastOSError; try for I := 0 to pHandleInfo^.NumberOfHandles - 1 do if pHandleInfo^.Information[I].Handle = hFile then if pHandleInfo^.Information[I].ProcessId = GetCurrentProcessId then begin ObjectTypeNumber := pHandleInfo^.Information[I].ObjectTypeNumber; Break; end; finally FreeMem(pHandleInfo); end; finally CloseHandle(hFile); end; SystemInformation := GetInfoTable(SystemProcessesAndThreadsInformation); if SystemInformation <> nil then try pHandleInfo := GetInfoTable(SystemHandleInformation); if pHandleInfo <> nil then try ProgressBar1.Position := 0; ProgressBar1.Max := pHandleInfo^.NumberOfHandles; for I := 0 to pHandleInfo^.NumberOfHandles - 1 do begin if pHandleInfo^.Information[I].ObjectTypeNumber = ObjectTypeNumber then begin hProcess := OpenProcess(PROCESS_DUP_HANDLE, True, pHandleInfo^.Information[I].ProcessId); if hProcess > 0 then try if DuplicateHandle(hProcess, pHandleInfo^.Information[I].Handle, GetCurrentProcess, @hFile, 0, False, DUPLICATE_SAME_ACCESS) then try if Application.Terminated then Exit; FilePath := GetFileNameFromHandle(hFile); if FilePath <> '' then begin FileDirectory := ''; for Drive := 0 to 25 do if DosDevices[Drive].DosQueryLen > 0 then if Copy(FilePath, 1, DosDevices[Drive].DosQueryLen) = DosDevices[Drive].DiskDosQuery then begin FileDirectory := DosDevices[Drive].DiskLabel; Delete(FilePath, 1, DosDevices[Drive].DosQueryLen); Break; end; if FileDirectory = '' then Continue; TempSI := SystemInformation; repeat if TempSI^.ProcessID = pHandleInfo^.Information[I].ProcessId then begin ProcessName := TempSI^.ModuleName; Break; end; TempSI := Pointer(DWORD(TempSI) + TempSI^.NextOffset); until TempSI^.NextOffset = 0; SetLength(TmpFileName, MAX_PATH); GetLongPathNameA(PChar(FileDirectory + FilePath), @TmpFileName[1], MAX_PATH); Memo1.Lines.Add(ProcessName + ': ' + TmpFileName); end; finally CloseHandle(hFile); end; finally CloseHandle(hProcess); end; end; ProgressBar1.Position := ProgressBar1.Position + 1; Application.ProcessMessages; end; finally FreeMem(pHandleInfo); end; finally FreeMem(SystemInformation); end; ShowMessage('All handles found.'); end; end. Целиком проект в аттаче... Прикреплённый файлEnumOpenFiles.zip (223.41 Кбайт, скачиваний: 370) |
Сообщ.
#3
,
|
|
|
Наконец-то с помощья дебаггера и IDAPro я разобрался как работает "chkdsk.exe"! По началу думал, что он знает все эти дескрипторы, а потом их закрывает. Но оказалось гораздо жухе. Вот структура: chkdsk.exe => vfat.dll => ifsutil.dll => ntdll.dll.
Вот, что делается до того как написать "Том отключен. ВCE ОТКРЫТЫЕ ДЕСКРИПТОРЫ ТОМА СТАЛИ НЕВЕРНЫ." (перевёл на Delphi, упрощённо): type WSTRING = record l0, l1 : Word; Str : PWideChar; end; PWSTRING = ^WSTRING; OBJECT_ATTRIBUTES = record Length: ULONG; RootDirectory: THandle; ObjectName: PWSTRING; Attributes: ULONG; SecurityDescriptor: PSECURITY_DESCRIPTOR; SecurityQualityOfService: Pointer; ///PSECURITY_QUALITY_OF_SERVICE; end; POBJECT_ATTRIBUTES = ^OBJECT_ATTRIBUTES; IO_STATUS_BLOCK = record Status: Integer; ///NTSTATUS; Pointer: Pointer; ///PVOID; Information: ^ULONG; end; PIO_STATUS_BLOCK = ^IO_STATUS_BLOCK; Function NtOpenFile( FileHandle: PHANDLE; DesiredAccess: ACCESS_MASK; ObjectAttributes: POBJECT_ATTRIBUTES; IoStatusBlock: PIO_STATUS_BLOCK; // out ShareAccess: ULONG; OpenOptions: ULONG): Integer; stdcall; external 'ntdll.dll' name 'NtOpenFile'; Function NtFsControlFile( FileHandle: THandle; Event: THandle; ApcRoutine: Pointer; ///PIO_APC_ROUTINE; ApcContext: Pointer; IoStatusBlock: PIO_STATUS_BLOCK; // out FsControlCode: ULONG; InputBuffer: Pointer; InputBufferLength: ULONG; OutputBuffer: Pointer; // out OutputBufferLength: ULONG): Integer; stdcall; external 'ntdll.dll' name 'NtFsControlFile'; Function NtClose(FileHandle: THandle): Integer; stdcall; external 'ntdll.dll' name 'NtClose'; // Закрывает дескрипторы устройства, возвращает успешность операции. // пример вызова для диска "E": DismountVolume('\??\E:') Function DismountVolume(DriveStr: PWideChar): Boolean; Var f : HFile; OA : OBJECT_ATTRIBUTES; SB : IO_STATUS_BLOCK; sX : WSTRING; Begin sX.l0:=Length(DriveStr)*2; sX.l1:=sX.l0+2; sX.Str:=DriveStr; OA.Length:=sizeof(OBJECT_ATTRIBUTES); OA.RootDirectory:=0; OA.ObjectName:=@sX; OA.Attributes:=$40; OA.SecurityDescriptor:=nil; OA.SecurityQualityOfService:=nil; if( NtOpenFile(@f, $100003, @OA, @SB, 3, $10) <> 0 )then Begin Result:=False; Exit; end; Result:=(NtFsControlFile(f, 0,nil,nil, @SB, $00090020,nil,0,nil,0)=0); NtClose(f); End; Для меня - это бесполезный код. Но может кому-то понадобится (например, для удаления неудаляющихся файлов). С диском 'C:' почему-то не работает. |
Сообщ.
#4
,
|
|
|
Цитата NtFsControlFile(f, 0,nil,nil, @SB, $00090020,nil,0,nil,0)=0); Лучше писать так: NtFsControlFile(f, 0,nil,nil, @SB, FSCTL_DISMOUNT_VOLUME, nil,0,nil,0)=0); Потом в будуших версиях Винды будите долго думать почему не работает...если вдруг IOCTL код изменится... |
Сообщ.
#5
,
|
|
|
Ну если он измениться, то всеравно, как минимум, придется пересобирать исходник
|
Сообщ.
#6
,
|
|
|
Вы правы! Но гораздо приятней и информативней читать FSCTL_DISMOUNT_VOLUME, нежели гадать что такое 0x00090020 !
|
Сообщ.
#7
,
|
|
|
Абсолютно согласен
|
Сообщ.
#8
,
|
|
|
Все ведет к тому:
Любая дисковая утилита должна перед тем как записать на носитель она должна заблокировать носитель для записи Но OC также закрывает доступ! Хотя досуп с ядра всегда есть в обоих случаях |
Сообщ.
#9
,
|
|
|
Цитата Arazel @ Все ведет к тому: Любая дисковая утилита должна перед тем как записать на носитель она должна заблокировать носитель для записи Но OC также закрывает доступ! Хотя досуп с ядра всегда есть в обоих случаях Что сказал? |
Сообщ.
#10
,
|
|
|
На сколько я понимаю, 'NtFsControlFile' - недокументированная функция Windows. Где-то можно скачать описание таких функций в .pas или .h? Может кто знает.
|
Сообщ.
#11
,
|
|
|
В гугле поискать не пробовал?
http://www.google.ru/search?q=NtFsControlFile&start=0&ie=utf-8&oe=utf-8&client=firefox-a&rls=org.mozilla:ru:official |