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。将要得到的模块的句柄。如果是当前模块,NULL
LPTSTR lpFilename, // path buffer 得到的文件名。
DWORD nSize // size of buffer 一般MAX_PATH就可以了