`
huozheleisi
  • 浏览: 1238232 次
文章分类
社区版块
存档分类
最新评论

用ini文件实现最近文件MRU

 
阅读更多
VC++的MFC对最近文件进行了包装,它使用户无需编写任何代码就可实现最近文件菜单。但这种方法也有缺陷:

①用户程序必须使用文档、视图结构,只能利用它提供的文档打开和保存文件,如果你使用自己编写的函数打开和保存文件,绕过了它的文档,则最近文件将无法使用。

②这种最近文件保存在注册表中,不易清除,容易形成注册表中垃圾,也没有提供用户清除最近文件的权利,不利于用户隐私的保护。

用ini文件实现最近文件可以解决上述问题,而且用编程控制MRU更灵活,可以提供更多功能。

一、ini文件的结构和访问方法

ini文件是纯文本文件,我们可以编程访问它,也可用任一种文本编辑软件打开它。如某RAV.ini文件打开后如下:

[INSTALLED]
FWBASE2K=1
FWBASENT=1
ToInstall=
CodePage=936
Closed=

[Setup]
ShowBgBmp=0

[Skin]
Current=1
Path=D:\ProgramFiles\Rising\Rav\Skin\Skin1\Skin.xml

[RAVMON]
MONSTATUS=255

其中[]括起的称为段,段下面的各部分称为项,访问时用(段名,项名)可访问指定项的值。我们可利用API函数直接读写ini文件。

㈠ 读ini文件中的项值

① 读整数值:

UINT GetPrivateProfileInt(LPCTSTR lpAppName,LPCTSTR lpKeyName,INT nDefault,LPCSTR lpFileName);

lpAppName为段名,lpKeyName为项名,nDefault为找不到该项时返回的默认值,lpFileName为ini文件名

返回值:若成功,返回指定项后的整数值,若该项不存在,返回nDefault的值

如:读取RAV.ini文件中[INSTALLED]段的FWBASE2K项的值

int FwbBase2K = ::GetPrivateProfileInt("INSTALLED", "FWBASE2K", 0, "RAV.ini");

如果[INSTALLED]段的FWBASE2K项存在,则FwbBase2K的值为读出的值,若不存在,则为函数调用中指定的0。

② 读字符串:

DWORD GetPrivateProfileString(LPCTSTR lpAppName,LPCTSTR lpKeyName,LPCTSTR lpDefault,LPTSTR lpReturnedString,DWORD nSize,LPCTSTR lpFileName);

lpAppName为段名,lpKeyName为项名,lpDefault为找不到该项时返回的默认值,lpReturnedString指向接收结果的字符串缓冲区,nSize为缓冲区长度,lpFileName为ini文件名

执行结果:若成功,lpReturnedString指示的串中为读出的项值,若该项不存在,lpReturnedString中为lpDefault的值

如:读取RAV.ini文件中[Skin]段的Path项的值

CString PathName;//接收串的变量

::GetPrivateProfileString("Skin", "Path", "", PathName.GetBuffer(MAX_PATH), MAX_PATH, "RAV.ini");

PathName.ReleaseBuffer();//释放多余空间

MAX_PATH为系统预定义的常数,其值可能是255。

㈡ 把数据写入ini文件

写入时没有写入整数的函数,只有写入字符串的函数

BOOL WritePrivateProfileString(LPCTSTR lpAppName,LPCTSTR lpKeyName,LPCTSTR lpString,LPCTSTR lpFileName);

lpAppName为段名,lpKeyName为项名,lpString为写入的字符串,lpFileName为ini文件名

返回值:若成功,返回true,否则返回false

若写入时,该ini文件不存在,则这个函数会自动建立这个文件,并建立相应的段和项。

如:把RAV.ini文件中[INSTALLED]段的FWBASE2K项的值设置为2,由于这是整数,应先转换为字符串后再写入

CString str;

str.Format("%d", 2);//把写入的整数转换为字符串

::WritePrivateProfileString( "INSTALLED", "FWBASE2K", str, "RAV.ini" );//写入ini文件

注意:实际应用中,ini文件名应采用绝对路径名,否则可能找不到要读写的ini文件。

二、用ini文件实现最近文件MRU

我设计了一个ini文件MruFile.ini保存最近文件,它的结构如下:

[MruFile]
FileNum=当前文件数
1=路径名1
2=路径名2
3=路径名3
4=路径名4

[Mru File]为段名,项FileNum为当前Mru文件数,项1、2、3、4放置各Mru文件名。这里只设计了4个最近文件,应用时可根据需要添加。

用ClassWizard添加一个没有基类的新类,名字起为CMruFile,作为MRU文件管理类,在它的头文件中有如下定义:

#defineMAXNUM4//最大MRU文件数

CStringm_IniFileName;//ini文件名
CStringm_PathName[MAXNUM];//MRU文件路径名
intm_CurNum;//当前最近文件数

在MruFile.cpp中定义下列函数:

①读取ini文件中的最近文件

voidCMruFile::ReadMru()
{
m_CurNum=::GetPrivateProfileInt("MruFile","FileNum",0,m_IniFileName);//读取当前文件数
CStringno;
for(inti=0;i<m_CurNum;i++)
{
no.Format("%d",i+1);//求项名
::GetPrivateProfileString("MruFile",no,"",m_PathName[i].GetBuffer(MAX_PATH),
MAX_PATH,m_IniFileName);//读取路径名
m_PathName[i].ReleaseBuffer();
}
SetMruMenu();//修改MRU菜单
}

这个函数在程序一开始就执行,用来把ini文件中的最近文件名读入字符串数组m_PathName中,然后修改最近文件菜单,使它们可以由最近文件菜单选择打开。

②最近文件写入ini文件

voidCMruFile::WriteMru()
{
CStringno;
no.Format("%d",m_CurNum);
::WritePrivateProfileString("MruFile","FileNum",no,m_IniFileName);//写当前文件数
for(inti=0;i<MAXNUM;i++)
{
no.Format("%d",i+1);
::WritePrivateProfileString("MruFile",no,m_PathName[i],m_IniFileName);//写路径名
}
}

这个函数在用户打开一个文件时执行,用来更新ini文件中的Mru列表。

③添加最近文件

设计思路:当打开的文件名nPathName在m_PathName[]列表中已存在,就把它提升到表头;若它不存在,则添加到表头,如果表已满(本例中最多可添加4个文件名),删除表尾的元素。

voidCMruFile::AddMru(CStringnPathName)
{
inti;
CStringstr1,str2;
if(m_CurNum)
{
if(nPathName.CompareNoCase(m_PathName[0])==0)//如果nPathName已在表头,结束
return;
}
str1=nPathName;
i=0;
while(i<m_CurNum&&nPathName.CompareNoCase(m_PathName[i])!=0)//在表中查找,不相等的元素下移
{
str2=m_PathName[i];
m_PathName[i]=str1;
str1=str2;
i++;
}
if(i<m_CurNum)
m_PathName[i]=str1;//nPathName已存在
elseif(m_CurNum<MAXNUM)
{
m_PathName[m_CurNum]=str1;//表未满
m_CurNum++;
}

SetMruMenu();//修改MRU菜单
WriteMru();//最近文件写入ini文件
}

这个函数也是在用户打开一个文件时执行,并且在WriteMru()函数之前进行,用于更新用数组m_PathName[]保存的Mru列表。

④清除最近文件

voidCMruFile::ClearMru()
{
m_CurNum=0;//清除最近文件数
for(inti=0;i<MAXNUM;i++)
m_PathName[i].Empty();//清空Mru列表
SetMruMenu();//清空Mru菜单
WriteMru();//清空ini文件中的Mru列表
}

在Mru菜单中有一个“清除最近文件”选项,当用户单击这个菜单项时,执行本函数。这有利于保护用户隐私。

⑤修改最近文件菜单

voidCMruFile::SetMruMenu()
{
CMenu*pMenu=AfxGetMainWnd()->GetMenu();//主菜单指针
CMenu*pFileMenu=pMenu->GetSubMenu(0);//“文件”菜单指针
CMenu*pMruMenu=pFileMenu->GetSubMenu(5);//“最近文件”菜单指针(5为最近文件菜单项在文件菜单中的位置)
pMruMenu->RemoveMenu(ID_MRU1,MF_BYCOMMAND);//删除各菜单项
pMruMenu->RemoveMenu(ID_MRU2,MF_BYCOMMAND);
pMruMenu->RemoveMenu(ID_MRU3,MF_BYCOMMAND);
pMruMenu->RemoveMenu(ID_MRU4,MF_BYCOMMAND);
if(m_CurNum>0)//重新插入各菜单项
pMruMenu->InsertMenu(ID_MRU_CLR,MF_BYCOMMAND,
ID_MRU1,m_PathName[0]);
if(m_CurNum>1)
pMruMenu->InsertMenu(ID_MRU_CLR,MF_BYCOMMAND,
ID_MRU2,m_PathName[1]);
if(m_CurNum>2)
pMruMenu->InsertMenu(ID_MRU_CLR,MF_BYCOMMAND,
ID_MRU3,m_PathName[2]);
if(m_CurNum>3)
pMruMenu->InsertMenu(ID_MRU_CLR,MF_BYCOMMAND,
ID_MRU4,m_PathName[3]);
}

当Mru文件列表发生变化时调用此函数。Mru菜单是一个可以修改的动态菜单,它的菜单项数目随着Mru文件数的变化而变化,菜单内容也随着Mru列表的内容变化而变化。

在这里ID_MRU1~ID_MRU4和ID_MRU_CLR是各菜单项的ID号,它们需事先在资源的字符串表中进行定义。

这样,一个用ini文件管理的最近文件类就做好了。使用时,在程序的打开文件OnFileOpen()函数和另存为OnFileSaveAs()函数调用这个类的相关函数就可以管理最近文件了。

详细用法可参考示例程序。

示例程序界面:

示例程序界面

本文所用MFC函数和API函数速查:

CMenu::GetSubMenu
CMenu::InsertMenu
CMenu::RemoveMenu
CString::CompareNoCase
CString::Empty
CString::Format
CString::GetBuffer
CString::ReleaseBuffer
CWnd::GetMenu

::GetPrivateProfileInt
::GetPrivateProfileString
::WritePrivateProfileString

http://221.199.150.103/jsj/Html/vc/wen/vcwen04.htm

分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics