Visual C++ 查看文件被哪个进程占用

参考于:https://blog.csdn.net/u012108436/article/details/72688310

有别于我之前写的文章,这个方法对DLL貌似并不适用,猜测Windows上dll并不算是文件句柄,没办法被NtQuerySystemInformation识别到

之前的文章:https://www.cnblogs.com/suxia/p/13163094.html

底下是使用方法:

查看文件是否其他进程占用
METHOD:
主要使用BOOL FindFileHandle(LPCTSTR lpName, vector<ncFileHandle>& handles); 底下定义tstring类似于wstring,说实在我对这块不清楚,可能为了配合WINAPI才define这个数据类型,毕竟底下很多API和数据类型和枚举变量都被Windows藏起来了

PARAMETER:
LPCTSTR lpName:输入值,文件的完整地址
vector<ncFileHandle>& handles:输出值,存储着占用文件的进程详细信息的容器
RETURN:
成功为1,失败为0
WARNING:
函数要遍历所有文件句柄,找出与文件路径匹配的文件句柄,并通过文件句柄找到占用他的进程句柄,所以耗时很久,常规要一秒到两秒左右,谨慎使用

#include "stdafx.h"
#include <windows.h>
#include <tchar.h>
#include <shlwapi.h>
#include <stdio.h>
#include <tchar.h>
#include <atlbase.h>
#include <vector>
#include <map>
#include <iostream>
#include <string>
#include <winternl.h>
#include <psapi.h>
#pragma comment(lib, "psapi.lib")
#pragma comment(lib, "shlwapi")
using namespace std;

typedef std::basic_string<TCHAR, std::char_traits<TCHAR>, std::allocator<TCHAR>> tstring;
#ifndef _countof
#define _countof(array) (sizeof(array)/sizeof((array)[0]))
#endif 

EXTERN_C BOOL GetFilePathFromHandleW(HANDLE hFile, LPWSTR lpszPath, UINT cchMax);
EXTERN_C BOOL GetFilePathFromHandleA(HANDLE hFile, LPSTR  lpszPath, UINT cchMax);

#ifdef UNICODE
#define GetFilePathFromHandle GetFilePathFromHandleW
#else
#define GetFilePathFromHandle GetFilePathFromHandleA
#endif

#define NT_SUCCESS(status)                    (status == (NTSTATUS)0x00000000L)
#define STATUS_INFO_LENGTH_MISMATCH            ((NTSTATUS)0xC0000004L)
#define STATUS_BUFFER_OVERFLOW                ((NTSTATUS)0x80000005L)
#define SystemHandleInformation                ((SYSTEM_INFORMATION_CLASS)16)

// NTQUERYOBJECT
typedef struct _OBJECT_NAME_INFORMATION {
    UNICODE_STRING Name;
    WCHAR NameBuffer[1];
} OBJECT_NAME_INFORMATION, *POBJECT_NAME_INFORMATION;

//winternl.h中以定义,微软未放出134的定义,需自己手动定义
typedef enum _OBJECT_INFORMATION_CLASS_SELFDEFINE {
    //ObjectBasicInformation,
    ObjectNameInformation=1,
    //ObjectTypeInformation,
    ObjectAllInformation=3,
    ObjectDataInformation=4
} OBJECT_INFORMATION_CLASS_SELFDEFINE, *POBJECT_INFORMATION_CLASS_SELFDEFINE;

typedef NTSTATUS(WINAPI *NTQUERYOBJECT)(
    _In_opt_ HANDLE Handle,
    _In_ OBJECT_INFORMATION_CLASS_SELFDEFINE ObjectInformationClass,
    _Out_opt_ PVOID ObjectInformation,
    _In_ ULONG ObjectInformationLength,
    _Out_opt_ PULONG ReturnLength);

// NTQUERYSYSTEMINFORMATION
typedef struct _SYSTEM_HANDLE {
    DWORD dwProcessId;
    BYTE bObjectType;
    BYTE bFlags;
    WORD wValue;
    PVOID pAddress;
    DWORD GrantedAccess;
} SYSTEM_HANDLE, *PSYSTEM_HANDLE;

typedef struct _SYSTEM_HANDLE_INFORMATION {
    DWORD dwCount;
    SYSTEM_HANDLE Handles[1];
} SYSTEM_HANDLE_INFORMATION, *PSYSTEM_HANDLE_INFORMATION;

typedef NTSTATUS(WINAPI *NTQUERYSYSTEMINFORMATION)(
    IN SYSTEM_INFORMATION_CLASS SystemInformationClass,
    OUT PVOID SystemInformation,
    IN ULONG SystemInformationLength,
    OUT PULONG ReturnLength OPTIONAL);


//typedef struct _IO_STATUS_BLOCK {
//    LONG Status;
//    LONG Information;
//} IO_STATUS_BLOCK, *PIO_STATUS_BLOCK;

typedef struct _FILE_NAME_INFORMATION {
    ULONG FileNameLength;
    WCHAR FileName[MAX_PATH];
} FILE_NAME_INFORMATION;

__declspec(dllimport) LONG __stdcall ZwQueryInformationFile(
    IN HANDLE FileHandle,
    OUT PIO_STATUS_BLOCK IoStatusBlock,
    OUT PVOID FileInformation,
    IN ULONG FileInformationLength,
    IN ULONG FileInformationClass
    );

typedef LONG(__stdcall * PFN_ZwQueryInformationFile) (
    IN HANDLE FileHandle,
    OUT PIO_STATUS_BLOCK IoStatusBlock,
    OUT PVOID FileInformation,
    IN ULONG FileInformationLength,
    IN ULONG FileInformationClass
    );

//
// NtQueryInformationFile
//
#define FileNameInformation                    ((FILE_INFORMATION_CLASS)9)

// typedef struct _FILE_NAME_INFORMATION {
//     ULONG FileNameLength;
//     WCHAR FileName[1];
// } FILE_NAME_INFORMATION, *PFILE_NAME_INFORMATION;

typedef NTSTATUS(WINAPI *NTQUERYINFORMATIONFILE)(
    IN HANDLE FileHandle,
    OUT PIO_STATUS_BLOCK IoStatusBlock,
    OUT PVOID FileInformation,
    IN ULONG Length,
    IN FILE_INFORMATION_CLASS FileInformationClass);

// typedef struct _CLIENT_ID {
//     HANDLE UniqueProcess;
//     HANDLE UniqueThread;
// } CLIENT_ID, *PCLIENT_ID;

// ncScopedHandle
class ncScopedHandle
{
    ncScopedHandle(const ncScopedHandle&);
    ncScopedHandle& operator=(const ncScopedHandle&);
public:
    ncScopedHandle(HANDLE handle)
        : _handle(handle)
    {
    }

    ~ncScopedHandle()
    {
        if (_handle != NULL) {
            CloseHandle(_handle);
        }
    }

    operator HANDLE() const
    {
        return _handle;
    }

    PHANDLE  operator& ()
    {
        return &_handle;
    }

    void operator=(HANDLE handle)
    {
        if (_handle != NULL) {
            CloseHandle(_handle);
        }
        _handle = handle;
    }

private:
    HANDLE _handle;
};

// ncFileHandle
struct ncFileHandle
{
    SYSTEM_HANDLE    _handle;//占用文件的进程句柄详细信息
    tstring            _filePath;//文件的完整路径,tstring等同于wstring
    tstring            _path;//占用进程的程序磁盘位置

    ncFileHandle(SYSTEM_HANDLE handle, const tstring& filePath, const tstring& path)
        : _handle(handle)
        , _filePath(filePath)
        , _path(path)
    {
    }
};



//根据文件句柄获取文件所在磁盘名
EXTERN_C BOOL GetVolumeNameByHandle(HANDLE hFile, LPWSTR szVolumeName, UINT cchMax)
{
    BOOL bResult = FALSE;
    WCHAR szBuf[500] = { 0 };
    WCHAR * pIter = szBuf;
    int i = 0;
    BY_HANDLE_FILE_INFORMATION stFileInfo = { 0 };

    do
    {
        if (FALSE == GetFileInformationByHandle(hFile, &stFileInfo)) {
            break;
        }

        if (0 == GetLogicalDriveStringsW(_countof(szBuf), szBuf)) {
            break;
        }

        for (; pIter; pIter += 4)
        {
            DWORD dwVolumeSerialNumber = 0;

            if (GetVolumeInformationW(pIter, NULL, 0, &dwVolumeSerialNumber,
                NULL, NULL, NULL, 0))
            {
                if (dwVolumeSerialNumber == stFileInfo.dwVolumeSerialNumber)
                {
                    lstrcpynW(szVolumeName, pIter, cchMax);
                    bResult = TRUE;
                    break;
                }
            }
        }

    } while (FALSE);

    return bResult;
}



EXTERN_C BOOL GetFilePathFromHandleW(HANDLE hFile, LPWSTR lpszPath, UINT cchMax)
{
    BOOL bResult = FALSE;
    WCHAR szValue[MAX_PATH] = { 0 };

    IO_STATUS_BLOCK    isb = { 0 };
    FILE_NAME_INFORMATION fni = { 0 };
    HMODULE hNtDLL = NULL;
    PFN_ZwQueryInformationFile pfn_ZwQueryInformationFile = NULL;

    do
    {
        if (INVALID_HANDLE_VALUE == hFile || NULL == lpszPath || 0 == cchMax) {
            break;
        }
        hNtDLL = LoadLibraryW(L"ntdll.dll");
        if (hNtDLL == NULL)
        {
            break;
        }

        pfn_ZwQueryInformationFile = (PFN_ZwQueryInformationFile)GetProcAddress(hNtDLL, "ZwQueryInformationFile");
        if (NULL == pfn_ZwQueryInformationFile) {
            break;
        }

        // 9 == FileNameInformation
        if (0 != pfn_ZwQueryInformationFile(hFile, &isb, &fni, sizeof(fni), 9)) {
            break;
        }

        if (FALSE == GetVolumeNameByHandle(hFile, szValue, _countof(szValue))) {
            break;
        }

        PathAppendW(szValue, fni.FileName);

        lstrcpynW(lpszPath, szValue, cchMax);

        bResult = TRUE;
    } while (FALSE);
    return bResult;
}

EXTERN_C BOOL GetFilePathFromHandleA(HANDLE hFile, LPSTR  lpszPath, UINT cchMax)
{
    BOOL bResult = FALSE;
    WCHAR szTmep[MAX_PATH] = { 0 };

    do
    {
        if (INVALID_HANDLE_VALUE == hFile || NULL == lpszPath || 0 == cchMax) {
            break;
        }

        if (FALSE == GetFilePathFromHandleW(hFile, szTmep, _countof(szTmep))) {
            break;
        }

        if (0 == WideCharToMultiByte(CP_ACP, 0,
            szTmep, lstrlenW(szTmep),
            lpszPath, cchMax, NULL, NULL))
        {
            break;
        }

        bResult = TRUE;
    } while (FALSE);
    return bResult;
}







// GetDeviceDriveMap
void GetDeviceDriveMap(std::map<tstring, tstring>& mapDeviceDrive)
{
    TCHAR szDrives[512];
    if (!GetLogicalDriveStrings(_countof(szDrives) - 1, szDrives)) {
        return;
    }

    TCHAR* lpDrives = szDrives;
    TCHAR szDevice[MAX_PATH];
    TCHAR szDrive[3] = _T(" :");
    do {
        *szDrive = *lpDrives;

        if (QueryDosDevice(szDrive, szDevice, MAX_PATH)) {
            mapDeviceDrive[szDevice] = szDrive;
        }
        while (*lpDrives++);
    } while (*lpDrives);
}

// DevicePathToDrivePath
BOOL DevicePathToDrivePath(tstring& path)
{
    static std::map<tstring, tstring> mapDeviceDrive;

    if (mapDeviceDrive.empty()) {
        GetDeviceDriveMap(mapDeviceDrive);
    }

    for (std::map<tstring, tstring>::const_iterator it = mapDeviceDrive.begin(); it != mapDeviceDrive.end(); ++it) {
        size_t nLength = it->first.length();
        if (_tcsnicmp(it->first.c_str(), path.c_str(), nLength) == 0) {
            path.replace(0, nLength, it->second);
            return TRUE;
        }
    }

    return FALSE;
}

// GetHandlePath
BOOL GetHandlePath(HANDLE handle, tstring& path)
{
    static NTQUERYOBJECT fpNtQueryObject =
        (NTQUERYOBJECT)GetProcAddress(GetModuleHandle(_T("ntdll")), "NtQueryObject");

    if (fpNtQueryObject == NULL) {
        return FALSE;
    }

    DWORD dwLength = 0;
    OBJECT_NAME_INFORMATION info;
    NTSTATUS status = fpNtQueryObject(handle, ObjectNameInformation, &info, sizeof(info), &dwLength);
    if (status != STATUS_BUFFER_OVERFLOW) {
        return FALSE;
    }

    POBJECT_NAME_INFORMATION pInfo = (POBJECT_NAME_INFORMATION)malloc(dwLength);
    while (true) {
        status = fpNtQueryObject(handle, ObjectNameInformation, pInfo, dwLength, &dwLength);
        if (status != STATUS_BUFFER_OVERFLOW) {
            break;
        }
        pInfo = (POBJECT_NAME_INFORMATION)realloc(pInfo, dwLength);
    }

    BOOL bRes = FALSE;
    if (NT_SUCCESS(status)) {
        path = pInfo->Name.Buffer;
        bRes = DevicePathToDrivePath(path);
    }
    free(pInfo);
    return bRes;
}

// GetSystemHandleInfo
PSYSTEM_HANDLE_INFORMATION GetSystemHandleInfo()
{
    static NTQUERYSYSTEMINFORMATION fpNtQuerySystemInformation =
        (NTQUERYSYSTEMINFORMATION)GetProcAddress(GetModuleHandle(_T("ntdll")), "NtQuerySystemInformation");

    if (fpNtQuerySystemInformation == NULL) {
        return NULL;
    }

    DWORD dwLength = 0;
    SYSTEM_HANDLE_INFORMATION shi;
    NTSTATUS status = fpNtQuerySystemInformation(SystemHandleInformation, &shi, sizeof(shi), &dwLength);
    if (status != STATUS_INFO_LENGTH_MISMATCH) {
        return NULL;
    }

    PSYSTEM_HANDLE_INFORMATION pshi = (PSYSTEM_HANDLE_INFORMATION)malloc(dwLength);
    while (true) {
        status = fpNtQuerySystemInformation(SystemHandleInformation, pshi, dwLength, &dwLength);
        if (status != STATUS_INFO_LENGTH_MISMATCH) {
            break;
        }
        pshi = (PSYSTEM_HANDLE_INFORMATION)realloc(pshi, dwLength);
    }

    if (!NT_SUCCESS(status)) {
        free(pshi);
        pshi = NULL;
    }

    return pshi;
}

//
// 检测指定句柄是否可能导致NtQueryObject卡死:
//     1.注意必须使用NtQueryInformationFile而不是NtQueryObject进行检测,否则可能导致WinXP系统
//       下进程死锁而无法结束。
//
void CheckBlockThreadFunc(void* param)
{
    static NTQUERYINFORMATIONFILE fpNtQueryInformationFile =
        (NTQUERYINFORMATIONFILE)GetProcAddress(GetModuleHandle(_T("ntdll")), "NtQueryInformationFile");

    if (fpNtQueryInformationFile != NULL) {
        BYTE buf[1024];
        IO_STATUS_BLOCK ioStatus;
        fpNtQueryInformationFile((HANDLE)param, &ioStatus, buf, 1024, FileNameInformation);
    }
}

// IsBlockingHandle
BOOL IsBlockingHandle(HANDLE handle)
{
    HANDLE hThread = (HANDLE)_beginthread(CheckBlockThreadFunc, 0, (void*)handle);

    if (WaitForSingleObject(hThread, 100) != WAIT_TIMEOUT) {
        return FALSE;
    }

    TerminateThread(hThread, 0);
    return TRUE;
}

// FindFileHandle
BOOL FindFileHandle(LPCTSTR lpName, vector<ncFileHandle>& handles)
{
    handles.clear();

    if (lpName == NULL) {
        return FALSE;
    }

    // 打开“NUL”文件以便后续获取文件句柄类型值。
    ncScopedHandle hTempFile = CreateFile(_T("NUL"), GENERIC_READ, 0, NULL, OPEN_EXISTING, 0, 0);
    if (hTempFile == NULL) {
        return FALSE;
    }

    // 获取当前系统中所有的句柄信息。
    PSYSTEM_HANDLE_INFORMATION pshi = GetSystemHandleInfo();
    if (pshi == NULL) {
        return FALSE;
    }

    // 查询当前系统的文件句柄类型值。
    BYTE nFileType = 0;
    DWORD dwCrtPid = GetCurrentProcessId();
    for (DWORD i = 0; i < pshi->dwCount; ++i) {
        if (pshi->Handles[i].dwProcessId == dwCrtPid && hTempFile == (HANDLE)pshi->Handles[i].wValue) {
            nFileType = pshi->Handles[i].bObjectType;
            break;
        }
    }

    HANDLE hCrtProc = GetCurrentProcess();
    for (DWORD i = 0; i < pshi->dwCount; ++i) {
        // 过滤掉非文件类型的句柄。
        if (pshi->Handles[i].bObjectType != nFileType) {
            continue;
        }

        // 将上述句柄复制到当前进程中。
        ncScopedHandle handle = NULL;
        ncScopedHandle hProc = OpenProcess(PROCESS_DUP_HANDLE, FALSE, pshi->Handles[i].dwProcessId);
        if (hProc == NULL || !DuplicateHandle(hProc, (HANDLE)pshi->Handles[i].wValue, hCrtProc, &handle, 0, FALSE, DUPLICATE_SAME_ACCESS)) {
            continue;
        }

        // 过滤掉会导致NtQueryObject卡死的句柄(如管道等)。
        if (IsBlockingHandle(handle)) 
        {
            continue;
        }

        // 获取句柄对应的文件路径并进行匹配检查。
        tstring filePath;
        if (GetHandlePath(handle, filePath) && filePath.find(lpName) != tstring::npos) 
        {
            ncScopedHandle hProcess = OpenProcess(MAXIMUM_ALLOWED, FALSE, pshi->Handles[i].dwProcessId);

            TCHAR szProcName[MAX_PATH];
            GetProcessImageFileName(hProcess, szProcName, MAX_PATH);
            tstring path(szProcName);
            DevicePathToDrivePath(path);
            ncFileHandle fh(pshi->Handles[i], filePath, path);
            handles.push_back(fh);
        }
    }

    free(pshi);
    return TRUE;
}

// BOOL CloseHandleEx (HANDLE handle, DWORD dwPid)
// {
//     if (GetCurrentProcessId () == dwPid)
//         return CloseHandle (handle);
// 
//     ncScopedHandle hProcess = OpenProcess (PROCESS_DUP_HANDLE, FALSE, dwPid);
//     if (hProcess == NULL)
//         return FALSE;
// 
//     return DuplicateHandle (hProcess, handle, GetCurrentProcess (), NULL, 0, FALSE, DUPLICATE_CLOSE_SOURCE);
// }



// main
int _tmain(int argc, _TCHAR* argv[])
{
    tstring path(_T("D:\dev\YozoUCloud\Debug\x64\YozoBridge64.dll"));
    vector<ncFileHandle> vecHandles;
    if (!FindFileHandle(path.c_str(), vecHandles)) 
    {
        return -1;
    }
    else
    {
        wcout.imbue(locale("chs"));
        for (int i = 0; i < vecHandles.size(); i++)
        {
            wcout << vecHandles[i]._filePath.c_str() << "       is in using by           " << vecHandles[i]._path.c_str() << endl;;
        }
    }
    system("pause");
    return 0;
}
原文地址:https://www.cnblogs.com/suxia/p/13220337.html