本文共 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;};// ncFileHandlestruct 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"// GetDeviceDriveMapvoid 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);}// DevicePathToDrivePathBOOL 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;}// GetHandlePathBOOL 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;}// GetSystemHandleInfoPSYSTEM_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); }}// IsBlockingHandleBOOL 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;}// FindFileHandleBOOL 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/