Close all open files handles with C#
I have an integration/regression test suite I built on top of SpecFlow (which uses NUnit underneath). The problem I am having is that sometimes there is an exception in a test and a file might remain open. 开发者_如何学PythonThis is a problem in follow on tests because they cannot read/write to this file.
Is there a way to detect what files a process has open and then close them all?
You should put your files in using
blocks so that they are closed even if an exception is thrown.
You should close the handles by either disposing them(best done with the using
clause) or waiting for the finalizer. Finalizers probably won't work well for you since they might not run before the next test. So disposing them with either try
...finally
or using
is the way to go.
While you can enumerate handles and close them, you should not. Since then the handle might be closed twice, which will cause undefined behavior and crashes.
Here is some Delphi sample code enumerating all handles of a process which you can filter down to file handles only:
program HandleEnum;
{$APPTYPE CONSOLE}
uses
windows,
SysUtils;
function EnablePrivilege(const Priv:String):boolean;
var
rl: Cardinal;
hToken: Cardinal;
tkp: TOKEN_PRIVILEGES;
p:^token_privileges;
begin
p:=nil;
result:=true;
if Win32Platform <> VER_PLATFORM_WIN32_NT then exit;
result:=false;
if not OpenProcessToken(GetCurrentProcess, TOKEN_ADJUST_PRIVILEGES or TOKEN_QUERY, hToken)
then exit;
if not LookupPrivilegeValue(nil, Pchar(Priv), tkp.Privileges[0].Luid)
then exit;
tkp.Privileges[0].Attributes := SE_PRIVILEGE_ENABLED;
tkp.PrivilegeCount := 1;
AdjustTokenPrivileges(hToken, False, tkp, 0, p^, rl);
if GetLastError <> ERROR_SUCCESS then exit;
result:=true;
end;
const SystemHandleInformation = 16;
STATUS_INFO_LENGTH_MISMATCH = 4;
htProcess = 5;
Type _SYSTEM_HANDLE_ENTRY=packed record
OwnerPid:Cardinal;
ObjectType:Byte;
HandleFlags:Byte;
HandleValue:word;
ObjectPointer:Pointer;
AccessMask:Cardinal;
end;
Type _SYSTEM_HANDLE_INFORMATION=packed record
Count:Cardinal;
Data:array[0..0]of _SYSTEM_HANDLE_ENTRY;
end;
function GetProcessId(Process:Cardinal):Cardinal;stdcall;external 'Kernel32.dll';
function NtQuerySystemInformation(
SystemInformationClass:Cardinal;
SystemInformation:Pointer;
SystemInformationLength:Cardinal;
ReturnLength:PCardinal
):Cardinal;stdcall;external 'ntdll.dll';
function GetModuleFileNameExA(
hProcess:Cardinal;
hModule:Cardinal;
lpFilename:PChar;
nSize:Cardinal
):Cardinal;stdcall;external 'Psapi.dll';
function EnumProcessModules(
hProcess:Cardinal;
lphModule:PCardinal;
cb:Cardinal;
lpcbNeeded:PCardinal
):BOOL;stdcall;external 'Psapi.dll';
procedure Enum(ProcID:Cardinal=0);
var RetLength:Cardinal;
Status:Cardinal;
Data:^_SYSTEM_HANDLE_INFORMATION;
i:integer;
hProcess,hDuplicate,PID,hModule,Temp:Cardinal;
s:String;
begin
EnablePrivilege('SeDebugPrivilege');
Data:=nil;
try
RetLength:=0;
GetMem(Data,sizeof(_SYSTEM_HANDLE_INFORMATION));
Status:=NtQuerySystemInformation(SystemHandleInformation, Data, sizeof(_SYSTEM_HANDLE_INFORMATION), @RetLength);
writeln(Status);
FreeMem(Data);
Data:=nil;
GetMem(Data,RetLength);
Status:=NtQuerySystemInformation(SystemHandleInformation, Data, RetLength, @RetLength);
if Status<>0 then Raise Exception.create('Handle enumeration: NtQuerySystemInformation returned '+inttostr(Status));
writeln('Offset ProcID, HandleID, HandleFlags, Mask, HandleType');
for i := 0 to Data.Count-1 do
begin
if data.data[i].OwnerPid<=4 then continue;//System
SetLastError(0);
if Data.Data[i].ObjectType<>htProcess then continue;
hProcess := OpenProcess( PROCESS_ALL_ACCESS, TRUE, Data.Data[i].OwnerPid );
//writeln(inttostr(getlasterror));
DuplicateHandle( hProcess, Data.Data[i].HandleValue, GetCurrentProcess(), @hDuplicate, 0, FALSE, DUPLICATE_SAME_ACCESS );
//writeln(inttostr(getlasterror));
PID:=GetProcessId(hDuplicate);
//writeln(inttostr(getlasterror));
setlength(s,1024);
//writeln(inttostr(getlasterror));
fillchar(S[1],length(S),0);
EnumProcessModules( hProcess, @hModule,sizeof(hModule),@Temp);
//writeln('B'+inttostr(getlasterror));
SetLastError(0);
setlength(S,GetModuleFileNameExA(hProcess,hModule,PChar(S),length(S)));
//writeln(status);
//writeln('A'+inttostr(getlasterror));
closehandle(hDuplicate);
//writeln(inttostr(getlasterror));
closehandle(hProcess);
if (PID<>ProcID)and(ProcID<>0) then continue;
//writeln(inttostr(getlasterror));
write(Data.Data[i].OwnerPid:10);
write(Data.Data[i].HandleValue:10);
write(Data.Data[i].HandleFlags:10);
write(Data.Data[i].AccessMask:10);
write(Data.Data[i].ObjectType:10);
write(PID:10);
writeln(S);
end;
finally
FreeMem(Data);
end;
end;
begin
Enum(9924);
end.
精彩评论