Q&A

  • C++용 DLL을 델파이에서 사용하는방법
안녕하세요..

델파이를 사랑하는 개발자 입니다.

이번에 보이스관련 프로그래밍을 하고 있는데 보이스카드 컨트롤 DLL이

VC++로 되어있고 카드제공사 에서도 델파이지원을 않해주니..쩝

혼자 머리 빡빡 취어뜯다가 이렇게 질문드립니다.

도움주실분 메일주세요

조금이나마 수고비도 생각하고 있습니다

에고 이나이에 C++을 언제 배워서 프로그래밍 하나..쩝

전문가 선후배 여러분의 애정어린 도움 급히 구합니다...

1  COMMENTS
  • Profile
    구창민 1999.04.25 21:51
    전형식 wrote:

    > 안녕하세요..

    > 델파이를 사랑하는 개발자 입니다.

    > 이번에 보이스관련 프로그래밍을 하고 있는데 보이스카드 컨트롤 DLL이

    > VC++로 되어있고 카드제공사 에서도 델파이지원을 않해주니..쩝

    > 혼자 머리 빡빡 취어뜯다가 이렇게 질문드립니다.

    > 도움주실분 메일주세요

    > 조금이나마 수고비도 생각하고 있습니다

    > 에고 이나이에 C++을 언제 배워서 프로그래밍 하나..쩝

    > 전문가 선후배 여러분의 애정어린 도움 급히 구합니다...



    전형식님 안녕하세요? 구창민입니다.

    아래는 천리안 다우데이타포럼(go borland)의 Delphi코너에서 퍼온

    정종선님이 쓰신 DLL을 이용한 C와의 만남 이란 강좌입니다.

    도움이 되실거 같아 올려드립니다.

    보시고 즐거운 코딩되시길 바랍니다.



    *********** < 강좌 : DLL을 이용한 C와의 만남 > ***********************

    안녕하십니까?

    윈도즈 디벨로퍼 편집자 정종선입니다.



    아래에 올린 델파이 관련기사에 대한 여러분의 호응에 답하기 위해

    이번 10월호 윈디의 '1st 델피언' 기사의 일부를 올립니다.



    이번 기사는 DLL을 이용하여 C와 오브젝트 파스칼, 다시말해서

    델파이를 연결시키는 방법에 대한 것입니다.

    DLL에 대해 잘 아시는 분은 예외이시겠지만 델파이를 보조도구로 검

    토하시는 C/C++ 프로그래머나 C와 링크가 필요한 델파이 프로그래머

    들에게 도움이 될 것으로 생각됩니다. 이번에 올리는 부분은 전체기사

    의 절반정도 되는 것으로 기본적인 방법에 대한 설명이 되었습니다. 나

    머지 반은 포인터 인수를 넘기는 방법에 대한 것으로 역시 중요한 내

    용이지만 일단 기본적인 방법을 배우는 데는 이 내용만으로도 충분합

    니다.



    ■ 윈디 10월호 '1st 델피언' ■

    ■ 문을 열어라 ■

    ■ : DLL을 이용한 C와 파스칼의 인터페이스 ■



    델파이가 세상에 나온지도 벌써 반년이 넘었다. 새로운 개발도구에게

    반년이란 아주 짧은 시간이다. 도구의 성능이나 안정성을 떠나, 개발과

    정에서 필연적으로 발생하는 다양한 문제에 대한 기술적인 해답이 축

    적되어야 하기 때문이다. 기술정보를 쉽게 구할 수 있는가 하는 문제는

    개발자가 개발도구를 선택하는 현실적인 기준이기도 하다.

    하지만 델파이의 경우는 예외로 쳐야 할 것 같다. 이미 델파이에 관련

    된 잡지와 서적이 수십권에 이르고 인터넷에는 하루가 멀다하고 관련

    사이트가 등장하고 있다. 온라인 잡지에서 지역별 델파이 사용자 모임

    그리고 컴포넌트 제작업체까지 다양한 형태의 사이트를 볼 수 있다(윈

    디 9월호 윈디 윈터넷 포럼 참조).



    ■ 델파이와 파스칼

    이렇게 델파이에 대한 기술정보가 봇물 터지듯 나올 수 있는 것은 컴

    포넌트 기반의 개발도구라는 델파이 자체에 대한 관심이 크기 때문이

    기도 하겠지만, 그동안 푸대접(?) 받던 파스칼 프로그래머들이 델파이

    를 새로운 도약의 전기로 삼고 있기 때문으로 생각된다.



    실제로 자료를 올리는 사람 중 많은 이가 파스칼 사용자들이고 자료의

    내용도 언어 중심으로 되어있는 것이 많다. 델파이 컴포넌트가 수도 없

    이 만들어지는 것도 같은 이유라고 생각된다. 창간호 특집기사에서 살

    펴본 바 있지만, 델파이 컴포넌트를 만드는 것은 델파이의 비주얼한 개

    발환경과는 무관한 파스칼(프로그래머)의 일이기 때문이다.



    기술정보가 풍부해지는 것은 마땅히 좋아할 일이지만 파스칼이라는 언

    어에 촛점을 맞춤으로써 컴포넌트 기반 개발도구라는 델파이의 본질적

    인 특성을 100% 살리지 못하게 되어서는 않될 것이다. 온라인에 공개

    되어있는 그 많은 델파이 컴포넌트를 가지고 소스분석에 시간을 들이

    기 보다는 그 것들를 잘 이용하여 쓸만한 응용 프로그램을 만들어 내

    는 것이 필요하다는 의미이다.



    ■ 해결책을 찾아서

    지난호에서 '델파이 가상 여행'을 다녀오겠다는 약속을 한 바있다. 기사

    꺼리 찾아나서는 것이지만 무작정 여행을 하기에는 인터넷은 너무 방

    대할 뿐 아니라 실속을 찾기 어려울 것 같아 다른 식으로 접근하기로

    앴다. 그것은 다름이 아니라 여행의 목적을 구체화시키는 것이었는데

    이번에는 그 동안 내가 접한 델파이에 관한 질문의 해답을 찾는 것을

    여행 목적으로 삼았다.



    통신에는 벌써 델파이에 대한 많은 질문과 답이 올라오고 있지만 그

    중에서 특별히 관심을 끈 것은 두가지 정도였다. 하나는 리포트스미스

    (RiportSmith) 없이 보고서를 출력하는 방법에 대한 것이고 다른 하나는

    델파이와 다른 언어의 링크, 보다 현실적으로 말하면 C로 작성한 윈도

    즈 DLL을 이용하는 방법에 대한 것이었다. 앞의 질문은 사실 특별한

    것은 못된다. 단지 남들은 어떻게 하는가에 대한 궁금증이 더 클 뿐이

    다. 이에 반해 뒤의 질문은 매우 중요한 문제이다. 특히 나처럼 C/C++

    를 사용하던 사람들에게는 델파이 선택에서 가장 결정적인 요인이 될

    수 있기 때문이다.



    델파이에서는 DLL을 쉽게 만들 수 있으며 또한 DLL을 호출하는데 전

    혀 문제가 없다. 하지만 주어진 문제는 C로 작성한 DLL, 혹은 소스없

    이 (딸랑) DLL만 있는 경우에 어떻게 그 안에 들어 있는 함수들을 활

    용할 수 있는가이다. 윈도즈 DLL이야 파스칼로 만들었건, C로 만들었

    건 한가지 인데, 파스칼 DLL을 호출할 수 있다면 아무 DLL이나 쓸

    수 있는것 아닌가? 답은 '맞습니다.'이지만 문제는 어떻게? 다음 목록은

    인터넷에서 가져온 이 문제에 대한 해결책을 담고 있는 자료들이다.



    ▶ How to Create Pascal and C Interface for a Windows DLL,

    from The Delphi Source,

    ▶ Wrapping C Functions with Delphi,

    from The Delphi Connection - July 1995,



    ▶ Athena's Object Pascal Answers,

    from Delphi Frequently Asked Questions,



    ▶ Unit to call 32bit DLLs in Windows NT and Windows 95,

    from Delphi Programming Code Examples,





    ■ DLL - 가장 중요한 재사용

    재사용(code reuse)은 델파이의 대표적인 장점중의 하나이다. 컴포넌트

    자체의 상속성에 의한 코드 재사용을 빼 놓더라도 자신이 작성한 폼을

    갤러리에 등록해서 다시 사용할 수 있으며 프로젝트 골격도 템플레이

    트로 만들어 재사용할 수 있다. 하지만 개발도구의 채택을 결정할 때

    가장 중요한 요소 중의 하나는 기본의 다른 언어와 다른 도구로 개발

    해 놓은 자원들을 얼마나 활용할 수 있는가라는 문제이다.



    윈도즈 환경에서 재사용이라는 문제는 당연히 DLL 부터 출발해야 한

    다. 사실 윈도즈란 운영체제 자체가 무수히 많은 DLL의 집합으로 이

    루어진 작품이기 때문이다. 윈도즈의 모든 시스템 파일들은 확장자에

    관계없이 본질적으로 DLL이다. 이른바 응용프로그램을 만들기위해 부

    르는 API 함수들도 그 DLL에서 엑스포트(Export)한 함수이므로 결국

    DLL을 빼놓고 윈도즈를 생각할 수는 없는 것이다.



    그리고 또 한가지 생각하지 않을 수 없는 것은 바로 C/C++이다. C는

    가장 대중적인 프로그램 언어일 뿐 아니라 윈도즈 API 또한 C를 기본

    으로 하고 있기 때문이다. 유닉스나 도스에서 C로 작성한 프로그램을

    윈도즈로 옮겨온다고 생각해 보자. 되도록이면 기존 코드의 많은 부분

    을 살리고 싶을 것이다. 물론 C에 기반한 윈도즈용 개발도구를 사용한

    다면 목적한 바를 어느정도 이룰 수 있겠지만 다른 언어를 기반으로

    하는 개발도구라면 문제가 달라진다.



    결국 C로 작성한 DLL을 델파이에서 사용하는 방법이 문제의 핵심이

    며 별 제한없이 DLL을 사용할 수 있다면 이는 델파이에서 가장 중요

    하고 방대한 재사용 수단이 되는 것이다.



    ■ 두가지 임포트 방법

    잘 아는 바와 같이 DLL(Dynamic Linking Library)은 실행파일에 링크, 포

    함되는 것이 아니라 프로그램 실행중에 링크된다. 이름 그대로 동적인

    링크를 의미한다. 따라서 실행파일에는 단지 어떤 DLL에 어떤 함수들

    이 있다라는 정도의 정보만 포함되면 된다. 이것을 이른바 DLL 함수의

    임포트(import)라고 한다.



    임포트의 방법은 그 시점에 따라 두가지로 구분된다. 하나는 링크시에

    임포트하는 것(정적 임포트라고 하자)이고 다른 하나는 API함수를 이

    용하여 프로그램 실행 중에 동적으로 임포트하는 방법이다.(링크시 임

    포트는 다시 두가지로 나뉘지만 흔히 사용하는 임플리시트(implicit

    Import)만 고려하자)



    정적 임포트는 링크할 대상이 필요하다. DLL은 링크되는 것이 아니므

    로 DLL함수 정보를 가진 다른 라이브러리가 필요하다. 이것을 이른바

    임포트 라이브러리라고 한다.



    비주얼 C++나 볼랜드 C++에서 DLL을 만들면 XXX.DLL 이라는 파일이

    외에 같은 이름에 LIB확장자를 가진 파일이 생성된다. 이 파일이 바로

    DLL과 그 안의 함수에 대한 정보를 담고 있는 임포트 라이브러리이다.

    그리고 이 파일을 링크시키는 것이 결국 정적 임포트를 의미한다.



    임포트 라이브러리를 통해 DLL에 대한 정보가 포함되는 정적 임포트

    에 반해 동적 임포트는 DLL에 대한 정보를 프로그래머가 정확히 알고

    있어야 사용할 수 있다. 그 말은 어떤 이름의 DLL에 어떤 이름의 함

    수가 있고 그 함수의 인수와 리턴값이 어떻게 된다는 것을 알아야한다.

    델파이에서도 역시 두가지 임포트가 모두 가능하다. 동적 임포트의 경

    우에는 C의 경우보다 오히려 복잡한 면이 있지만 정적 임포트는 오히

    려 더 간단하다. 우선 C에서 반드시 필요한 임포트 라이브러리가 델파

    이, 정확하게 말하면 파스칼에서는 필요없기 때문이다.



    그럼 간단한 예제를 통해 두가지 방법을 알아보자.



    ■ LENGTH.DLL : 계산은 C로

    이번 글에는 모두 4개의 예제가 사용된다. 그 중 두개는 정적 임포트

    와 동적임포트의 차이를 설명하기 위한 것이고 나머지 둘은 DLL함수

    에 포인터형 매개변수를 넘기는 방법을 설명하기 위한 것이다. 따라서

    실행될 프로그램의 관점에서는 두개의 예제라고 볼 수 있다.



    임포트 방법의 차이를 보여주기 위한 첫번째 예제 프로그램은 아주 간

    단하다. C로 작성한 LENGTH.DLL에는 점의 좌표를 받아 원점까지의

    거리를 계산하여 돌려주는 함수, GetLength()가 들어 있다. 이 함수를 이

    용하여 델파이 프로그램에서는 클라이언트 윈도우의 왼쪽 끝에서 마우

    스 커서까지의 거리를 표시하면 되는 것이다.



    다음은 거리를 계산하는 C함수 내용이다.



    long FAR PASCAL __export GetLength(int X, int Y)

    {

    return (long)sqrt((double)X*X+(double)Y*Y);

    }





    <그림 1> LENGTH.LL을 이용한 예제 프로그램



    <그림 1>의 실행화면과 같이 윈도우의 윗부분에 패널을 하나 넣고 마

    우스 좌표와 거리를 표시할 에디트 컨트롤을 집어넣었다. 좌표값은 커

    서의 움직임에 따라 계속 갱신되지만 거리는 마우스의 왼쪽버튼이 눌

    릴 때만 표시되도록 했다. 따라서 다음 두개의 이벤트 핸들러를 작성해

    야 한다.



    {CALLDLL.PAS}

    procedure TForm1.FormMouseDown(Sender: TObject; Button: TMouseButton;

    Shift: TShiftState; X, Y: Integer);



    begin

    if Button = mbLeft then

    Edit3.Text := IntToStr(GetLength(X, Y));

    end;



    procedure TForm1.FormMouseMove(Sender: TObject; Shift: TShiftState; X,

    Y: Integer);

    begin

    Edit1.Text := IntToStr(X);

    Edit2.Text := IntToStr(Y);

    if Shift = [ssLeft] then

    Edit3.Text := IntToStr(GetLength(X, Y));

    end;



    여기에서 MouseDown 이벤트 핸들러에서 호출한 GetLength(X, Y)가 바로

    DLL 함수이다. 자 이제부터다.



    ■ 정적 임포트

    C의 경우에는 정적 임포트를 위해 두개의 파일이 필요하다. 하나는 앞

    서 말한 임포트 라이브러리 파일이고 다른 하나는 DLL 모듈의 헤더파

    일이다. 헤더화일에는 함수의 원형(prototype)이 포함 되어 있고 임포트

    라이브러리에는 DLL에서 함수의 위치에 대한 정보가 들어있다.



    델파이, 다시말해서 파스칼에는 헤더파일이라는 것이 없다. 그리고 LIB

    파일을 링크할 수 도 없다. 파스칼에는 오직 파스칼 유닛(unit)만 존재

    하며 파스칼 유닛이 결국 두 개의 역할을 다 수행해야 한다.



    다 아시겠지만 파스칼 유닛에는 인터페이스(interface)라는 영역과 임플

    리맨테이션(implementation)이라는 영역이 있으며 이 중 interface 영역은

    C로 말하면 헤더파일에 해당한다. 여기에 실마리가 있다. 결론 부터

    예기하면 DLL 임포트를 위한 새로운 파스칼 유닛을 만들어 인터페이

    스 영역에 DLL함수의 원형을 파스칼 문법에 맞게 선언해주고 임플리

    멘테이션 영역에 그 함수를 가지고 있는 모듈과 함수의 위치를 정의하

    면 된다. 다음 코드 리스트는 C에서 사용하는 헤더파일(LENGTH.H)과

    모듈 정의 파일(LENGTH.DEF), 그리고 마지막으로 이에 대응하는 파스

    칼 유닛(LENGTH.PAS)를 차례로 나열한 것이다.



    < LENGTH.H >

    long FAR PASCAL export GetLength(int, int);



    < LENGTH.DEF >

    LIBRARY LENGTH



    EXETYPE WINDOWS



    CODE PRELOAD MOVEABLE DISCARDABLE

    DATA PRELOAD SINGLE



    HEAPSIZE 1024



    EXPORTS

    WEP @1 RESIDENTNAME

    GetLength @2





    unit Length;



    interface

    function GetLength(X, Y: Integer): LongInt;



    implementation

    function GetLength; external 'LENGTH' index 2;



    end.



    이제 이 파스칼 유닛을 프로젝트에 포함시키고 메인유닛(calldll.pas)에

    uses영역에 써주기만 하면 된다. 훨씬 간단할 뿐 아니라 깔끔하다. 특

    히 파스칼 방식의 호출을 위해 필요한 코드 즉, FAR PASCAL export, 가

    필요없으니 보기에도 깨끗하다.



    코드를 보면 아시겠지만 C와 파스칼의 함수원형 선언 문법은 약간 다

    르다. 그리고 데이타 형도 차이가 있다. 정확히 대응하는 것도 있지만

    그렇지 못한 경우도 있다. 자세한 내용은 파스칼 안내서와 앞에 적어

    놓은 자료 중 첫번째 글을 참고하기 바란다.



    ■ 동적 임포트

    정적임포트는 가장 많이 사용되는 방법이고 링크만 제대로 지정하면

    파스칼 함수와 똑같이 사용할 수 있기 때문에 아주 편리하다. 하지만

    많은 DLL을 사용하는 어플리케이션의 경우에는 프로그램이 시작할 때

    그많은 DLL 모듈을 올리는(Load) 것보다 실행중에 필요에 따라 모듈을

    올리고 내리는 것이 바람직할 수 있다.



    동적 임포트를 위해서는 세개의 API 함수를 사용해야 한다. 우선 모듈

    을 올리는 LoadLibrary(), 그리고 모듈에서 함수의 위치를 읽어오는

    GetProcAddress(), 마지막으로 사용이 끝난 모듈을 내리는 FreeLibrary()를

    사용한다. API 함수라면 걱정할 것이 없다. 파스칼은 원칙적으로 모든

    윈도즈 API를 사용할 수 있으며, 또한 그것이 델파이를 다른 비주얼

    개발도구와 차별시키는 가장 큰 강점 중의 하나이기 때문이다. 다음은

    동적 임포트를 위한 C와 파스칼의 코드를 적어 본 것이다.



    C에서의 동적 임포트



    void RunDllFunc ()

    {

    HINSTANCE hDll;

    FARPROC lpFunc;

    long Distance;



    hDll = LoadLibrary("LENGTH.DLL);

    lpFunc = GetProcAddress(hDll, "GetLength");

    if(lpFunc != NULL)

    Distance = (*lpFunc) (X, Y);



    FreeLibrary(hDll);

    }





    파스칼 에서의 동적 임포트



    procedure RunDllFunc;

    type

    TGetDistance = function(X, Y: Integer): LongInt;



    var

    hDll: THandle;

    GetDistance: TGetDistance;

    FuncPtr: TFarProc;

    Distance: LongInt;

    begin

    hDll := LoadLibrary('LENGTH.DLL');

    FuncPtr := GetProcAddress(hDll, 'GetLength');

    @GetDistance := FuncPtr;



    if @GetDistance <> nil then

    Distance := GetDistance(X, Y);



    FuncPtr := nil;

    FreeLibrary(hDll);



    end;



    파스칼에서 포인터를 사용하는 것은 일반적인 방법이 아니기 때문에

    코드가 조금 복잡한 면이 있다. 하지만 함수를 호출하는 시점에서는 일

    반 함수와 차이없이 이름만으로 호출할 수 있다는 것은 작지않은 잇점

    이라고 볼 수 있다.(물론 C에서도 이렇게 할 수 있기는 하다)



    위의 코드는 함수하나에 임포트의 과정을 보여주기 위한 것일 뿐 실제

    로 델파이에서 써먹기 위해서는 각 부분을 적당한 이벤트 헨들러에 나

    누어 넣어야 한다. 다음은 예제 프로그램의 RUNDLL.PAS 유닛에서 관

    련된 부분만 뽑은 것이다.



    unit Rundll;



    interface



    uses

    SysUtils, WinTypes, WinProcs, Messages, Classes, Graphics, Controls,

    Forms, Dialogs, StdCtrls, ExtCtrls;



    type

    TGetDistance = function(X, Y: Integer): LongInt;

    TForm1 = class(TForm)

    Panel1: TPanel;

    ...

    private

    GetDistance: TGetDistance;

    FuncPtr: TFarProc;

    hDll: THandle;

    end;

    ...



    implementation

    {$R *.DFM}



    procedure TForm1.FormMouseDown(Sender: TObject; Button: TMouseButton;

    Shift: TShiftState; X, Y: Integer);



    begin

    if Button = mbLeft then

    Edit3.Text := IntToStr(GetDistance(X, Y));

    end;



    ...



    procedure TForm1.FormCreate(Sender: TObject);

    begin

    hDll := LoadLibrary('LENGTH.DLL');

    FuncPtr := GetProcAddress(hDll, 'GetLength');

    @GetDistance := FuncPtr;

    end;



    procedure TForm1.FormClose(Sender: TObject; var Action: TCloseAction);

    begin

    FuncPtr := nil;

    FreeLibrary(hDll);

    end;



    end.



    결과적으로 동적임포트에 비해 유닛은 하나 줄었지만 타입과 변수에

    대한 정의, 그리고 두개의 이벤트 핸들러를 더 작성한 셈이 되었다.



    Delphi

    WWW 사이트

    당연히 http://www.borland.com 입니다.



    Phildelphia Delphi Users' Group

    http://www.datascraft.db.com



    Coriolis Group Delphi EXplorer

    http://www.coriolis.com



    Delphi Station

    http://www.teleport.com/~cwhite/wilddelphi.html



    Delphi Hacker's Corner

    http://www.it.kth.se/~ao/DHC



    Delphi Online Magazine

    http://www.teleport.com/~sig/dol.html



    Delphi Connection (Online Magazine)

    http://www.pennant.com/delphi.html



    Delphi WWW Forum

    http://www.pennant.com/delphi/hn/dconn.html



    New York Delphi User Group

    http://www.iscinc.com/nydug.html



    Delphi Northbay SIG

    http://super.sonic.net:80/delphisig/index.html



    Delphi's Super Page (from Poland)

    http://sunsite.icm.edu.pl/~robert/delphi//



    City Zoo

    http://www.mindspring.com/~cityzoo/cityzoo.htm



    StarTech Computer Services Delphi Components Page

    http://www.NeoSoft.com/~startech/delphi/delphi.htm



    SynchroniciTech Page on Delphi (includes Delphi CGI components)

    http://super.sonic.net:80/ann/delphi/index.html



    Preferred Solutions Ltd price drawing for Delphi Component

    http://www.mani.net/~russt/ddraw.html



    Grumpfish Borland Delphi Support

    http://www.teleport.com/~grump/delphi.html



    CIUPKC Software

    http://www.webcom.com/~kilgalen/welcom.html



    AppVision's Delphi Jump Center

    http://www.win.net/~ingenuity/delphi.htm



    Complete Solution's Delphi Information

    http://webster.skypoint.net:80/members/zeta/delphi.html



    Dave McDermitt's Delphi Destination

    http://vislab-wwa.nps.navy.mil/~drmcderm/delphi.html



    Michael's Delphi Home-Page

    http://linux.rz.fh-hannover.de/~holthoefer/delphi.html



    Ander's Private Corner of the Web

    http://www.it.kth.ce/~ao