패키지를 사용하는 이유는 무엇입니까?
대답은 간단합니다. 패키지의 힘 때문입니다. 디자인 타임 패키지는 사용자 정의 구성 요소의 릴리스 및 설치를 단순화합니다. 런타임 패키지는 기존 프로그래밍에 새로운 힘을 불어넣습니다. 재사용 가능한 코드를 런타임 라이브러리로 컴파일하면 여러 애플리케이션에서 공유할 수 있습니다. 모든 애플리케이션은 패키지를 통해 표준 구성 요소에 액세스할 수 있으며 Delphi 자체가 이를 수행합니다. 응용 프로그램은 실행 파일에 별도의 구성 요소 라이브러리를 복사할 필요가 없으므로 시스템 리소스와 디스크 공간이 크게 절약됩니다. 또한 패키지는 애플리케이션별 코드만 컴파일하면 되므로 컴파일에 소요되는 시간을 줄여줍니다.
패키지를 동적으로 사용할 수 있다면 더 많은 이점을 얻을 수 있습니다. 패키지는 애플리케이션 개발에 대한 새로운 모듈식 접근 방식을 제공합니다. 때로는 선택적 HR 모듈과 함께 제공되는 회계 시스템과 같이 특정 모듈을 응용 프로그램의 선택적 구성 요소로 만들고 싶을 수도 있습니다. 기본 애플리케이션만 설치하면 되는 경우도 있고, 추가 HR 모듈을 설치해야 하는 경우도 있습니다. 이 모듈식 접근 방식은 패키지 기술을 통해 쉽게 구현할 수 있습니다. 과거에는 DLL을 동적으로 로드해야만 이 작업을 수행할 수 있었지만 Delphi의 패키징 기술을 사용하면 애플리케이션의 각 모듈 유형을 번들로 "패키징"할 수 있습니다. 특히, 패키지에서 생성된 클래스 개체는 응용 프로그램이 소유하므로 응용 프로그램의 개체와 상호 작용할 수 있습니다.
런타임 패키지 및 애플리케이션
많은 개발자는 Delphi 패키지를 구성 요소를 배치하는 장소로만 생각하지만 실제로는 패키지를 모듈식 애플리케이션 설계에 사용할 수 있고 사용해야 합니다.
패키지를 사용하여 애플리케이션을 모듈화하는 방법을 보여주기 위해 예제를 만들어 보겠습니다.
1. Form1과 Form2의 두 가지 형식을 사용하여 새로운 Delphi 프로그램을 만듭니다.
2. 자동으로 생성된 양식 목록(PROject | Options | Forms)에서 Form2를 제거합니다.
3. Form1에 버튼을 배치하고 버튼의 OnClick 이벤트 핸들러에 다음 코드를 입력합니다.
TForm2.Create(application)을 사용하여 수행
시작하다
쇼모달;
무료;
끝;
4. Unit1의 사용 절에 Unit2를 추가하는 것을 잊지 마십시오.
5. 프로젝트를 저장하고 실행합니다.
클릭하면 다른 양식이 생성되고 표시되는 버튼이 있는 양식을 표시하는 간단한 애플리케이션을 만들었습니다.
그런데 위 예제의 Form2를 재사용 가능한 모듈에 포함시키고 여전히 정상적으로 작동하게 하려면 어떻게 해야 할까요?
대답은: 바오!
Form2용 패키지를 만들려면 다음 작업이 필요합니다.
1. 프로젝트 관리자를 엽니다(보기 | 프로젝트 관리자).
2. 프로젝트 그룹을 마우스 오른쪽 버튼으로 클릭하고 "새 프로젝트 추가..."를 선택합니다.
3. "새" 프로젝트 목록에서 "패키지"를 선택합니다.
4. 이제 패키지 편집기를 볼 수 있습니다.
5. "포함" 항목을 선택하고 "추가" 버튼을 클릭합니다.
6. 그런 다음 "찾아보기..." 버튼을 클릭하고 "Unit2.pas"를 선택합니다.
7. 이제 패키지에는 "Unit2.pas" 유닛이 포함되어야 합니다.
8. 마지막으로 패키지를 저장하고 컴파일합니다.
이제 패키지가 완성되었습니다. Project/BPL 디렉터리에 "package1.bpl"이라는 파일이 있어야 합니다. (BPL은 Borland Package Library의 약자이고, DCP는 Delphi CompiledPackage의 약자입니다.)
이 패키지가 완성되었습니다. 이제 패키지 옵션 스위치를 켜야 합니다.
원본 응용 프로그램을 다시 컴파일하십시오.
1. 프로젝트 관리자에서 "Project1.exe"를 두 번 클릭하여 프로젝트를 선택합니다.
2. 마우스 오른쪽 버튼을 클릭하고 "옵션..."을 선택합니다(메뉴에서 프로젝트 | 옵션...을 선택할 수도 있음).
3. "패키지" 옵션 페이지를 선택합니다.
4. "런타임 패키지로 빌드" 확인란을 선택합니다.
5. "런타임 패키지" 편집 상자: "Vcl50;Package1"을 편집하고 "확인" 버튼을 클릭합니다.
6. 참고: 응용 프로그램에서 Unit2를 제거하지 마십시오.
7. 애플리케이션을 저장하고 실행합니다.
애플리케이션은 이전과 같이 실행되지만 파일 크기에서 차이를 확인할 수 있습니다.
Project1.exe의 크기는 이전의 293K와 비교하여 이제 14K에 불과합니다. 리소스 브라우저를 사용하여 EXE 및 BPL 파일의 내용을 보면 이제 Form2 DFM 및 코드가 패키지에 저장되어 있는 것을 볼 수 있습니다.
Delphi는 컴파일 중에 패키지의 정적 링크를 완료합니다. (이것이 EXE 프로젝트에서 Unit2를 제거할 수 없는 이유입니다.)
이를 통해 무엇을 얻을 수 있는지 생각해 보십시오. 패키지에 데이터 액세스 모듈을 생성할 수 있으며 데이터 액세스 규칙을 변경할 때(예: BDE 연결에서 ADO 연결로 전환) 패키지를 약간 수정하고 다시 게시할 수 있습니다. 또는 "이 옵션은 현재 버전에서는 사용할 수 없습니다."라는 메시지를 표시하는 한 패키지에서 양식을 만든 다음 동일한 이름을 가진 다른 패키지에서 완전한 기능을 갖춘 양식을 만들 수 있습니다. 이제 우리는 별다른 노력 없이도 "Pro" 및 "Enterprise" 버전의 제품을 보유하게 되었습니다.
패키지의 동적 로드 및 언로드
대부분의 경우 정적으로 링크된 DLL이나 BPL이면 충분합니다. 하지만 BPL을 출시하고 싶지 않다면 어떻게 될까요? "지정된 디렉터리에서 동적 링크 라이브러리 Package1.bpl을 찾을 수 없습니다."는 응용 프로그램이 종료되기 전에 얻을 수 있는 유일한 메시지입니다. 아니면 모듈식 애플리케이션에서 플러그인을 원하는 만큼 사용할 수 있나요?
런타임에 BPL에 동적으로 연결해야 합니다.
DLL의 경우 LoadLibrary 함수를 사용하는 간단한 방법이 있습니다.
함수 LoadLibrary(lpLibFileName: Pchar): HMODULE;stdcall;
DLL을 로드한 후 GetProcAddress 함수를 사용하여 DLL의 내보낸 함수 및 메서드를 호출할 수 있습니다.
함수 GetProcAddress(hModule: HMODULE; lpProcName:LPCSTR): FARPROC;
마지막으로 FreeLibrary를 사용하여 DLL을 제거합니다.
function FreeLibrary(hLibModule: HMODULE): BOOL;stdcall;
다음 예에서는 Microsoft의 HtmlHelp 라이브러리를 동적으로 로드합니다.
function TForm1.ApplicationEvents1Help(명령: Word; 데이터: 정수; var CallHelp: Boolean):Boolean;
유형
TFNHtmlHelpA = function(hwndCaller: HWND; pszFile: PansiChar; uCommand: UINT;dwData: Dword): HWND;
var
도움말모듈: H모듈;
HtmlHelp: TFNHtmlHelpA;
시작하다
결과 := 거짓;
HelpModule := LoadLibrary('HHCTRL.OCX');
HelpModule <> 0이면
시작하다
@HtmlHelp := GetProcAddress(HelpModule, 'HtmlHelpA');
@HtmlHelp <> nil이면
결과 := HtmlHelp(Application.Handle,Pchar(Application.HelpFile), Command,Data) <> 0;
FreeLibrary(도움말모듈);
끝;
CallHelp := 거짓;
끝;
BPL을 동적으로 로드
우리는 BPL을 처리하기 위해 동일한 간단한 방법을 사용할 수 있습니다. 아니면 기본적으로 동일한 간단한 방법이라고 해야 할까요?
LoadPackage 함수를 사용하여 패키지를 동적으로 로드할 수 있습니다.
function LoadPackage(const 이름: 문자열): HMODULE;
그런 다음 GetClass 함수를 사용하여 TPerpersistClass 유형 객체를 생성합니다.
function GetClass(const AclassName: 문자열):TPertantClass;
모든 작업이 완료되면 UnLoadPackage(Module:HModule);를 사용하세요.
원본 코드를 약간 변경해 보겠습니다.
1. 프로젝트 관리자에서 "Project1.exe"를 선택합니다.
2. 마우스 오른쪽 버튼을 클릭하고 "옵션..."을 선택하십시오.
3. "패키지" 옵션 페이지를 선택합니다.
4. "런타임 패키지" 편집 상자에서 "Package1"을 제거하고 확인 버튼을 클릭합니다.
5. Delphi 툴바에서 "프로젝트에서 파일 제거" 버튼을 클릭합니다.
6. "Unit2 | Form2"를 선택하고 확인을 클릭하십시오.
7. 이제 "Unit1.pas" 소스 코드의 사용 절에서 Unit2를 제거합니다.
8. Button1의 OnClick 시간 코드를 입력합니다.
9. HModule 및 TPerpersistClass 유형의 변수 두 개를 추가합니다.
var
패키지모듈: HModule;
AClass: TPertantClass;
10. LoadPackage 함수를 사용하여 Pacakge1 패키지를 로드합니다.
PackageModule := LoadPackage('Package1.bpl');
11. PackageModule이 0인지 확인합니다.
12. GetClass 함수를 사용하여 영구 유형을 만듭니다.
AClass := GetClass('TForm2');
13. 이 영속 유형이 nil이 아니면 이전 유형으로 돌아갈 수 있습니다.
이 유형의 객체를 동일한 방식으로 생성하고 사용합니다.
TcustomForm처럼 TComponentClass(AClass).Create(Application)을 사용합니다.
시작하다
쇼모달;
무료;
끝;
14. 마지막으로 UnloadPackage 프로세스를 사용하여 패키지를 제거합니다.
UnloadPackage(PackageModule);
15. 프로젝트를 저장합니다.
OnClick 이벤트 핸들러의 전체 목록은 다음과 같습니다.
절차 TForm1.Button1Click(발신자: Tobject);
var
패키지모듈: HModule;
AClass: TPertantClass;
시작하다
PackageModule := LoadPackage('Package1.bpl');
PackageModule <> 0이면
시작하다
AClass := GetClass('TForm2');
AClass <> nil이면
TcustomForm처럼 TComponentClass(AClass).Create(Application)을 사용합니다.
시작하다
쇼모달;
무료;
끝;
UnloadPackage(PackageModule);
끝;
끝;
불행히도 그게 전부는 아닙니다.
문제는 GetClass 함수가 등록된 유형만 검색할 수 있다는 것입니다. 일반적으로 폼에서 참조되는 폼 클래스와 컴포넌트 클래스는 폼이 로드될 때 자동으로 등록됩니다. 하지만 우리의 경우에는 양식을 일찍 로드할 수 없습니다. 그러면 유형을 어디에 등록합니까? 정답은 가방 속에 있습니다. 패키지의 각 유닛은 패키지가 로드될 때 초기화되고 패키지가 언로드될 때 정리됩니다.
이제 예제로 돌아갑니다.
1. 프로젝트 관리자에서 "Package1.bpl"을 두 번 클릭합니다.
2. "포함" 섹션에서 "Unit2" 옆에 있는 + 기호를 클릭합니다.
3. "Unit2.pas"를 두 번 클릭하여 유닛 소스 코드 편집기를 활성화합니다.
4. 파일 끝에 초기화 섹션을 추가합니다.
5. RegisterClass 프로시저를 사용하여 양식 유형을 등록합니다.
RegisterClass(TForm2);
6. 마무리 섹션을 추가합니다.
7. UnRegisterClass 프로시저를 사용하여 양식 유형을 등록 취소합니다.
UnRegisterClass(TForm2);
8. 마지막으로 패키지를 저장하고 컴파일합니다.
이제 "Project1"을 안전하게 실행할 수 있으며 이전과 마찬가지로 작동하지만 이제 원하는 대로 패키지를 로드할 수 있습니다.
끝
패키지를 정적 또는 동적으로 사용하는지 여부에 관계없이 프로젝트 옵션 | 런타임 패키지로 빌드를 설정하세요.
패키지를 제거하기 전에 패키지의 모든 클래스 개체를 삭제하고 등록된 모든 클래스를 등록 취소해야 합니다. 다음 프로세스가 도움이 될 수 있습니다.
절차 DoUnloadPackage(모듈: HModule);
var
i: 정수;
남: TMemoryBasicInformation;
시작하다
for i := Application.ComponentCount - 1부터 0까지
시작하다
VirtualQuery(GetClass(Application.Components[i].ClassName), M, Sizeof(M));
if (Module = 0) 또는 (HMODULE(M.AllocationBase) = Module) then
응용 프로그램.구성 요소[i].무료;
끝;
UnregisterModuleClasses(모듈);
UnloadPackage(모듈);
끝;
패키지를 로드하기 전에 애플리케이션은 등록된 모든 클래스의 이름을 알아야 합니다. 이 상황을 개선하는 한 가지 방법은 패키지에 등록된 모든 클래스의 이름을 애플리케이션에 알려주는 등록 메커니즘을 만드는 것입니다.
예
다중 패키지: 패키지는 순환 참조를 지원하지 않습니다. 즉, 단위는 이미 해당 단위를 참조하는 단위를 참조할 수 없습니다(헤헤). 이로 인해 호출 양식의 특정 값이 호출된 메소드에 의해 설정되기가 어렵습니다.
이 문제에 대한 해결책은 호출 개체와 패키지의 개체 모두에서 참조하는 몇 가지 추가 패키지를 만드는 것입니다. 애플리케이션을 모든 양식의 소유자로 만드는 방법을 상상해 보십시오. 변수 Application은 Forms.pas에서 생성되고 VCL50.bpl 패키지에 포함됩니다. 당신은 당신의 애플리케이션이 VCL50.pas를 컴파일해야 할 뿐만 아니라 패키지에 VCL50도 필요하다는 것을 알아차렸을 것입니다.
세 번째 예에서는 고객 정보와 요청 시 고객 주문을 (동적으로) 표시하는 애플리케이션을 설계합니다.
그럼 어디서부터 시작하면 될까요? 모든 데이터베이스 애플리케이션과 마찬가지로
절차는 동일합니다. 연결해야 합니다. TDataBase 연결을 포함하는 메인 데이터 모듈을 생성합니다. 그런 다음 이 데이터 모듈을 패키지(cst_main)에 캡슐화합니다.
이제 애플리케이션에서 고객 양식을 생성하고 DataModuleMain을 참조합니다(VCL50과 cst_main을 정적으로 연결합니다).
그런 다음 고객 주문 양식을 포함하고 cst_main이 필요한 새 패키지(cst_ordr)를 생성합니다. 이제 애플리케이션에서 cst_ordr을 동적으로 로드할 수 있습니다. 동적 패키지가 로드되기 전에 이미 메인 데이터 모듈이 존재하므로 cst_ordr은 애플리케이션의 메인 데이터 모듈 인스턴스를 직접 사용할 수 있습니다.
위 그림은 이 애플리케이션의 기능 다이어그램입니다.
교체 가능한 패키지: 패키지의 또 다른 사용 사례는 교체 가능한 패키지를 만드는 것입니다. 이 기능을 구현하는 데는 패키지의 동적 로드 기능이 필요하지 않습니다. 시간 제한이 있는 프로그램의 평가판을 출시하고 싶다고 가정해 보겠습니다. 이를 달성하는 방법은 무엇입니까?
먼저 "Splash" 양식(일반적으로 "Trial"이라는 단어가 포함된 그림)을 만들고 응용 프로그램 시작 중에 표시합니다. 그런 다음 애플리케이션에 대한 일부 정보를 제공하는 "정보" 양식을 만듭니다. 마지막으로 소프트웨어가 최신 버전인지 테스트하는 함수를 만듭니다. 우리는 이 두 가지 형태와 이 기능을 패키지로 캡슐화하여 소프트웨어 평가판과 함께 출시합니다.
유료 버전의 경우 이전 두 양식과 동일한 클래스 이름을 사용하여 "Splash" 양식과 "About" 양식을 만들고 테스트 함수(아무 작업도 수행하지 않음)를 만들어 동일한 패키지에 캡슐화합니다. 이름.
뭐라고요? 이것이 유용한가요? 음, 우리는 소프트웨어의 평가판을 대중에게 공개할 수 있습니다. 고객이 앱을 구매하는 경우 평가판이 아닌 패키지만 보내면 됩니다. 단 한 번의 설치와 한 번의 등록 패키지 업그레이드만 필요하므로 소프트웨어 릴리스 프로세스가 크게 단순화됩니다.
이 패키지는 Delphi 및 C++ Builder 개발 커뮤니티를 위한 모듈식 설계의 또 다른 문을 열어줍니다. 패키지를 사용하면 더 이상 창 핸들을 전달할 필요가 없고, 더 이상 콜백 함수나 다른 DLL 기술이 필요하지 않습니다. 이는 또한 모듈식 프로그래밍의 개발 주기를 단축시킵니다. 우리가 해야 할 일은 Delphi의 패키지가 작동하도록 하는 것뿐입니다.