3

I slightly adapted the code from here:

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

to return the list of handle names by a given pID, I add every entry to a TStringList. The issue is the names contain garbage like "?" in filenames which contain unicode characters, for example "xxx ★ 5" comes up as "xxx ? 5"

What is wrong in the code?

Community
  • 1
  • 1
hikari
  • 3,393
  • 1
  • 33
  • 72

1 Answers1

8

Seeing a question mark for any non-ASCII character is the hallmark of an attempt to convert from Unicode to ANSI.

The problem is in this function:

function GetFileNameHandleThr(Data: Pointer): DWORD; stdcall;
var
  dwReturn: DWORD;
  FileNameInfo: FILE_NAME_INFORMATION;
  ObjectNameInfo: TOBJECT_NAME_INFORMATION;
  IoStatusBlock: IO_STATUS_BLOCK;
  pThreadParam: TGetFileNameThreadParam;
begin
  ZeroMemory(@FileNameInfo, SizeOf(FILE_NAME_INFORMATION));
  pThreadParam := PGetFileNameThreadParam(Data)^;
  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.Result := Result;
      WideCharToMultiByte(CP_ACP, 0, @ObjectNameInfo.Name.Buffer[ObjectNameInfo.Name.MaximumLength - ObjectNameInfo.Name.Length], ObjectNameInfo.Name.Length, @pThreadParam.FileName[0], MAX_PATH, nil, nil);
    end
    else
    begin
      pThreadParam.Result := STATUS_SUCCESS;
      Result := STATUS_SUCCESS;
      WideCharToMultiByte(CP_ACP, 0, @FileNameInfo.FileName[0], IoStatusBlock.Information, @pThreadParam.FileName[0], MAX_PATH, nil, nil);
    end;
  end;
  PGetFileNameThreadParam(Data)^ := pThreadParam;
  ExitThread(Result);
end;

This converts from Unicode to ANSI in the calls to WideCharToMultiByte. You simply need to remove that part of the code.

You'll need to modify this record

TGetFileNameThreadParam = packed record
  hFile    : THandle;
  Result   : NT_STATUS;
  FileName : array [0..MAX_PATH - 1] of AnsiChar;
end;

so that FileName is an array of WideChar.

TGetFileNameThreadParam = packed record
  hFile    : THandle;
  Result   : NT_STATUS;
  FileName : array [0..MAX_PATH - 1] of WideChar;
end;

And then adapt GetFileNameHandleThr accordingly. I've not studied it in detail, but I expect you need something like this:

function GetFileNameHandleThr(Data: Pointer): DWORD; stdcall;
var
  dwReturn: DWORD;
  FileNameInfo: FILE_NAME_INFORMATION;
  ObjectNameInfo: TOBJECT_NAME_INFORMATION;
  IoStatusBlock: IO_STATUS_BLOCK;
  pThreadParam: TGetFileNameThreadParam;
begin
  ZeroMemory(@FileNameInfo, SizeOf(FILE_NAME_INFORMATION));
  pThreadParam := PGetFileNameThreadParam(Data)^;
  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.Result := Result;
      Move(ObjectNameInfo.Name.Buffer[ObjectNameInfo.Name.MaximumLength - ObjectNameInfo.Name.Length], pThreadParam.FileName[0], Min(ObjectNameInfo.Name.Length, MAX_PATH)*SizeOf(WideChar));
      //WideCharToMultiByte(CP_ACP, 0, @ObjectNameInfo.Name.Buffer[ObjectNameInfo.Name.MaximumLength - ObjectNameInfo.Name.Length], ObjectNameInfo.Name.Length, @pThreadParam.FileName[0], MAX_PATH, nil, nil);
    end
    else
    begin
      pThreadParam.Result := STATUS_SUCCESS;
      Result := STATUS_SUCCESS;
      Move(FileNameInfo.FileName[0], pThreadParam.FileName[0], Min(IoStatusBlock.Information, MAX_PATH)*SizeOf(WideChar));
      //WideCharToMultiByte(CP_ACP, 0, @FileNameInfo.FileName[0], IoStatusBlock.Information, @pThreadParam.FileName[0], MAX_PATH, nil, nil);
    end;
  end;
  PGetFileNameThreadParam(Data)^ := pThreadParam;
  ExitThread(Result);
end;

I've not run this code so you may need to work on it further. However, it's obvious that the problem is down to the conversion to ANSI.

I've now run this code and it works well.

David Heffernan
  • 601,492
  • 42
  • 1,072
  • 1,490
  • Filenames are now empty with those changes. – hikari Jan 14 '13 at 13:32
  • 3
    I expect that's because you failed to make all the changes. You probably didn't change `TGetFileNameThreadParam` to use `WideChar` buffer. It's important that you don't just treat this as a black box and understand fully how the code works. – David Heffernan Jan 14 '13 at 13:41