开发一个windows监控服务
来源:互联网 发布:网络社交软件盈利 编辑:程序博客网 时间:2024/06/03 20:11
1 PE和哈希值 1
1.1PE 1
1.2 哈希值、md5、sha1、crc 2
2 遍历文件夹 3
3 匹配pe文件代码 7
4 技巧 13
5 c++托管类 13
6 监控 13
1 PE和哈希值
1.1PE
PE(Portable Executable)格式,是微软Win32环境可移植可执行文件(如exe、dll、vxd、sys和vdm等)的标准文件格式。PE格式衍生于早期建立在VAX(R)VMS(R)上的COFF(Common Object File Format)文件格式。
那么如何判断一个文件是否为PE格式的文件?
1、首先检验文件头部第一个字的值是否等于 IMAGE_DOS_SIGNATURE,是则 DOS MZ header 有效。
2、一旦证明文件的 DOS header 有效后,就可用e_lfanew来定位 PE header 了。
3、比较 PE header 的第一个字的值是否等于IMAGE_NT_HEADER。如果前后两个值都匹配,那我们就认为该文件是一个有效的PE文件。
上面的是百度的结果
翻译通俗点
1、查找MZ头是否为0X4D5A
2、如果上面条件符合 则用e_lfanew指针定位pe头,e_lfanew一般位于0X3C
3、如果上面条件符合 则判断pe头是否为0x4550
都符合 则是有效PE文件
1.2 哈希值、md5、sha1、crc
哈希值就是文件的身份证,不过比身份证还严格。他是根据文件大小,时间,类型,创作着,机器等计算出来的,很容易就会发生变化,谁也不能预料下一个号码是多少,也没有更改他的软件。
经常有人问,说CRC、MD5、SHA1都是计算一个校验值的,到底有何区别
相同点:
CRC、MD5、SHA1都是通过对数据进行计算,来生成一个校验值,该校验值用来校验数据的完整性。
不同点:
1. 算法不同。CRC采用多项式除法,MD5和SHA1使用的是替换、轮转等方法;
2. 校验值的长度不同。CRC校验位的长度跟其多项式有关系,一般为16位或32位;MD5是16个字节(128位);SHA1是20个字节(160位);
3. 校验值的称呼不同。CRC一般叫做CRC值;MD5和SHA1一般叫做哈希值(Hash)或散列值;
4. 安全性不同。这里的安全性是指检错的能力,即数据的错误能通过校验位检测出来。CRC的安全性跟多项式有很大关系,相对于MD5和SHA1要弱很多;MD5的安全性很高,不过大概在04年的时候被山东大学的王小云破解了;SHA1的安全性最高。
5. 效率不同,CRC的计算效率很高;MD5和SHA1比较慢。
6. 用途不同。CRC一般用作通信数据的校验;MD5和SHA1用于安全(Security)领域,比如文件校验、数字签名等。
以下是我从其他地方找的总结:
MD5可靠性
首先是不可逆
其次,这个码具有高度的离散性,也就是说,原信息的一点点变化就会导致MD5的巨大变化,
最后由于这个码有128位那么长,所以任意信息之间具有相同MD5码的可能性非常之低,通常被认为是不可能的。
crc比较短,md5比较长
所以md5相对来说冲突的可能性要小很多
如果要求不高,是防范传输误码之类的用crc就可以了,crc效率要高很多
如果要防范人为恶意破坏,需要用md5,慢就慢点,图个可靠性加强
2 遍历文件夹
#include "stdafx.h"#include <stdlib.h>#include <iostream>#include <io.h>#include <windows.h> #include <string>#include <vector>#include <iomanip>#define MY_NOT_CHECK 1;#define MY_CHECKED 0;using namespace std;int main(){ string path("c:\\Windows\\system32"), exd("exe"); vector<WIN32_FIND_DATA> files; void getFiles(string path, string exd, vector<WIN32_FIND_DATA>& files); getFiles(path, exd, files); sort(files); show(files);}/*遍历文件夹,得到所有exe文件*/void getFiles(string path, string exd, vector<WIN32_FIND_DATA>& files){ /************************************************************************/ /* 获取文件夹下所有文件名 输入: path : 文件夹路径 exd : 所要获取的文件名后缀,如jpg、png等; 文件名, exd = "" 输出: files : 获取的文件名列表 */ /************************************************************************/ BOOL SetPrivilege(HANDLE hToken, LPCTSTR lpszPrivilege, BOOL bEnablePrivilege); //文件句柄 HANDLE hFile = INVALID_HANDLE_VALUE,hExe = INVALID_HANDLE_VALUE; //文件信息 WIN32_FIND_DATA fileinfo,exeinfo; string pathName, exdName = "\\*"; PVOID OldValue = NULL; // 关闭系统重定向 if (Wow64DisableWow64FsRedirection(&OldValue)) { // 查找指定路径 hFile = FindFirstFile(pathName.assign(path).append(exdName).c_str(), &fileinfo); if (FALSE == Wow64RevertWow64FsRedirection(OldValue)) { return; } } // 查找指定路径 // hFile = FindFirstFile(pathName.assign(path).append(exdName).c_str(), &fileinfo); // 是否查找失败 if (hFile == INVALID_HANDLE_VALUE) { // 是否因为权限不足,若不足,则提升权限 if (GetLastError() == 5) { HANDLE hToken; BOOL bRet = OpenProcessToken( GetCurrentProcess(), // 进程句柄(当前进程) TOKEN_ALL_ACCESS, // 全权访问令牌 &hToken // 返回的参数 进程令牌句柄 (就是AdjustTokenPrivileges的第一个参数) ); // 获取进程的令牌句柄 if (bRet != TRUE) { cout << "获取令牌句柄失败!" << endl; return; } BOOL set = SetPrivilege(hToken, SE_DEBUG_NAME, TRUE); if (!set || GetLastError() != ERROR_SUCCESS) { // 设置权限失败 cout << "提升权限失败 error:" << GetLastError() << endl; cout << "此文件夹缺少权限访问: " << pathName.assign(path).append("\\").c_str() << endl; return; } // 权限设置成功,继续执行 hFile = FindFirstFile(pathName.assign(path).append(exdName).c_str(), &fileinfo); cout << "权限设置完成" << endl; cout << GetLastError()<<endl; } else { // 不是权限问题 cout << "FindFirstFile failed " << GetLastError() << endl; system("pause"); return; } } int flag = MY_NOT_CHECK; int lastError = 0; // 遍历 do { //如果是文件夹,迭代之 if ((fileinfo.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)) { if (strcmp(fileinfo.cFileName, ".") != 0 && strcmp(fileinfo.cFileName, "..") != 0) getFiles(pathName.assign(path).append("\\").append(fileinfo.cFileName), exd, files); } else { //如果不是文件夹 /* if (strcmp(fileinfo.cFileName, ".") != 0 && strcmp(fileinfo.cFileName, "..") != 0) { for (int i = 0; fileinfo.cFileName[i + 3] != '\0'; i++) { // 判断是否exe if (fileinfo.cFileName[i] == '.' && (fileinfo.cFileName[i + 1] == 'e' || fileinfo.cFileName[i + 1] == 'E') && (fileinfo.cFileName[i + 2] == 'x' || fileinfo.cFileName[i + 2] == 'X') && (fileinfo.cFileName[i + 1] == 'e' || fileinfo.cFileName[i + 1] == 'E') && fileinfo.cFileName[i + 4] == '\0') { files.push_back(fileinfo); break; } } } */ // 如果当前目录还未查找过,查找当前目录的exe文件 if (flag) { // 关闭系统重定向 if (Wow64DisableWow64FsRedirection(&OldValue)) { // 查找指定路径 hExe = FindFirstFile(pathName.assign(path).append("\\*." + exd).c_str(), &exeinfo); if (FALSE == Wow64RevertWow64FsRedirection(OldValue)) { return; } } // hExe = FindFirstFile(pathName.assign(path).append("\\*.exe").c_str(), &exeinfo); if (hExe == INVALID_HANDLE_VALUE) { lastError = GetLastError(); if (lastError == 2) { //cout << setiosflags(ios::left) << setw(50) << path << " 此目录下没有exe " << endl; } else { cout << " 查找exe失败 " << lastError << endl; return; } } // 遍历所有本文件夹下的exe文件 if (lastError != 2) { do { files.push_back(exeinfo); } while (FindNextFile(hExe, &exeinfo)); // 查找完成,此目录已不用遍历,跳出 flag = MY_CHECKED; FindClose(hExe); } } } } while (FindNextFile(hFile, &fileinfo)); FindClose(hFile); return;}
3 匹配pe文件代码
#include <stdio.h> #include "assert.h" #include <windows.h> #include "TCHAR.H" #ifdef UNICODE #define IsPEFile IsPEFileW #define IsDigiSig IsDigiSigW #else #define IsPEFile IsPEFileA #define IsDigiSig IsDigiSigA #endif // !UNICODE //获得DOS头 LPVOID GetDosHeader(LPVOID lpFile) { assert(lpFile != NULL); PIMAGE_DOS_HEADER pDosHeader = NULL; if (lpFile != NULL) pDosHeader = (PIMAGE_DOS_HEADER)lpFile; return (LPVOID)pDosHeader; } //获得NT头 LPVOID GetNtHeader(LPVOID lpFile,BOOL& bX64) { assert(lpFile != NULL); bX64 = FALSE; PIMAGE_NT_HEADERS32 pNtHeader32 = NULL; PIMAGE_NT_HEADERS64 pHeaders64 = NULL; PIMAGE_DOS_HEADER pDosHeader = NULL; if (lpFile != NULL) pDosHeader = (PIMAGE_DOS_HEADER)GetDosHeader(lpFile); //判断是否合法 if (pDosHeader->e_magic !=IMAGE_DOS_SIGNATURE) return NULL; pNtHeader32 = (PIMAGE_NT_HEADERS32)((DWORD)pDosHeader + pDosHeader->e_lfanew); //判断是不是正常的PE文件 if (pNtHeader32->Signature != IMAGE_NT_SIGNATURE) return NULL; if (pNtHeader32->FileHeader.Machine==IMAGE_FILE_MACHINE_AMD64) //64bit { bX64 = TRUE; pHeaders64 = (PIMAGE_NT_HEADERS64)((DWORD)pDosHeader + pDosHeader->e_lfanew); return pHeaders64; } return pNtHeader32; } //获得可选头 LPVOID GetOptionHeader(LPVOID lpFile,BOOL& bX64) { assert(lpFile != NULL); bX64 = FALSE; LPVOID pOptionHeader = NULL; BOOL bX64Nt = FALSE; LPVOID pNtHeader = (LPVOID)GetNtHeader(lpFile,bX64Nt); if (pNtHeader == NULL) return NULL; if (bX64Nt) //64bit { bX64 = TRUE; pOptionHeader = (LPVOID) PIMAGE_OPTIONAL_HEADER64((DWORD)pNtHeader +sizeof( IMAGE_FILE_HEADER )+ sizeof(DWORD)) ; }else { pOptionHeader = (LPVOID) PIMAGE_OPTIONAL_HEADER32((DWORD)pNtHeader + sizeof( IMAGE_FILE_HEADER )+ sizeof(DWORD)); } return pOptionHeader; } /* * 获取字段 */ BOOL IsDigiSigEX(HANDLE hFile) { if (hFile == INVALID_HANDLE_VALUE) //文件对象 return FALSE; HANDLE hFileMapping = CreateFileMapping(hFile,NULL,PAGE_READONLY,0, 0, NULL); if (hFileMapping == NULL) { CloseHandle(hFile); return FALSE; } LPVOID lpFile = MapViewOfFile(hFileMapping,FILE_MAP_READ,0, 0, 0); if (lpFile==NULL) //文件视图对象 { CloseHandle(hFileMapping); CloseHandle(hFile); return FALSE; } IMAGE_DATA_DIRECTORY secData = { 0 }; LPVOID pOptionHeader = NULL; BOOL bX64Opheader = FALSE; pOptionHeader = (LPVOID)GetOptionHeader( lpFile,bX64Opheader ); if(pOptionHeader != NULL && bX64Opheader) { secData = ((PIMAGE_OPTIONAL_HEADER64)pOptionHeader)->DataDirectory[IMAGE_DIRECTORY_ENTRY_SECURITY]; } else if(pOptionHeader != NULL) { secData = ((PIMAGE_OPTIONAL_HEADER32)pOptionHeader)->DataDirectory[IMAGE_DIRECTORY_ENTRY_SECURITY]; } UnmapViewOfFile(lpFile); CloseHandle(hFileMapping); CloseHandle(hFile); if ( ( secData.VirtualAddress != 0 ) && ( secData.Size != 0 ) ) return TRUE; return FALSE; } //A版函数 BOOL IsDigiSigA(LPCSTR pPath) { HANDLE hFile = CreateFileA(pPath,GENERIC_READ,FILE_SHARE_READ, NULL,OPEN_EXISTING,FILE_FLAG_SEQUENTIAL_SCAN,NULL); return IsDigiSigEX(hFile); } //W版函数 BOOL IsDigiSigW(LPCWSTR pPath) { HANDLE hFile = CreateFileW(pPath,GENERIC_READ,FILE_SHARE_READ, NULL,OPEN_EXISTING,FILE_FLAG_SEQUENTIAL_SCAN,NULL); return IsDigiSigEX(hFile); } //实际判断PE文件操作 BOOL IsPEFileEX(HANDLE hFile,BOOL &bIsSucceed) { if (hFile == INVALID_HANDLE_VALUE) //文件对象 return FALSE; HANDLE hFileMapping = CreateFileMapping(hFile,NULL,PAGE_READONLY,0, 0, NULL); if (hFileMapping == NULL) { CloseHandle(hFile); return FALSE; } LPVOID lpFile = MapViewOfFile(hFileMapping,FILE_MAP_READ,0, 0, 0); if (lpFile==NULL) //文件视图对象 { CloseHandle(hFileMapping); CloseHandle(hFile); return FALSE; } PIMAGE_DOS_HEADER pDosHeader=NULL; PIMAGE_NT_HEADERS32 pNtHeader32 = NULL; //取得Dos头部 pDosHeader = (PIMAGE_DOS_HEADER)lpFile; if (pDosHeader->e_magic != IMAGE_DOS_SIGNATURE) { UnmapViewOfFile(lpFile); CloseHandle(hFileMapping); CloseHandle(hFile); return TRUE; } //获取NT头 pNtHeader32 = (PIMAGE_NT_HEADERS32)((DWORD)pDosHeader + pDosHeader->e_lfanew); //判断是不是PE文件 if (pNtHeader32->Signature != IMAGE_NT_SIGNATURE) { UnmapViewOfFile(lpFile); CloseHandle(hFileMapping); CloseHandle(hFile); return TRUE; } UnmapViewOfFile(lpFile); CloseHandle(hFileMapping); CloseHandle(hFile); bIsSucceed = TRUE; return TRUE; } /* * 函数执行成功返回true,失败返回false,A版 * 第二个参数为true表示是PE文件,false表示非PE文件 */ BOOL IsPEFileA(LPCSTR pPath,BOOL &bIsSucceed) { bIsSucceed = FALSE; HANDLE hFile = CreateFileA(pPath,GENERIC_READ,FILE_SHARE_READ, NULL,OPEN_EXISTING,FILE_FLAG_SEQUENTIAL_SCAN,NULL); return IsPEFileEX(hFile,bIsSucceed); } //W版 BOOL IsPEFileW(LPCWSTR pPath,BOOL &bIsSucceed) { bIsSucceed = FALSE; HANDLE hFile = CreateFileW(pPath,GENERIC_READ,FILE_SHARE_READ, NULL,OPEN_EXISTING,FILE_FLAG_SEQUENTIAL_SCAN,NULL); return IsPEFileEX(hFile,bIsSucceed); } int main() { char *test = "D:\\1.txt"; //WCHAR *test = _T("D:\\tes.exe"); BOOL bIsSucceed = FALSE; printf("%d\n",IsPEFile(test,bIsSucceed)); //实现的第一个函数 printf("%d\n",bIsSucceed); printf("%d",IsDigiSig(test)); //实现的第二个函数,参数文件句柄 getchar(); return 0; }
4 技巧
4.1 System命名空间:项目-属性-常规-公共语言运行库支持;
4.2 运行环境:Debug-最后一个(properties)-general-Common Language Runtime Support–选择 Common Language Runtime Support(/clr),(注意不要选择为 Common Language Runtime Support, Old Syntax (/clr:oldSyntax),这是旧语法,两个有好多不同);
5 c++托管类
https://blogs.msdn.microsoft.com/oliverlu/2004/12/28/ccli1/
6 监控
FindFirstChangeNotification
FileSystemWatcher
ReadDirectoryChangesW(文件大会漏掉,)
客户端监控被监控目录(含所有子目录)下的变化,我们可以采用ReadDirectoryChangesW 函数 ,该函数实现对指定的目录进行监控,并且返回详细的文件变化信息。
函数原型:
BOOL WINAPI ReadDirectoryChangesW(
__in HANDLE hDirectory;
__out LPVOID lpBuffer;
__in DWORD nBufferLength;
__in BOOL bWatchSubtree; // 监视目录. 一般选择 TRUE
__in DWORD dwNotifyFilter; // 对文件过滤的方式和标准
__out_opt LPDWORD lpBytesReturned;// 将接收的字节数转入lpBuffer参数
__inout_opt LPOVERLAPPED lpOverlapped; // 一般选择 NULL
__in_opt LPOVERLAPPED_COMPLETION_ROUTINE lpCompletionRoutine;
// 一般选择 NULL
);
1.首先该函数使用CreateFile打开目录,打开目录的时候一定要包含FILE_LIST_DIRECTORY参数,它规定了一种必需的访问权限,并返回目录的句柄。
2.lpBuffer,这个缓冲区定义的是FILE_NOTIFY_INFORMATION结构,它存储了文件或目录变化的数据。
3.dwNotifyFilter,对监控文件变化方式的过滤,可以采用以下的一种或几种的组合:
a) FILE_NOTIFY_CHANGE_FILE_NAME:任何文件名改变,都会查看所在目录或子目录的变更,并将结果通知给等待操作返回。
b) FILE_NOTIFY_CHANGE_DIR_NAME:任何目录名称改变 都会查看所在目录或子目录的变更,并将结果通知给等待操作返回。
c) FILE_NOTIFY_CHANGE_ATTRIBUTES:任何属性变化,都会查看所在目录或子目录的变更,并将结果通知给等待操作返回。
d) FILE_NOTIFY_CHANGE_SIZE:任何文件大小的变化,都会查看所在目录或子目录的变更,并将结果通知给等待操作返回。
e) FILE_NOTIFY_CHANGE_LAST_WRITE: 任何改变过去修改时间的文件 ,都会查看所在目录或子目录的变更,并将结果通知给等待操作返回。
f) FILE_NOTIFY_CHANGE_LAST_ACCESS:任何改变文件最近访问时间,都会查看所在目录或子目录的变更,并将结果通知给等待操作返回。
g) FILE_NOTIFY_CHANGE_CREATION:任何改变文件的创建时间的,都会查看所在目录或子目录的变更,并将结果通知给等待操作返回。
h) FILE_NOTIFY_CHANGE_SECURITY: 任何安全描述符被改变的,都会查看所在目录或子目录的变更,并将结果通知给等待操作返回。
完全满足我们要求监控的条目。
4.如果函数失败,返回值是零,否则是非零:
ReadDirectoryChangesW 返回类型:
Value Meaning
FILE_ACTION_ADDED
0x00000001 The file was added to the directory.
FILE_ACTION_REMOVED
0x00000002 The file was removed from the directory.
FILE_ACTION_MODIFIED
0x00000003 The file was modified. This can be a change in the time stamp or attributes.
FILE_ACTION_RENAMED_OLD_NAME
0x00000004 The file was renamed and this is the old name.
FILE_ACTION_RENAMED_NEW_NAME
0x00000005 The file was renamed and this is the new name.
根据他不同的返回值,与服务器端进行通信做出不同的同步操作。
5. 该函数具体的做法是:首先使用CreateFile获取要监控目录的句柄;然后在一个判断循环里面调用ReadDirectoryChangesW,并且把自己分配的用来存放目录变化通知的内存首地址、内存长度、目录句柄传给该函数。用户代码在该函数的调用中进行同步等待。当目录中有文件发生改变,控制函数把目录变化通知存放在指定的内存区域内,并把发生改变的文件名、文件所在目录和改变通知处理。
void WINAPI CheckAddedFile( LPDIRECTORY_INFO lpdi, PFILE_NOTIFY_INFORMATION lpfni) { TCHAR szFullPathName[MAX_PATH]; TCHAR szFileName[MAX_PATH]; memcpy( szFileName, lpfni->FileName ,lpfni->FileNameLength); szFileName[lpfni->FileNameLength/sizeof(TCHAR)]=0; lstrcpy( szFullPathName, lpdi->lpszDirName ); lstrcat( szFullPathName, L"\\" ); lstrcat( szFullPathName, szFileName ); wprintf( L"%s\n", szFullPathName ); wprintf( L"%s added\n",szFileName);//Zz renamed\n",szFileName);}/********************************************************************** HandleDirectoryChanges() Purpose: This function receives notification of directory changes and calls CheckChangedFile() to display the actual changes. After notification and processing, this function calls ReadDirectoryChangesW to reestablish the watch. Parameters: HANDLE hCompPort - Handle for completion port Return Value: None Comments:********************************************************************/void WINAPI HandleDirectoryChange( DWORD dwCompletionPort ) { DWORD numBytes; DWORD cbOffset; LPDIRECTORY_INFO di; LPOVERLAPPED lpOverlapped; PFILE_NOTIFY_INFORMATION fni; BOOL r; do { // Retrieve the directory info for this directory // through the completion key GetQueuedCompletionStatus( (HANDLE) dwCompletionPort, &numBytes, (LPDWORD) &di, &lpOverlapped, INFINITE); if ( di ) { fni = (PFILE_NOTIFY_INFORMATION)di->lpBuffer; do { cbOffset = fni->NextEntryOffset;// if( fni->Action == FILE_ACTION_MODIFIED )// CheckChangedFile( di, fni ); if( fni->Action == FILE_ACTION_ADDED) //Zz FILE_ACTION_RENAMED_NEW_NAME) CheckAddedFile( di, fni ); fni = (PFILE_NOTIFY_INFORMATION)((LPBYTE) fni + cbOffset); } while( cbOffset ); // Reissue the watch command r=ReadDirectoryChangesW( di->hDir, di->lpBuffer, MAX_BUFFER, FALSE, FILE_NOTIFY_CHANGE_FILE_NAME,//|FILE_NOTIFY_CHANGE_LAST_WRITE, &di->dwBufLength, &di->Overlapped, NULL); if (0==r) { wprintf( L"ReadDirectoryChangesW error! GetLastError()==%d\n",GetLastError()); no_loop=1;break;// } } else { no_loop=1; } } while( di );}PI函数GetModuleFileName():获得应用程序目录相对路径MFC函数GetModuleFileName():获得应用程序目录绝对路径采用.\\也能获得应用程序当前目录当前目录不一定等于应用程序执行文件的所在目录,一个应用程序被启动时,当前目录是可以被任意设置的。GetModuleFileName()得到模块的完整路径名,例如,你载入c:\windows\system32\a.dll,得到模块句柄h,则你可以用GetModuleFileName()得到h模块的完整路径名。.\\一般用在包含头文件的语句中。另一个是程序编译后起作用的,例如,打开自定义的配置文件等。注:直接用LoadLibrary()或AfxLoadLibrary()载入dll,该函数返回值就是handle;如果你隐式载入dll, 用GetModuleHandle("dll文件名")也可以得到handle;在开发工程中,往往需要知道当前程序本身所在目录。一种方法是在程序安装的时候利用安装程序把文件路径写入注册表。在较大的程序中,这种方法比较常用另一种,就是在程序得到路径。这样,程序随便移动到哪里,都可以得到正确的路径。这也是这里介绍的方法。/*得到帮助文件的路径*/ CString strFullName = AfxGetApp()->m_pszHelpFilePath; //得到的是:X:\XXXX\XXX.hlp //AfxGetApp()->m_pszAppName 得到应用程序名称 //AfxGetApp()->m_pszExeName 得到程序文件名,不包括扩展名 /*得到解析路径*/ char drive[_MAX_DRIVE]; char dir[_MAX_DIR]; _splitpath(strAppName, drive, dir, NULL,NULL); CString strPath; strPath.Format("%s%s", drive, dir); //得到当前运行程序所在目录,strPath即为得到的当前运行程序所目录/*得到全路径*/ TCHAR exeFullPath[MAX_PATH]; // MAX_PATH GetModuleFileName(NULL,exeFullPath,MAX_PATH);//得到程序模块名称,全路径,也就是当前运行程序的全路径。利用方法一的解析路径的方法,即可得到程序所在路径。GetModuleFileName函数原型DWORD GetModuleFileName(HMODULE hModule, // handle to module。将要得到的模块的句柄。如果是当前模块,NULLLPTSTR lpFilename, // path buffer 得到的文件名。DWORD nSize // size of buffer 一般MAX_PATH就可以了
- 开发一个windows监控服务
- C# 监控Windows服务
- 用C#开发Windows服务监控系统使用
- 用C#开发Windows服务监控系统使用
- 用C#开发Windows服务监控系统使用
- 用C#开发Windows服务监控系统使用
- 用C#开发Windows服务监控系统使用
- 用C#开发Windows服务监控系统使用
- ServiceController操作Windows服务 监控Windows服务
- nagios 监控 windows 相关服务
- 使用IBM Tivoli Monitoring Universal Agent自定义开发Windows Terminal Service监控服务
- 创建一个Windows 服务
- windows监控网站,重启服务
- windows服务监控和操作相关
- windows下python监控系统服务
- windows apache 服务 监控重启脚本
- JConsole监控本地windows Tomcat服务
- Python对Windows服务进行监控
- git提交至远程仓库步骤
- IntelliJ IDEA2017 构建nwjs项目
- 最近两个月需要掌握的技术
- 单片机时钟程序(1)main.c
- PHP流封装协议
- 开发一个windows监控服务
- lintcode/leetcode由易至难第19题:Teemo Attacking
- Servlet-监听器获得当前在线人数
- hashMap学习总结
- XML基础
- jsp如何连接MySQL数据库
- Java中字符串中连续相同字符去重
- 剑指offer-翻转单词顺序
- 2017.6.26学习记录 java基础MyEclipse