복잡한 델파이 DLL 생성의 예. 델파이 애플리케이션에서 DLL 로드하기

동료들에게!

이 기사에서 나는 다음 질문에 답하려고 노력할 것입니다. DLL? 그것은 무엇을 위한 것입니까? 그리고 생성 및 사용 방법 DLL도움을 받아 델파이.

DLL이란 무엇입니까??

동적 링크 라이브러리아니면 줄여서 DLL다른 프로그램에서 사용할 수 있는 일련의 데이터나 기능이 포함된 라이브러리입니다.

사용 분야:

  • 리소스 저장: 아이콘, 사운드, 커서 등 리소스를 단일 라이브러리로 결합하여 실행 파일의 크기를 절약합니다.
  • 개별 프로그램 모듈 및 인터페이스 형태의 배치. 애플리케이션을 부분적으로 업데이트할 수 있는 기회가 주어지며 동적 연결을 사용하면 기본 프로그램을 다시 시작하지 않고도 모듈을 업데이트할 수 있습니다.
  • 플러그인(PlugIn)으로 사용합니다. 우리는 메인 프로그램 코드를 다시 작성하지 않고도 애플리케이션의 기능을 확장할 수 있는 기회를 제공합니다.
  • 라이브러리는 작성된 언어에 관계없이 다양한 프로그래밍 언어로 사용될 수 있습니다.

자신만의 DLL 만들기.

라이브러리를 만들려면 메뉴로 이동하세요. 파일 -> 다른그리고 선택 델파이 프로젝트 -> 동적 링크 라이브러리.
라이브러리 생성을 위한 템플릿이 포함된 코드 텍스트가 열립니다.

도서관 프로젝트1; System.SysUtils, System.Classes를 사용합니다. ($R *.res) 시작 끝.

자원 배치

나중에 메인 프로그램에서 사용할 아이콘을 라이브러리에 추가해 보겠습니다.
프로젝트에 리소스를 추가하는 방법은 문서에 자세히 설명되어 있습니다.

'프로그램 정보' 양식을 추가해 보겠습니다.
딸깍 하는 소리 파일 -> 새로운 -> VCL 양식. 텍스트와 "확인" 버튼을 추가해 보겠습니다.

표준을 표시하는 함수를 추가해 보겠습니다. 메세지 박스질문, "예", "아니요" 버튼 및 결과 형식 진실또는 거짓.

함수 YesNoDlg(const 질문: PChar): 부울; 표준 호출; 시작 결과:= (MessageBox(0, 질문, "확인", MB_YESNO + MB_ICONQUESTION) = ID_YES); 끝;

키워드에 주목하라 표준 호출. 함수를 호출할 때 매개변수와 결과가 전달되는 방식을 정확하게 결정합니다. 통화 계약 위키 페이지에서 자세한 내용을 읽을 수 있습니다.

또한 메인 프로그램의 플러그인으로 사용될 프로시저도 추가할 것입니다. 그러면 우리가 만든 창을 열 수 있도록 기본 프로그램 메뉴에 "프로그램 정보" 버튼이 추가됩니다.
절차 코드:

절차 플러그인(양식: TForm); 표준 호출; var i: 정수; mi: TMenuItem; i:= 0부터 Form.ComponentCount까지 - 1은 (Form.Components[i].ClassName = "TMenuItem") 및 (Form.Components[i].Name = "miHelp")인 경우 시작하고 mi:= TMenuItem을 시작합니다. .Create(Form.Components[i]); mi.Caption:= "프로그램 정보"; mi.OnClick:= fmAbout.onAboutButtonClick; TMenuItem(Form.Components[i]).Add(mi); 출구; 끝; 끝; 끝;

프로그램의 형식은 매개변수로 프로시저에 전달됩니다. “라는 메뉴 항목이 있는지 확인합니다. miHelp"그리고 메뉴가 발견되면 메뉴에 버튼을 추가합니다.

이제 라이브러리에서 사용할 수 있는 함수와 프로시저를 표시하겠습니다.
다음 줄을 추가해 보겠습니다.

플러그인, YesNoDlg를 내보냅니다.

함수를 컴파일해보자 프로젝트 -> 짓다또는 단축키를 사용하여 Shift+F9.
코드에 오류가 없으면 확장자를 가진 파일이 프로젝트 폴더에 나타나야 합니다. DLL.

이제 우리 라이브러리를 사용할 애플리케이션을 만드는 것으로 넘어가겠습니다.

애플리케이션에서 양식을 만들어 보겠습니다. 기본여기서는 프로그램 -> 종료 및 도움말 버튼이 있는 구성 요소를 추가합니다. 마지막으로 이름을 설정해 보겠습니다. miHelp:

양식 코드로 넘어 갑시다.

라이브러리의 함수 DLL두 가지 방법으로 연결할 수 있습니다:
공전— 프로그램이 시작되면 라이브러리가 연결됩니다. 라이브러리나 함수 이름을 찾을 수 없으면 프로그램에서 오류가 발생하고 실행되지 않습니다.
동적— 라이브러리는 함수 호출 직전이나 특정 이벤트 발생 시 연결됩니다.

정적 연결 방법을 고려해 보겠습니다.

유형 TfmMain = class(TForm) MainMenu: TMainMenu; miProgram: TMenuItem; miExit: TMenuItem; miHelp: TMenuItem; 절차 FormCreate(보내는 사람: TObject); 절차 miExitClick(Sender: TObject); 개인 공공 목적; 절차 PlugIn(양식: TForm); 표준 호출; 외부 "SampleDLL.dll"; 바르...

예어 외부이는 이 함수가 외부 라이브러리에서 연결되었음을 나타냅니다.

이벤트용 onCreate양식, 프로시저 호출 추가 플러그인:

프로시저 TfmMain.FormCreate(Sender: TObject); PlugIn(Self) 시작; 끝;

매개변수로 형태현재 양식을 프로시저에 전달합니다(키워드 본인).

프로그램을 시작할 때 메인 메뉴의 "도움말" 섹션에 "프로그램 정보" 항목이 있어야 합니다.

동적 연결 방법으로 넘어 갑시다.

세 가지 기능이 필요합니다 WinApi:

로드라이브러리
라이브러리를 컴퓨터 메모리에 로드합니다. 결과적으로 메모리의 라이브러리에 대한 포인터를 반환합니다. 오류가 발생하면 0을 반환합니다.

LoadLibrary(lpLibFileName: LPCWSTR): HMODULE;

lpLib파일 이름— 라이브러리 파일 이름.

GetProc주소
라이브러리에서 이름으로 함수를 찾으세요. 결과는 함수 포인터가 됩니다. 함수가 발견되지 않으면 반환됩니다. .

GetProcAddress(hModule: HMODULE; lpProcName: LPCSTR): FARPROC;

h모듈
lpProc이름— 함수 이름.

무료라이브러리
컴퓨터 메모리에서 라이브러리를 언로드합니다. 결과는 성공하면 True, 실패하면 False가 됩니다.

FreeLibrary(hLibModule: HMODULE): BOOL;

hLib모듈— 로드된 라이브러리에 대한 포인터입니다.

이제 동적 메서드를 사용하여 리소스와 아이콘을 가져오고 "종료" 버튼에 함수 호출을 추가합니다. 예아니요Dlg프로그램이 종료되었는지 확인하세요.
동일한 onCreate 이벤트에 코드를 추가합니다.

프로시저 TfmMain.FormCreate(Sender: TObject); var DLLHandle: THandle; PlugIn(Self) 시작; DLLHandle:= LoadLibrary("SampleDLL.dll"); DLLHandle = 0이면 Exception.Create("'SampleDLL' 라이브러리를 포함할 수 없습니다!"); Self.Icon.LoadFromResourceName(DLLHandle, "my_icon")을 시도해 보세요. 마지막으로 FreeLibrary(DLLHandle); 끝; 끝;

그리고 이벤트를 위해 클릭 시메뉴 항목 "종료":

프로시저 TfmMain.miExitClick(Sender: TObject); var DLLHandle: THandle; Dlg: 함수(const 질문: PChar): 부울; 표준 호출; start DLLHandle:= LoadLibrary("SampleDLL.dll"); DLLHandle = 0이면 Exception.Create("'SampleDLL' 라이브러리를 포함할 수 없습니다!"); @Dlg:= GetProcAddress(DLLHandle, "YesNoDlg")를 시도해 보세요. 할당되지 않은 경우(@Dlg) Exception.Create(""YesNoDlg"라는 함수를 "SampleDLL" 라이브러리에서 찾을 수 없습니다!"); if Dlg("프로그램을 종료하시겠습니까?") then Close; 마지막으로 FreeLibrary(DLLHandle); 끝; 끝;

모든 내용을 올바르게 작성했다면 프로그램을 시작한 후 양식 아이콘이 변경되고 "프로그램 정보" 버튼이 추가되어야 하며 이를 클릭하면 양식이 표시됩니다. 에 대한종료 버튼을 누르면 프로그램에서 "프로그램을 종료하시겠습니까?"라는 확인 메시지가 표시됩니다.

기능을 사용하는 이 작은 예가 도움이 되기를 바랍니다. DLL도서관.
프로젝트 소스를 다운로드할 수 있습니다.

적어도 대다수의 PC 사용자, 특히 이 기사를 읽고 있는 프로그래머라면 DLL이 무엇인지 알고 있습니다. 이 기사에서는 DLL에 관한 일반적인 질문을 모두 살펴보겠습니다.

우리가 정확히 무엇을 살펴볼 것인가:

  1. 평소와 같이 "Hello World" 영역에서 첫 번째 DLL을 생성합니다.
  2. 우리 프로그램에서 이 DLL의 기능을 사용하는 방법을 배우자.
  3. 특정 DLL이 내보내는 함수를 보는 방법을 알아봅시다.
  4. 다른 것일 수도 있습니다....

DLL 생성 프로세스

가장 간단한 것부터 시작해 보겠습니다. 첫 번째 DLL을 작성하는 것입니다. 여기에는 "Hello World" 메시지를 표시하는 함수가 하나만 포함됩니다.

  1. Delphi를 시작합니다(저는 Delphi 6을 사용하고 있습니다).
  2. 다음: 파일 -> 새로 만들기 ->기타

새로 만들기 탭에서 DLL 마법사 개체를 두 번 클릭합니다. 새로운 프로젝트가 열립니다. 예를 들어 MyFirstDLL이라는 이름으로 저장합니다.

순수 모듈에는 다음과 같은 내용이 있습니다.

라이브러리 MyFirstDLL; SysUtils, 클래스를 사용합니다. ($R *.res) 시작 끝.

이제 Dialogs 모듈에서 ShowMessage()를 호출하는 함수 하나만 작성해 보겠습니다. 따라서 절차를 시작하기 전에 Uses 섹션에 Dialogs 모듈을 추가해 보겠습니다. 대략적으로 얻어야 ​​할 것은 다음과 같습니다.

라이브러리 MyFirstDLL; 대화 상자를 사용합니다. 절차 MyFirstFunc; 표준 호출; ShowMessage("Hello World")를 시작합니다. 끝; MyFirstFunc를 내보냅니다. 시작 끝.

보시다시피 여기에는 그다지 복잡한 것이 없습니다. 내가 말할 유일한 것은 이름과 색인(번호)으로 함수를 호출할 수 있다는 것입니다. 이를 위해서는 다음과 같이 작성해야 합니다:

MyFirstFunc 인덱스 1을 내보냅니다.

이 코드에서 명확하지 않은 부분이 있으면 먼저 직접 알아보세요. 이정도면 문제 없을거 같은데... 그런데 문제가 있으면 포럼.! 계속해서, 이제 다른 프로젝트에서 이 (MyFirstFunc) 기능을 어떻게 사용할 수 있습니까?

DLL 함수 사용

첫 번째 단계가 완료되었습니다. 이제 시간 문제일 뿐입니다... 이 기능을 어떻게 사용할 수 있습니까?

다운로드 방법에는 최소한 두 가지가 있습니다.

  1. 공전
  2. 동적

두 번째 방법이 더 좋습니다. 로드하는 동안 발생하는 모든 것을 모니터링하고 로드하는 동안 발생하는 모든 오류를 수정할 수 있기 때문입니다. 하지만 먼저 첫 번째 방법을 살펴보겠습니다.

새 프로젝트를 만들고 양식에 버튼 하나를 놓은 다음 이 버튼의 OnClick 이벤트에서 다음을 작성합니다.

프로시저 TForm1.Button1Click(Sender: TObject); MyProc() 시작; 끝;

하지만 그게 전부는 아닙니다! 프로젝트의 구현 섹션에 다음을 작성하십시오.

구현 절차 MyProc(); 표준 호출; 외부 "MyFirstDLL.dll" 이름 "MyFirstFunc";

준비가 된! 프로젝트를 컴파일하고 버튼을 누르세요! 메시지가 나타나면 모든 것이 정상입니다!

이제 동적 로딩 방법을 살펴보겠습니다. 이 방법은 LoadLibrary() 함수를 사용하고, 마지막으로 언로드는 FreeLibrary()를 사용합니다.

예를 보세요:

프로시저 TForm1.Button1Click(Sender: TObject); TMyFunc 유형 = 프로시저; var DLLInstance: THandle; MyFunc: TMyFunc; start DLLInstance:= LoadLibrary(PChar("MyFirstDLL.dll")); if (DLLInstance = 0) then start MessageDlg("DLL을 로드할 수 없습니다.", mtError, , 0); 출구; 끝; @MyFunc:= GetProcAddress(DLLInstance, "MyFirstFunc")를 시도해 보세요. if Assigned(@MyFunc) then MyFunc() else MessageDlg("필요한 프로시저를 찾을 수 없습니다!.", mtError, , 0); 마지막으로 FreeLibrary(DLLInstance); 끝; 끝;

LoadLibrary() 함수로 DLL을 성공적으로 로드한 후 GetProcAddress()를 사용하여 DLL에서 프로시저를 호출할 함수의 주소를 찾습니다. 결국 FreeLibrary()를 꼭 하셔야 합니다. 이는 매우 중요하므로 성공적으로 다운로드한 후 FreeLibrary()까지의 모든 코드를 try finally 블록에 포함시켰습니다. 이렇게 하면 try Except 블록 내에서 작업을 실행할 때 예기치 않은 예외가 발생하더라도 FreeLibrary가 실행됩니다.

요점은 LoadLibrary와 FreeLibrary에 대한 성공적인 호출이 쌍을 이루어야 한다는 것입니다. 그것이 바로 그 이유입니다. 프로세스에 의해 로드된 각 라이브러리에 대해 시스템은 LoadLibrary에 대한 호출이 성공할 때마다 1씩 증가하는 내부 카운터를 유지 관리합니다. 따라서 FreeLibrary가 실행되면 이 카운터가 감소하고, 0이 되면 이 프로세스에서 해당 라이브러리가 더 이상 필요하지 않으며 메모리에서 안전하게 제거할 수 있다는 의미입니다.

페어링 규칙을 준수하지 않으면 메모리에서 라이브러리가 조기에 언로드되거나(FreeLibrary가 충분하지 않은 경우) 메모리에서 라이브러리가 "고착"될 수 있습니다.

이 규칙을 따르면 LoadLibrary / FreeLibrary 호출의 중첩 가능성에 대해 걱정할 필요가 없습니다.

특정 DLL의 기능 보기

이제 DLL을 포함하는 PE 형식 파일에서 모든 함수 이름을 추출하는 방법을 살펴보겠습니다. 여기서는 PE 형식의 구조를 고려하지 않으므로 소스에 대한 설명은 없습니다.

따라서 새 프로젝트를 만들고 양식에 ListBox를 놓으면 함수 이름이 표시됩니다.

전체 프로젝트는 다음과 같습니다.

유닛 유닛1; 인터페이스는 Windows, 메시지, SysUtils, 변형, 클래스, 그래픽, 컨트롤, 양식, 대화 상자, StdCtrls를 사용합니다. type TForm1 = class(TForm) lb: TListBox; 절차 FormCreate(보내는 사람: TObject); 개인(개인 선언) cmdline: 문자열; 이미지베이스: DWord; DosHeader: PImageDosHeader; PeHeader: PImageNtHeaders; PExport: PImageExportDirectory; p이름:PDWord; 이름: PChar; 공개(공개 선언) end; var Form1: TForm1; 구현($R *.dfm) 프로시저 TForm1.FormCreate(Sender: TObject); 절차 FatalOsError; ShowMessage(SysErrorMessage(GetLastError()))를 시작합니다. 중단; 끝; 변수 i: 정수; if (ParamCount() IMAGE_DOS_SIGNATURE) then FatalOsError를 시도해 보십시오. PEHeader:= PImageNtHeaders(DWord(ImageBase) + DWord(DosHeader^._lfanew)); if (PEHeader^.Signature IMAGE_NT_SIGNATURE) then FatalOsError; PExport:= PImageExportDirectory(ImageBase + DWord(PEHeader^.OptionalHeader.DataDirectory.VirtualAddress)); pname:= PDWord(ImageBase + DWord(PExport^.AddressOfNames)); i:= 0에서 PExport^.NumberOfNames - 1 시작 이름:= PChar(PDWord(DWord(ImageBase) + PDword(pname)^)); lb.Items.Add(이름); inc(p이름); 끝; 마지막으로 FreeLibrary(ImageBase); 끝; Application.ShowMainForm을 제외하고:= False; 신청.종료; 끝; 끝; 끝.

코드를 직접 알아내고 싶은데 문제가 해결되지 않는다면, 저희 포럼이 확실히 도움이 될 것입니다. 들어오세요!

뷰어를 모든 DLL에 연결합니다.

함수가 포함된 기성 DLL이 있고 함수 뷰어가 있습니다. 추가 작업의 편의를 위해 일부 기능을 추가하는 것이 남아 있습니다. 해보자... 탐색기에서 폴더를 엽니다. 도구 -> 폴더 옵션으로 이동하세요. "파일 형식" 탭으로 이동하세요. 목록에서 DLL 형식을 찾습니다. 그러한 것이 없으면 "만들기"버튼을 클릭하고 "확장"필드에 DLL을 작성하십시오. 확인을 클릭하세요. 우리가 만든 유형인 DLL을 찾습니다. 그것을 선택하고 "고급"을 클릭하십시오. 다음 "만들기"에서는 "작업" 필드에 DLL 뷰어와 같이 상황에 맞는 메뉴에 표시될 내용을 작성합니다. 검토를 통해 우리는 우리 프로그램을 찾고 있습니다.

모두 준비되었습니다!

이제 DLL 형식 파일을 마우스 오른쪽 버튼으로 클릭하면 DLL 뷰어가 메뉴에 나타납니다. 그것을 선택하고 모든 기능을 확인하세요!

그게 다입니다. 관심을 가져주셔서 감사합니다!

저는 뉴스레터의 다음 호에 대해 여러분께 알려드리고자 합니다.
Borland Delphi에서 DLL을 개발하고 사용하는 데 따른 문제. 신규 가입자에게 알려드립니다.
메일링 아카이브(13호)에 있는 기사의 첫 번째 부분을 볼 수 있습니다.
저에게 편지를 보냈지만 답변을 받지 못하신 분들께 사과드립니다. 가까운 시일 내에 이 문제를 해결하도록 노력하겠습니다.
그럼 계속합시다.

동적 라이브러리에 있는 프로시저나 함수를 사용하기 전에
DLL을 RAM에 로드해야 합니다. 라이브러리를 로드할 수 있습니다.
정적 로딩과 동적 로딩의 두 가지 방법 중 하나를 사용합니다.
두 가지 방법 모두 장점과 단점이 있습니다.
정적 로딩은 동적 라이브러리가 자동으로 로드된다는 의미입니다.
이를 사용하는 응용 프로그램이 시작될 때. 이 다운로드 방법을 사용하려면,
내보내는 내용을 설명할 때 외부 키워드를 사용해야 합니다.
동적 라이브러리 함수 또는 프로시저. 프로그램이 시작되면 DLL이 자동으로 로드됩니다.
다음과 같은 방식으로 여기에서 내보낸 루틴을 사용할 수 있습니다.
마치 애플리케이션 모듈 내부에 설명된 것처럼 말이죠.
이는 DLL에 있는 코드를 사용하는 가장 쉬운 방법입니다.
이 방법의 단점은 라이브러리 파일이
응용 프로그램에 링크가 있는데 링크가 없으면 프로그램 로드가 거부됩니다.
동적 방법의 핵심은 애플리케이션이 시작될 때 라이브러리를 로드하지 않는다는 것입니다.
그리고 정말 필요한 순간에 말이죠. 스스로 판단하십시오. 왜냐하면 기능이 설명되어 있기 때문입니다.
동적 라이브러리에서는 프로그램 실행의 10%에서만 사용되며 절대 사용되지 않습니다.
정적 로딩 방법을 사용하는 것은 의미가 없습니다. 이 경우 메모리에서 라이브러리 언로드
또한 귀하의 통제하에 수행됩니다. 이 방법의 또 다른 장점
DLL을 로드한다는 것은 (명백한 이유로) 애플리케이션의 시작 시간을 줄이는 것을 의미합니다.
이 방법의 단점은 무엇입니까? 제가 보기에 가장 중요한 것은 다음과 같습니다.
이 방법은 위에서 설명한 정적 로딩보다 더 까다롭습니다.
먼저 Windows API LoadLibrary 함수를 사용해야 합니다.
내보낸 프로시저나 함수에 대한 포인터를 얻으려면 다음을 수행해야 합니다.
GetProcAddress 함수를 사용하세요. DLL 사용을 마친 후
FreeLibrary를 사용하여 언로드해야 합니다.
DLL에서 로드된 프로시저 및 함수 호출.
프로시저와 함수를 호출하는 방법은 동적 라이브러리를 로드한 방법에 따라 다릅니다.
이 서브루틴이 위치한 곳입니다.
정적으로 로드된 DLL에서 함수와 프로시저를 호출하는 것은 매우 간단합니다. 원래는 앱에서
내보낸 함수(프로시저)에 대한 설명을 포함해야 합니다. 그 후에는 사용할 수 있습니다
마치 애플리케이션 모듈 중 하나에 설명된 것과 같습니다.
DLL에 포함된 함수나 프로시저를 가져오려면 다음을 사용해야 합니다.
선언의 외부 수정자. 예를 들어 위에서 고려한 HelloWorld 절차의 경우
호출 애플리케이션에는 다음 줄이 있어야 합니다.
절차 SayHello(AForm: TForm); 외부 myfirstdll.dll";
외부 키워드는 컴파일러에게 프로시저를 찾을 수 있음을 알려줍니다.
동적 라이브러리(이 경우 - myfirstdll.dll).
이 프로시저에 대한 호출은 다음과 같습니다.
...
HelloWorld(자체);
...
함수와 프로시저를 가져올 때 이름과 인터페이스를 작성할 때 특히 주의하세요!
사실은 애플리케이션을 컴파일하는 동안 개체 이름의 정확성을 확인하지 않는다는 것입니다.
DLL에서 내보낸 기능은 구현되지 않으며, 어떤 기능을 잘못 설명했다면
그러면 애플리케이션의 실행 단계에서만 예외가 발생합니다.
DLL에서 가져오기는 프로시저(함수) 이름, 시퀀스 번호 또는
다른 이름으로.
첫 번째 경우에는 프로시저 이름과 해당 프로시저를 가져오는 라이브러리를 선언하기만 하면 됩니다.
(우리는 이것을 조금 더 높게 보았습니다). 시퀀스 번호로 가져오려면 바로 다음 번호를 지정해야 합니다.
절차 HelloWorld(AForm: TForm); 외부 myfirstdll.dll 인덱스 15;
이 경우 가져올 때 프로시저에 지정하는 이름은 다음과 같을 필요가 없습니다.
이는 DLL 자체에 지정되었습니다. 저것들. 위 항목은 다음을 의미합니다.
동적 라이브러리 myfirstdll.dll에서 내보낸 프로시저를 가져오고 있습니다.
15번째, 애플리케이션 내에서 이 프로시저의 이름은 SayHello입니다.
어떤 이유로 위에서 설명한 가져오기 방법을 사용하지 않는 경우,
하지만 여전히 가져온 함수(프로시저)의 이름을 변경하려면 세 번째 방법을 사용할 수 있습니다.
절차 CoolProcedure; 외부 myfirstdll.dll 이름 "DoSomethingReallyCool";
여기서 가져온 CoolProcedure 프로시저의 이름은 DoSomethingReallyCool입니다.
동적으로 로드된 라이브러리에서 가져온 프로시저 및 함수 호출
위에서 논의한 방법보다 다소 복잡합니다. 이 경우에는 신고해야 합니다.
사용할 함수나 프로시저에 대한 포인터입니다.
HelloWorld 절차를 기억하시나요? 어떻게 해야 하는지 알아보겠습니다
DLL을 동적으로 로드하는 경우 실행하기 위해 호출합니다. 우선, 당신은
이 절차를 설명하는 유형을 선언해야 합니다.
유형
THelloWorld = 절차(AForm: TForm);
이제 GetProcAddress get을 사용하여 동적 라이브러리를 로드해야 합니다.
프로시저에 대한 포인터, 실행을 위해 이 프로시저를 호출하고 마지막으로 메모리에서 DLL을 언로드합니다.
다음은 이 작업을 수행하는 방법을 보여주는 코드입니다.

DLLInstance: THandle ;

HelloWorld:THelloWorld;

시작하다

(DLL 로드)

(우리는 포인터를 얻습니다)

(실행을 위한 프로시저 호출)

헬로월드(셀프) ;

(RAM에서 DLL 언로드)

FreeLibrary(DLLInstance) ;

끝 ;

위에서 언급했듯이 DLL을 정적으로 로드하는 것의 단점 중 하나는 다음을 수행할 수 없다는 것입니다.
하나 이상의 라이브러리가 없을 때 애플리케이션 작업을 계속합니다. 다이나믹의 경우
다운로드하면 이러한 상황을 프로그래밍 방식으로 처리하고 프로그램이 실행되는 것을 방지할 수 있습니다.
스스로 떨어졌다." LoadLibrary 및 GetProcAddress 함수에서 반환된 값을 기반으로 다음을 수행할 수 있습니다.
라이브러리가 성공적으로 로드되었는지, 애플리케이션에 필요한 절차가 라이브러리에서 발견되었는지 확인합니다.
아래 코드는 이를 보여줍니다.

절차 TForm1.DynamicLoadBtnClick (보내는 사람: TObject ) ;

유형

THelloWorld = 프로시저(AForm: TForm);

DLLInstance: THandle ;

HelloWorld:THelloWorld;

시작하다

DLLInstance:= LoadLibrary("myfirstdll.dll" ) ;

DLLInstance = 0이면 시작됩니다.

메시지Dlg( "DLL을 로드할 수 없습니다", mtError, [ mbOK], 0 ) ;

출구 ;

끝 ;

@HelloWorld:= GetProcAddress(DLLInstance, "HelloWorld" ) ;

@HelloWorld가 nil이면

헬로월드(셀프)

또 다른

메시지Dlg( "필요한 절차를 찾을 수 없습니다!.", mtError, [ mbOK], 0 ) ;

FreeLibrary(DLLInstance) ;

끝 ;

DLL은 코드뿐만 아니라 양식도 저장할 수 있습니다.
게다가 동적 라이브러리에서 양식을 만들고 배치하는 것은 작업하는 것과 크게 다르지 않습니다.
일반 프로젝트의 양식을 사용합니다. 먼저 라이브러리를 작성하는 방법을 살펴보겠습니다.
양식을 포함하는 방법에 대해 설명하고 DLL에서 MDI 기술을 사용하는 방법에 대해 설명하겠습니다.
예제를 사용하여 양식이 포함된 DLL의 개발을 보여 드리겠습니다.
그럼 먼저 새로운 동적 라이브러리 프로젝트를 만들어 보겠습니다.
이렇게 하려면 File|New 메뉴 항목을 선택한 다음 DLL 아이콘을 두 번 클릭합니다.
그 후에는 다음과 유사한 코드가 표시됩니다.

결과 프로젝트를 저장합니다. DllForms.dpr이라고 부르겠습니다.
이제 새 양식을 만들어야 합니다. 이것은 다양한 방법으로 수행될 수 있습니다.
예를 들어 File|New Form 메뉴 항목을 선택합니다. 양식에 일부 구성 요소를 추가합니다.
양식 이름을 DllForm으로 지정하고 결과 모듈을 DllFormUnit.pas라는 이름으로 저장하겠습니다.
프로젝트의 기본 모듈로 돌아가서 그 안에 ShowForm 함수를 배치해 보겠습니다. 해당 작업에는 다음이 포함됩니다.
양식을 만들어 화면에 표시합니다. 이를 위해 아래 코드를 사용하십시오.

형식: TDLLForm;

시작하다

결과:= Form.ShowModal ;

양식.무료;

끝 ;

프로젝트를 오류 없이 컴파일하려면 사용 섹션에 Forms 모듈을 추가해야 합니다.
내보내기 키워드를 사용하여 함수를 내보냅니다.
수출
쇼폼;
프로젝트를 컴파일하고 dllforms.dll 파일을 얻습니다. 이 간단한 단계가 전부입니다
ShowForm 함수가 stdcall 키워드를 사용하여 선언되었음을 확인하기 위해 수행해야 할 작업입니다.
함수를 내보낼 때 규칙을 사용하도록 컴파일러에 신호를 보냅니다.
표준 통화 호출 규칙에 따라. 이런 방식으로 함수를 내보내면
Delphi에서 생성된 애플리케이션뿐만 아니라 개발된 DLL을 사용할 수 있는 기능.
호출 규칙은 함수를 호출할 때 인수가 전달되는 방식을 결정합니다.
stdcall, cdecl, pascal, Register 및 safecall의 다섯 가지 주요 규칙이 있습니다.
이에 대한 자세한 내용은 Delphi 도움말 파일의 "호출 규칙" 섹션을 참조하세요.
또한 ShowForm 함수에서 반환된 값은 다음과 같습니다.
ShowModal 값에 해당합니다. 이렇게 하면 일부 정보를 전송할 수 있습니다.
호출 애플리케이션의 양식 상태에 대해 설명합니다.
다음은 두 개의 목록입니다. 첫 번째 목록에는 파일의 전체 코드가 포함되어 있습니다.
DLL 프로젝트(양식이 있는 모듈은 여기에 표시되지 않음), 두 번째는 호출 애플리케이션 모듈입니다.
방금 개발한 라이브러리를 사용합니다.

libraryDllForms;

용도

"DllFormUnit.pas"의 DllFormUnit(DllForm) ;

($R *.RES)

함수 ShowForm: 정수 ; 표준 호출 ;

형식: TDLLForm;

시작하다

양식:= TDLLForm.Create(애플리케이션) ;

결과:= Form.ShowModal ;

양식.무료;

끝 ;

시작하다

끝.


단위 TestAppUnit;

상호 작용

용도

Windows, 메시지, SysUtils, 클래스, 그래픽,

컨트롤, 양식, 대화 상자, StdCtrls;

유형

TForm1 = 클래스(TForm)

버튼1: T버튼;

절차 Button1Click(Sender: TObject ) ;

사적인

(개인 선언)

공공의

(공개 선언)

끝 ;

Form1: TForm1;

함수 ShowForm: 정수 ; 표준 호출 ;

외부 "dllforms.dll" ;

구현

($R *.DFM)

절차 TForm1.Button1Click (보내는 사람: TObject ) ;

시작하다

끝 ;

끝.

함수를 내보낼 때도 stdcall 키워드가 사용되었습니다.
DLL에서 하위 양식을 사용할 때는 특별한 주의를 기울여야 합니다. 예를 들어,
호출 응용 프로그램에서 기본 양식에는 MDIForm과 동일한 FormStyle 속성 값이 있습니다.
그런 다음 DLL에서 MDIChild 형식을 호출하려고 하면 화면에 오류 메시지가 나타납니다.
활성 MDI 양식이 없다고 표시됩니다.
자식 창을 표시하려고 하는 순간 VCL은 정확성을 확인합니다.
애플리케이션 기본 양식의 FormStyle 속성입니다. 그러나 우리의 경우 모든 것이 사실인 것 같습니다.
그래서 거래는 무엇입니까? 문제는 이러한 검사를 수행할 때 Application 개체가 고려된다는 것입니다.
호출 애플리케이션에 속하지 않고 동적 라이브러리 자체에 속합니다.
당연하게도 DLL에는 기본 양식이 없으므로 검사에서 오류가 발생합니다.
이러한 상황을 방지하려면 동적 라이브러리의 Application 개체를 할당해야 합니다.
호출 애플리케이션의 Application 객체. 당연히 이것은 다음과 같은 경우에만 작동합니다.
호출 프로그램이 VCL 애플리케이션인 경우. 또한 메모리에서 라이브러리를 언로드하기 전에
라이브러리의 Application 객체 값을 원래 상태로 되돌려야 합니다.
이렇게 하면 메모리 관리자가 라이브러리가 차지하는 RAM을 정리할 수 있습니다.
따라서 라이브러리의 기본 Application 개체에 대한 포인터를 저장해야 합니다.
값을 복원하는 데 사용할 수 있는 전역 변수에 있습니다.
이제 조금 돌아가서 배치 작업에 필요한 단계를 나열해 보겠습니다.
DLL MDIChild 형식.
동적 라이브러리에서 TApplication 유형의 전역 변수를 생성합니다.
전역 변수에 응용 프로그램 DLL 개체에 대한 포인터를 저장합니다.
동적 라이브러리의 Application 개체에 Application에 대한 포인터를 할당합니다.
호출 응용 프로그램.
MDIChild 양식을 작성하고 이를 사용하여 작업합니다.
동적 라이브러리의 Application 객체 값을 원래 상태로 되돌립니다.
그리고 메모리에서 DLL을 언로드합니다.
첫 번째 단계는 간단합니다. DLL 모듈 상단에 다음 코드를 배치하기만 하면 됩니다.
var
DllApp:T응용 프로그램;
그런 다음 Application 개체의 값을 변경하고 하위 폼을 만드는 프로시저를 만듭니다.
절차는 다음과 같습니다.

절차 ShowMDIChild(MainApp: TApplication) ;

아동: TMDIChild;

시작하다

할당되지 않은 경우(DllApp) 시작

DllApp:= 애플리케이션;

애플리케이션:= MainApp;

끝 ;

하위:= TMDIChild.Create(Application.MainForm) ;

어린이.쇼 ;

끝 ;

이제 우리가 해야 할 일은 Application 개체의 반환 값을 제공하는 것뿐입니다.
원래 상태로. MyDllProc 프로시저를 사용하여 이 작업을 수행합니다.

프로시저 MyDLLProc(이유: Integer ) ;

시작하다

이유 = DLL_PROCESS_DETACH인 경우

(DLL이 언로드되었습니다. 응용 프로그램 포인터의 값을 복원합니다.)

할당된 경우(DllApp)

응용프로그램:= DllApp;

끝 ;

결론 대신.
동적 링크 라이브러리를 사용하는 것은 언뜻 보이는 것만큼 어렵지 않습니다.

동적 링크 라이브러리를 사용하는 것은 언뜻 보이는 것만큼 어렵지 않습니다.
DLL은 애플리케이션 성능을 최적화할 수 있는 광범위한 기회를 제공합니다.
프로그래머 자신의 작업도 마찬가지입니다. DLL을 사용하면 인생이 더 쉬워질 수도 있습니다!
http://subscribe.ru/
이메일: [이메일 보호됨]찾다
Subscribe.Ru에서 APORT로

Delphi 프로그래밍 환경은 DLL을 빠르게 생성하기 위한 내장 도구를 제공합니다.

구체적인 함수를 포함하는 라이브러리를 만들어 보겠습니다.

GetArea(a, b, c: REAL):REAL.

이 함수는 삼각형의 변의 길이를 입력으로 사용합니다. 이 함수는 주어진 삼각형의 면적을 반환합니다.

p:=(a+b+c)/2;

결과:=SQRT(p*(p-a)*(p-b)*(p-c))

D를 발사하다엘피, 그러면 우리는 관습에 얽매이지 않는 행동을 합니다. 메뉴 항목 선택 FN에우영형거기, N 탭에서 열리는 창에서에우DLL W 아이콘을 클릭하세요이자드. (알고리즘은 버전에 따라 다름)

그러면 템플릿 DLL 파일이 생성됩니다. 일반 모듈과 매우 유사합니다(단위)디엘피, 연산자로 시작합니다도서관. 나중에 DLL이 제공될 이름으로 프로젝트를 저장합니다.도착. 이름GetArea사용할 수 없습니다. 이미 함수 이름으로 사용 중입니다.

이제 연산자 뒤에용도함수의 텍스트를 작성하지만 제목이 일부 변경되었습니다.

기능 GetArea(a, b, c:REAL):REAL;내보내다;

EXPORT 키워드는 함수를 내보낼 수 있고 외부 프로그램에서 볼 수 있음을 나타냅니다.

함수 텍스트 뒤에 추가하겠습니다.

GetArea;

EXPORTS 문은 라이브러리에서 내보낸 모든 프로시저와 함수를 나열합니다. 이것은 우리 도서관의 일종의 카탈로그입니다.

라이브러리를 실행하는 것은 불가능합니다. 컴파일만 가능합니다. 이를 수행하려면 프로젝트 → 빌드 메뉴 항목을 선택하십시오. 모든 작업이 올바르게 완료되면 현재 디렉터리의 디스크에 geta.dll이라는 파일이 생성됩니다. 이곳은 우리 도서관입니다.

중요 사항: STRING 유형의 매개변수를 라이브러리에 있는 프로시저 및 함수에 전달할 때 어느 정도 미묘함이 있습니다.

STRING 유형의 매개변수를 전달할 수 있으려면 라이브러리와 이를 호출하는 프로그램 모두의 USES 문에서 ShareMem 모듈의 연결을 지정해야 하며, 심지어 이 모듈이 목록에서 첫 번째가 되도록 해야 합니다. . 또한 라이브러리와 함께 borlndmm.dll 파일(Delphi 배포판에 포함되어 있음)을 포함해야 합니다. 이러한 상황을 피하는 것은 쉽습니다. 텍스트 유형 매개변수의 경우 ShortString 데이터 유형(일반 문자열이지만 최대 255자 길이) 및 PChar(텍스트 문자열에 대한 포인터)를 사용해야 합니다.

DLL 호출

DLL에서 프로시저와 함수를 호출하는 방법에는 두 가지가 있습니다. 첫 번째 경우, 우리는 프로그램 개발 단계에서 어떤 DLL에 연결할지 미리 알고 있습니다(대부분 우리는 이 DLL을 직접 생성합니다). 두 번째 경우에는 "외부" 라이브러리를 포함하여 임의의 라이브러리에 연결합니다.

정적 연결

첫 번째 메서드를 구현하려면 정적 연결, 새로운 일반 애플리케이션을 생성하고 세 개의 입력 필드 LabeledEdit1...LabeledEdit3, 버튼 및 Tlabel 구성 요소를 양식에 배치합니다. IMPLEMENTATION 문 뒤에 geta.dll 라이브러리에서 GetArea 함수를 가져오는 줄을 추가합니다.

함수 GetArea(a,b,c:real):REAL; 멀리; 외부 "게타";

EXTERNAL이라는 단어는 이 함수의 본문이 지정된 이름의 라이브러리에 있음을 나타내고, FAR이라는 단어는 호출 프로그램이 메모리의 한 페이지에 위치하므로 "긴" 4바이트 주소의 사용을 지정합니다. , DLL 라이브러리는 다른 곳에 있습니다. 물론, geta.dll 파일은 현재 애플리케이션의 모든 파일이 위치한 동일한 디렉터리에 있어야 합니다.

버튼 클릭 핸들러에서 배열을 생성하여 라이브러리 함수에 전달하고 결과를 화면에 표시해야 합니다.

절차 TForm1.Button1Click(Sender: TObject);

DLL 작업

DLL- 동적 링크 라이브러리는 다른 프로그램에서 동일한 기능을 재사용할 수 있게 해주는 동적 링크 라이브러리입니다. 실제로, 이는 매우 편리한 도구입니다. 특히, 일단 작성된 라이브러리는 많은 프로그램에서 사용될 수 있기 때문입니다. 오늘 수업에서는 DLL을 사용하여 작업하는 방법과 DLL을 만드는 방법을 배웁니다!

자, 시작합시다!

먼저 첫 번째 동적 링크 라이브러리를 만들어 보겠습니다! Delphi로 이동하여 즉시 File -> New -> Other 메뉴로 이동합니다.

목록에서 동적 링크 라이브러리를 선택합니다(Delphi 2009 이전 버전에서는 해당 항목을 DLL 마법사라고 함).

결과적으로 코드가 있는 창만 있습니다. 여기에는 어떤 양식도 없습니다.

이제 재미가 시작됩니다. 라이브러리에 첫 번째 프로시저를 작성해 보겠습니다.

라이브러리프로젝트2; //dll을 생성할 때 //프로그램 대신 라이브러리라는 단어가 사용된다는 점을 이미 알고 계실 것입니다. // 의미 라이브러리. SysUtils, 대화 상자, 클래스를 사용합니다. // 주목! 이러한 모듈을 지정하는 것을 잊지 마십시오. 그렇지 않으면 코드가 작동하지 않습니다. ($R *.res) (이 부분은 DLL 코드입니다.) Procedure FirstCall; 표준 호출; 내보내다; //Stdcall – 이 연산자를 사용하면 매개변수가 스택에 //오른쪽에서 왼쪽으로 배치되고 //표준 값에 맞춰 정렬됩니다. //Export는 원칙적으로 생략될 수 있으며 //프로시저 내보내기를 명확히 하는 데 사용됩니다. 또는 기능. Begin ShowMessage("dll의 첫 번째 프로시저"); //화면에 메시지 호출 End; 절차 DoubleCall; 표준 호출; 내보내다; Begin ShowMessage("내 두 번째 절차"); //화면에 메시지 호출 End; FirstCall, DoubleCall을 내보냅니다. //내보내기에는 내보낸 요소 목록이 포함되어 있습니다. //나중에 일부 프로그램에서 가져올 것입니다. 시작 끝.

지금은 여기서 멈추겠습니다. 왜냐면... 간단한 예에서는 이것으로 충분합니다. 이제 프로젝트를 저장하고 개인적으로 Project2.dll이라는 이름으로 저장한 다음 Ctrl+F9 키 조합을 눌러 라이브러리를 컴파일합니다. dpr 파일을 저장한 폴더에 dll 확장자를 가진 파일이 나타날 것입니다. 이것은 방금 생성된 라이브러리입니다. 나는 그것을 Project2.dll이라고 불렀습니다.

이제 이 라이브러리에서 프로시저를 호출해 보겠습니다. 표준 구성표에 따라 새 애플리케이션을 만듭니다. 우리 앞에는 특별한 것이 없고 단지 형태만 있을 뿐입니다. 새 응용 프로그램을 일부 폴더에 저장하십시오. 그리고 새로 생성된 dll 라이브러리를 같은 폴더에 복사하세요. 저것들. 이 예에서는 Project2.dll

이제 라이브러리에서 함수를 호출하는 방법을 선택해야 합니다. 호출 방법은 총 2가지가 있습니다.

방법 1번

아마도 이것은 라이브러리에 있는 프로시저를 호출하는 가장 간단한 방법일 것입니다.

하나의 라이브러리로만 작업하는 데 이상적입니다.

자, 가자...

구현 키워드 뒤에 다음 코드를 작성합니다.

여기서는 아마도 이미 짐작했듯이 프로그램에 프로시저의 이름을 알려주고 해당 프로시저가 dll 라이브러리에 있다고 말합니다. 제 경우에는 Project2.dll이라는 이름이 붙었습니다.

이제 이러한 프로시저를 호출하려면 코드의 어느 곳에나 해당 이름을 삽입하기만 하면 됩니다. 이제 그렇게 하겠습니다. 표준 탭에서 2개의 Button 구성 요소를 양식에 놓고 각 항목에 OnClick 이벤트 핸들러를 만듭니다.

첫 번째 버튼의 OnClick:

OnClick 두 번째 버튼:

그게 다야!

방법 2:

첫 번째 것보다 더 복잡하지만 장점이 있으며 가장 중요한 것은 플러그인에 이상적이라는 것입니다.

이 방법을 사용하려면 먼저 여러 전역 변수를 선언해야 합니다.

그런 다음 구현 키워드 뒤에 라이브러리를 로드하는 프로시저를 작성합니다.

프로시저 LoadMyLibrary(파일 이름: 문자열);시작하다 LibHandle:= LoadLibrary(PWideChar(파일 이름));//라이브러리를 로드 중입니다! // 주목! 2009 Delphi 미만 버전의 PChar LibHandle = 0이면 시작 MessageBox(0," 라이브러리를 로드할 수 없습니다.",0,0); 출구; 끝; FirstCall:= GetProcAddress(LibHandle,"FirstCall");//객체에 대한 포인터 가져오기 //라이브러리 모듈에 대한 첫 번째 매개변수 링크 //dll에 있는 객체의 두 번째 매개변수 이름 DoubleCall:= GetProcAddress(LibHandle,"DoubleCall"); @FirstCall = nil이면 시작됩니다.//라이브러리에 이 함수가 있는지 확인합니다. MessageBox(0,"라이브러리를 로드할 수 없습니다.",0,0); 출구; 끝; @DoubleCall = nil이면 시작됩니다.//라이브러리에 이 함수가 있는지 확인합니다. MessageBox(0,"라이브러리를 로드할 수 없습니다.",0,0); 출구; 끝; 끝

그런 다음 양식에서 방금 생성된 프로시저를 사용하여 라이브러리를 로드하는 OnCreate 이벤트 핸들러를 생성합니다.

프로시저 TForm1.FormCreate(Sender: TObject);시작하다 LoadMyLibrary("Project2.dll");끝;

이제 다시 라이브러리에서 필요한 프로시저를 호출하려면 코드 어디에나 해당 프로시저의 이름을 삽입하기만 하면 됩니다. 이렇게 하려면 표준 탭에서 2개의 Button 구성 요소를 양식에 놓고 각 단추에 OnClick 이벤트 핸들러를 만듭니다.

첫 번째 버튼의 OnClick:

프로시저 TForm1.Button1Click(Sender: TObject); FirstCall을 시작합니다. // dll에 위치한 프로시저의 이름 End;

OnClick 두 번째 버튼:

프로시저 TForm1.Button2Click(Sender: TObject); DoubleCall을 시작합니다. // dll에 위치한 프로시저의 이름 End;

마지막으로 폼에 OnDestroy 이벤트 핸들러를 생성하여 메모리에서 dll 라이브러리를 언로드합니다.

그게 다야! 두 번째 방법은 꽤 번거로웠지만 라이브러리에 저장된 객체를 명확히 할 수 있다는 장점이 있습니다.