前言
我寫Delphi程序是從MIS系統(tǒng)入門的,開始嘗試子系統(tǒng)劃分的時候采用的是MDI窗體的結構。隨著系統(tǒng)功能的擴充,不斷有新的子系統(tǒng)加入系統(tǒng)中,單個工程會變得非常大,每次做一點修改都要重新編譯,單個工程的形式也不利于團隊協(xié)作。為了提高工作效率,我希望利用DLL動態(tài)鏈接庫的形式實現(xiàn)插件結構的編程。
插件結構的編程需要一個插件容器來控制各DLL的運行情況,將劃分好的每個子系統(tǒng)安排到一個DLL庫文件中。對每個DLL程序需要為容器預留接口函數(shù),一般接口函數(shù)包括:啟動調(diào)用DLL庫的函數(shù)、關閉DLL庫的函數(shù)。通過接口函數(shù),插件容器可以向DLL模塊傳遞參數(shù)實現(xiàn)動態(tài)控制。具體實現(xiàn)細節(jié)我將在下文說明并給出響應代碼。
您可能需要先了解一下DELPHI中UNIT的結構,工程的結構。本文沒有深入討論DLL編程的理論細節(jié),只是演示了一些實用的代碼,我當時學習的是劉藝老師的《DELPHI深入編程》一書。
我也處于DELPHI的入門階段,只是覺得這次的DLL開發(fā)有一些值得討論的地方,所以寫這篇文章,希望各位能對我做的不好的地方慷慨建議。
示例程序簡介
為了便于閱讀我將使用一個MIS系統(tǒng)的部分程序代碼演示插件編程的一些方法。示例程序是典型的C/S結構DBMS應用程序,我們關注的部分將是框架程序(下文簡稱Hall)的控制語句和dll插件程序的響應控制。
1、程序結構
插件容器Hall使用一個獨立的工程創(chuàng)建,Hall的主窗口的作用相當于MDI程序中的MDI容器窗體,Hall中將顯式調(diào)用Dll中的接口函數(shù)。
每個插件程序獨立使用各自的工程,與普通工程不同的是,DLL工程創(chuàng)建的是Dll Wizard,相應編譯生成的文件是以DLL為后綴。
2、接口設計
實例程序Narcissus中我們預留兩個接口函數(shù):
ShowDLLForm
該函數(shù)將應用程序的句柄傳遞給DLL子窗口,DLL程序?qū)討B(tài)創(chuàng)建DLL窗體的實例。還可以將一些業(yè)務邏輯用參數(shù)的形式傳遞給DLL子窗口,比如窗體名稱、當前登陸的用戶名等。初次調(diào)用一個DLL窗體實例時使用此函數(shù)創(chuàng)建。
FreeDLLForm
該函數(shù)將顯示釋放DLL窗口實例,在退出應用程序時調(diào)用每個DLL窗體的FreeDLLForm方法來釋放創(chuàng)建的實例,不然會引起內(nèi)存只讀錯誤。同樣,也可以將一些在釋放窗體時需要做的業(yè)務邏輯用參數(shù)的形式傳遞給DLL窗體。
3、調(diào)試方式
DLL窗體程序無法直接執(zhí)行,需要有一個插件容器來調(diào)用。應此我們需要先實現(xiàn)一個基本的Hall程序,然后將Hall.exe保存在一個固定的目錄中。對每個DLL工程做如下設置:
1) 打開DLL工程
2) 選擇菜單 Run – Parameters
3) 在彈出的窗口中瀏覽到我們的容器Hall.exe
這樣在調(diào)試DLL程序時將會自動調(diào)用Hall程序,利用Hall中預留的調(diào)用接口調(diào)試DLL程序。
1、檢測主程序大小,防止*補丁之類:
Function TForm1.GesSelfSf: integer;
var
F: file of byte;
begin
Filemode:=0;
Assignfile(F,'.\FileName.exe');
Reset(f);
Result:=Filesize(F);
Closefile(F);
end;
2、檢測創(chuàng)建日期和時間,讓*補丁實效:
Function TForm1.FinDate:String;
var
t:TDate;
begin
ShortDateFormat:='yyyy-mm-dd';
t:=FileDateToDateTime(FileAge('FileName.exe'));
Result:=DateToStr(t);
end;
3、注冊碼加密函數(shù)嵌入數(shù)學函數(shù),增加*難度:
(略)
4、必要時自己刪除自己(主程序):
procedure TForm1.Funll;
var
hModule:THandle;
buff:array[0..255]of Char;
hKernel32:THandle;
pExitProcess,pDeleteFileA,pUnmapViewOfFile:Pointer;
begin
hModule:=GetModuleHandle(nil);
GetModuleFileName(hModule, buff, sizeof(buff));
CloseHandle(THandle(4));
hKernel32:=GetModuleHandle('KERNEL32');
pExitProcess:=GetProcAddress(hKernel32, 'ExitProcess');
pDeleteFileA:=GetProcAddress(hKernel32, 'DeleteFileA');
pUnmapViewOfFile:=GetProcAddress(hKernel32, 'UnmapViewOfFile');
asm
LEA EAX, buff
PUSH 0
PUSH 0
PUSH EAX
PUSH pExitProcess
PUSH hModule
PUSH pDeleteFileA
PUSH pUnmapViewOfFile
RET
end;
begin
Funll;
end;
end;
具體怎么使用,那要看你自己的意愿了和需要了。反正我是這樣做的,我的軟件ADSL撥號計時器只在很早版本上出過注冊機,后來的v3.70出過*補丁——其實只是破掉了啟動時提示注冊的對話框,實質(zhì)上根本沒*。用了上述的著法以后,到現(xiàn)在的v5.28版本,再沒有過什么*補丁或注冊機。
如果現(xiàn)在的v5.28版本誰能*,將立即公布程序源碼。
附:注冊機*法的原理以及應對方法
認識注冊機*法
顧名思義,寫注冊機來*軟件注冊的方法,就是模仿你的注冊碼生成算法或者逆向注 冊碼驗證算法而寫出來的和你一模一樣的注冊機。如果被寫出注冊機,你的軟件只好免費了?;蛘吣惚仨毟鼡Q算法,但以前注過冊的合法用戶都得被迫更換注冊碼了。
    
   
              
              我寫Delphi程序是從MIS系統(tǒng)入門的,開始嘗試子系統(tǒng)劃分的時候采用的是MDI窗體的結構。隨著系統(tǒng)功能的擴充,不斷有新的子系統(tǒng)加入系統(tǒng)中,單個工程會變得非常大,每次做一點修改都要重新編譯,單個工程的形式也不利于團隊協(xié)作。為了提高工作效率,我希望利用DLL動態(tài)鏈接庫的形式實現(xiàn)插件結構的編程。
插件結構的編程需要一個插件容器來控制各DLL的運行情況,將劃分好的每個子系統(tǒng)安排到一個DLL庫文件中。對每個DLL程序需要為容器預留接口函數(shù),一般接口函數(shù)包括:啟動調(diào)用DLL庫的函數(shù)、關閉DLL庫的函數(shù)。通過接口函數(shù),插件容器可以向DLL模塊傳遞參數(shù)實現(xiàn)動態(tài)控制。具體實現(xiàn)細節(jié)我將在下文說明并給出響應代碼。
您可能需要先了解一下DELPHI中UNIT的結構,工程的結構。本文沒有深入討論DLL編程的理論細節(jié),只是演示了一些實用的代碼,我當時學習的是劉藝老師的《DELPHI深入編程》一書。
我也處于DELPHI的入門階段,只是覺得這次的DLL開發(fā)有一些值得討論的地方,所以寫這篇文章,希望各位能對我做的不好的地方慷慨建議。
示例程序簡介
為了便于閱讀我將使用一個MIS系統(tǒng)的部分程序代碼演示插件編程的一些方法。示例程序是典型的C/S結構DBMS應用程序,我們關注的部分將是框架程序(下文簡稱Hall)的控制語句和dll插件程序的響應控制。
1、程序結構
插件容器Hall使用一個獨立的工程創(chuàng)建,Hall的主窗口的作用相當于MDI程序中的MDI容器窗體,Hall中將顯式調(diào)用Dll中的接口函數(shù)。
每個插件程序獨立使用各自的工程,與普通工程不同的是,DLL工程創(chuàng)建的是Dll Wizard,相應編譯生成的文件是以DLL為后綴。
2、接口設計
實例程序Narcissus中我們預留兩個接口函數(shù):
ShowDLLForm
該函數(shù)將應用程序的句柄傳遞給DLL子窗口,DLL程序?qū)討B(tài)創(chuàng)建DLL窗體的實例。還可以將一些業(yè)務邏輯用參數(shù)的形式傳遞給DLL子窗口,比如窗體名稱、當前登陸的用戶名等。初次調(diào)用一個DLL窗體實例時使用此函數(shù)創(chuàng)建。
FreeDLLForm
該函數(shù)將顯示釋放DLL窗口實例,在退出應用程序時調(diào)用每個DLL窗體的FreeDLLForm方法來釋放創(chuàng)建的實例,不然會引起內(nèi)存只讀錯誤。同樣,也可以將一些在釋放窗體時需要做的業(yè)務邏輯用參數(shù)的形式傳遞給DLL窗體。
3、調(diào)試方式
DLL窗體程序無法直接執(zhí)行,需要有一個插件容器來調(diào)用。應此我們需要先實現(xiàn)一個基本的Hall程序,然后將Hall.exe保存在一個固定的目錄中。對每個DLL工程做如下設置:
1) 打開DLL工程
2) 選擇菜單 Run – Parameters
3) 在彈出的窗口中瀏覽到我們的容器Hall.exe
這樣在調(diào)試DLL程序時將會自動調(diào)用Hall程序,利用Hall中預留的調(diào)用接口調(diào)試DLL程序。
1、檢測主程序大小,防止*補丁之類:
Function TForm1.GesSelfSf: integer;
var
F: file of byte;
begin
Filemode:=0;
Assignfile(F,'.\FileName.exe');
Reset(f);
Result:=Filesize(F);
Closefile(F);
end;
2、檢測創(chuàng)建日期和時間,讓*補丁實效:
Function TForm1.FinDate:String;
var
t:TDate;
begin
ShortDateFormat:='yyyy-mm-dd';
t:=FileDateToDateTime(FileAge('FileName.exe'));
Result:=DateToStr(t);
end;
3、注冊碼加密函數(shù)嵌入數(shù)學函數(shù),增加*難度:
(略)
4、必要時自己刪除自己(主程序):
procedure TForm1.Funll;
var
hModule:THandle;
buff:array[0..255]of Char;
hKernel32:THandle;
pExitProcess,pDeleteFileA,pUnmapViewOfFile:Pointer;
begin
hModule:=GetModuleHandle(nil);
GetModuleFileName(hModule, buff, sizeof(buff));
CloseHandle(THandle(4));
hKernel32:=GetModuleHandle('KERNEL32');
pExitProcess:=GetProcAddress(hKernel32, 'ExitProcess');
pDeleteFileA:=GetProcAddress(hKernel32, 'DeleteFileA');
pUnmapViewOfFile:=GetProcAddress(hKernel32, 'UnmapViewOfFile');
asm
LEA EAX, buff
PUSH 0
PUSH 0
PUSH EAX
PUSH pExitProcess
PUSH hModule
PUSH pDeleteFileA
PUSH pUnmapViewOfFile
RET
end;
begin
Funll;
end;
end;
具體怎么使用,那要看你自己的意愿了和需要了。反正我是這樣做的,我的軟件ADSL撥號計時器只在很早版本上出過注冊機,后來的v3.70出過*補丁——其實只是破掉了啟動時提示注冊的對話框,實質(zhì)上根本沒*。用了上述的著法以后,到現(xiàn)在的v5.28版本,再沒有過什么*補丁或注冊機。
如果現(xiàn)在的v5.28版本誰能*,將立即公布程序源碼。
附:注冊機*法的原理以及應對方法
認識注冊機*法
顧名思義,寫注冊機來*軟件注冊的方法,就是模仿你的注冊碼生成算法或者逆向注 冊碼驗證算法而寫出來的和你一模一樣的注冊機。如果被寫出注冊機,你的軟件只好免費了?;蛘吣惚仨毟鼡Q算法,但以前注過冊的合法用戶都得被迫更換注冊碼了。

