Login dark

title: MFC文件读取技术CStdioFile
author: love02xp
date: 2018-03-26 12:40:05
category: [编程代码]
tags: [学习,代码,VC]
draft: true
cover: "http://image.cool02.com/blogimages/0CategoryCode2.jpg"
preview:


#####[阅读原文]()



正确的文本文件读写过程 

1. 定义文件变量; 
2. 打开指定的文件; 
3. 向从文本文件中写入信息; 
4. 从文本文件中读取信息; 
5. 关闭文件 <!--more-->

下面具体介绍如何实现这些过程。
 
### 1. 定义文件变量 
定义文件变量格式: CStdioFile 文件变量; 
例如,定义一个名称为 f1 的文件变量,语句如下: CStdioFile f1; 
### 2. 打开指定文件 
    可以直接通过 CStdioFile 的构造函数来打开磁盘文件,同时可以用标志位指定打开方式(只读、只写、读写等): 
CStdioFile(LPCTSTR lpszFileName,UINT nOpenFlags); 
其中, lpszFileName 表示要打开的文件名,可以是相对路径或绝对路径 
nOpenFlags 设置文件打开方式标志位,可以指定用" | "连接多个标志位。下面是常用的打开标志: 
CFile::typeText :以文本文件的形式打开文件 
CFile::typeBinary :以二进制文件的形式打开文件 
CFile::modeCreate :如果指定文件名的文件不存在,则新建文件;如果文件存在并且没有设置 CFile::modeNoTruncate 标志,则清空文件。 
CFile::modeNoTruncate :如果文件存在,不把它的长度删除为 0 (即不清空文件中的数据)。 
CFile::modeRead :以只读方式打开文件 
CFile::modeReadWrite :以可读可写方式打开文件 
CFile::modeWrite :以只写方式打开文件 
CFile::shareDenyNone :文件打开后,不禁止其他进程对文件的读写操作 
CFile::shareExclusive :文件打开后,禁止其他进程对文件的读写操作 
CFile::shareDenyRead :文件打开后,禁止其他进程对文件的读操作 
CFile::shareDenyWrite :文件打开后,禁止其他进程对文件的写操作 
此外,可以不在构造函数中打开文件,而仅仅调用空的构造函数 CStidoFile (),然后用 CStdioFile::Open() 打开文件。 Open 函数的前两个参数和非空构造函数的参数相同,其声明如下: 
BOOL Open(LPCTSTR lpszFileName,UINT nOpenFlags,CFileException* pError=NULL); 
第 3 个参数与打开失败时的异常处理有关。 
##### 实例 1 :以只读方式打开一个文件 
步骤: 
使用 AppWizard 创建一个对话框应用程序,删除其自动产生的所有控件,添加一个 Button 控件。双击控件,在相应的函数里添加代码:

char * pszFileName="C:\myfile.txt";
CStdioFile myFile;
CFileException fileException;
if(!myFile.Open(pszFileName,CFile::modeCreate|CFile::typeText|CFile::modeRead),&fileException)
{
TRACE("Can't open file %s, error = %un",pszFileName,fileException.m_cause);
}
运行结果:如果 C: 下没有 myfile.txt 文件,则新生成该文件。

### 3. 向从文本文件中写入信息 
CStdioFile 提供了函数 WriteString 来向文本文件中写入文本, WriteString 函数的格式如下: 
void WriteString(LPCTSTR lpsz); 
WriteString 的参数 lpsz 是一个以 "\0" 字符结束的字符串,要把这个字符串的内容写入文件。 
提示 :使用 WriteString 函数时,如果希望每执行一次 WriteString ,文本文件中的内容就会自动换行一次,那么就需要在需要换行的地方输出" \n ": 
myFile.WriteString(" 第 1 行 \n") ; 
##### 实例 2 :向文件中写入文本 
建立 MFC 基于对话框的程序,删除自动添加的所有控件,添加一个"确定"按钮,双击按钮,按默认添加事件函数,双击按钮,在相应的函数处添加如下代码:

char* pszFileName="C:\myfile.txt";
CStdioFile myFile;
CFileException fileException;
if(myFile.Open(pszFileName,CFile::typeText|CFile::modeCreate|CFile::modeReadWrite),&fileException)
{
myFile.WriteString(" 第 1 行 n");
CString strOrder;
strOrder.Format("%d,%.3f",66,88.88);
myFile.WriteString(strOrder);
}
else
{
TRACE("Can't open file %s,error=%un",pszFileName,fileException.m_cause);
}
程序运行结果: C:myfile.txt 文件中内容如下:
第 1 行
66,88.880


### 4. 从文本文件中读取信息 
CStidoFile 提供了函数 ReadString 来读取文本, ReadString 有两种形式,一种为: 
virtual LPTSTR ReadString(LPTSTR lpsz, UINIT nMax); 
ReadString 函数的参数如下: 
lpsz :是用户提供的一个指向字符串的指针,它用来接受从文件读出的文本,以 "\0" 结束。 
nMax 是本次所允许读入的文本字符个数,不计" \0" 字符,也就是说最多能读入 nMax-1 个文本字符。 
ReadString 的返回值是一个 LPTSTR 类型的指针,它指向从文件读出的文本字符串,如果到达文件尾,则返回 NULL 。 
ReadString 的另一种形式为: 
BOOL ReadString(CString& rString); 
参数 rString 用来容纳从文件读出的文本。 
CString 版本忽略回车换行符,返回值是一个布尔值。如果返回值为 FALSE ,表示因到达文件尾而没有读到任何字符。 
提示: 每执行一次 ReadString ,就会自动从文本文件中读取一行数据,同时文件操作指针会自动跳转到下一行。 
##### 实例 3 :从文件中读取文本信息 
步骤:创建基于对话框的 MFC 程序,删除所有自动添加的控件,添加按钮控件,为按钮添加事件,并在相应的函数处,添加如下代码: 

char* pszFileName="C:\myfile.txt";
CStdioFile myFile;
CFileException fileException;
if(myFile.Open(pszFileName,CFile::typeText|CFile::modeReadWrite),&fileException)
{
myFile.SeekToBegin();
CString str1;
myFile.ReadString(str1);
CString str2;
myFile.ReadString(str2);
AfxMessageBox(str1+str2);
}
else
{
TRACE("Can't open file %s,error=%un",pszFileName,fileException.m_cause);
}
myFile.Close();
程序运行结果:为程序 F9 设置断点,然后 F5 单步执行,结果如下:


### 5. 关闭文件 
对文件的操作完成后,使用 CloseFile 关闭文件。 
函数 CStdioFile::Close 关闭一个文件,一般一个文件使用完毕就应该关闭它: 
myFile.Close(); 
错误的文本文件读写过程 
在读写文本文件的时候,最常见的错误是 --- 操作文件不存在。这种错误产生的典型原因有: 
#### 1. 路径错误 

char * pszFileName="C:\Windows\MyFile.txt";
CStdioFile myFile;
CFileException fileException;
if(!myFile.Open(pszFileName,CFile::modeCreate|CFile::typeText|CFile::modeReadWrite),&fileException)
{
// 文件操作代码
}
else
{
TRACE("Can't open file %s, error = %un",pszFileName,fileException.m_cause);
}
myFile.Close();


由于将文件变量与一个绝对路径的文件名关联,而程序的数据通常存储在相对路径下,所以一旦相对路径和相对路径不一致时,就会出错。 
举例而言,上一段程序本意是想从 windows 的安装目录下面的 MyTextFile.txt 文件中读取一行数据,但是如果操作系统安装的路径不是 C:\Windwos ,而是 C:\Winnt, 那么这段程序就会出错。 
解决方法是在程序中使用相对路径,改正后的程序如下:

// 获取 windows 路径
LPTSTR lpBuffer=new char[MAX_PATH];
::GetWindowsDirectory(lpBuffer,MAX_PATH);
strcat(lpBuffer,"\MyFile.txt");
CStdioFile myFile;
CFileException fileException;
if(myFile.Open(lpBuffer,CFile::typeText|CFile::modeCreate|CFile::modeReadWrite),&fileException)
{
// 文件操作代码
}
else
{
TRACE("Can't open file %s, error = %un",pszFileName,fileException.m_cause);
}
CString strFileTitle="MyFile.txt";
CStdioFile myFile;
CFileException fileException;
if(myFile.Open(strFileTitle,CFile::typeText|CFile::modeReadWrite),&fileException)
{
// 文件操作代码
myFile.WriteString(" 测试! ");
}
else
{
TRACE("Can't open file %s, error = %un",pszFileName,fileException.m_cause);
}
myFile.Close();


#### 2. 操作文件不存在 
如果应用程序所有路径下面不存在 MyFile.txt 文件,那么在 WriteString 写入信息时就会出错。 
解决办法就是在程序中打开文件前要检查是否存在此文件。如下程序: 

CString strFileTitle="MyFile.txt";
CFileFind finder;
if(finder.FindFile(strFileTitle))
{
CStdioFile myFile;
CFileException fileException;
if(myFile.Open(lpBuffer,CFile::typeText|CFile::modeCreate|CFile::modeReadWrite),&fileException)
{
// 文件操作代码
}
else
{
TRACE("Can't open file %s, error = %un",pszFileName,fileException.m_cause);
}
}
else
{
TRACE("Can't find file %sn",strFileTitle);
}
myFile.Close();


#### 3. 超越文件权限进行读写操作 
在打开文件的过程中,通过参数指定了文件的读写权限,如果读写的操作没有和相应的权限对应,就会出现错误。 
下面的程序就是典型的忽略了文件操作权限的例子: 

CString strFileTitle="MyFile.txt";
CStdioFile myFile;
CFileException fileException;
if(myFile.Open(strFileTitle,CFile::typeText|CFile::modeCreate|CFile::NoTruncate|CFile::modeRead),&fileException)
{
// 文件操作代码
myFile.WriteString(" 测试 !");
}
else
{
TRACE("Can't open file %s,error=%un",strFileTitle,fileException.m_cause);
}
myFile.Close();


支招儿 : 
#### 1. 准确定位文件的路径 
操作文件的过程中,经常需要将文本文件放在程序自身的目录中,但是如果仅仅在程序中使用不指定任何路径信息的相对路径,如: 
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();


#### 2. 读文本文件指定的一行,并得到文本文件的总行数。 
读文本文件指定的一行,并得到文本文件的总行数 
要统计文本文件的总行数,可以从头逐行读,直到文件尾,程序: 

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(using shared MFC DLL) 
注意: 选择 File->New 菜单项,在弹出对话框的左边的列表框中选择 MFC AppWizard(DLL). 
在 project name 文本框中输入项目名称, HookTest ; location 中输入项目的存盘路径;选中 Create new workspace ;在 platForms 列表中选择 Win32 选项。 
单击 OK 按钮继续下一步,在弹出的对话框中设置 DLL 类型为 MFC Extension 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::StartHook()
{
glhTestHook=SetWindowsHookEx(WH_KEYBOARD,KeyboradProc,glhkInstance,0);
if(glhTestHook!=NULL)
return TRUE;
return FALSE;
}
//
/*
HHOOK SetWindowsHookEx(int idHook,HOOKPROC lpfn,INSTANCE hMod,DWORD dwThreadId)
idHook: 钩子类型,它是和钩子函数类型一一对应的,例如, WH_KEYBOARD 表示安装的是键盘钩子, WH_MOUSE 表示的是鼠标钩子等。
lpfn :钩子函数的地址
hMod: 钩子函数所在的实例的句柄,对于线程钩子,该参数为 NULL ;对于系统钩子,该参数为钩子函数的 DLL 句柄
dwThreadId: 指定钩子所监视的线程的线程号,对于全局钩子,该参数为 NULL.
SetWindowsHookEx 返回所安装的钩子句柄。
调用 StartHook 函数后,所有键盘的消息都会转移到 KeyboradProc 函数中,通过数组 nkey 的值与 wParam 参数相比较,可以知道用户按下的是哪个键,通过对 IParam 值的判断,可以知道是按下键还是释放键,然后播放键对应的声音文件即可。
*/
//
// 卸载钩子
BOOL CHookTest::StopHook()
{
BOOL bResult=FALSE;
if(glhTestHook)
{
bResult=UnhookWindowsHookEx(glhTestHook);
if(bResult)
{
glhTestHook=NULL;
}
}
return bResult;
}
//

第二部分: INI 文件

INI 文件的读写

Windows 操作系统将 win.ini 作为记录当前系统状态,并根据其记录内容对系统进行配置的一种便捷的方法,且众多的应用软件也广泛的使用该类型的配置文件来对软件进行记录和配置。

配置设置文件( INI )文件是 windows 操作系统中的一种特殊的 ASCII 文件,以 ini 为文件扩展名。该文件也被称为初始化文件 initialization file 和概要文件 profile ,通常应用程序可以拥有自己的配置设置文件来存储状态信息。一般来说私有的配置设置文件比较小,这样可以减少程序在初始化时读取配置文件时的信息量,从而提高程序的启动速度、提高应用程序和系统的性能。
如果带存取的信息涉及到 windows 系统环境或是其他应用程序时,就必须在 windows 系统的配置文件 win.ini 中记录并在访问的同时发送 WM_WININICHANGE 消息给所有的顶层窗口,通知其他的程序系统配置文件已做了更改。但由于 win.ini 中不仅记录了系统的有关信息,也存储着许多其他应用软件的配置数据,所以访问的数据量要远比私有配置文件大的多。

掌握内容 :
了解 INI 文件的结构;能够正确灵活的应用 INI 文件存取信息;避免 INI 文件读写的常见误区。
INI 文件存储管理
配置文件里的信息之所以能为系统和终生的软件所读取并识别,是由于其内部对数据的存取采用了预先约定的"项 - 值对( entry-value pairs )"存储结构,并对待存取的数据分门别类地进行调理清晰的存储。

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,LPCTSTR lpString,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

  1. INI 文件的读过程
    将信息从 INI 文件中读出到程序中所用的函数为 :

DWORD GetPrivateProfileString(LPCTSTR lpAppName,LPCTSTR lpKeyName,LPCTSTR lpDefault,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,INT nDefault,LPCTSTR lpFileName); 
nStudAge=GetPrivateProfileInt("Info","Age",10,"C:\\student.ini"); 
运行结果:可以通过设置断点,来查看 strStudName 和 nStudAge 的内容。 

错误的 INI 文件读写

1. 路径指示错误

INI 文件的路径必须完整,文件名前面的各级目录必须存在,否则写入不成功,该函数返回 FALSE 值。
如下程序:

CString strName,strTemp; 
int nAge; 
strName=" 张三 "; 
nAge=12; 
::WritePrivateProfileString("Info","Name",strName,"c:\\Info\student.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 的内容。