Имя процесса по HANDLE возвращаемому CreateFile

Возможно ли не перебирая хэндлы процессов узнать имя процесса блочащего некоторый файл?


Ответы (2 шт):

Автор решения: greg zakharov

В простейшем случае можно поступить примерно так:

#ifndef UNICODE
  #define UNICODE
#endif

#ifndef WIN32_LEAN_AND_MEAN
   #define WIN32_LEAN_AND_MEAN
#endif

#include <windows.h>
#include <iostream>
#include <vector>

#pragma comment (lib, "ntdll.lib")

#ifdef __cplusplus
extern "C" {

typedef LONG NTSTATUS;

#define STATUS_INFO_LENGTH_MISMATCH (0xC0000004L)
#define NT_SUCCESS(Status) (((NTSTATUS)(Status)) >= 0L)

typedef struct _IO_STATUS_BLOCK {
   union {
      NTSTATUS Status;
      PVOID    Pointer;
   };
   ULONG_PTR Information;
} IO_STATUS_BLOCK, *PIO_STATUS_BLOCK;

typedef enum _FILE_INFORMATION_CLASS {
   FileProcessIdsUsingFileInformation = 47
} FILE_INFORMATION_CLASS, *PFILE_INFORMATION_CLASS;

typedef struct _FILE_PROCESS_IDS_USING_INFORMATION {
   ULONG NumberOfProcessIdsInList;
   ULONG_PTR ProcessIdsList[1];
} FILE_PROCESS_IDS_USING_INFORMATION, *PFILE_PROCESS_IDS_USING_INFORMATION;

NTSYSCALLAPI
NTSTATUS
NTAPI
NtQueryInformationFile(
   _In_ HANDLE FileHandle,
   _Out_ PIO_STATUS_BLOCK IoStatusBlock,
   _Out_writes_bytes_(FileInformationLength) PVOID FileInformation,
   _In_ ULONG FileInformationLength,
   _In_ FILE_INFORMATION_CLASS FileInformationClass
);

NTSYSAPI
ULONG
NTAPI
RtlNtStatusToDosError(
   _In_ NTSTATUS Status
);

}
#endif

int wmain(void) {
  // допустим, файл C:\sandbox\input.txt открыт Far Manager или чем-то ещё
  HANDLE h = ::CreateFile(L"С:\\sandbox\\input.txt", 0, 0, nullptr, OPEN_EXISTING, 0, nullptr);
  if (INVALID_HANDLE_VALUE != h) {
    ULONG sz{};
    IO_STATUS_BLOCK isb{};
    NTSTATUS nts = STATUS_INFO_LENGTH_MISMATCH;
    std::vector<BYTE> buf(sizeof(FILE_PROCESS_IDS_USING_INFORMATION));

    do { // сперва нужно получить истинный размер буфера, иногда он более выделенного нами ранее
      sz = static_cast<ULONG>(buf.size());
      nts = ::NtQueryInformationFile(h, &isb, &buf[0], sz,  FileProcessIdsUsingFileInformation);
      if (STATUS_INFO_LENGTH_MISMATCH != nts) break; // статус изменился, не меняем размер буфера
      buf.resize(buf.size() * 2);
    } while (STATUS_INFO_LENGTH_MISMATCH == nts);

    if (!NT_SUCCESS(nts)) {
      std::wcout << L"[!] Err: 0x" << std::hex << ::RtlNtStatusToDosError(nts) << std::endl;
    }
    else {
      auto pids = reinterpret_cast<PFILE_PROCESS_IDS_USING_INFORMATION>(&buf[0]);
      for (ULONG i = 0; i < pids->NumberOfProcessIdsInList; i++) {
        std::wcout << L"ID: " << pids->ProcessIdsList[i];

        if ((i + 1) != pids->NumberOfProcessIdsInList)
          std::wcout << L", ";
      }
      std::wcout << std::endl;
    }
    std::vector<BYTE> ().swap(buf); // принудительно освобождаем вектор

    if (!::CloseHandle(h))
      std::wcout << L"[!] Err: 0x" << std::hex << ::GetLastError() << std::endl;
  }
  return 0;
}
→ Ссылка
Автор решения: MSDN.WhiteKnight

Начать с того, что если файл заблокирован в буквальном смысле, т.е. открыт с параметром FileShare=0, то вы и HANDLE получить не сможете.

Ну а по имени файла список процессов, которые его открыли, можно получить с помощью RmGetList:

#include <stdio.h>
#include <Windows.h>
#include <restartmanager.h>
#pragma comment(lib, "Rstrtmgr.lib")

void PrintProcesses(HANDLE hFile) {
    DWORD hSess = 0;
    WCHAR buffer[1000];
    WCHAR key[50] = L"MySession";
    DWORD res;

    res=GetFinalPathNameByHandleW(hFile, buffer, 1000, 0);
    
    if (res == 0) {
        wprintf(L"GetFinalPathNameByHandleW failed\n");
        return;
    }

    res = RmStartSession(&hSess, 0, key);

    if (res != 0) {
        wprintf(L"Cannot start session\n");
        return;
    }

    LPCWSTR filenames[] = { buffer };
    res = RmRegisterResources(hSess, 1, filenames, 0, NULL, 0, NULL);

    if (res != 0) {
        wprintf(L"Cannot register resource\n");
        return;
    }

    UINT procInfoNeeded;
    UINT procInfo = 10;
    DWORD rebootReasons;
    RM_PROCESS_INFO info[10];
    res = RmGetList(hSess, &procInfoNeeded, &procInfo, info, &rebootReasons);

    if (res != 0) {
        wprintf(L"Cannot get list of processes\n");
        return;
    }

    for (int i = 0; i < procInfo; i++)
        wprintf(L"%u: %s\n", info[i].Process.dwProcessId, info[i].strAppName);
}
→ Ссылка