经过多少日夜的潜心编程,你的软件终于大功告成时,你一定想马上将她呈现在用户面前,于是她的包装(安装程序)便成了当务之急。当然你可以选用InstallShield来制作软件的安装程序,但在它带给你制作简便的同时,却给你匠心独具的软件赋予了千篇一律的包装。
一个好的安装程序有助于增加软件的特色、提升软件的品位。本文介绍用Visual C++来制作通用安装程序的方法,由此你便拥有了一个独具特色的通用安装程序。
1.在硬盘中创建安装目录
软件由若干文件组成,我们通常将其中大部分文件拷贝至安装目录下,另有小部分文件须拷贝至特定的目录(如C:\Windows\System)下。首先应在硬盘中创建安装目录。Windows的API函数CreateDirectory()可创建目录。
2.获取特定目录
若软件中包含须拷贝至特定目录的文件,那么应获取所需的特定目录。GetWindowsDirectory()可得到Windows目录;GetSystemDirectory()可得到Windows的系统目录,SHGetSpecialFolderLocation()可得到Windows的特定目录(如Desktop、Start Menu\Programs、Recent、Help等)。
3.拷贝文件
创建安装目录和获取特定目录之后,将软件中的所有文件分别拷贝至这些目录中,CopyFile()可完成文件的拷贝。在拷贝文件的同时,最好用进度条显示文件拷贝的进度。
4.创建快捷方式
完成软件中所有文件的拷贝之后,在“桌面”或“程序”中创建快捷方式,使之与应用程序相关联。
5.具体实现
假定软件中有5个文件:Myprog.exe,Myprog.hlp,Myprog.dll,Example.dat及Myprog.vxd。其中前4个文件须拷贝至安装目录下,第5个文件须安装在Windows的系统目录下。
5.1 创建基于对话框的项目
项目名称为Setup,资源中对话框至少应有两个编辑控件IDC_EDIT1、IDC_EDIT2(对应的CString变量为m_strEdit1、m_strEdit2),分别为“安装目录”、“快捷方式名称”,并且将按钮“确定”改为“安装”,人工输入资源ID的名称IDC_PROGRESSBAR,作为进度条的ID。
5.2 编写自定义函数
编写自定义函数ReadConfig()和CreateShortcut()。其中ReadConfig()从安装配置文件Setup.cfg中读取安装信息,CreateShortcut()生成快捷方式。
5.2.1 配置信息文件Setup.cfg
[General]
Topic=安装《我的程序》 //对话框的标题
SetupDir=C:\Mysoft //安装目录
Shortcut=我的程序 //快捷方式名称
ProgName=Myprog.exe //应用程序名称
[CopyFiles] //须拷贝的文件
Source1=Myprog.exe //第1个源文件
Target1=Myprog.exe //第1个目标文件(与源文件可不同名)
Source2=Myprog.hlp
Target2=Myprog.hlp
Source3=Myprog.dll
Target3=Myprog.dll
Source4=Example.dat
Target4=Example.dat
Source5=Myprog.vxd
Target5=>Sy\Myprog.vxd //该文件拷贝至Windows系统目录下
………… //拷贝的文件数目不限
[End]
5.2.2 读取配置信息函数
BOOL CSetupDlg::ReadConfig(){//从Setup.cfg中读取配置信息
CFile fileCfg;
if(!fileCfg.Open("Setup.cfg",CFile::modeRead)){
MessageBox("Can’t open file: Setup.cfg");//打开文件错误
return false;
}
char pcBuf[1024];//假定文件长度小于1024
fileCfg.Read(pcBuf,fileCfg.GetLength());//配置信息读入到pcBuf[]中
CString strBuf=pcBuf;
char pcItem[20][30]={"Topic","SetupDir","Shortcut","ProgName", "Source1","Target1","Source2","Target2","Source3","Target3","Source4","Target4","Source5","Target5","Source6","Target6","Source7","Target7","Source8","Target8"};//配置信息条目,这里列举20个,可根据需要增减
int nLoc=-1;//各条目的起始位置
for(int i=0;i<20;i++){
nLoc=strBuf.Find(pcItem[i]);
if(nLoc>=0){ //得到各条目内容char pcCont[][](CSetupDlg成员变量),每条内容最多为30个字符
strcpy(pcCont[i],strBuf.Mid(nLoc+strlen(pcItem[i])+1,30));
for(int j=0;j<30;j++){
if(pcCont[i][j]==0x0d)//遇回车符,则置结束符
pcCont[i][j]=0;
}
nCopyFiles=(i-2)/2;// 须拷贝的文件个数,CSetupDlg成员变量
}
}
fileCfg.Close();
return true;
}
5.2.3 创建快捷方式函数
BOOL CSetupDlg::CreateShortcut(LPSTR szProgPath,LPSTR szShortcut)
{// szProgPath为目标程序路径,szShortcut为快捷方式名称
IShellLink* pISL;
IPersistFile* pIPF;
HRESULT hr;
WCHAR wszShortcut[MAX_PATH];
hr=CoCreateInstance(CLSID_ShellLink,0,1,IID_IShellLink,(void**)&pISL);
//创建IShellLink接口
if(FAILED(hr)) return false;
pISL->SetPath(szProgPath);//设置目标应用程序
pISL->SetShowCmd(…);//设置显示方式
pISL->SetWorkingDirectory(…);//设置工作目录(其它设置参阅MSDN)
hr=pISL->QueryInterface(IID_IPersistFile,(void**)&pIPF);
//得到IPersistFile接口
if(FAILED(hr)){
pISL->Release();return false;}
MultiByteToWideChar(0,0,szShortcut,-1,wszShortcut,MAX_PATH);//将char*转换为WCHAR*格式
hr=pIPF->Save(wszShortcut,false);//保存快捷方式数据
pIPF->Release();
pISL->Release();
if(FAILED(hr)) return false;
SHChangeNotify(SHCNE_ALLEVENTS,SHCNF_PATH|SHCNF_FLUSH,szShortcut,0);//通知系统更新
return true;
}
5.3 修改对话框初始化函数
通过ClassWizard映射消息WM_INITDIALOG,生成OnInitDialog()函数。在其中增加初始化的内容。
BOOL CSetupDlg::OnInitDialog()
{
…………//自动生成的内容
nCopyFiles=0;//初始化须拷贝的文件数
ReadConfig();//读取配置信息
SetWindowText(pcCont[0]);//设置对话框标题
SetDlgItemText(IDC_EDIT1,pcCont[1]);//初始化安装目录
SetDlgItemText(IDC_EDIT2,pcCont[2]);//初始化快捷方式名称
return true;
}
5.4 修改OnOK()函数
对话框中的按钮“确定”改为“安装”,但其ID名称仍为IDOK,通过ClassWizard映射该消息,生成OnOK()函数。在其中增加的内容为:
(1)创建、设置进度条
(2)取得当前目录
(3)取得Windows特定目录
(4)创建安装目录
(5)拷贝文件
(6)生成快捷方式
void CSetupDlg::OnOK()
{
GetDlgItemText(IDC_EDIT1,m_strEdit1);//将IDC_EDIT1的值赋给m_strEdit1
GetDlgItemText(IDC_EDIT2,m_strEdit2);//将IDC_EDIT2的值赋给m_strEdit2
GetDlgItem(IDOK)->EnableWindow(false);//不能第二次按“安装”
//设置进度条(在对话框中添加进度条控件,为其添加控制变量m_progCtrl)
m_prgCtrl.SetRange(0,100);//设置进度条的范围
m_prgCtrl.SetPos(0); //设置进度条的初始位置
//取得当前目录
char szCurrentDir[MAX_PATH];
GetCurrentDirectory(MAX_PATH,szCurrentDir);
if(szCurrentDir[strlen(szCurrentDir)-1]=='\\') //去掉最后的'\'
szCurrentDir[strlen(szCurrentDir)-1]=0;
//取得Windows和系统目录
char szWindowsDir[MAX_PATH];
char szSystemDir[MAX_PATH];
GetWindowsDirectory(szWindowsDir,MAX_PATH);
GetSystemDirectory(szSystemDir,MAX_PATH);
//取得桌面和程序的目录
LPITEMIDLIST lpIIDL;
char szDesktopDir[MAX_PATH];//桌面的目录
SHGetSpecialFolderLocation(HWND_DESKTOP,CSIDL_DESKTOP,&lpIIDL);
SHGetPathFromIDList(lpIIDL,szDesktopDir);
char szProgramDir[MAX_PATH];//程序的目录
SHGetSpecialFolderLocation(HWND_DESKTOP,CSIDL_PROGRAMS,&lpIIDL);
SHGetPathFromIDList(lpIIDL,szProgramDir);
//创建安装目录
char szDir[MAX_PATH];
if(m_strEdit1.GetAt(m_strEdit1.GetLength()-1)!='\\')
m_strEdit1+='\\';//若m_strEdit1最后不为'\',则加上'\'
for(int i=0;i<M_STREDIT1.GETLENGTH();I++){ 目录必须逐级创建
if(m_strEdit1.GetAt(i)=='\\'){
strcpy(szDir,m_strEdit1);
szDir[i]=0;
CreateDirectory(szDir,NULL);
}
}
m_strEdit1=m_strEdit1.Left(m_strEdit1.GetLength()-1); //去掉最后的'\'
//拷贝文件
char szSourceFile[MAX_PATH];
char szTargetFile[MAX_PATH];
for(i=0;i<NCOPYFILES;I++){
sprintf(szSourceFile,"%s\\%s",szCurrentDir,pcCont[i*2+4]);
if(pcCont[i*2+5][0]=='>' && pcCont[i*2+5][3]=='\\'){//拷贝至特定目录下
if(pcCont[i*2+5][1]=='W' && pcCont[i*2+5][2]=='i')//Windows目录
sprintf(szTargetFile,"%s\\%s",szWindowsDir,&pcCont[i*2+5][4]);
else if(pcCont[i*2+5][1]=='S'&&pcCont[i*2+5][2]=='y')//System目录
else return;
}
else //拷贝至安装目录下
sprintf(szTargetFile,"%s\\%s",m_strEdit1,pcCont[i*2+5]);
if(!CopyFile(szSourceFile,szTargetFile,FALSE)){
char pcMessage[50];
sprintf(pcMessage,"拷贝文件 %s 失败",pcCont[i*2+4]);
MessageBox(pcMessage,"错误信息",MB_ICONERROR|MB_OK);
return;
}
m_prgCtrl.SetPos((i+1)*100/nCopyFiles);//显示拷贝文件的进度
}
//创建快捷方式
char szProgPath[MAX_PATH];//应用程序的路径全名
sprintf(szProgPath,"%s\\%s",m_strEdit1,pcCont[3]);
char szShortcut[MAX_PATH];//快捷方式名称
//在桌面创建快捷方式
sprintf(szShortcut,"%s\\%s.lnk",szDesktopDir,m_strEdit2);
if(!CreateShortcut(szProgPath,szShortcut)) return;
//在程序中创建快捷方式
sprintf(szShortcut,"%s\\%s.lnk",szProgramDir,m_strEdit2);
if(!CreateShortcut(szProgPath,szShortcut)) return;
CDialog::OnOK();//关闭安装窗口
}
5.5 定制安装界面
前面介绍了制作安装程序的基本方法,为了使安装程序独具特色,应在界面上下些功夫。如通过重载OnEraseBkgnd()函数,在其中加入显示位图的代码,可制作非常漂亮的安装界面。又如映射WM_CTLCOLOR消息,在生成的函数OnCtlColor()中增加设置文本显示属性的代码,可改变显示文本及背景的颜色,进一步美化安装界面。
5.6 安装程序的使用
将上述项目编译链接成发行版的应用程序Setup.exe,须与配置文件Setup.cfg配对使用。在制作安装盘时,首先编辑Setup.cfg文件,以纯文本格式存盘,再将Setup.exe、Setup.cfg与须安装的其它文件一同拷入安装盘,运行Setup.exe即可实现软件的安装。
6.说明
本程序在Windows 98(中文版)、Visual C++ 6.0环境下编译通过。