0

IMPORTANT: we use DELPHI 7.

I have a problem to get the ProcessId that is using a file.

Just to let you know, in our software we need to show to the user what Process is using the file that we need.

To do that, I used NtQuerySystemInformation to get the Process list and Handle list.

After that, I used OpenProcess on those Process Ids with Handles that are files (ObjectType = 28)

And then I used DuplicateHandle to get the File Handle.

Let's see the code:

function GetProcessIdUsingFile(const TargetFileName:string): DWORD;
var
 hProcess    : THandle;
 hFile       : THandle;
 SystemInformationLength : DWORD;
 Index       : Integer;
 pHandleInfo : PSYSTEM_HANDLE_INFORMATION;
 hQuery      : THandle;
 FileName    : String;
 ReturnInformation : LongBool;
 FileInfo : _BY_HANDLE_FILE_INFORMATION;

 //Novo
 cbRet : DWORD;
 cbSize : DWORD;
begin
  Result:= 0;
  cbSize := $5000;
  GetMem(pHandleInfo, cbSize);
  repeat
    cbSize := cbSize * 2;
    ReallocMem(pHandleInfo, cbSize);
    hQuery := NtQuerySystemInformation(SystemHandleInformation, pHandleInfo, cbSize, @cbRet);
  until hQuery <> STATUS_INFO_LENGTH_MISMATCH;

  try
    if(hQuery = STATUS_SUCCESS) then
    begin
      for Index:=0 to pHandleInfo^.uCount-1 do
        if pHandleInfo^.Handles[Index].ObjectType=28 then
          begin
            hProcess := OpenProcess(PROCESS_DUP_HANDLE, FALSE, pHandleInfo^.Handles[Index].uIdProcess);
            if(hProcess <> INVALID_HANDLE_VALUE) then
            begin
              try
                if not DuplicateHandle(hProcess, pHandleInfo^.Handles[Index].Handle,GetCurrentProcess(), @hFile,  0 ,FALSE, DUPLICATE_SAME_ACCESS) then
                   hFile := INVALID_HANDLE_VALUE;

                if (hFile<>INVALID_HANDLE_VALUE) then
                  begin
                    try
                      FileName := GetFileNameByHandle(hFile);
                    finally
                      CloseHandle(hFile);
                    end;
                  end
                else
                   FileName := '';
              finally
                CloseHandle(hProcess);
              end;

               if CompareText(ExtractFileName(FileName), TargetFileName)=0 then
                  Result := pHandleInfo^.Handles[Index].uIdProcess;
            end;
          end;
    end;
  finally
   if pHandleInfo<>nil then
     FreeMem(pHandleInfo);
  end;
end;

So, I created GetFileNameByHandle

This function receives a File Handle from the previous code and tries to get its File Name using NtQueryInformationFile and NtQueryObject.

But, every call of NtQueryInformationFile returns STATUS_OBJECT_TYPE_MISMATCH. How can I get its File Name?

Let me show you the code of this function:

function GetFileNameByHandle(hFile: THandle): String;
var
  dwReturn: DWORD;
  FileNameInfoRecord: FILE_NAME_INFORMATION;
  ObjectNameInfo: TOBJECT_NAME_INFORMATION;
  IoStatusBlock: IO_STATUS_BLOCK;
  ntSTATUS : DWORD;
  FileName : array [0..MAX_PATH - 1] of WCHAR;
begin
  FillChar(FileName, SizeOf(FileName), 0);
  FillChar(FileNameInfoRecord.FileName, SizeOf(FileNameInfoRecord.FileName), 0);
  FileNameInfoRecord.FileNameLength := 0;
  ntSTATUS := NtQueryInformationFile(hFile, @IoStatusBlock,  @FileNameInfoRecord, SizeOf(FILE_NAME_INFORMATION) * 2 + 1, FileNameInformation);
  if ntSTATUS = STATUS_SUCCESS then
    begin
       // NEVER ENTER HERE... WHY?
       ntSTATUS := NtQueryObject(hFile, ObjectNameInformation,  @ObjectNameInfo, MAX_PATH * 2, @dwReturn);
       if ntSTATUS = STATUS_SUCCESS then
       begin
         WideCharToMultiByte(CP_ACP, 0, @ObjectNameInfo.Name.Buffer[ObjectNameInfo.Name.MaximumLength - ObjectNameInfo.Name.Length], ObjectNameInfo.Name.Length, @FileName[0], MAX_PATH, nil, nil);
       end
       else
       begin
         ntSTATUS := STATUS_SUCCESS;
         WideCharToMultiByte(CP_ACP, 0, @FileNameInfoRecord.FileName[1], IoStatusBlock.Information, @FileName[0], MAX_PATH, nil, nil);
       end;
     end;
  Result := WideCharToString(@FileName);
end;

After that, I compare Filenames in GetProcessIdUsingFile function to return the PID.

I used this code as a base, and tried some modifications, but nothing work:

Delphi - finding the process that is accessing a file from my program

If you guys need more information, please LMK.

marc_s
  • 732,580
  • 175
  • 1,330
  • 1,459
  • 1
    To get a file name from a handle, you can use [`GetFileInformationByHandleEx()`](https://learn.microsoft.com/en-us/windows/win32/api/winbase/nf-winbase-getfileinformationbyhandleex) instead of `NtQueryInformationFile`+`NtQueryObject`. – Remy Lebeau Aug 07 '23 at 23:51
  • @RemyLebeau I tried to use `GetFileInformationByHandleEx` but it always return False too. – Michael Marcel Aug 08 '23 at 11:30
  • @RemyLebeau And `GetLastError` returns `ERROR_INVALID_HANDLE` after I use `GetFileInformationByHandleEx` instead of my own routine `GetFileNameByHandle` – Michael Marcel Aug 08 '23 at 11:42
  • 1
    Both `NtQueryInformationFile` and `GetFileInformationByHandleEx` are telling you that you have an invalid file handle. You need to debug your code to figure out why. – Remy Lebeau Aug 08 '23 at 14:16
  • 1
    I can't seem to find a declaration for `FILE_NAME_INFORMATION` in delphi's files. If your declaration is exactly like it is defined by microsoft, `SizeOf(FILE_NAME_INFORMATION)` will return 6. So you'd be telling `NtQueryInformationFile` you have a 13 bytes buffer (which wouldn't actually be true) and be at risk (a very likely one in that case) of buffer overflow. Even if you have a different declaration for `FILE_NAME_INFORMATION`, there is no declaration where `SizeOf(FILE_NAME_INFORMATION) * 2 + 1` doesn't put you at risk of buffer overflow, unless you declared it way bigger than needed... – Ken Bourassa Aug 08 '23 at 21:03
  • 1
    But all that might just be a red herring anyway if GetFileInformationByHandleEx returns you ERROR_INVALID_HANDLE anyway. – Ken Bourassa Aug 08 '23 at 21:04
  • @KenBourassa Yeah... It's the main problem, why I'm getting an invalid handle of the file... D: – Michael Marcel Aug 10 '23 at 19:23

0 Answers0