플러그인 구조를 프로그래밍하려면 각 DLL의 동작을 제어하고, 분할된 각 하위 시스템을 DLL 라이브러리 파일로 정리하는 플러그인 컨테이너가 필요합니다. 각 DLL 프로그램에 대해 인터페이스 함수는 컨테이너용으로 예약되어야 합니다. 일반적으로 인터페이스 함수에는 DLL 라이브러리 호출을 시작하는 함수와 DLL 라이브러리를 닫는 함수가 포함됩니다. 인터페이스 기능을 통해 플러그인 컨테이너는 매개변수를 DLL 모듈에 전달하여 동적 제어를 달성할 수 있습니다. 구체적인 구현 내용을 설명하고 아래 응답 코드를 알려드리겠습니다.
DELPHI의 UNIT 구조와 프로젝트 구조를 먼저 이해해야 할 수도 있습니다. 이 기사에서는 DLL 프로그래밍의 이론적 세부 사항을 심도 있게 다루지는 않고, Liu Yi가 쓴 "DELPHI 심층 프로그래밍"이라는 책을 공부하던 중에 몇 가지 실용적인 코드만 설명합니다.
저도 DELPHI 입문 단계에 있는데, 이번 DLL 개발에 있어서 논의할 만한 부분이 있다고 느껴서 제가 잘 하고 있지 못한 부분에 대해 아낌없는 조언을 해주시기를 바랍니다.
샘플 프로그램 소개
읽기 쉽도록 MIS 시스템의 프로그램 코드 일부를 사용하여 플러그인 프로그래밍의 몇 가지 방법을 보여 드리겠습니다. 샘플 프로그램은 일반적인 C/S 구조 DBMS 애플리케이션으로 프레임워크 프로그램(이하 Hall)의 제어문과 dll 플러그인 프로그램의 응답 제어에 중점을 둡니다.
1. 프로그램 구조
플러그인 컨테이너 Hall은 독립적인 프로젝트를 사용하여 생성됩니다. Hall의 기본 창은 MDI 프로그램의 MDI 컨테이너 형식과 동일하며 Dll의 인터페이스 기능은 Hall에서 명시적으로 호출됩니다.
각 플러그인 프로그램은 일반 프로젝트와 달리 자체 프로젝트를 독립적으로 사용합니다. DLL 프로젝트는 Dll Wizard를 생성하고 해당 컴파일로 생성된 파일에는 접미사 DLL이 있습니다.
=550) window.open('/upload/20080315181507424.jpg');" src="http://files.VeVB.COm/upload/20080315181507424.jpg" onload="if(this.width>'550')this.width='550';if(this.height>'1000')this.height='1000';"
2. 인터페이스 디자인
예제 프로그램 Narcissus에서는 두 가지 인터페이스 기능을 예약했습니다.
ShowDLLForm
이 함수는 응용 프로그램의 핸들을 DLL 하위 창에 전달하고 DLL 프로그램은 DLL 형식의 인스턴스를 동적으로 생성합니다. 양식 이름, 현재 로그인된 사용자 이름 등과 같은 일부 비즈니스 논리를 매개변수 형태로 DLL 하위 창에 전달할 수도 있습니다. 이 함수를 사용하면 처음 호출될 때 DLL 양식 인스턴스를 생성할 수 있습니다.
FreeDLL양식
이 함수는 DLL 창 인스턴스의 릴리스를 표시하고 각 DLL 형식의 FreeDLLForm 메서드를 호출하여 응용 프로그램을 종료할 때 생성된 인스턴스를 릴리스합니다. 그렇지 않으면 메모리 읽기 전용 오류가 발생합니다. 마찬가지로 양식을 DLL 양식에 매개변수 형태로 릴리스할 때 수행해야 하는 일부 비즈니스 로직을 전달할 수도 있습니다.
3. 디버깅 방법
DLL 형식 프로그램은 직접 실행할 수 없으며 이를 호출하려면 플러그인 컨테이너가 필요합니다. 따라서 먼저 기본 Hall 프로그램을 구현한 다음 Hall.exe를 고정된 디렉터리에 저장해야 합니다. 각 DLL 프로젝트에 대해 다음 설정을 지정합니다.
1) DLL 프로젝트를 엽니다.
2) 메뉴 실행 매개변수 선택
3) 팝업 창에서 컨테이너 Hall.exe를 찾습니다.
이러한 방식으로 DLL 프로그램을 디버깅할 때 Hall 프로그램이 자동으로 호출되며 DLL 프로그램은 Hall에 예약된 호출 인터페이스를 사용하여 디버깅됩니다.
플러그인 프로그램의 기본 구현
DLL 프로그램의 설계 방법은 프로젝트가 자동으로 생성되는 WINAPP와 달리 모든 창이 DLL 라이브러리에 특수 "리소스"로 저장되어 수동으로 호출해야 한다는 점을 제외하면 일반 WINAPP의 설계 방법과 크게 다르지 않습니다. 인터페이스 함수를 선언하는 방법은 매우 간단합니다.
1) 유닛의 구현 섹션에 함수를 선언합니다.
2) 함수 선언문 끝에 stdcall 표시를 추가합니다.
3) 프로젝트 코드(Project View Source)의 시작 문 이전에 내보내기 문을 사용하여 함수 인터페이스를 선언합니다.
코드를 간결하게 만들기 위해 저는 개인적으로 프로젝트에 독립적으로 유닛 유닛(File New - Unit)을 추가하고 이 유닛에서 출력될 모든 함수 본문을 정의하는 것을 잊지 마세요. 참조되는 형태의 단위입니다. 이 장치의 이름을 UnitEntrance로 지정하고 ShowDLLForm 함수에 표시할 창을 초기화한 다음 Show 메서드를 호출하여 이를 표시합니다. HALL은 로그인한 사용자 이름을 매개 변수로 전달하고 일부 권한 제어를 수행할 수 있습니다. 인터페이스 초기화에 반영됩니다.
코드는 다음과 같습니다
단위 UnitOfficeEntrance;
인터페이스
용도
Windows, 메시지, SysUtils, 변형, 클래스, 그래픽, 컨트롤, 양식;
function ShowDLLForm(AHandle: THandle; ACaption: 문자열; AUserID: 문자열):boolean;stdcall;
function FreeDLLForm(AHandle: THandle; ACaption: 문자열; AUserID: 문자열):boolean;stdcall;
구현
use UnitOfficialMainForm; //MAINFORM 단위로 변경
var
DLL_Form:TFormOfficialMain; //MAINFORM 이름으로 변경
//---------------
//이름: ShowDLLForm
//Func: DLL 플러그인이 입력 함수를 호출합니다.
//Para: 이 양식의 AHandle 첨부 프로그램 핸들;
//Rtrn: 해당 없음
//인증: CST
//날짜: 2005-6-3
//---------------
function ShowDLLForm(AHandle: THandle; ACaption: 문자열; AUserID: 문자열):boolean;
시작하다
결과:=true;
노력하다
Application.Handle:=AHandle; //메인 프로그램 컨테이너에 고정됨
DLL_Form:=TFormOfficialMain.Create(Application) //MAINFORM의 이름으로 변경합니다.
노력하다
DLL_Form을 사용하여 수행
시작하다
캡션 := A캡션;
StatusBar.Panels.Items[0].Text := AUserID;
//UI 구성
보여주다 ;
끝;
제외하고
e:예외 수행
시작하다
dll_form.Free;
끝;
끝;
제외하고
결과:=false;
끝;
끝;
//---------------
//이름: FreeDLLForm
//Func: DLL 플러그인이 내보내기 기능을 호출합니다.
//Para: AHandle 첨부된 프로그램 핸들
//Rtrn: 참/거짓
//인증: CST
//날짜: 2005-6-11
//---------------
function FreeDLLForm(AHandle: THandle; ACaption: 문자열; AUserID: 문자열):boolean;
시작하다
Application.Handle:=AHandle; //메인 프로그램 컨테이너에 고정됨
if DLL_Form.Showing then DLL_Form.Close; //창이 먼저 열리고 닫히면 FORM.CLOSEQUERY를 트리거하여 닫기 프로세스를 취소할 수 있습니다.
DLL_Form.Showing이 아닌 경우 다음
시작하다
DLL_Form.Free;
결과:=true;
end //아직 열려 있으며 CLOSEQUERY.CANCLOSE=FALSE를 나타냅니다.
또 다른
시작하다
결과:=false;
끝;
끝;
끝.
DLL 프로젝트 파일 코드는 다음과 같습니다.
도서관 관계자;
{ DLL 메모리 관리에 대한 중요 참고 사항: ShareMem은
도서관의 USES 절과 프로젝트의 첫 번째 단원(선택
프로젝트-뷰 소스) DLL이 프로시저를 내보내거나
문자열을 매개변수나 함수 결과로 전달하는 함수입니다.
DLL과 주고받는 모든 문자열에 적용됩니다.
ShareMem은 레코드와 클래스에 중첩되어 있습니다.
BORLNDMM.DLL 공유 메모리 관리자는 함께 배포되어야 합니다.
BORLNDMM.DLL을 사용하지 않으려면 문자열 정보를 전달하세요.
PChar 또는 ShortString 매개변수 사용 }
용도
SysUtils,
수업,
'UnitOfficialDetailForm.pas' {FormOfficialDetail}의 UnitOfficialDetailForm,
'UnitOfficialMainForm.pas' {FormOfficialMain}의 UnitOfficialMainForm,
'UnitOfficeEntrance.pas'의 UnitOfficeEntrance,
'../../Public/Library/UnitOfficialClass.pas'의 UnitOfficialClass,
'../../Public/Library/UnitMyDataAdatper.pas'의 UnitMyDataAdatper,
'../../Public/Library/UnitMyHeaders.pas'의 UnitMyHeaders;
{$R *.res}
ShowDLLForm,FreeDLLForm을 내보냅니다. //인터페이스 함수
시작하다
끝.
플러그인 프로그램이 DLL 창을 호출하면 창 인스턴스가 HALL 창 위에 유지되므로 폐색에 대해 걱정할 필요가 없습니다.
컨테이너 프로그램 구현
1. 인터페이스 기능 소개
DLL 라이브러리에서 함수를 호출하는 방법에는 명시적 호출과 암시적 호출이 있습니다. 명시적 호출이 더 유연하므로 명시적 호출을 사용합니다. Delphi에서는 인터페이스 함수에 대한 함수 유형을 선언한 다음 함수 유형의 인스턴스를 인스턴스화해야 합니다. 인스턴스는 실제로 함수에 대한 포인터입니다. 포인터를 통해 함수에 액세스하고 매개변수를 전달하고 얻을 수 있습니다. 반환 값. 유닛 파일의 인터페이스 섹션에 함수 클래스 선언을 추가합니다.
유형
//인터페이스 함수 유형을 정의합니다. 인터페이스 함수는 DLL 인터페이스에서 나옵니다.
TShowDLLForm = Function(AHandle:THandle; ACaption: 문자열; AUserID:string):Boolean;stdcall;
TFreeDLLForm = Function(AHandle:THandle; ACaption: 문자열; AUserID:string):boolean;stdcall;
호출 라이브러리 함수를 표시하려면 다음 단계가 필요합니다.
1) DLL 라이브러리 파일 로드
2) 함수 주소 얻기
3) 기능 실행
4) DLL 라이브러리 출시
다음으로 이러한 단계를 자세히 논의하겠습니다.
2. DLL 라이브러리 파일 로드
DLL 라이브러리는 API 함수 LoadLibrary를 호출하여 메모리에 로드할 수 있습니다. 여기서는 DLL이 메모리 관리에 미치는 영향을 논의하지 않습니다. LoadLibrary의 매개변수는 DLL 파일의 주소 경로입니다. 로드가 성공하면 대상 파일이 존재하지 않거나 다른 이유로 인해 DLL이 로드되는 경우 CARDINAL 유형 변수가 반환됩니다. 파일이 실패하면 0이 반환됩니다.
3. 인터페이스 기능 인스턴스화
인터페이스 함수 포인터를 얻기 위한 API 함수는 GetProcAddress(라이브러리 파일 핸들, 함수 이름)입니다. 함수가 발견되면 해당 함수의 포인터가 반환됩니다. 실패하면 NIL이 반환됩니다.
위에서 정의한 함수 타입을 이용하여 함수 포인터 변수를 정의한 후, @ 연산자를 사용하여 함수 주소를 얻어내면, 포인터 변수를 이용하여 함수에 접근할 수 있습니다. 주요 코드는 다음과 같습니다.
…
var
ShowDLLForm: TShowDLLForm; //DLL 인터페이스 함수 인스턴스
FreeDLLForm: TFreeDLLForm;
시작하다
노력하다
시작하다
APlugin.ProcAddr := LoadLibrary(PChar(sPath));
APlugin.FuncFreeAddr := GetProcAddress(APlugin.ProcAddr,'FreeDLLForm');
APlugin.FuncAddr := GetProcAddress(APlugin.ProcAddr ,'ShowDLLForm');
@ShowDLLForm:=APlugin.FuncAddr;
@FreeDLLForm:=APlugin.FuncFreeAddr;
if ShowDllForm(Self.Handle, APlugin.Caption, APlugin.UserID) then
결과:=참
…
4. 구체적인 실시방법
플러그인을 구조화된 방식으로 관리하고 향후 시스템 확장을 용이하게 하기 위해 데이터베이스에 기록된 사용 가능한 DLL 정보를 결합한 다음 데이터베이스 기록을 쿼리하여 DLL 프로그램에 동적으로 액세스할 수 있습니다.
1) 시스템 모듈 테이블 설계
MIS 시스템의 경우 기존 DBS 조건을 사용하여 시스템 모듈 테이블을 구축하여 시스템 모듈에 매핑된 DLL 파일 및 관련 정보를 기록할 수 있습니다.
필드명 역할 유형
AutoID 인덱스INT
modAlias 모듈 별칭 VARCHAR
modName 모듈 이름 VARCHAR
modWndClass 양식 고유 식별자 VARCHAR
modFile DLL 경로 VARCHAR
modMemo 메모 TEXT
・모듈 별칭은 특히 팀 개발 중 팀 구성원이 참조할 수 있도록 프로그래밍 설계 단계에서 명명 규칙을 통일하는 데 사용됩니다.
・모듈 이름은 DLL 창의 제목으로 SHOWDLLFORM 함수에 ACAPTION 매개변수로 전달됩니다.
・양식의 고유 식별자는 DLL 하위 모듈에 있는 기본 창의 CLASSNAME이며 런타임에 제어할 창을 결정하는 데 사용됩니다.
・DLL 경로는 DLL 파일 이름을 저장하며 프로그램에서 절대 경로로 변환됩니다.
2) 플러그인 정보 데이터 구조
플러그인 관련 정보를 기록하는 데이터 인터페이스를 정의하면 DLL 플러그인을 중앙에서 제어할 수 있습니다. 인터페이스 섹션에 다음 코드를 추가합니다.
유형
//플러그인 정보 클래스 정의
TMyPlugins = 클래스
Caption:String; //DLL 양식 제목
DllFileName:String //DLL 파일 경로
WndClass:String //양식 식별;
사용자ID:문자열; //사용자 이름
ProcAddr:THandle; //LOADLIBRARY에 의해 로드된 라이브러리 핸들
FuncAddr:포인터 //SHOWDLLFORM 함수 포인터
FuncFreeAddr:포인터 //FREEDLLFORM 함수 포인터
끝;
…
각 플러그인에 대한 TMyPlugins 인스턴스를 생성합니다. 이러한 인스턴스의 초기화 방법은 아래에서 설명합니다.
3) 플러그인 로딩 기능
이 예에서는 HALL에서 하위 창 열기를 트리거하는 이벤트에 DLL 창이 로드되고 표시됩니다. 버튼 이벤트가 발생한 후 먼저 플러그인 구조 인스턴스에 따라 DLL이 로드되었는지 확인하고 로드되지 않은 경우 창 표시 또는 닫기를 제어하고 데이터 테이블에 액세스합니다. 플러그인 구조에 필드를 할당한 다음 포인터를 가져오는 작업을 실행합니다.
부분코드는 다음과 같습니다
…
//---------------
//이름: OpenPlugin
//Func: 플러그인 정보 제어 프로세스: 초기화==》권한 설정==》DLL 로드 창
//Para: APlugin-TMyPlugins sAlias 별칭 값;
//Rtrn: 해당 없음
//인증: CST
//날짜: 2005-6-2
//---------------
절차 TFormHall.OpenPlugin(AFromActn: TAction ;APlugin:TMyPlugins; sAlias:string; sUserID:string);
var hWndPlugin:HWnd;
시작하다
//플러그인 창이 로드되었는지 확인합니다. hWndPlugin:=FindWindow(PChar(APlugin.WndClass),nil);
if hWndPlugin <> 0 then //플러그인 창이 로드되었습니다.
시작하다
IsWindowVisible(hWndPlugin)이 아닌 경우
시작하다
AFromActn.Checked := 참;
ShowWindow(hWndPlugin,SW_SHOWDEFAULT); //표시
끝
또 다른
시작하다
AFromActn.checked := 거짓;
ShowWindow(hWndPlugin,SW_HIDE);
끝;
Exit; //플러그인 생성 프로세스를 종료합니다.
끝;
//플러그인 클래스 인스턴스 초기화
그렇지 않은 경우 초기화MyPlugins(APlugin,sAlias)
시작하다
showmessage('플러그인 클래스 초기화 오류.');
출구;
끝;
//현재 권한 값을 가져옵니다.
APlugin.UserID := sUserID;
//DLL 창 로드
LoadShowPluginForm(APlugin)이 아닌 경우
시작하다
showmessage('센터 플러그인 로딩 오류.');
출구;
끝;
끝;
//---------------
//이름: 초기화MyPlugins
//Func: MYPLUGIN 인스턴스 초기화(Caption | DllFileName | IsLoaded)
//파라: APlugin-TMyPlugins
//Rtrn: 해당 없음
//인증: CST
//날짜: 2005-6-2
//---------------
함수 TFormHall.InitializeMyPlugins(APlugin:TMyPlugins; sAlias:String):Boolean;
var
strSQL:문자열;
myDA:TMyDataAdapter;
시작하다
결과:=거짓;
myDA:=TMyDataAdapter.Create;
strSQL:='SELECT * FROM SystemModuleList WHERE modAlias='+QuotedStr(sAlias);
노력하다
myDA.RetrieveData(strSQL);
제외하고
E:예외 수행
시작하다
결과:=false;
myDA.Free;
출구;
끝;
끝;
노력하다
시작하다
myDA.MyDataSet을 사용하면
시작하다
IsEmpty가 아닌 경우
시작하다
APlugin.Caption:= FieldByName('modName').Value;
APlugin.DllFileName := FieldByName('modFile').Value;
APlugin.WndClass := FieldByName('modWndClass').Value;
결과:=참;
끝;
닫다;
end; //끝...할 일...
end; //시도 끝
제외하고
E:예외 수행
시작하다
결과:=거짓;
myDA.Free;
출구;
end; //예외 종료
end; //시도 끝...예외
myDA.Free;
끝;
//---------------
//이름: LoadShowPluginForm
//Func: DLL 플러그인을 로드하고 창을 표시합니다.
//파라: APlugin-TMyPlugins
//Rtrn: true가 성공적으로 생성되었습니다.
//인증: CST
//날짜: 2005-6-2
//---------------
함수 TFormHall.LoadShowPluginForm(const APlugin:TMyPlugins):boolean;
var
ShowDLLForm: TShowDLLForm; //DLL 인터페이스 함수 인스턴스
FreeDLLForm: TFreeDLLForm;
sPath:string; //DLL 파일의 전체 경로
시작하다
노력하다
시작하다
sPath:=ExtractFilepath(Application.ExeName)+ 'plugins/' + APlugin.DllFileName;
APlugin.ProcAddr := LoadLibrary(PChar(sPath));
APlugin.FuncFreeAddr := GetProcAddress(APlugin.ProcAddr,'FreeDLLForm');
APlugin.FuncAddr := GetProcAddress(APlugin.ProcAddr ,'ShowDLLForm');
@ShowDLLForm:=APlugin.FuncAddr;
@FreeDLLForm:=APlugin.FuncFreeAddr;
if ShowDllForm(Self.Handle, APlugin.Caption, APlugin.UserID) then
결과:=참
또 다른
결과:=거짓;
끝;
제외하고
E:예외 수행
시작하다
결과:=거짓;
ShowMessage('플러그인 모듈을 로드하는 중 오류가 발생했습니다. PLUGINS 디렉터리의 파일이 완전한지 확인하세요.');
끝;
끝;
끝;
…
4) DLL 창 제어
3)의 코드에서 알 수 있듯이 DLL 창을 열고 닫는 작업은 프레젠테이션 계층에서만 수행됩니다. 창을 닫는다고 해서 실제로 DLL 창이 해제되는 것은 아닙니다. 단지 API 함수 FindWindow를 호출하여 창에 따라 양식 핸들을 가져오는 것뿐입니다. 식별자(즉, Form.name) 사용 SHOWWINDOW 함수의 nCmdShow 매개변수는 창의 표시/숨기기를 제어합니다.
사실 이게 제 프로그램 구현의 약점인데, DLL 창에서 Self.close 메소드를 사용하게 되면 메모리 오류가 발생하게 되는데, 이는 제한된 기능으로 인해 해결할 수 있는 방법이 없습니다. 최후의 수단. 따라서 각 DLL 프로그램의 메인 창에 있는 닫기 버튼을 숨겨야 합니다. :-피
5) DLL 라이브러리 출시
프로그램이 종료되면 플러그인 정보 인스턴스에 따라 DLL 라이브러리를 하나씩 릴리스해야 합니다. DLL 라이브러리를 해제하는 함수는 다음과 같습니다.
절차 TFormHall.ClosePlugin(aPLG:TMyPlugins);
var
FreeDLLForm:TFreeDLLForm;
시작하다
aPLG.ProcAddr = 0이면 종료합니다.
aPLG.FuncFreeAddr = nil이면 종료합니다.
@FreeDLLForm:=aPLG.FuncFreeAddr;
FreeDLLForm(Application.Handle,'','')이 아니면
showMessage('err');
끝;
요약
이 예제 프로그램의 실행 효과는 다음과 같습니다.
=550) window.open('/upload/20080315181507979.jpg');" src="http://files.VeVB.COm/upload/20080315181507979.jpg" onload="if(this.width>'550')this.width='550';if(this.height>'1000')this.height='1000';"
위의 방법들 중 능력의 한계로 인해 해결되지 않은 문제가 많기 때문에 합리적이지 않은 은폐 방법을 채택해 봤습니다. 모두가 조금 노력한 후에 더 나은 솔루션을 설계할 수 있기를 바랍니다. 더 많은 것을 배울 수 있는 좋은 방법입니다.