开发者

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.
0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜