摘 要:本文给出怎样利用资源文件,快速写出一个通用安装程序的方法。利用这种方法,可以把所有的安装文件打包进一个EXE文件中,对于不同的应用,只需编辑资源文件RC既可。
关键词:资源文件RC、安装程序、回调函数、快捷方式、C++Builder
对于需要发布自编的应用程序的朋友来说,一个专业化的安装程序是必不可少的,它应该是易用的、个性化的、专业化的,如果需要的话还可以提供加锁、防拷贝等功能。目前专业的安装程序生成工具很多,由于不是源码级的,故而不可能随心所欲增减功能。最好的解决方法是:如果很简单,就自己编一个通用的安装程序。按下面的方法,你就可以很简单的编写自己的安装程序。
方法的主要指导思想就是,把被安装的程序文件定义成安装程序的资源,这样,对一般朋友感到无从下手的文件打包工作,就完全交给编译器了,它自动把所有的文件编译、连结到一个EXE文件中。此方法可以支持数千个文件的打包。
当单一的安装文件生成后,如果体积过大,可以使用WinZip Self-Extractor生成自解压安装文件。WinZip Self-Extractor工具可以提供解压后自动运行包中某一程序的功能,当指定的程序运行完后,WinZip Self-Extractor自动删除解压出来的安装文件。例子中有代码使WinZip Self-Extractor在等待时隐藏起来。
下面本文以实际例子来说明,如何定义资源文件,以及如何复制文件到目标目录,如何中止长操作,如何建立快捷方式。
为了突出重点,例子是一个“朴素”的安装程序,它只突出了核心代码,而没有写漂亮的界面,相信对于有编程序经验的朋友,如果需要,可以很快写出有个性的安装界面来。例子用C++Builder4编写,在Windows98下运行通过。
建立例子程序:
打开C++Builder4,建立一个有如下两个Form的应用程序。(注意互相包含头文件)
建立如下文本文件data.rc,并加入到工程中。它定义了哪些文件安装到哪去、需建立哪些目录、要为哪些文件建立快捷方式等等。也可以自己定义其它内容在应用程序引用。
//data.rc
STRINGTABLE
BEGIN
//0: 要复制到应用程序目录的文件数目
//从1到4999 : 要复制的文件名,与f1到f4999相对应
0 "14"
1 "data.rc"
2 "project1.cpp"
3 "unit1.cpp"
4 "unit2.cpp"
5 "unit1.h"
6 "unit2.h"
7 "unit1.dfm"
8 "unit2.dfm"
9 "project1.bpr"
10 "work\\unit2.bak"
11 "work\\data.rc"
12 "work\\mytest1.dll"
13 "work\\mytest2.dll"
14 "work\\mytest3.dll"
//5000: 需新建的子目录个数
//从5001到5999: 需新建的子目录,须按级列出
5000 "3"
5001 "test1"
5002 "test1\\test2"
5003 "work"
//6000: 需复制到windows系统目录中批定的目录中的文件个数
//从6001到6999: 需复制到windows系统目录中批定的目录中的文件,与f6001到f6999相对应
6000 "3"
6001 "system\\mytest1.dll" //到c:\windows\system中
6002 "fonts\\mytest2.dll"
6003 "mytest3.dll"
//7000: 需建立到开始莱单中的快捷方式的个数
//从7001到7100: 是需要建立快捷方式的文件和快捷方式的名字,一行文件名,一行快捷方式名字
7000 "3"
7001 "test1\\test2"
7002 "打开test2"
7003 "project1.bpr"
7004 "工程文件"
7005 "uint1.cpp"
7006 "测试"
10000 "MyTest" //默认的安装目录名
10001 "安装程序例" //开始莱单中的程序组
END
//被安装的文件列表,不在当前目录的需指定目录
f1 MYRES "work\\data.rc"
f2 MYRES "project1.cpp"
f3 MYRES "unit1.cpp"
f4 MYRES "unit2.cpp"
f5 MYRES "unit1.h"
f6 MYRES "unit2.h"
f7 MYRES "unit1.dfm"
f8 MYRES "unit2.dfm"
f9 MYRES "project1.bpr"
f10 MYRES "work\\unit2.bak"
f11 MYRES "work\\data.rc"
f12 MYRES "work\\mytest1.dll"
f13 MYRES "work\\mytest2.dll"
f14 MYRES "work\\mytest3.dll"
//被安装到系统目录中的文件
f6001 MYRES "work\\mytest1.dll"
f6002 MYRES "work\\mytest2.dll"
f6003 MYRES "work\\mytest3.dll"
作为例子,此资源文件定义的是,把本例的所有源文件安装到指定的目录中,以及一些功能的演示,如向系统目录内复制文件。(注意f1,由于编译时data.rc已打开,它不能再打开,所以拷贝一个副本到work子目录中,从work中把它编到资源中。)
Unit1.cpp的内容始下,黑斜体为自己加入的代码,其它为C++Builder4自动生成的。
//-------------------------------------------------------------------------#include <vcl.h>
#include <shlobj.h> //创建快捷方式需要
#pragma hdrstop
#include "Unit1.h"
#include "Unit2.h" //可以访问Form2
//-------------------------------------------------------------------------#pragma package(smart_init)
#pragma resource "*.dfm"
TForm1 *Form1;
bool cancel=false; //用于终止安装
bool ok=false;
unsigned int allfilesize=0; //被安装文件的总的大小
char setuppath[MAX_PATH]; //安装路径
HWND myexe; //资源句柄
char buf[MAX_PATH];
char buf1[MAX_PATH];
//-------------------------------------------------------------------------BOOL CALLBACK MyHideWinzip(HWND hwnd ,unsigned int p)
{ //枚举窗口的回调函数,用于隐藏WinZip Self-Extractor
//回调直至本函数返回false,或全部窗口被枚举
char* buf=new char[200];
GetWindowText(hwnd,buf, 200);
if(strstr(buf,"WinZip Self-Extractor - "))
{ShowWindow(hwnd,SW_HIDE);
return(false);
}
delete[] buf;
return(true);
}
//-------------------------------------------------------------------------void MyWriteFile(int i,char* path)
{ //用于把i号资源写入安装目录path中
HWND fileres,file,ff;
unsigned long aa;
int ressize;
LoadString(myexe,i,buf,MAX_PATH); //从资源文件中装入文件名
lstrcpy(buf1,"f");
lstrcat(buf1,IntToStr(i).c_str());
fileres=FindResource(myexe,buf1,"MYRES");
ressize=SizeofResource(myexe,fileres);
lstrcpy(buf1,path);
lstrcat(buf1,buf);
file=LoadResource(myexe,fileres); //装入与文件名对应的资源
ff=CreateFile(buf1,GENERIC_WRITE,0,NULL,OPEN_ALWAYS,FILE_ATTRIBUTE_NORMAL,NULL);
Form1->StatusBar1->SimpleText="正在复制文件:"+AnsiString(buf1);
Form1->Memo1->Lines->Add("正在复制文件:"+AnsiString(buf1));
WriteFile(ff,file,ressize,&aa,NULL);
CloseHandle(ff);
float temp;
temp=(float)ressize/(float)allfilesize*100;
Form1->ProgressBar1->Position=Form1->ProgressBar1->Position+int(temp+0.5);
}
|