查看文件是否被其他进程访问

项目中写了一个穿网闸文件传输程序,定期扫描指定文件夹,并将文件传输至网闸对侧。但在使用过程中发现部分文件经常被截断传输,实际上程序中对该问题已经做了处理,Linux系统使用lsof命令可以直接插到该文件当前有没有被写入,Windows系统使用尝试重命名文件方式来确定文件有没有被占用。但实际应用中在Windwos环境安装的openssh来提供sftp server,该程序在文件传输过程中,可以随意重命名,甚至删除文件。试了下free sftp,倒是与期望的一样,会独占文件。

为了解决上述问题,有个openfiles命令可以支持,但执行太慢,影响使用。查了相关资料,Windows上没有更好的办法,原则上需要遍历所有进程的内核对象,得到这些内核对象的路径,与文件路径进行比对。

代码如下:

.h

#pragma once
#include "stdafx.h"
#include <stdio.h>
#include <tchar.h>
#include <windows.h>
#include <atlbase.h>
#include <shlwapi.h>
#include <tlhelp32.h>
#include <sys/timeb.h>
#include <strsafe.h>
#include <shellapi.h>
#include <vector>
#include <map>
#include <winternl.h>
#include <psapi.h>
#include <string>
using namespace std;


#pragma comment(lib, "psapi.lib")
#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)
#define FileNameInformation ((FILE_INFORMATION_CLASS)9)


typedef std::basic_string<TCHAR, std::char_traits<TCHAR>, std::allocator<TCHAR>> tstring;


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


typedef enum _OBJECT_INFORMATION_CLASS
{
ObjectBasicInformation,
ObjectNameInformation,
ObjectTypeInformation,
ObjectAllInformation,
ObjectDataInformation
} OBJECT_INFORMATION_CLASS, *POBJECT_INFORMATION_CLASS;


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


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 NTSTATUS (WINAPI *NTQUERYINFORMATIONFILE)(
IN HANDLE FileHandle,
OUT PIO_STATUS_BLOCK IoStatusBlock,
OUT PVOID FileInformation,
IN ULONG Length,
IN FILE_INFORMATION_CLASS FileInformationClass);


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 _path;
ncFileHandle (SYSTEM_HANDLE handle, const tstring& filePath, const tstring& path)
: _handle (handle)
, _filePath (filePath)
, _path (path)
{
}

};


.cpp

// CheckFileOccupied.cpp : Defines the entry point for the application.
//


#include "stdafx.h"
#include "CheckFileOccupied.h"


// 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] = {0};
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 (hThread == NULL)
{
return FALSE;
}
if (WaitForSingleObject(hThread, 100) != WAIT_TIMEOUT)
{
CloseHandle(hThread);
return FALSE;
}
CloseHandle(hThread);
TerminateThread (hThread, 0); //线程内申请的资源无法回收,周期调用将有内存泄漏
return TRUE; 
}


BYTE GetFileType()
{
// 打开“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;
}
}
CloseHandle(hTempFile);
hTempFile = NULL;
return nFileType;
}


// FindFileHandle
BOOL FindFileHandle (HANDLE currentProcess,DWORD watchPid,LPCTSTR lpName,BYTE fileType)
{
BOOL ret = FALSE;


if (lpName == NULL)
{
return ret;
}
PSYSTEM_HANDLE_INFORMATION pshi = GetSystemHandleInfo();
if (pshi == NULL)
{
return ret;
}
for (DWORD i = 0; i < pshi->dwCount; ++i)
{
// 过滤掉非文件类型的句柄。
if (pshi->Handles[i].bObjectType != fileType)
{
continue;
}
if (pshi->Handles[i].dwProcessId != watchPid)
{
continue;
}


ncScopedHandle handle = NULL;
ncScopedHandle hProc = OpenProcess(PROCESS_DUP_HANDLE, FALSE, watchPid);
if (hProc == NULL)
{
continue;
}
// 将上述句柄复制到当前进程中。
if(!DuplicateHandle(hProc, (HANDLE)pshi->Handles[i].wValue, currentProcess, &handle, 0, FALSE, DUPLICATE_SAME_ACCESS))
{
continue;
}
//过滤掉会导致NtQueryObject卡死的句柄(如管道等)。
if (IsBlockingHandle (handle))
{
continue;
}
// 获取句柄对应的文件路径并进行匹配检查。
tstring filePath;
if (GetHandlePath(handle, filePath) && filePath.find(lpName) != tstring::npos)
{
ret = TRUE;
break;
}
}
free(pshi);
return ret;
}


DWORD GetProcessIDByName(_TCHAR* pName)
{
HANDLE hSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
if (INVALID_HANDLE_VALUE == hSnapshot)
{
return NULL;
}
PROCESSENTRY32 pe = { sizeof(pe) };
for (BOOL ret = Process32First(hSnapshot, &pe); ret; ret = Process32Next(hSnapshot, &pe))
{
if (_tcscmp(pe.szExeFile, pName) == 0)
{
CloseHandle(hSnapshot);
return pe.th32ProcessID;
}
}
CloseHandle(hSnapshot);
return 0;
}


int APIENTRY _tWinMain(HINSTANCE hInstance,HINSTANCE hPrevInstance,LPTSTR lpCmdLine,int nCmdShow)
{
int argc = -1;
LPWSTR *argList = NULL;
argList = CommandLineToArgvW(GetCommandLine(), &argc);
if(argList == NULL || argc != 3)
{
return -1;
}

_TCHAR* watchProcess = argList[1];
_TCHAR* filePathName = argList[2];


DWORD watchPid = GetProcessIDByName(watchProcess);
HANDLE currentProcess = GetCurrentProcess();

BYTE fileType = GetFileType();


return FindFileHandle(currentProcess,watchPid,filePathName,fileType);
}



  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值