실행파일을 실행하다보면 다음과 에러가 발생하고서 실행이 안되는 경우가 가끔 발생한다.
Class TComPort not found.
이 글은 해당 문제의 근본적인 원인을 정확히 파악하지는 못했지만
해당 문제가 발생하는 조건 및 옵션 별 테스트를 통해 해당 문제가 발생하지 않도록 하는 방법을
알리고자 하는것이다.
조건 1 : DLL 프로젝트에 TForm을 포함한 리소스가 포함되어 있다.
조건 2 : DLL Form과 그 DLL을 사용하는 프로젝트의 Form에 문제가 되는 같은 콤포넌트가 올라가 있다.
조건 3 : 기본 콤포넌트에 대해서 발생하기 보다는 추가로 설치한 콤포넌트 클래스에서 발생하는 경우가 많다.
조건 4 : dll 임포트 라이브러리(*.lib)를 DLL을 사용하는 프로젝트에 포함한다.
조건 5 : 아래 테이블과 같은 옵션 조건에 따라 발생한다.(프로젝트옵션>Packages > Build with runtime packages)
즉 양쪽다 runtime packages로 작업했을때만 문제가 발생하지 않는다.
|
DLL 프로젝트 |
DLL을 사용하는 프로젝트 |
결과 |
Build with runtime packages 옵션 |
체크 |
체크 |
정상(문제없음) |
Build with runtime packages 옵션 |
체크 안함 |
체크 |
에러 발생 |
Build with runtime packages 옵션 |
체크 |
체크 안함 |
에러 발생 |
Build with runtime packages 옵션 |
체크 안함 |
체크 안함 |
에러 발생 |
dll에는 TForm 리소스를 포함시키지 않으면 문제가 없을것이다.
하지만 DLL에 Form을 포함시키고 위 문제를 피할 수 있는 방법은 위 테이블에서 네번째 조건인 양쪽 프로젝트 모두 runtime packages 옵션을 꺼두는 방법 밖에 없었다.
또 한가지 해결책은 DLL을 정적로딩(임포트 라이브러리) 하지 않고
api의 LoadLibrary를 사용해서 동적으로 로딩하면 문제를 피해갈 수 있다.
하지만 함수가 많지 않을때는 괜찮지만 함수가 많아질수록 동적 로딩은 사용하기가 부담된다.
사실 함수 하나 마다 다 코딩으로 로딩(GetProcAddress(...)) 하는건 여간 귀찮은 일이 아니다.
[ 동적 로딩 ]
void __fastcall TF_Test::Button4Click(TObject *Sender)
{
HINSTANCE mInstance;
AnsiString file = ExtractFilePath( Application->ExeName) + "iPadLink.dll";
mInstance = LoadLibrary(file.c_str());
if(mInstance==NULL)
{
ShowMessage("iPadLink.dll Loading Fail :" + file);
}
else
{
ShowMessage("iPadLink.dll Loading OK :" + file);
// 정상적으로 로딩됨~
}
}
위의 조건은 완전한 조건이 아니다
알수 없는 조건으로 이런 현상이 생기는 경우도 있었다.
그러므로 DLL에 Form을 포함해서 사용하는 것은 무엇가 알수 없는 찜찜한 문제를 안고 가야만한다.
자신이 직접 만든 DLL이 아니라도
씨++빌더를 이용해서 타 업체에서 만든 DLL 라이브러리( Form을 포함하고있는 )을 사용할때도
가끔 한번씩 우리가 만든 DLL과 알 수 없는 충돌을 일으키는 경우가 꽤 자주 발생한다.
( 프로그램이 실행되다가 죽는다던지 , 그쪽 업체 DLL이 제대로 동작하지 않는다던지 하는 문제)
그때 마다 DLL 로딩 순서 등을 바꿔가며 어떻게든 해결하고 있지만
근본 원인을 알 수 있었던 적은 거의 없다.
[ 정적 LIB 로 링크하면 ]
정적 라이브러리를 만들고 Form을 추가해서 ComPort 콤포넌트를 올렸을 경우에는
위 문제가 발생하지 않는다.
다만 만든 폼의 Form ( *.DFM ) 파일을 사용하는 프로젝트 링크에서 가지고 있어야 한다.
[ BPL 패키지로 만들어 사용 ]
DLL에 폼을 넣어서 만들지 말고 BPL에 넣어서 만들어 보면 괜찮을 듯하다.
BPL은 보통 콤포넌트 패키지를 만들때 사용하는데
여기에 Form 을 추가하여 패키지로 올리면 괜찮을 것 같다.
--> 단점 : bpl로 만드는 파일에는 form 파일이 리소스로 포함되어 있지 않는듯하다.
사용하는 폼파일(*.dfm)이 bpl을 사용하는 프로젝트 링크시에 연결이 되어야 한다.
즉 사용하는 프로젝트 소스에서 DLL에서 사용하는 폼 파일(*.dfm 파일)들을 가지고 있어야 Build 할 수 있다.
[ 샘플 프로젝트 설명 ]
압축을 풀면
DLL 폴더와 Test 폴더가 있다.
Test 폴더에서 TenkeyTest.exe를 실행하면 해당 메시가 바로 뜬다.
사용 버전 : 씨++빌더 6.0
DLL프로젝트 Form과 사용하는 프로젝트의 Form에 TComPort 가 단순히 올려줘있다.
그 콤포넌트를 이용해 기능을 구현 한 것은 없다.
샘플 소스는 프로젝트 옵션 및 콤포넌트 설치 여부에 따라 Build시 에러가 발생할 수 있다.
빌드를 위해서는 ComPort 콤포넌트가 설치되 있어야 한다.
Build with runtime packages 옵션과 관현하여
Form을 포함한 dll에서는 여러가지 문제가 발생할 가능성이 있습니다.
이 문제로 한때 한참 원인을 찾았는데
임프님께서 설명해주더군요
http://cbuilder.borlandforum.com/impboard/impboard.dll?action=read&db=bcb_qna&no=50394
원인은 Builder6.0을 기준으로 VCL의 vcl60.bpl 이 dll과 exe 두군데 다 load되기 때문입니다.
그래서 Application 및 Screen 객체들이 싱글톤으로 설계되어있다곤 하지만
실제로는 dll 과 exe에 각각 생성되죠
그래서 exe에 있는 Form에서 참조하는 Application과
Dll에서 참조하는 Application객체가 다른놈이 되어버립니다.
[해결책은]
1. 님께서 말쓴하신대로 Build with runtime packages 옵션을 체크해서 해결할수도 있구요
2. Dll에 Application을 exe에 있는 Application으로 설정
dll을 loadlibrary한 직후(Form이나 각종 컴포넌트를 생성하기전)에
exe에 있는 Application 및 Screen객체등을 dll 쪽으로 넘겨서 그쪽에 셋팅해줍니다.
dll 에서
void DllAppIni(TApplication *App)
{
Application=App;
}
그런후에 Form을 생성하거나 Dialog를 쓰거나 하면 괜찮습니다.
그럼..