使用DLL封装窗体和业务类
生活随笔
收集整理的這篇文章主要介紹了
使用DLL封装窗体和业务类
小編覺得挺不錯的,現在分享給大家,幫大家做個參考.
軟件開發的一種架構思路:
使用DLL封裝一組窗體和業務類,真正的模塊化編程,方便工作組進行開發的分工與合作。
打個比方:
經常開發進銷存軟件,那么我們把“入庫單、返廠單、盤點單。。。”等單據的界面窗口以及業務類封裝進一個DLL中。而將“計量單位、供應商檔案、商品檔案。。?!钡然拘畔⒌慕缑娲翱谝约皹I務類也封裝進一個DLL中。窗口類可使用“可視化窗體繼承或框架繼承”,比如“入庫單同返廠單。。?!钡葐螕晒蚕硪粋€單據祖先類。
談Delphi編程中資源文件的應用
?????一、初級應用篇
?????資源文件一般為擴展名為res的文件,在VC中資源文件用得非常普遍,但Delphi在其聯機幫助中對資源文件沒作什么介紹。其實利用其自帶的資源編譯工具BRCC32.EXE(一般位于DelphiBIN目錄下),我們完全可以做出跟VC一樣效果的文件來。
?????資源文件最大的好處是能將一些在必要時才調用的文件跟可執行文件一起編譯,生成一個文件。這樣做最大的好處就是使外部文件免遭破壞。例如在一個程序中你要臨時調用一幅圖片,一般作法是把圖片放在某一路徑下(通常是主程序所在路徑),但如果用戶路徑誤刪你的圖片文件則可能使程序找不到相應文件而出錯崩潰。另外,如果你想自己的程序界面美觀,想用一些自定義光標,也要用到資源文件。
?????資源文件的使用步驟為:
?????1.編寫rc腳本文本
?????用記事本或其它文本編輯器編寫一個擴展名為rc的文件。例如:
?????mycur?cursor?move.cur?//加入光標
?????mypic?Bitmap?Water.BMP?//加入位圖
?????mywav?WAVE?happy.wav?//加入聲音
?????myAVI?AVI?EPOEN.AVI?//加入視頻
?????myIco?ICON?CJT.ICO?//加入圖標
?????格式分別為在資源文件中的名稱->類型->實際文件名稱,例如上面第一行定義一個名為mycur的光標,實際名稱為加入光標move.cur。
?????2.將rc文件編譯成res資源文件
?????將腳本文件和實際文件拷到Brcc32.EXE所在目錄,執行DOS命令。格式為:Brcc32?腳本文件(回車),例如有一名為myfirst.rc的腳本文件,則執行Brcc32?myfirst.rc(回車)即可。如果你是懶人,也可新建一批處理文件,內容只有一行:Brcc32?mufist.rc。(因為Delphi安裝后一般會在自動批處理文件中指明搜索路徑的)如果編譯成功,則會生成一個結尾為res的文件,這個文件就是我們需要的資源文件。
?????3.在Delphi單元中加入資源文件
?????將生成的res資源文件拷貝到你所編程序的路徑下,在單元文件{$R?*DFM}后加上一句{$R?mufirst.res},則將res文件加入去,編譯后資?源文件即已包含在可執行文件中了。若你有多個資源文件,也按上法依次加入。
?????4.在Delphi程序中調用資源文件
?????資源文件在Delphi中的關鍵字為hinstance,下面給出具體用法。
?????<1>光標的調用
?????首先在程序中定義一個值大于0的常量,因為Delphi本身用0到負16來索引默認的光標,所以我們制定的光標應從表面上1開始索引。然后在窗口的Oncreat事件中添加以下代碼:screen.cursor[35]:=Loadcursor(hinstance,'mycur');其中35為大于1的常量,mycur為光標在資源文件中的名字。如果希望在其他控件上使用定制光標,例如Panel控件,只需在程序的適當處加入以下代碼:Panel1.cursor:=35;
?????<2>位圖的調用
?????新建一項工程,添加一Timage控件,在需要顯示的地方寫以下代碼(其中"mypic"為位圖資源文件中的名稱):
?????Var?mymap:Hbitmap;
?????begin?
?????mymap:=LoadBitmap(hinstance,'mypic');
?????Image1.picture.Bitmap.Handle:=mymap;
?????end;
?????〈3〉AVI文件的調用
?????新建一工程,添加一Animate控件,在需要的地方加入(其中myAVI為視頻文件在資源文件中的名稱):
?????animater1.resname:='myAVI';
?????animater1.Active:=true;
?????〈4〉調用WAV文件
?????在uses中加入mmsystm單元,以便在程序中播放WAV文件。播放時Playsound(pchar('mywav'),hinstance,sndsync?or?snd_resource);其中mywav為聲音文件在資源中的名稱。
?????〈5〉加入光標
?????加入光標比較容易,只要將res文件加入單元文件中即可。但需注意,名稱最好取"W"."WW"等,使第一個字母盡量靠后,以免與主程序的圖標順序顛倒。這樣一來,別人在使用你的程序時如果想選擇其它圖標就有很多選擇了。
?????補充:
?????1.資源類型除上述類型外,還可以字體文件,字符串文件等;
?????2.資源文件不但可以在標準圖形界面下使用還可在控制臺下使用。下面我們來試驗一下:新建一工程,將唯一的一個Form刪除,然后修改工程文件。增加一句{$Apptype?console},在uses子句中加入mmsystem,并將其它引用單元刪掉。將Begin和end之間語句刪掉。至此,我們就可和Turbo?PASCAL下編程序一樣,且還可以調用windows的API和資源。將資源文件----{$R?myfist.res}加入。在Begin和end之間寫下:
?????writeln('演示程序,按任意鍵開始!');
?????readln;
?????playsound(pchar('mywav'),hinstance,snd_sync?or?snd_resource);
?????writeln('演示結束!');
?????運行程序,將彈出一個標準DOS窗口,按任意鍵播放聲音文件。是不是很COOL呢?我曾下載過一個播放器,在其安裝目錄下我發現有一“DOS程序”,用鼠標雙擊它便彈出一個DOS窗口,顯示DOS時代特有的畫圖,并有背景音樂!可能就是用這個方法做的。
?????3.Delphi本身自帶了一個叫Image?Editor的工具,同樣可以編輯資源文本,但和本文的方法比較,可得出下表:
?????Image?Editor?Brcc32
?????BMP?只支持16位色?任意色
?????光標?黑白兩色?任意色
?????ICO?只支持16位色?任意色
?????AVI?不支持?支持
?????WAV?不支持?支持
?????字體?不支持?支持
?????字符串?不支持?支持
?????上面說的是直接在程序本身的調用。其實資源文件還有其它用法。比如說在你的程序攜帶其它文件,要用的時候釋放出來。例如:myexe?exefile?'ha1.exe'//腳本文件
?????下面是自定義釋放函數ExtractRes,本例中使用如下:ExtractRes('exefile','myexe','c:new.exe');就把ha1.exe以new.exe為名字保存到C盤根目錄下了。
?????function?TForm1.ExtractRes(ResType,?ResName,?ResNewName:?string):?boolean;
?????var
?????Res:?TResourceStream;
?????begin
?????try
?????Res?:=?TResourceStream.Create(Hinstance,?Resname,?Pchar(ResType));
?????try
?????Res.SavetoFile(ResNewName);
?????Result?:=?true;
?????finally
?????Res.Free;
?????end;
?????except
?????Result?:=?false;
?????end;
?????
?????二、中級應用篇:
?????上面我們已經知道如何把一副BMP圖像從資源文件里面讀出來,但是BMP文件太大了,JPG文件應用的相對較多。那么如何把JPG圖像讀出來呢?用資源文件加流方式即可。具體方法如下:
?????(1)MyJpg?JPEG My.JPG
?????(2)Var
????? Stream:TStream;
????? MyJpg:TJpegImage;
????? Begin
????? Stream:=TResourceStream.Cceat(HINSTANCE,'MyJpg','JPEG');
????? Try
????? MyJpg:=TJpegImage.Create;
????? Try
????? MyJpg.LoadfromStream(Stream);
????? Image1.Picture.Assignc(MyJpg);
????? Finally
????? MyJpg.Free;
????? end;
????? Finally
????? Stream.Free;
????? end;
????? end;
?????讀取其它圖片文件也是一樣的。比如說gif動畫文件,當然前提是你有一個gif.pas,這個單元很多站點都有的,可以自己去找找。實際應用中我還發現用上面的代碼可以直接顯示資源文件中的ICON和BMP。
?????說到圖形處理,實際上還可以用Delphi創建、調用純圖標資源的DLL。比如說你可以看看超級解霸目錄下的Dll,很多就是純圖標資源而已。具體方法如下:
?????(1)創建一個Hicon.RES文件,這里不再重復;
?????(2)新建一文本文件Icon.dpr,內容如下:
?????library?Icon;
?????{$R?Icon.RES}
?????begin
?????end.
?????用Delphi打開編譯即可得到Icon.dll。
?????(3)實際調用方法如下:
?????......
????? Private
????? Hinst:THANDLE;
?????......
????? Var Hicon:THANDLE;
?????begin
????? Hinst:=Loadlibrary('Icon.dll');
????? If Hinst=0 Then Exit;
????? Hicon:=Loadicon(Hinst,Pchar(Edit1.Text));
???????If Hicon<>0 Then Image1.Picture.Icon.Handle:=Hicon;
????? FreeLibrary(Hinst);
?????end;
?????如果你的程序想在國際上供使用不同語言的人使用的話,用Dll來存放字符資源將是一個好方法。因為Dll不象ini文件那樣可以被人隨便修改,特別是有時侯如果想保存一些版權信息的話用Dll就再好不過了。比如說你準備開發一個“漢字簡繁體翻譯器”軟件,準備提供Gb32、Big5碼和英文三種語言菜單給用戶,那么你可以試試用Dll
來保存字符資源。
?????我們需要建立三個Dll。第一步當然是寫Rc文件,舉Gb32碼為例,內容如下:
?????/*MySc.rc*/
?????#define?IDS_MainForm_Caption??1
?????#define?IDS_BtnOpen_Caption???2
?????#define?IDS_BtnSave_Caption???3
?????#define?IDS_BtnBig5_Caption???4
?????#define?IDS_BtnGb32_Caption???5
?????#define?IDS_BtnHelp_Caption???6
?????#define?IDS_Help_Shelp????????7
?????Stringtable
?????{
?????IDS_MainForm_Caption,"漢字簡繁體翻譯器"
?????IDS_BtnOpen_Caption,"打開文件"
?????IDS_BtnSave_Caption,"保存文件"
?????IDS_BtnBig5_Caption,"轉換成Big5"
?????IDS_BtnGb32_Caption,"轉換成Gb32"
?????IDS_BtnHelp_Caption,"幫助"
?????IDS_Help_Shelp,"輸入文字或打開文件后按需要點擊按鈕即可轉換!"
?????}
?????另外兩個Dll用同樣的方法生成。
?????第二步是Brcc32編譯為Res文件后用上面的方法得到Dll文件。下面來應用一下:新建一個工程,放上五個Button:BtnOpen、BtnSave、BtnBig5、BtnGb32和BtnHelp,還有一個TComboBox:CbSelect用來選擇語言種類的。
具體代碼如下:
?????unit?Unit1;
?????interface
?????......
???????private
?????????SHelp:?string;
?????????function?SearchLanguagePack:?TStrings;
?????????procedure?SetActiveLanguage(LanguageName:?string);
?????????{?Private?declarations?}
?????......
?????implementation
?????procedure?TForm1.CbSelectChange(Sender:?TObject);
?????begin
???????SetActiveLanguage(CbSelect.Text);//調用相應Dll文件讀取相應字符.
?????end;
?????procedure?TForm1.FormCreate(Sender:?TObject);
?????begin
???????CbSelect.Items.AddStrings(SearchLanguagePack);//搜索當前目錄下所有的Dll文件名稱
?????end;
?????function?TForm1.SearchLanguagePack:?TStrings;
?????var
???????ResultStrings:?TStrings;
???????DosError:?integer;
???????SearchRec:?TsearchRec;
?????begin
???????ResultStrings?:=?TStringList.Create;
???????DosError?:=?FindFirst(ExtractFilePath(ParamStr(0))?+?'*.dll',?faAnyFile,?SearchRec);
???????while?DosError?=?0?do
?????????begin
??????????ResultStrings.Add(ChangeFileExt(SearchRec.Name,?''));
??????????DosError?:=?FindNext(SearchRec);
?????????end;
???????FindClose(SearchRec);
???????Result?:=?ResultStrings;
?????end;
?????
?????procedure?TForm1.SetActiveLanguage(LanguageName:?string);
?????var
???????Hdll:?Hmodule;
???????MyChar:?array[0..254]?of?char;
???????DllFileName:?string;
?????begin
???????DllFileName?:=?ExtractFilePath(ParamStr(0))?+?LanguageName?+?'.dll';
???????if?not?FileExists(DllFileName)?then?Exit;
???????Hdll?:=?loadlibrary(Pchar(DllFileName));
?????
???????Loadstring(hdll,?1,?MyChar,?254);
???????Self.Caption?:=?MyChar;
?????//讀取字符資源,1表示資源文件中定義的1
???????Loadstring(hdll,?1,?MyChar,?254);
???????Self.Caption?:=?MyChar;
?????
???????Loadstring(hdll,?2,?MyChar,?254);
???????BtnOpen.Caption?:=?MyChar;
?????
???????Loadstring(hdll,?3,?MyChar,?254);
???????BtnSave.Caption?:=?MyChar;
?????
???????Loadstring(hdll,?4,?MyChar,?254);
???????BtnBig5.Caption?:=?MyChar;
?????
???????Loadstring(hdll,?5,?MyChar,?254);
???????BtnGb32.Caption?:=?MyChar;
?????
???????Loadstring(hdll,?6,?MyChar,?254);
???????BtnHelp.Caption?:=?MyChar;
?????
???????Loadstring(hdll,?7,?MyChar,?254);
???????SHelp?:=?MyChar;
?????
???????Freelibrary(hdll);
???????Application.Title?:=?Self.Caption;
???????BtnOpen.Visible?:=?True;
???????BtnSave.Visible?:=?True;
???????BtnBig5.Visible?:=?True;
???????BtnGb32.Visible?:=?True;
???????BtnHelp.Visible?:=?True;
?????end;
?????procedure?TForm1.BtnHelpClick(Sender:?TObject);
?????begin
???????Application.MessageBox(Pchar(SHelp),?'Http://lovejingtao.126.com',?MB_ICONINFORMATION);
?????end;
?????end.
?????可能你會說,這種方法還不如我自己在程序中直接定義三種具體的值來的方便。甚至我自己自定義一個結構好了,用不著用DLL那么麻煩的。但是如果你的程序要用的字符很多呢?比如說Windows操作系統,本身就有簡體中文、繁體中文、英文等版本,用Dll的話只要直接替換DLL即可,而不用每發行一個版本就打開代碼來修改一次。這樣一來可以大大減少工作量和出錯的機會。
?????說到這里,再多說一句:Windows系統本身很多Dll帶有了圖片等資源,我們可以在程序中直接調用,這樣一來我們的EXE也可以減少不少!當然最小的方法是實時生成技術。老外曾經寫了一個67KB的程序就是利用了這個方法,感興趣的朋友可以到http://go4.163.com/lovejingtao/ha1.exe下載。
?????
?????三、高級應用篇
?????Delphi是個很有效率的開發工具,但是它有一個缺點就是生成的EXE文件太大。一個程序就算只有一個空窗口體積也有286KB。如果直接用API來寫的話程序體積是小了,但是又太繁瑣,無法立即看到界面效果,根本談不上是可視化開發。其實并非“魚與熊掌不可兼得”,利用資源文件我們就可以輕松達到這個目的。
?????在開始之前,我們需要一個可以編輯資源文件的工具。這類工具很多,比如說Resource?WorkShop就是非常好的一個。如果一時找不到,利用VC的編輯器來也是可以的。下面我們就以VC的為例示范如何創建一個窗口資源文件。
?????運行VC,打開菜單“File/New”,將出現一個多項選擇頁。我們選擇“Files/Resource?Template”,在右邊的File填上Demo,Location選擇保存路徑,然后點擊按鈕OK返回VC開發環境。
?????選擇菜單“Insert/Resource”,將出現一個資源類型選擇框。我們把鼠標移到Dialog上面,不用展開,點擊右邊的New即可,這時候返回VC開發環境并出現一個只有關閉按鈕和兩個Button的窗體。將鼠標選定窗體,擊右鍵選擇最后一項Properties,將出現一個設置窗口,將ID改為“MAINFORM”(注意:跟下面添加的其它控件的屬性設置方法不同,主窗口的ID必須把雙引號寫上去,而且名稱必須為大寫。否則程序將找不到資源。程序會一運行就退出了。)Caption改為“安裝程序”,這時候可以立刻看到窗口的標題變成了“安裝程序”,把Styles的Minimize?box選上,More?Styles的Center勾上使程序運行時的位置居中。當然你也可以設置它的坐標,其它保留默認值即可?;氐介_發環境,在控件框里面分別選擇一個Static?Text,一個Edit?Box,一個Button和一個Group?Box添加到窗體上面,把它們按照自己的愛好排列整齊,然后逐個修改它們的屬性。方法就是按照上面說的選定控件后擊右鍵選擇最后一項Properties,在出現的屬性框里面修改。其中屬性如下:Group?Box的Caption屬性清空,Static?Text的Caption屬性改為“請選擇安裝目錄:”,Edit?Box的ID改為10001,第一個Button的ID為10002,Caption屬性為“選擇”,第二個Button的ID為10003,Caption屬性為“安裝”,第三個Button的ID為10004,Caption屬性為“退出”。
?????為了使程序更加完美,我們為它再添加一個菜單IDR_MENU1。選擇“Insert/Resource/Menu”,我們這里只簡單添加一項“文件/退出”,其中“退出”的ID為10005。然后在主窗口的屬性Menu設定為IDR_MENU1即可。
?????為了使程序更加美觀,我們再添加一個小圖標,同時這也將是我們程序的圖標。選擇“Insert/Resource/Iconv/Import”,選擇一個圖標文件,并將它的ID設置為"MAINICON"(注意:必須把雙引號寫上而且字母為大寫),為窗口添加一個Picture控件并設置它的屬性Type:Icon,Image下拉選擇剛才的圖標MainIcon即可。
?????如果你想為程序在鼠標添加一些信息也是可以的。選擇“Insert/Resource/Version”即可。到這里我們已經完成了一個簡單的“安裝程序”的窗體設計,實際上我們現在就可以在Delphi中調用它了。我們先把“勞動成果”保存起來,選擇“File/Save?As”,在文件類型里選擇“32-bit?Resource?File(.res)”保存為“Demo.res”,文件大小大約為2.65KB。
?????新建一個擴展名為dpr的文本文件MyDemo.Dpr,鍵入如下代碼:
?????Uses?Windows,Messages;
?????{$R?Demo.Res}
?????function?MainDialogProc(
???????DlgWin:?hWnd;
???????DlgMessage:?UINT;
???????DlgWParam:?WPARAM;
???????DlgLParam:?LPARAM
???????)
???????:?integer;?stdcall;
?????begin
????????Result?:=?0;
?????case?DlgMessage?of
????????WM_Close:
??????????begin
??????????PostQuitMessage(0);
??????????Exit;
??????????end;
???????end;
?????end;
?????begin
???????DialogBox(hInstance,?'MAINFORM',?0,?@MainDialogProc);
?????end.
?????用Delphi打開它編譯一次即可產生一個大小為19KB的EXE。是不是很小?!實際上,你甚至只用一行代碼就把它Show出來,不過程序無法關閉而已:
?????Uses?Windows;
?????{$R?Demo.Res}
?????function?MainDialogProc:?integer;
?????begin
????????Result?:=?0;
?????end;
?????begin
???????DialogBox(hInstance,?'MAINFORM',?0,?@MainDialogProc);
?????end.
?????上面的程序只不過是一個空窗口而已,現在我們來寫代碼響應按下相應按鈕響應的事件。完整代碼如下:
?????program?MyDemo;
?????uses?Windows,?Messages,?shlobj;
?????const
???????ID_Edit?=?10001;
???????ID_Selet?=?10002;
???????ID_Setup?=?10003;
???????ID_Quit?=?10004;
???????ID_Exit?=?10005;
?????{$R?Demo.Res}
?????var
???????MainWin:?HWND;
?????
?????function?My_Gettext:?string;
?????var
???????Textlength:?Integer;
???????Text:?PChar;
???????s:?string;
?????begin
???????TextLength?:=?GetWindowTextLength(GetDlgItem(MainWin,?ID_Edit));
???????GetMem(Text,?TextLength?+?1);
???????GetWindowText(GetDlgItem(MainWin,?ID_Edit),?Text,?TextLength?+?1);
???????s?:=?text;
???????FreeMem(Text,?TextLength?+?1);
???????Result?:=?s;
?????end;
?????
?????function?Getmyname:?string;
?????var
???????i,?j:?integer;
?????begin
???????J?:=?3;
???????for?i?:=?1?to?length(ParamStr(0))?do
?????????if?ParamStr(0)[i]?=?''?then?J?:=?I;
???????Result?:=?copy(ParamStr(0),?J?+?1,?length(ParamStr(0))?-?J);
?????end;
?????
?????function?SelectDirectory(handle:?hwnd;?const?Caption:?string;?const?Root:?WideString;?out?Directory:?string):?Boolean;
?????var
???????lpbi:?_browseinfo;
???????buf:?array[0..MAX_PATH]?of?char;
???????id:?ishellfolder;
???????eaten,?att:?cardinal;
???????rt:?pitemidlist;
???????initdir:?pwidechar;
?????begin
???????result?:=?false;
???????lpbi.hwndOwner?:=?handle;
???????lpbi.lpfn?:=?nil;
???????lpbi.lpszTitle?:=?pchar(caption);
???????lpbi.ulFlags?:=?BIF_RETURNONLYFSDIRS?+?BIF_EDITBOX;
???????SHGetDesktopFolder(id);
???????initdir?:=?pwchar(root);
???????id.ParseDisplayName(0,?nil,?initdir,?eaten,?rt,?att);
???????lpbi.pidlRoot?:=?rt;
???????getmem(lpbi.pszDisplayName,?MAX_PATH);
???????try
?????????result?:=?shgetpathfromidlist(shbrowseforfolder(lpbi),?buf);
???????except
?????????freemem(lpbi.pszDisplayName);
???????end;
???????if?result?then
?????????begin
??????????directory?:=?buf;
??????????if?length(directory)?<>?3?then?directory?:=?directory?+?'';
?????????end;
?????end;
?????
?????function?MainDialogProc(
???????DlgWin:?hWnd;
???????DlgMessage:?UINT;
???????DlgWParam:?WPARAM;
???????DlgLParam:?LPARAM
???????)
???????:?integer;?stdcall;
?????var
???????MyIcon:?HICON;
???????Sdir:?string;
?????begin
???????Result?:=?0;
???????case?DlgMessage?of
?????????WM_INITDIALOG:
??????????begin
??????????MyIcon?:=?LoadIcon(hInstance,?'MainIcon');
??????????SetClassLONG(DlgWin,?GCL_HICON,?MyIcon);
??????????MainWin?:=?DlgWin;
??????????end;
?????????WM_Close:
??????????begin
??????????PostQuitMessage(0);
??????????Exit;
??????????end;
?????????WM_COMMAND:
??????????case?LOWORD(DlgWParam)?of
?????
??????????ID_Selet:
??????????begin
??????????if?SelectDirectory(DlgWin,?'請選擇安裝目錄',?'',?Sdir)
??????????then?SendMessage(GetDlgItem(DlgWin,?ID_Edit),?WM_SETTEXT,?0,?lParam(pChar(Sdir)));
??????????end;
??????????ID_Setup:
??????????begin
??????????if?My_Gettext?=?''?then
??????????begin
??????????MessageBox(DlgWin,?'請先選擇安裝文件夾!',?'信息',?MB_ICONINFORMATION?+?MB_OK);
??????????Exit;
??????????end;
??????????CopyFile(pchar(ParamStr(0)),?pchar(My_Gettext?+?Getmyname),?false);
??????????MessageBox(DlgWin,?'安裝完畢!',?'信息',?MB_ICONINFORMATION?+?MB_OK);
??????????PostQuitMessage(0);
??????????Exit;
??????????end;
??????????ID_Quit:
??????????begin
??????????PostQuitMessage(0);
??????????EXIT;
??????????end;
??????????ID_Exit:
??????????begin
??????????if?MessageBox(DlgWin,?'你點擊了菜單“退出”,你確定退出程序嗎?',?'信息',?MB_ICONQUESTION?+?MB_OKCANCEL)?=?IDOK?then
??????????PostQuitMessage(0);
??????????Exit;
??????????end;
??????????end;
???????end;
?????end;
?????begin
???????DialogBox(hInstance,?'MAINFORM',?0,?@MainDialogProc);
?????end. 與50位技術專家面對面20年技術見證,附贈技術全景圖
使用DLL封裝一組窗體和業務類,真正的模塊化編程,方便工作組進行開發的分工與合作。
打個比方:
經常開發進銷存軟件,那么我們把“入庫單、返廠單、盤點單。。。”等單據的界面窗口以及業務類封裝進一個DLL中。而將“計量單位、供應商檔案、商品檔案。。?!钡然拘畔⒌慕缑娲翱谝约皹I務類也封裝進一個DLL中。窗口類可使用“可視化窗體繼承或框架繼承”,比如“入庫單同返廠單。。?!钡葐螕晒蚕硪粋€單據祖先類。
談Delphi編程中資源文件的應用
?????一、初級應用篇
?????資源文件一般為擴展名為res的文件,在VC中資源文件用得非常普遍,但Delphi在其聯機幫助中對資源文件沒作什么介紹。其實利用其自帶的資源編譯工具BRCC32.EXE(一般位于DelphiBIN目錄下),我們完全可以做出跟VC一樣效果的文件來。
?????資源文件最大的好處是能將一些在必要時才調用的文件跟可執行文件一起編譯,生成一個文件。這樣做最大的好處就是使外部文件免遭破壞。例如在一個程序中你要臨時調用一幅圖片,一般作法是把圖片放在某一路徑下(通常是主程序所在路徑),但如果用戶路徑誤刪你的圖片文件則可能使程序找不到相應文件而出錯崩潰。另外,如果你想自己的程序界面美觀,想用一些自定義光標,也要用到資源文件。
?????資源文件的使用步驟為:
?????1.編寫rc腳本文本
?????用記事本或其它文本編輯器編寫一個擴展名為rc的文件。例如:
?????mycur?cursor?move.cur?//加入光標
?????mypic?Bitmap?Water.BMP?//加入位圖
?????mywav?WAVE?happy.wav?//加入聲音
?????myAVI?AVI?EPOEN.AVI?//加入視頻
?????myIco?ICON?CJT.ICO?//加入圖標
?????格式分別為在資源文件中的名稱->類型->實際文件名稱,例如上面第一行定義一個名為mycur的光標,實際名稱為加入光標move.cur。
?????2.將rc文件編譯成res資源文件
?????將腳本文件和實際文件拷到Brcc32.EXE所在目錄,執行DOS命令。格式為:Brcc32?腳本文件(回車),例如有一名為myfirst.rc的腳本文件,則執行Brcc32?myfirst.rc(回車)即可。如果你是懶人,也可新建一批處理文件,內容只有一行:Brcc32?mufist.rc。(因為Delphi安裝后一般會在自動批處理文件中指明搜索路徑的)如果編譯成功,則會生成一個結尾為res的文件,這個文件就是我們需要的資源文件。
?????3.在Delphi單元中加入資源文件
?????將生成的res資源文件拷貝到你所編程序的路徑下,在單元文件{$R?*DFM}后加上一句{$R?mufirst.res},則將res文件加入去,編譯后資?源文件即已包含在可執行文件中了。若你有多個資源文件,也按上法依次加入。
?????4.在Delphi程序中調用資源文件
?????資源文件在Delphi中的關鍵字為hinstance,下面給出具體用法。
?????<1>光標的調用
?????首先在程序中定義一個值大于0的常量,因為Delphi本身用0到負16來索引默認的光標,所以我們制定的光標應從表面上1開始索引。然后在窗口的Oncreat事件中添加以下代碼:screen.cursor[35]:=Loadcursor(hinstance,'mycur');其中35為大于1的常量,mycur為光標在資源文件中的名字。如果希望在其他控件上使用定制光標,例如Panel控件,只需在程序的適當處加入以下代碼:Panel1.cursor:=35;
?????<2>位圖的調用
?????新建一項工程,添加一Timage控件,在需要顯示的地方寫以下代碼(其中"mypic"為位圖資源文件中的名稱):
?????Var?mymap:Hbitmap;
?????begin?
?????mymap:=LoadBitmap(hinstance,'mypic');
?????Image1.picture.Bitmap.Handle:=mymap;
?????end;
?????〈3〉AVI文件的調用
?????新建一工程,添加一Animate控件,在需要的地方加入(其中myAVI為視頻文件在資源文件中的名稱):
?????animater1.resname:='myAVI';
?????animater1.Active:=true;
?????〈4〉調用WAV文件
?????在uses中加入mmsystm單元,以便在程序中播放WAV文件。播放時Playsound(pchar('mywav'),hinstance,sndsync?or?snd_resource);其中mywav為聲音文件在資源中的名稱。
?????〈5〉加入光標
?????加入光標比較容易,只要將res文件加入單元文件中即可。但需注意,名稱最好取"W"."WW"等,使第一個字母盡量靠后,以免與主程序的圖標順序顛倒。這樣一來,別人在使用你的程序時如果想選擇其它圖標就有很多選擇了。
?????補充:
?????1.資源類型除上述類型外,還可以字體文件,字符串文件等;
?????2.資源文件不但可以在標準圖形界面下使用還可在控制臺下使用。下面我們來試驗一下:新建一工程,將唯一的一個Form刪除,然后修改工程文件。增加一句{$Apptype?console},在uses子句中加入mmsystem,并將其它引用單元刪掉。將Begin和end之間語句刪掉。至此,我們就可和Turbo?PASCAL下編程序一樣,且還可以調用windows的API和資源。將資源文件----{$R?myfist.res}加入。在Begin和end之間寫下:
?????writeln('演示程序,按任意鍵開始!');
?????readln;
?????playsound(pchar('mywav'),hinstance,snd_sync?or?snd_resource);
?????writeln('演示結束!');
?????運行程序,將彈出一個標準DOS窗口,按任意鍵播放聲音文件。是不是很COOL呢?我曾下載過一個播放器,在其安裝目錄下我發現有一“DOS程序”,用鼠標雙擊它便彈出一個DOS窗口,顯示DOS時代特有的畫圖,并有背景音樂!可能就是用這個方法做的。
?????3.Delphi本身自帶了一個叫Image?Editor的工具,同樣可以編輯資源文本,但和本文的方法比較,可得出下表:
?????Image?Editor?Brcc32
?????BMP?只支持16位色?任意色
?????光標?黑白兩色?任意色
?????ICO?只支持16位色?任意色
?????AVI?不支持?支持
?????WAV?不支持?支持
?????字體?不支持?支持
?????字符串?不支持?支持
?????上面說的是直接在程序本身的調用。其實資源文件還有其它用法。比如說在你的程序攜帶其它文件,要用的時候釋放出來。例如:myexe?exefile?'ha1.exe'//腳本文件
?????下面是自定義釋放函數ExtractRes,本例中使用如下:ExtractRes('exefile','myexe','c:new.exe');就把ha1.exe以new.exe為名字保存到C盤根目錄下了。
?????function?TForm1.ExtractRes(ResType,?ResName,?ResNewName:?string):?boolean;
?????var
?????Res:?TResourceStream;
?????begin
?????try
?????Res?:=?TResourceStream.Create(Hinstance,?Resname,?Pchar(ResType));
?????try
?????Res.SavetoFile(ResNewName);
?????Result?:=?true;
?????finally
?????Res.Free;
?????end;
?????except
?????Result?:=?false;
?????end;
?????
?????二、中級應用篇:
?????上面我們已經知道如何把一副BMP圖像從資源文件里面讀出來,但是BMP文件太大了,JPG文件應用的相對較多。那么如何把JPG圖像讀出來呢?用資源文件加流方式即可。具體方法如下:
?????(1)MyJpg?JPEG My.JPG
?????(2)Var
????? Stream:TStream;
????? MyJpg:TJpegImage;
????? Begin
????? Stream:=TResourceStream.Cceat(HINSTANCE,'MyJpg','JPEG');
????? Try
????? MyJpg:=TJpegImage.Create;
????? Try
????? MyJpg.LoadfromStream(Stream);
????? Image1.Picture.Assignc(MyJpg);
????? Finally
????? MyJpg.Free;
????? end;
????? Finally
????? Stream.Free;
????? end;
????? end;
?????讀取其它圖片文件也是一樣的。比如說gif動畫文件,當然前提是你有一個gif.pas,這個單元很多站點都有的,可以自己去找找。實際應用中我還發現用上面的代碼可以直接顯示資源文件中的ICON和BMP。
?????說到圖形處理,實際上還可以用Delphi創建、調用純圖標資源的DLL。比如說你可以看看超級解霸目錄下的Dll,很多就是純圖標資源而已。具體方法如下:
?????(1)創建一個Hicon.RES文件,這里不再重復;
?????(2)新建一文本文件Icon.dpr,內容如下:
?????library?Icon;
?????{$R?Icon.RES}
?????begin
?????end.
?????用Delphi打開編譯即可得到Icon.dll。
?????(3)實際調用方法如下:
?????......
????? Private
????? Hinst:THANDLE;
?????......
????? Var Hicon:THANDLE;
?????begin
????? Hinst:=Loadlibrary('Icon.dll');
????? If Hinst=0 Then Exit;
????? Hicon:=Loadicon(Hinst,Pchar(Edit1.Text));
???????If Hicon<>0 Then Image1.Picture.Icon.Handle:=Hicon;
????? FreeLibrary(Hinst);
?????end;
?????如果你的程序想在國際上供使用不同語言的人使用的話,用Dll來存放字符資源將是一個好方法。因為Dll不象ini文件那樣可以被人隨便修改,特別是有時侯如果想保存一些版權信息的話用Dll就再好不過了。比如說你準備開發一個“漢字簡繁體翻譯器”軟件,準備提供Gb32、Big5碼和英文三種語言菜單給用戶,那么你可以試試用Dll
來保存字符資源。
?????我們需要建立三個Dll。第一步當然是寫Rc文件,舉Gb32碼為例,內容如下:
?????/*MySc.rc*/
?????#define?IDS_MainForm_Caption??1
?????#define?IDS_BtnOpen_Caption???2
?????#define?IDS_BtnSave_Caption???3
?????#define?IDS_BtnBig5_Caption???4
?????#define?IDS_BtnGb32_Caption???5
?????#define?IDS_BtnHelp_Caption???6
?????#define?IDS_Help_Shelp????????7
?????Stringtable
?????{
?????IDS_MainForm_Caption,"漢字簡繁體翻譯器"
?????IDS_BtnOpen_Caption,"打開文件"
?????IDS_BtnSave_Caption,"保存文件"
?????IDS_BtnBig5_Caption,"轉換成Big5"
?????IDS_BtnGb32_Caption,"轉換成Gb32"
?????IDS_BtnHelp_Caption,"幫助"
?????IDS_Help_Shelp,"輸入文字或打開文件后按需要點擊按鈕即可轉換!"
?????}
?????另外兩個Dll用同樣的方法生成。
?????第二步是Brcc32編譯為Res文件后用上面的方法得到Dll文件。下面來應用一下:新建一個工程,放上五個Button:BtnOpen、BtnSave、BtnBig5、BtnGb32和BtnHelp,還有一個TComboBox:CbSelect用來選擇語言種類的。
具體代碼如下:
?????unit?Unit1;
?????interface
?????......
???????private
?????????SHelp:?string;
?????????function?SearchLanguagePack:?TStrings;
?????????procedure?SetActiveLanguage(LanguageName:?string);
?????????{?Private?declarations?}
?????......
?????implementation
?????procedure?TForm1.CbSelectChange(Sender:?TObject);
?????begin
???????SetActiveLanguage(CbSelect.Text);//調用相應Dll文件讀取相應字符.
?????end;
?????procedure?TForm1.FormCreate(Sender:?TObject);
?????begin
???????CbSelect.Items.AddStrings(SearchLanguagePack);//搜索當前目錄下所有的Dll文件名稱
?????end;
?????function?TForm1.SearchLanguagePack:?TStrings;
?????var
???????ResultStrings:?TStrings;
???????DosError:?integer;
???????SearchRec:?TsearchRec;
?????begin
???????ResultStrings?:=?TStringList.Create;
???????DosError?:=?FindFirst(ExtractFilePath(ParamStr(0))?+?'*.dll',?faAnyFile,?SearchRec);
???????while?DosError?=?0?do
?????????begin
??????????ResultStrings.Add(ChangeFileExt(SearchRec.Name,?''));
??????????DosError?:=?FindNext(SearchRec);
?????????end;
???????FindClose(SearchRec);
???????Result?:=?ResultStrings;
?????end;
?????
?????procedure?TForm1.SetActiveLanguage(LanguageName:?string);
?????var
???????Hdll:?Hmodule;
???????MyChar:?array[0..254]?of?char;
???????DllFileName:?string;
?????begin
???????DllFileName?:=?ExtractFilePath(ParamStr(0))?+?LanguageName?+?'.dll';
???????if?not?FileExists(DllFileName)?then?Exit;
???????Hdll?:=?loadlibrary(Pchar(DllFileName));
?????
???????Loadstring(hdll,?1,?MyChar,?254);
???????Self.Caption?:=?MyChar;
?????//讀取字符資源,1表示資源文件中定義的1
???????Loadstring(hdll,?1,?MyChar,?254);
???????Self.Caption?:=?MyChar;
?????
???????Loadstring(hdll,?2,?MyChar,?254);
???????BtnOpen.Caption?:=?MyChar;
?????
???????Loadstring(hdll,?3,?MyChar,?254);
???????BtnSave.Caption?:=?MyChar;
?????
???????Loadstring(hdll,?4,?MyChar,?254);
???????BtnBig5.Caption?:=?MyChar;
?????
???????Loadstring(hdll,?5,?MyChar,?254);
???????BtnGb32.Caption?:=?MyChar;
?????
???????Loadstring(hdll,?6,?MyChar,?254);
???????BtnHelp.Caption?:=?MyChar;
?????
???????Loadstring(hdll,?7,?MyChar,?254);
???????SHelp?:=?MyChar;
?????
???????Freelibrary(hdll);
???????Application.Title?:=?Self.Caption;
???????BtnOpen.Visible?:=?True;
???????BtnSave.Visible?:=?True;
???????BtnBig5.Visible?:=?True;
???????BtnGb32.Visible?:=?True;
???????BtnHelp.Visible?:=?True;
?????end;
?????procedure?TForm1.BtnHelpClick(Sender:?TObject);
?????begin
???????Application.MessageBox(Pchar(SHelp),?'Http://lovejingtao.126.com',?MB_ICONINFORMATION);
?????end;
?????end.
?????可能你會說,這種方法還不如我自己在程序中直接定義三種具體的值來的方便。甚至我自己自定義一個結構好了,用不著用DLL那么麻煩的。但是如果你的程序要用的字符很多呢?比如說Windows操作系統,本身就有簡體中文、繁體中文、英文等版本,用Dll的話只要直接替換DLL即可,而不用每發行一個版本就打開代碼來修改一次。這樣一來可以大大減少工作量和出錯的機會。
?????說到這里,再多說一句:Windows系統本身很多Dll帶有了圖片等資源,我們可以在程序中直接調用,這樣一來我們的EXE也可以減少不少!當然最小的方法是實時生成技術。老外曾經寫了一個67KB的程序就是利用了這個方法,感興趣的朋友可以到http://go4.163.com/lovejingtao/ha1.exe下載。
?????
?????三、高級應用篇
?????Delphi是個很有效率的開發工具,但是它有一個缺點就是生成的EXE文件太大。一個程序就算只有一個空窗口體積也有286KB。如果直接用API來寫的話程序體積是小了,但是又太繁瑣,無法立即看到界面效果,根本談不上是可視化開發。其實并非“魚與熊掌不可兼得”,利用資源文件我們就可以輕松達到這個目的。
?????在開始之前,我們需要一個可以編輯資源文件的工具。這類工具很多,比如說Resource?WorkShop就是非常好的一個。如果一時找不到,利用VC的編輯器來也是可以的。下面我們就以VC的為例示范如何創建一個窗口資源文件。
?????運行VC,打開菜單“File/New”,將出現一個多項選擇頁。我們選擇“Files/Resource?Template”,在右邊的File填上Demo,Location選擇保存路徑,然后點擊按鈕OK返回VC開發環境。
?????選擇菜單“Insert/Resource”,將出現一個資源類型選擇框。我們把鼠標移到Dialog上面,不用展開,點擊右邊的New即可,這時候返回VC開發環境并出現一個只有關閉按鈕和兩個Button的窗體。將鼠標選定窗體,擊右鍵選擇最后一項Properties,將出現一個設置窗口,將ID改為“MAINFORM”(注意:跟下面添加的其它控件的屬性設置方法不同,主窗口的ID必須把雙引號寫上去,而且名稱必須為大寫。否則程序將找不到資源。程序會一運行就退出了。)Caption改為“安裝程序”,這時候可以立刻看到窗口的標題變成了“安裝程序”,把Styles的Minimize?box選上,More?Styles的Center勾上使程序運行時的位置居中。當然你也可以設置它的坐標,其它保留默認值即可?;氐介_發環境,在控件框里面分別選擇一個Static?Text,一個Edit?Box,一個Button和一個Group?Box添加到窗體上面,把它們按照自己的愛好排列整齊,然后逐個修改它們的屬性。方法就是按照上面說的選定控件后擊右鍵選擇最后一項Properties,在出現的屬性框里面修改。其中屬性如下:Group?Box的Caption屬性清空,Static?Text的Caption屬性改為“請選擇安裝目錄:”,Edit?Box的ID改為10001,第一個Button的ID為10002,Caption屬性為“選擇”,第二個Button的ID為10003,Caption屬性為“安裝”,第三個Button的ID為10004,Caption屬性為“退出”。
?????為了使程序更加完美,我們為它再添加一個菜單IDR_MENU1。選擇“Insert/Resource/Menu”,我們這里只簡單添加一項“文件/退出”,其中“退出”的ID為10005。然后在主窗口的屬性Menu設定為IDR_MENU1即可。
?????為了使程序更加美觀,我們再添加一個小圖標,同時這也將是我們程序的圖標。選擇“Insert/Resource/Iconv/Import”,選擇一個圖標文件,并將它的ID設置為"MAINICON"(注意:必須把雙引號寫上而且字母為大寫),為窗口添加一個Picture控件并設置它的屬性Type:Icon,Image下拉選擇剛才的圖標MainIcon即可。
?????如果你想為程序在鼠標添加一些信息也是可以的。選擇“Insert/Resource/Version”即可。到這里我們已經完成了一個簡單的“安裝程序”的窗體設計,實際上我們現在就可以在Delphi中調用它了。我們先把“勞動成果”保存起來,選擇“File/Save?As”,在文件類型里選擇“32-bit?Resource?File(.res)”保存為“Demo.res”,文件大小大約為2.65KB。
?????新建一個擴展名為dpr的文本文件MyDemo.Dpr,鍵入如下代碼:
?????Uses?Windows,Messages;
?????{$R?Demo.Res}
?????function?MainDialogProc(
???????DlgWin:?hWnd;
???????DlgMessage:?UINT;
???????DlgWParam:?WPARAM;
???????DlgLParam:?LPARAM
???????)
???????:?integer;?stdcall;
?????begin
????????Result?:=?0;
?????case?DlgMessage?of
????????WM_Close:
??????????begin
??????????PostQuitMessage(0);
??????????Exit;
??????????end;
???????end;
?????end;
?????begin
???????DialogBox(hInstance,?'MAINFORM',?0,?@MainDialogProc);
?????end.
?????用Delphi打開它編譯一次即可產生一個大小為19KB的EXE。是不是很小?!實際上,你甚至只用一行代碼就把它Show出來,不過程序無法關閉而已:
?????Uses?Windows;
?????{$R?Demo.Res}
?????function?MainDialogProc:?integer;
?????begin
????????Result?:=?0;
?????end;
?????begin
???????DialogBox(hInstance,?'MAINFORM',?0,?@MainDialogProc);
?????end.
?????上面的程序只不過是一個空窗口而已,現在我們來寫代碼響應按下相應按鈕響應的事件。完整代碼如下:
?????program?MyDemo;
?????uses?Windows,?Messages,?shlobj;
?????const
???????ID_Edit?=?10001;
???????ID_Selet?=?10002;
???????ID_Setup?=?10003;
???????ID_Quit?=?10004;
???????ID_Exit?=?10005;
?????{$R?Demo.Res}
?????var
???????MainWin:?HWND;
?????
?????function?My_Gettext:?string;
?????var
???????Textlength:?Integer;
???????Text:?PChar;
???????s:?string;
?????begin
???????TextLength?:=?GetWindowTextLength(GetDlgItem(MainWin,?ID_Edit));
???????GetMem(Text,?TextLength?+?1);
???????GetWindowText(GetDlgItem(MainWin,?ID_Edit),?Text,?TextLength?+?1);
???????s?:=?text;
???????FreeMem(Text,?TextLength?+?1);
???????Result?:=?s;
?????end;
?????
?????function?Getmyname:?string;
?????var
???????i,?j:?integer;
?????begin
???????J?:=?3;
???????for?i?:=?1?to?length(ParamStr(0))?do
?????????if?ParamStr(0)[i]?=?''?then?J?:=?I;
???????Result?:=?copy(ParamStr(0),?J?+?1,?length(ParamStr(0))?-?J);
?????end;
?????
?????function?SelectDirectory(handle:?hwnd;?const?Caption:?string;?const?Root:?WideString;?out?Directory:?string):?Boolean;
?????var
???????lpbi:?_browseinfo;
???????buf:?array[0..MAX_PATH]?of?char;
???????id:?ishellfolder;
???????eaten,?att:?cardinal;
???????rt:?pitemidlist;
???????initdir:?pwidechar;
?????begin
???????result?:=?false;
???????lpbi.hwndOwner?:=?handle;
???????lpbi.lpfn?:=?nil;
???????lpbi.lpszTitle?:=?pchar(caption);
???????lpbi.ulFlags?:=?BIF_RETURNONLYFSDIRS?+?BIF_EDITBOX;
???????SHGetDesktopFolder(id);
???????initdir?:=?pwchar(root);
???????id.ParseDisplayName(0,?nil,?initdir,?eaten,?rt,?att);
???????lpbi.pidlRoot?:=?rt;
???????getmem(lpbi.pszDisplayName,?MAX_PATH);
???????try
?????????result?:=?shgetpathfromidlist(shbrowseforfolder(lpbi),?buf);
???????except
?????????freemem(lpbi.pszDisplayName);
???????end;
???????if?result?then
?????????begin
??????????directory?:=?buf;
??????????if?length(directory)?<>?3?then?directory?:=?directory?+?'';
?????????end;
?????end;
?????
?????function?MainDialogProc(
???????DlgWin:?hWnd;
???????DlgMessage:?UINT;
???????DlgWParam:?WPARAM;
???????DlgLParam:?LPARAM
???????)
???????:?integer;?stdcall;
?????var
???????MyIcon:?HICON;
???????Sdir:?string;
?????begin
???????Result?:=?0;
???????case?DlgMessage?of
?????????WM_INITDIALOG:
??????????begin
??????????MyIcon?:=?LoadIcon(hInstance,?'MainIcon');
??????????SetClassLONG(DlgWin,?GCL_HICON,?MyIcon);
??????????MainWin?:=?DlgWin;
??????????end;
?????????WM_Close:
??????????begin
??????????PostQuitMessage(0);
??????????Exit;
??????????end;
?????????WM_COMMAND:
??????????case?LOWORD(DlgWParam)?of
?????
??????????ID_Selet:
??????????begin
??????????if?SelectDirectory(DlgWin,?'請選擇安裝目錄',?'',?Sdir)
??????????then?SendMessage(GetDlgItem(DlgWin,?ID_Edit),?WM_SETTEXT,?0,?lParam(pChar(Sdir)));
??????????end;
??????????ID_Setup:
??????????begin
??????????if?My_Gettext?=?''?then
??????????begin
??????????MessageBox(DlgWin,?'請先選擇安裝文件夾!',?'信息',?MB_ICONINFORMATION?+?MB_OK);
??????????Exit;
??????????end;
??????????CopyFile(pchar(ParamStr(0)),?pchar(My_Gettext?+?Getmyname),?false);
??????????MessageBox(DlgWin,?'安裝完畢!',?'信息',?MB_ICONINFORMATION?+?MB_OK);
??????????PostQuitMessage(0);
??????????Exit;
??????????end;
??????????ID_Quit:
??????????begin
??????????PostQuitMessage(0);
??????????EXIT;
??????????end;
??????????ID_Exit:
??????????begin
??????????if?MessageBox(DlgWin,?'你點擊了菜單“退出”,你確定退出程序嗎?',?'信息',?MB_ICONQUESTION?+?MB_OKCANCEL)?=?IDOK?then
??????????PostQuitMessage(0);
??????????Exit;
??????????end;
??????????end;
???????end;
?????end;
?????begin
???????DialogBox(hInstance,?'MAINFORM',?0,?@MainDialogProc);
?????end. 與50位技術專家面對面20年技術見證,附贈技術全景圖
總結
以上是生活随笔為你收集整理的使用DLL封装窗体和业务类的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Deep Learning 【Natur
- 下一篇: 数据结构基础(3) --Permuta