C++ Builder创建和调用dll中的资源
程序开发中经常会用到一些图标、图片、光标、声音等,我们称它们为资源(Resource)。当多个窗口用到同样的资源时,可以将这些公共的资源放到一个dll文件里调用,这样,由于定位资源比在磁盘中定位文件花费时间少,所以应用程序执行会更快。多种资源放在一个文件中,减少了图标、图片、光标、声音等文件数量,从而可以减小应用程序的大小。不用怕用户在使用过程中的不小心而损坏了资源文件致使程序无法正常运行。当然也可以不编为dll,而和应用程序放一起,用到时再LoadFromFile,但这样容易丢失,一旦丢失了将影响程序的运行。
下面介绍如何将资源装入dll文件并在应用程序中调用。
一、创建资源文件
首先将要用到的资源放在同一个文件夹下,新建一记事本,将后缀名改为.rc(此处暂以rc.rc的文件名为例)。注意事项:创建的资源文件名不要和工程名相同,因为bcb创建工程时会自动创建一个和工程名相同的资源文件,并且最好将资源文件保存到和工程文件同一个文件夹中。打开创建的rc.rc文件并按照如下格式写入资源信息:
资源标识符 关键字 资源文件名
格式说明:
① 资源标识符:程序中调用资源时的特定标号,由自己定义。
② 关键字:标识资源文件类型;例如:
Wave : 资源文件是声音文件;
RCDATA: JPEG文件;
AVI : AVI动画;
ICON : 图标文件;
BITMAP: 位图文件;
CURSOR: 光标文件;
RMI : RMI音乐文件;
MIDI : MIDI音乐文件
EXEFILE:可执行文件
JPEG: jpg图片
EXEFILE: exe可执行文件
③ 资源文件名:加入的资源文件名(要带扩展名,可以带路径名构成全文件名);
④ 举例:假如有三个图形:“open.bmp”、“save.bmp”、“ico.ico”,奖在rc.rc文件里写下如下信息:
OPEN BITMAP open.bmp
SAVE BITMAP save.bmp
ICO ICON ico.ico
资源文件名可以不加引号,也可以加引号。保存rc.rc。
二、创建dll工程
1、在C++ Builder中新建DLL Wizard,在弹出的对话框上选择Use VCL,如下图:
下面介绍如何将资源装入dll文件并在应用程序中调用。
一、创建资源文件
首先将要用到的资源放在同一个文件夹下,新建一记事本,将后缀名改为.rc(此处暂以rc.rc的文件名为例)。注意事项:创建的资源文件名不要和工程名相同,因为bcb创建工程时会自动创建一个和工程名相同的资源文件,并且最好将资源文件保存到和工程文件同一个文件夹中。打开创建的rc.rc文件并按照如下格式写入资源信息:
资源标识符 关键字 资源文件名
格式说明:
① 资源标识符:程序中调用资源时的特定标号,由自己定义。
② 关键字:标识资源文件类型;例如:
Wave : 资源文件是声音文件;
RCDATA: JPEG文件;
AVI : AVI动画;
ICON : 图标文件;
BITMAP: 位图文件;
CURSOR: 光标文件;
RMI : RMI音乐文件;
MIDI : MIDI音乐文件
EXEFILE:可执行文件
JPEG: jpg图片
EXEFILE: exe可执行文件
③ 资源文件名:加入的资源文件名(要带扩展名,可以带路径名构成全文件名);
④ 举例:假如有三个图形:“open.bmp”、“save.bmp”、“ico.ico”,奖在rc.rc文件里写下如下信息:
OPEN BITMAP open.bmp
SAVE BITMAP save.bmp
ICO ICON ico.ico
资源文件名可以不加引号,也可以加引号。保存rc.rc。
二、创建dll工程
1、在C++ Builder中新建DLL Wizard,在弹出的对话框上选择Use VCL,如下图:
点击“OK”后完成创建,并保存到上述资源文件所在文件夹。
2、点菜单上的“工程”/“添加到工程”(或Shift+F11),选择上述写好的rc.rc文件。
3、在Unit1.cpp里添加以下两个函数:
//-----------------------------------------------------------------------------
//加载bmp位图
extern "C" __declspec(dllexport) HBITMAP __stdcall GetBitmap(AnsiString rcName);
HBITMAP __stdcall GetBitmap(AnsiString rcName)
{ return ::LoadBitmap(HInstance, rcName.c_str()); }
//加载ico图标
extern "C" __declspec(dllexport) HICON __stdcall GetIcon(AnsiString);
HICON __stdcall GetIcon(AnsiString rcName)
{ return ::LoadIcon(HInstance, rcName.c_str());
}
//-----------------------------------------------------------------------
其中rcName为资源标识符。
4、保存工程,编译(Alt+F9),并生成Project1(Ctrl+F9)。现在便得到了Project1.dll。
三、调用dll中的资源
重新新建一工程(Application),在Form1上加两个BitBtn,并将其Caption属性分别设为“打开”和“存储”,在Form1的OnShow函数下写下如下代码:
//---------------------------------------------------------------------------
void __fastcall TForm1::FormShow(TObject *Sender)
{
HBITMAP __stdcall (*GetBitmap)(AnsiString); //定义函数原型
HICON __stdcall (*GetIcon)(AnsiString); //定义函数原型
HINSTANCE Hdl;
Hdl = ::LoadLibrary("Project1.dll"); //载入DLL
if(Hdl != NULL)
{
GetBitmap=(HBITMAP __stdcall (*)(AnsiString))::GetProcAddress(Hdl,"GetBitmap");
//取函数入口地址
if(GetBitmap!= NULL)
{BitBtn1->Glyph->Handle=GetBitmap("OPEN");
BitBtn2->Glyph->Handle=GetBitmap("SAVE");
}
GetIcon=(HICON __stdcall (*)(AnsiString))::GetProcAddress(Hdl,"GetIcon");
//获取函数入口地址
if(GetIcon!= NULL)
{Icon->Handle= GetIcon("ICO"); }//改变窗体的图标
::FreeLibrary(Hdl);
}
else {MessageBox(Handle,"不能载入资源Project.dll!","错误",48);}
}
//---------------------------------------------------------------------------
然后运行,则窗体的图标变为资源中的图标,两个BitBtn也加载了图形。
到此已完成dll中资源的调用功能。
四、其它资源
上述只介绍了位图和图标的存取,分别用到LoadIcon和LoadBitmap,对于其它资源的存取如下:
位图:
mage1->Picture->Bitmap->Handle=LoadBitmap(HInstance,'资源标识符');
或Image1->Picture->Bitmap->LoadFromResourceName(HInstance,'资源标识符');
鼠标:
Screen->Cursors[1]=LoadCursor(HInstance,'资源标识符');
Screen->Cursors[2]=LoadCursor(HInstance,'资源标识符2');
Form1->Cursor=1; //仅用于Form1
Image1->Cursor=2;//仅用于Image1
Screen->Cursor=1;// 整个应用程序上都改变
在这里定义了两套鼠标,使用的话需要在rc文件种定义两次鼠标资源文件。
图标:
Application->Icon->Handle = LoadIcon(HInstance,'资源标识符');
AVI文件
在工程中添加一TAnimate控件(在Win32控件面板上),在需要的地方加入:
Animate1->ResName="MyAvi" ; //资源标识
Animate1->Active = True ;
实践中的一点结果:并不是所有的AVI资源都可以用TAnimate组件来播放,编定程序时要测试。遇到不能用TAnimate组件来播放的AVI资源,则可以把它从资源文件里分离出来,再使用相应的播放组件比如TMediaPlayer来播放。使用完再把分离出来的临时文件删除掉。
Wave文件
#Include"MMSystem.h"
char *wav_handle ;
HRSRC h = FindResource(HInstance,"MyWav","WAV");
HGLOBAL h1 = LoadResource(HInstance, h);
wav_handle = (char *)LockResource(h1);
PlaySound(wav_handle,NULL, SND_MEMORY | SND_ASYNC);
FreeResource(h1);
//PlaySound(wav_handle,NULL, SND_MEMORY | SND_ASYNC|SND_LOOP);
//重复播放
JPEG图片
#include"jpeg.hpp"
TJPEGImage *Fjpg=new TJPEGImage();
TResourceStream *FStream=new TResourceStream((int)HInstance,"MyJpg","JPEG");
Fjpg->LoadFromStream(FStream) ;
Image2->Picture->Bitmap->Assign(Fjpg);
EXE文件
先分离出来,再执行。
SetCurrentDir(path); //设置当前工作目录
TResourceStream &res = *new TResourceStream( (int)HInstance, AnsiString("process"),"EXEFILE" ) ;
String file=path+"//process.exe";
res.SaveToFile(file);
delete &res;
STARTUPINFO si;
PROCESS_INFORMATION pi;
ZeroMemory(&si,sizeof(si));
si.cb=sizeof(si);
ZeroMemory(&pi,sizeof(pi));
CreateProcess(file3.c_str(),NULL,NULL,NULL,FALSE,CREATE_NO_WINDOW,NULL,NULL,&si,&pi);
注意,这里CREATE_NO_WINDOW为不显示窗口,根据需求设置不同的参数,请参照MSDN。
其它资源
可以把资源文件中的源文件分离出来,建立一个临时的物理文件存在于应用程序路径下,然后可以用相应类型的组件来或方法来使用该文件。当程序退出时再不忘把该临时文件删除掉。
例如:
string tmpDirectory ;
TResourceStream myres ;
tmpDirectory = ExtractFilePath(paramstr(0));
if (FileExists(tmpDirectory + "//Music1.RMI"))
myres = TResourceStream->Create(hinstance,"music1","RMI");
myres->SaveToFile(tmpDirectory + "Music1.RMI");//从资源文件中分离出来
myres->Free;
程序退出的时候删除:
if(FileExists(tempDirectory + '//music1.RMI'))
DeleteFile(tempDirectory + '//music1.RMI');
2、点菜单上的“工程”/“添加到工程”(或Shift+F11),选择上述写好的rc.rc文件。
3、在Unit1.cpp里添加以下两个函数:
//-----------------------------------------------------------------------------
//加载bmp位图
extern "C" __declspec(dllexport) HBITMAP __stdcall GetBitmap(AnsiString rcName);
HBITMAP __stdcall GetBitmap(AnsiString rcName)
{ return ::LoadBitmap(HInstance, rcName.c_str()); }
//加载ico图标
extern "C" __declspec(dllexport) HICON __stdcall GetIcon(AnsiString);
HICON __stdcall GetIcon(AnsiString rcName)
{ return ::LoadIcon(HInstance, rcName.c_str());
}
//-----------------------------------------------------------------------
其中rcName为资源标识符。
4、保存工程,编译(Alt+F9),并生成Project1(Ctrl+F9)。现在便得到了Project1.dll。
三、调用dll中的资源
重新新建一工程(Application),在Form1上加两个BitBtn,并将其Caption属性分别设为“打开”和“存储”,在Form1的OnShow函数下写下如下代码:
//---------------------------------------------------------------------------
void __fastcall TForm1::FormShow(TObject *Sender)
{
HBITMAP __stdcall (*GetBitmap)(AnsiString); //定义函数原型
HICON __stdcall (*GetIcon)(AnsiString); //定义函数原型
HINSTANCE Hdl;
Hdl = ::LoadLibrary("Project1.dll"); //载入DLL
if(Hdl != NULL)
{
GetBitmap=(HBITMAP __stdcall (*)(AnsiString))::GetProcAddress(Hdl,"GetBitmap");
//取函数入口地址
if(GetBitmap!= NULL)
{BitBtn1->Glyph->Handle=GetBitmap("OPEN");
BitBtn2->Glyph->Handle=GetBitmap("SAVE");
}
GetIcon=(HICON __stdcall (*)(AnsiString))::GetProcAddress(Hdl,"GetIcon");
//获取函数入口地址
if(GetIcon!= NULL)
{Icon->Handle= GetIcon("ICO"); }//改变窗体的图标
::FreeLibrary(Hdl);
}
else {MessageBox(Handle,"不能载入资源Project.dll!","错误",48);}
}
//---------------------------------------------------------------------------
然后运行,则窗体的图标变为资源中的图标,两个BitBtn也加载了图形。
到此已完成dll中资源的调用功能。
四、其它资源
上述只介绍了位图和图标的存取,分别用到LoadIcon和LoadBitmap,对于其它资源的存取如下:
位图:
mage1->Picture->Bitmap->Handle=LoadBitmap(HInstance,'资源标识符');
或Image1->Picture->Bitmap->LoadFromResourceName(HInstance,'资源标识符');
鼠标:
Screen->Cursors[1]=LoadCursor(HInstance,'资源标识符');
Screen->Cursors[2]=LoadCursor(HInstance,'资源标识符2');
Form1->Cursor=1; //仅用于Form1
Image1->Cursor=2;//仅用于Image1
Screen->Cursor=1;// 整个应用程序上都改变
在这里定义了两套鼠标,使用的话需要在rc文件种定义两次鼠标资源文件。
图标:
Application->Icon->Handle = LoadIcon(HInstance,'资源标识符');
AVI文件
在工程中添加一TAnimate控件(在Win32控件面板上),在需要的地方加入:
Animate1->ResName="MyAvi" ; //资源标识
Animate1->Active = True ;
实践中的一点结果:并不是所有的AVI资源都可以用TAnimate组件来播放,编定程序时要测试。遇到不能用TAnimate组件来播放的AVI资源,则可以把它从资源文件里分离出来,再使用相应的播放组件比如TMediaPlayer来播放。使用完再把分离出来的临时文件删除掉。
Wave文件
#Include"MMSystem.h"
char *wav_handle ;
HRSRC h = FindResource(HInstance,"MyWav","WAV");
HGLOBAL h1 = LoadResource(HInstance, h);
wav_handle = (char *)LockResource(h1);
PlaySound(wav_handle,NULL, SND_MEMORY | SND_ASYNC);
FreeResource(h1);
//PlaySound(wav_handle,NULL, SND_MEMORY | SND_ASYNC|SND_LOOP);
//重复播放
JPEG图片
#include"jpeg.hpp"
TJPEGImage *Fjpg=new TJPEGImage();
TResourceStream *FStream=new TResourceStream((int)HInstance,"MyJpg","JPEG");
Fjpg->LoadFromStream(FStream) ;
Image2->Picture->Bitmap->Assign(Fjpg);
EXE文件
先分离出来,再执行。
SetCurrentDir(path); //设置当前工作目录
TResourceStream &res = *new TResourceStream( (int)HInstance, AnsiString("process"),"EXEFILE" ) ;
String file=path+"//process.exe";
res.SaveToFile(file);
delete &res;
STARTUPINFO si;
PROCESS_INFORMATION pi;
ZeroMemory(&si,sizeof(si));
si.cb=sizeof(si);
ZeroMemory(&pi,sizeof(pi));
CreateProcess(file3.c_str(),NULL,NULL,NULL,FALSE,CREATE_NO_WINDOW,NULL,NULL,&si,&pi);
注意,这里CREATE_NO_WINDOW为不显示窗口,根据需求设置不同的参数,请参照MSDN。
其它资源
可以把资源文件中的源文件分离出来,建立一个临时的物理文件存在于应用程序路径下,然后可以用相应类型的组件来或方法来使用该文件。当程序退出时再不忘把该临时文件删除掉。
例如:
string tmpDirectory ;
TResourceStream myres ;
tmpDirectory = ExtractFilePath(paramstr(0));
if (FileExists(tmpDirectory + "//Music1.RMI"))
myres = TResourceStream->Create(hinstance,"music1","RMI");
myres->SaveToFile(tmpDirectory + "Music1.RMI");//从资源文件中分离出来
myres->Free;
程序退出的时候删除:
if(FileExists(tempDirectory + '//music1.RMI'))
DeleteFile(tempDirectory + '//music1.RMI');
结论:这篇文章是网上的。我照着做了一遍,遇到以下问题:
1、创建DLL Wizard 的程序,编译时报错:invalid bitmap format
2、即便Remove 掉 rc资源,编译程序无错,但是没有dll 和 lib 文件生成 ,只有obj文件
3、测试dll 程序 编译报错:nonportable pointer comparsion
期待网友帮忙,非常感谢!