Kori 12월 13일, 2022에 포스트됨 공유하기 12월 13일, 2022에 포스트됨 Thushara Wedagedara의 "How To Use A C++ DLL In Any Delphi Program" 을 번역했습니다. (원문 작성: 2021년 9월, 최종 번역: 2022년 12월) 인터넷에는 유용한 C++ 라이브러리가 엄청나게 풍부하다. 이 블로그에서도 C++를 훌륭하게 사용하기와 관련하여 많이 소개하고 있다. 일반적으로 C++는 매우 높은 성능을 제공한다. 우리는 C++ 라이브러리의 소스 코드를 가지고 있다면 패키지를 만들어서 델파이 프로그램에서 그 C++을 사용하게 만들 수 있다. 하지만, C++ 라이브러리의 소스 코드가 없는 경우가 종종 있다. 일반적으로 상용 C++ 라이브러리는 C++ 헤더 몇 개와 정적 라이브러리 파일(.lib)만 제공하고, .cpp 소스 파일은 제공하지 않는다. 이런 경우에 델파이 애플리케이션에서 해당 C++ 라이브러리를 사용하고 싶다면, 프록시(Proxy) DLL을 사용하면 가능하다. 기존 C++ 코드를 델파이에 통합하려는 경우라면 이 방식이 매우 유용하다. 하지만, 만약 새 C++ 코드를 작성하고 싶은 경우라면, RAD 스튜디오의 일부인 C++빌더를 사용할 수 있다. C++빌더는 C++ 개발자를 위해 잘 정비된 통합 환경을 제공한다. 목차 1 C++ DLL과 델파이를 연결해 줄 프록시 DLL을 만들려면? 2 델파이 프로그램에서 프록시 dll을 통해 C++ 라이브러리를 사용하는 방법을 알아보자. 3 이 웹퍼(Wrapper) 유닛에서 우리는 프로젝트로 가져오려는 정적 라이브러리의 헤더 파일을 include 해야 한다. 4 C++ DLL 안에 있는 함수를 "내보내기(export) 되도록" 표시하는 방법 5 델파이 애플리케이션에서 이 C++ 프록시 DLL을 사용하는 방법은? 1 C++ DLL과 델파이를 연결해 줄 프록시 DLL을 만들려면? DLL을 델파이와 연결하려면, 그 DLL은 C++가 아니라 윈도우 API 스타일로 된 간단한 API를 노출해야 한다. 무슨 C++ 컴파일러든 가지고 있다면, 우리는 프록시 DLL을 컴파일할 수 있다. 엠바카데로 Dev-Cpp 오픈 소스 컴파일러 비주얼 스튜디오 C++빌더는 RAD 스튜디오에서 C++를 담당하는데, 델파이에서 C++을 직접 사용할 수 있도록 하는 패키지 파일(BPL)을 빌드할 수도 있다. C++빌더는 델파이 자체와 매우 밀접하게 통합된다는 장점이 있다. 즉, C++빌더 안에서 오브젝트 파스칼 파일을 사용할 수 있고, 델파이 안에서 C++ BPL 파일을 사용할 수 있다. 더 많은 정보 보기. 이제 간단한 예를 보자. 아래의 C++을 사용하고 싶은데, 우리가 가지고 있는 파일은 DLL 파일과 그 파일에 선언된 헤더 파일이고, 구현이 담긴 .cpp 파일은 없는 상황이라고 가정 해보자. #pragma once class CppClass { int data; public: CppClass(); ~CppClass(); void setData(int); int getSquare(); }; 아래 설명에는 예제로 사용된 소스의 주요 내용을 설명하는데, 소스 코드 전체는 아래 링크에 있다: https://github.com/PacktPublishing/Delphi-High-Performance/tree/master/Chapter%208/StaticLib1 2 델파이 프로그램에서 프록시 dll을 통해 C++ 라이브러리를 사용하는 방법을 알아보자. 우리는 프록시 DLL을 만들어야 한다. 즐겨 사용하는 IDE를 사용하여 새 C++ DLL 프로젝트를 하나 생성한다. 그러면 자동으로 “dllmain.cpp” 파일이 추가된다. 하지만 이 파일 외에도 우리는 정적 라이브러리를 덮는(Wrap) 유닛이 필요하다. 새 유닛을 하나 추가하고 이름을 “StaticLibWrapper.cpp”으로 지정한다. 이제 이 유닛을 사용하여 C++ DLL을 웹핑(Wrapping)하자. 3 이 웹퍼(Wrapper) 유닛에서 우리는 프로젝트로 가져오려는 정적 라이브러리의 헤더 파일을 include 해야 한다. #include "stdafx.h" #include "CppClass.h" 이제, 이 프로젝트 폴더 안에 해당 정적 라이브러리의 헤더 파일들을 복사하여 넣는다. 다음으로, 이 프로젝트 안에 이 정적 라이브러리를 포함할 차례이다. 그러기 위해 프로젝트 옵션의 library directories에 그 정적 라이브러리 폴더를 추가한다. 또는 비주얼 스튜디오에서라면 “Configuration Properties | Linker | General | Additional Library Directories settings”를 사용한다. 4 C++ DLL 안에 있는 함수를 "내보내기(export) 되도록" 표시하는 방법 헤더 파일을 include 한 다음에는 DLL에 있는 함수들이 내보내기(export) 되도록 표기하는 매크로를 정의하는 코드를 작성한다. #define EXPORT comment(linker, "/EXPORT:" __FUNCTION__ "=" __FUNCDNAME__) 다음으로, C++ 오브젝트들을 캐시하는 IndexAllocator 클래스를 구현한다. 이 클래스에는 포인터 배열이 들어 간다. 이 클래스는 "Allocate", "Release" 및 "Get"라는 세 가지 함수가 있어서 포인터를 캐시에 저장하고, 캐시를 해제하고, 인덱스에 맞게 포인터를 가져온다. bool Allocate(int& deviceIndex, void* obj) bool Release(int deviceIndex) void* Get(int deviceIndex) 그런 다음, IndexAllocator 오브젝트를 할당 및 할당 해제하는 Initialize 및 Finalize 함수가 필요하다. extern "C" int WINAPI Initialize() extern "C" int WINAPI Finalize() 그런 다음, CppClass 클래스의 인스턴스를 만들고 캐시에 저장하도록 아래 함수를 작성한다. extern "C" int WINAPI CreateCppClass (int& index) 위 구문에서, "C"를 사용하여 동일한 이름을 내보내 지도록 하고, WINAPI를 사용하여 호출 규약을 변경한다. DestroyCppClass도 이와 비슷하다. 이어서 그 뒤에 있는 코드 즉 내보내지는 주 함수인 "CppClass_setValue" 및 "CppClass_getSquare"를 살펴보자. 이 함수를 사용자가 호출하면 캐시에서 오브젝트를 가져오고 해당 함수를 호출하고 값을 받을 수 있다. extern "C" int WINAPI CppClass_setValue(int index, int value) { #pragma EXPORT CppClass* instance = (CppClass*)GAllocator->Get(index); if (instance == NULL) return -1; else { instance->setData(value); return 0; } } extern "C" int WINAPI CppClass_getSquare(int index, int& value) { #pragma EXPORT CppClass* instance = (CppClass*)GAllocator->Get(index); if (instance == NULL) return -1; else { value = instance->getSquare(); return 0; } } 첫 번째 함수는 오브젝트의 인덱스와 값을 받아서 해당 오브젝트의 변수 값을 설정한다. 두 번째 함수는 인덱스를 받아서 그 인덱스에 해당하는 오브젝트의 "getSquare" 함수를 호출하고 결과 값을 value 변수에 담는다. 5 델파이 애플리케이션에서 이 C++ 프록시 DLL을 사용하는 방법은? DLL은 정적(static) 또는 동적(dynamic)으로 연결할 수 있다. 정적 로딩을 사용하면 응용 프로그램이 시작될 때 DLL이 로드된다. 동적 로딩을 사용하면 "LoadLibrary"를 호출할 때까지 DLL이 로드되지 않는다. 이 예제에서는 정적 로딩을 사용하겠다. DLL에서 내보내진 함수들을 선언(declare)하자. const CPP_CLASS_LIB = 'DllLib1.dll'; function Initialize: integer; stdcall; external CPP_CLASS_LIB name 'Initialize' delayed; function Finalize: integer; stdcall; external CPP_CLASS_LIB name 'Finalize' delayed; function CreateCppClass(var index: integer): integer; stdcall; external CPP_CLASS_LIB name 'CreateCppClass' delayed; function DestroyCppClass(index: integer): integer; stdcall; external CPP_CLASS_LIB name 'DestroyCppClass' delayed; function CppClass_setValue(index: integer; value: integer): integer; stdcall; external CPP_CLASS_LIB name 'CppClass_setValue' delayed; function CppClass_getSquare(index: integer; var value: integer): integer; stdcall; external CPP_CLASS_LIB name 'CppClass_getSquare' delayed; 우리가 만든 프록시 DLL을 사용하기 위해 필요한 함수들이다. 델파이 애플리케이션이 생성될 때 “Initialize” 함수를 호출하고 애플리케이션이 파괴될 때 “Finalize” 함수를 호출해야 한다. 프록시 DLL을 델파이 코드에서 사용할 때는, 먼저 "CreateCppClass"를 호출하여 객체를 생성해야 한다. 그러면 해당 클래스 ID가 설정되어 사용할 수 있게 된다. 그리고 나면, 우리는 이 DLL의 모든 함수를 호출할 수 있다. 마지막에는 반드시 "DestroyCppClass" 함수를 호출하여 클래스 인스턴스를 파괴해야 한다. 예를 들면 델파이에서 다음과 같이 사용할 수 있다. procedure TfrmCppClassDemo.btnImportLibClick(Sender: TObject); var idxClass: Integer; value: Integer; begin if CreateCppClass(idxClass) <> 0 then ListBox1.Items.Add('CreateCppClass failed') else if CppClass_setValue(idxClass, SpinEdit1.Value) <> 0 then ListBox1.Items.Add('CppClass_setValue failed') else if CppClass_getSquare(idxClass, value) <> 0 then ListBox1.Items.Add('CppClass_getSquare failed') else begin ListBox1.Items.Add(Format('square(%d) = %d', [SpinEdit1.Value, value])); if DestroyCppClass(idxClass) <> 0 then ListBox1.Items.Add('DestroyCppClass failed') end; end; 위 과정을 마치면, 우리의 델파이 애플리케이션이 C++ DLL을 사용하여 작동한다. 아래 그림과 같다. 인용하기 이 댓글 링크 다른 사이트에 공유하기 더 많은 공유 선택 사항
Recommended Posts
이 토의에 참여하세요
지금 바로 의견을 남길 수 있습니다. 그리고 나서 가입해도 됩니다. 이미 회원이라면, 지금 로그인하고 본인 계정으로 의견을 남기세요.