본문 바로가기
Delphi Tip/파일

DLL(Dynamic-link library)란 (5)

by MonoSoft 2023. 6. 27.
728x90
반응형

 

 

DLL(Dynamic-link library)란 (4) 델파이에서의 DLL 작성은 그것을 이용하기 만큼이나 쉽다.

 

일반적인 코딩 스타일 을 그대로 유지하면 되기 때문이다. 하나의 차이점이라면

프로젝트 파일에서 program 대신 library를 기입하는 것이다. 그에 더해 exports절만 살짝 넣어주면 된다. 물론 우리가 일반적으로 사용하는 VCL하의 프로젝트 구조와는 약간 차이가 있지만, 콘솔(커맨드라인) 프로그램을 작성하는 경우 이런 형태가 일반적이다.

아래 형태의 프로젝트 내용이 이상하게 보이는 사람은 그냥 '정통 스타일의 파스 칼 구조'라고 보면 된다. 물론 반드시 '특정 구조로 작성하라'라는 규칙은 없다.

 

library MinMax; // program 대신 library를 기입한다.

function Min(X, Y: Integer): Integer; stdcall; begin

if X < Y then Min := X else Min := Y; end;

function Max(X, Y: Integer): Integer; stdcall; begin

if X > Y then Max := X else Max := Y; end;

exports // exports 절이 추가된다.

Min index 1, // function이나 procedure명과 함께 인덱스를 준다.

Max index 2;

begin

end.

아주 간단하게 Integer형의 두 인자를 받아서 작은값, 큰값을 돌려주는 function 을 가진 DLL이다. 선두에 program 대신 library를 두면 델파이는 .EXE파일 대신 에 .DLL파일을 생성하게 된다. DLL의 사용을 위해 improt를 사용하듯이 DLL에서 외부와의 인터페이스로 진입점(Entry Point)을 설정해 주려면 export절을 이용하 여야 한다. export는 import의 요구가 있을시 필요한 프로시져의 주소를 준다.

위의 예에서 procedure의 명을 주어 그 포인터를 억세스할 수 있도록 하고 인덱 스 번호도 함께 부여하고 있다.

stdcall 호출규약이 export를 위해 사용되고 있다는 것에 유의하자. procedure나 function을 호출할 때 인자, 반환값등의 관리를 위해 스택을 다루는 방법이 언어 마다 다르다. stdcall을 둠으로써 다른 언어로 작성된 프로그램이 우리가 만든 DLL을 사용할 수 있게 된다. 델파이 언어로 작성된 프로그램에서 이용한다면 문 제가 없지만 다른언어에서 사용할 수 있도록 하기 위해 stdcall 호출규약을 이용 하는 것이다. DELPHI 3를 이용한 ISAPI 프로젝트등을 작성해본 사람을 알겠지만 DLL 자체가 여러개의 유닛으로 구성되는 일이 종종 있다. 예를들어 아래의 것을 프로젝트 파일에 두고 실제 procedure나 function등은 개별 유닛에 등록하는 것 도 많이 쓰는 방법이다. 이 때 uses절을 통해 각 유닛을 참조한다.

library Editors;

uses EdInit, EdInOut, EdFormat, EdPrint; exports

InitEditors index 1,

DoneEditors index 2,

InsertText index 3,

DeleteSelection index 4,

FormatSelection index 5,

PrintSelection index 6,

...

SetErrorHandler index 53;

begin

InitLibrary;

end.

Export절에 대하여

export절은 라이브러리나 프로그램의 선언부중 어디에나 몇번이고 올 수 있다.

일반적으로 오브젝트 파스칼 코딩시 type절, const절, var절등이 필요한 위치에 필요한 만큼 몇번이고 쓸 수 있는 것과 같다. 각 export절의 내용은 export 시킬 procedure나 function의 식별자(이름)로 채워진다. export절이 어디에 위치하던 상관없지만 export절에 사용되는 procedure나 function은 반드시 export절의 위 치보다 선행위치에 있어야 한다. export절에서는 UnitName.GetTime 등과 같이 유 닛명과 마침표(.)를 이용하여 특정 유닛내의 procedure나 function임을 지정해 줄 수도 있다. 이를 완전지정 식별자(fully qualified identifire)라고 부른다.

index절은 옵션이다. index절에 사용되는 수치는 Cardinal(델파이1에는 없음)형 으로 2기가(1에서 2,147,483,647)까지의 인덱스를 지정할 수 있다. 하지만 이 인 덱스는 DLL 구성시 문자열로 구성된다. 큰 수치일수록 자리수도 많은 것이다. 이 것은 DLL을 import하기 위해 테이블을 검진할때의 수행속도에 영향을 준다. 따라 서 될 수 있으면 작은 수치의 인덱스 번호를 지정하는 것이 좋다. 인덱스 번호로 DLL import시 지정된 인덱스의 번호로 export된 것이 진입점(entry point)로 이 용된다. 인덱스절은 생략될 수도 있는데, 이 때는 자동으로 순차적인 번호가 매 겨진다. 이렇게 자동으로 매겨진 인덱스 번호는 사실 import를 위해 DLL의 export 테이블을 훑어갈 때 가장 빠른 결과를 가져다 준다. 그 번호가 순차적이 기 때문이다.

또한 export절의 내용에 name절을 넣을 수도 있다. 이것은 name 'ProcedureName'

과 같은 형태로 이용되며 실제 프로시져명과 다른 이름으로 export하고 싶을 때 사용하는 방법으로 사용법은 아래와 같다.

export

...

InitEditors index 1 name 'InitEditorsA'; ...

name절을 생략하면 프로시져명과 동일한 스펠링과 동일한 대소문자로 name절이 자동으로 구성된다.

DLL을 위한 library가 아니라 program도 export절을 포함할 수 있지만, Windows 는 한 어플리케이션이 다른 어플리케이션에서 사용되도록 자신의 function이나 procedure를 export하는 것이 불가능하기 때문에 사실상 무의미하다. AS/400용 델파이에서 이것이 이용될 수 있는지는 별들에게 물어보면 된다.

◇ 라이브러리의 초기화 코드

DLL의 수행부(주 begin..end 블록)에는 라이브러리의 초기화 코드를 넣어줄 수 있다. 단, 이부분은 DLL이 메모리에 적재될 때 딱 한번 호출된다는 것을 반드시 명심하자. DLL의 적재는 그것이 호출될 때 딱한번 이루어진다. 이미 다른 어플리 케이션에서 DLL을 사용하고 있다면 새로 실행된 어플리케이션은 '이미 적재된'

DLL을 이용할 뿐 그것을 새로 적재하지는 않는다. 따라서 '어플리케이션의 실행 시 DLL 초기화 코드가 실행될 것이다'류의 상상은 단지 상상에 그친다. DLL에 익 숙하지 않은 사람 중 몇몇이 이 부분에서 헤매는 경우를 봤기 때문에 언급해 둔 다.

초기화 코드에는 대체로 라이브러리에 포함된 윈도우 procedure들을 위한 window 클래스들을 등록하는 것이나 라이브러리의 전역변수에 초기값을 넣는등의 작업이 들어간다.

이에 더해 초기화 코드에는 OS가 라이브러리를 메모리에서 제거할 때, 즉 Unload 할 때 호출되는 exit procedure를 지정할 수도 있다. exit procedure는 ExitProc 변수를 이용하여 지정하는데, 이에 대한 사항은 뒤에서 자세히 다루겠다.

초기화 코드에 들어갈 수 있는 다른 것으로는 exit code가 있다. 이것은 ExitCode 변수를 이용해 지정할 수 있는데, 라이브러리상의 에러코드 역할을 한 다. ExitCode는 델파이의 System 유닛에 선언되어 있다. ExitCode에 0이 아닌 값 을 넣으면 이것이 에러코드의 역할을 하며 DLL이 메모리에서 제거되고(Unload) 어플리케이션은 이것을 통지(주로 exception이 발생) 받는다. 만약 초기화 코드 부분을 실행 중일 때 예외(exception)이 발생하여 초기화 코드부분을 빠져나왔다 면 DLL을 호출한 어플리케이션에는 DLL의 로딩이 실패했다는 통지가 보내진다.

아래는 라이브러리의 초기화 코드와 exit procedure의 예이다. 아래 예는 전역변 수인 SaveExit를 이용해 디폴트 exit procedure를 임시로 저장해두고 exit procedure를 LibExit로 지정한다. LibExit procedure내부에서는 자신이 필요한 작업을 마친 후 ExitProc을 저장해둔 SaveExit로 재설정하는 것을 유의해 보라.

library Test;

var

SaveExit: Pointer;

procedure LibExit;

begin

...

{ Library exit code }

...

ExitProc := SaveExit; { Restore exit procedure chain } end;

begin

:

{ Library initialization code } :

SaveExit := ExitProc; { Save exit procedure chain } ExitProc := @LibExit; { Install LibExit exit procedure } end.

DLL이 메모리에서 해제(unload)될 때 델파이는 ExitProc을 호출한다. 이것을 ExitProc가 nil이 될 때까지 계속 반복한다. 이것은 실제로 델파이가 일반 실행 파일에서 사용하는 방법과 동일하다. program절이 포함된 프로젝트파일인 경우에 도 이 ExitProc는 같은 용도로 이용된다.

DLL의 초기화 코드는 그것을 호출한 program이나 library의 수행부(주 begin..end)보다 항상 먼저 실행된다는 사실을 기억해야 한다. 반면 DLL의 finalization부는 호출한 program이나 library의 수행부의 finalization부 이후 에 실행된다.

◇ DLL에서의 전역변수

DLL에서 선언된 전역변수는 단지 DLL안에서만 인식된다. DLL은 자신을 호출하는 모듈의 변수를 읽을 수도 없고, 자신의 변수를 export하여 다른 모듈에서 읽도록 할 수도 없다. DLL에서 가능한 인터페이스는 procedure나 function 뿐이기 때문 이다. 따라서 상호간의 변수값 참조는 function이나 proce

728x90
반응형

'Delphi Tip > 파일' 카테고리의 다른 글

프로그램간의 메모리공유  (0) 2023.07.05
DLL만들기와 사용방법  (0) 2023.06.28
DLL(Dynamic-link library)란 (4)  (0) 2023.06.26
DLL(Dynamic-link library)란 (3)  (0) 2023.06.23
DLL(Dynamic-link library)란 (2)  (0) 2023.06.22

댓글