博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
查看文件是否被其他进程访问
阅读量:4137 次
发布时间:2019-05-25

本文共 9268 字,大约阅读时间需要 30 分钟。

项目中写了一个穿网闸文件传输程序,定期扫描指定文件夹,并将文件传输至网闸对侧。但在使用过程中发现部分文件经常被截断传输,实际上程序中对该问题已经做了处理,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);
}

转载地址:http://fpqvi.baihongyu.com/

你可能感兴趣的文章
为什么你的共享内存key为0且无法删除?
查看>>
玩转消息队列之C/C++代码
查看>>
linux中的ethtool命令
查看>>
迟延分段与分片: Segmentation and Checksum Offloading: Turning Off with ethtool (好文)
查看>>
一同事出现了http 404错误
查看>>
一同事出现了http 500错误
查看>>
最近遇到了http 400错误
查看>>
又是权限的问题啊啊啊!------远程定位一个与我不相关的问题
查看>>
udp引发的一起血案------message too long
查看>>
require_once失败------又是软链接惹的祸
查看>>
while (1) 引发的血案
查看>>
IP分片在哪里重组? 为什么?
查看>>
那一年(2012年), 我第一听说三次握手------快找工作了, 居然没有听说过tcp三次握手!
查看>>
UDP迭代服务器示例和UDP流量控制的缺失
查看>>
syn flood攻击和syn cookie预防简介------cat /proc/sys/net/ipv4/tcp_syncookies
查看>>
很少有人能说清楚listen函数的blacklog的含义, 那就让linux来说说吧!------笔试考过
查看>>
在linux下玩转带有超时时间的connect函数------某次面试遇到过
查看>>
csdn服务器给我返回http 500, 呵呵哒!------后来才知道, 每天最多写5篇, 我晕啊, 能给个友好点的提示不?
查看>>
当对端IP可达但没有监听对应的端口时候, 对端会发出RST报文
查看>>
当接收端的内核缓冲区中的数据没有全部交给应用程序时, 如果接收端关闭socket(比如调用close或者进程挂掉),就会回以RST报文
查看>>