안녕하세요^^ 오늘도 머리의 한계를 느끼며 질문하러 왔습니다
우선 사용환경은 vs2005와 rad 2010이고요
우선 질문내용은 김태성님이 올려주실 builder에서 만든 dll을 vs에서 돌리는 방법에서 본 바대로 dll를 만들어 보려고 한것입니다
링크거는 방법을 몰라서...--- 강좌/문서 143번입니다.
내용을 ctrl+c, ctrl+v로 돌려 본건데요
어떻게 했는지 순서대로 말해볼께요
1. vs에서 mfc확장dll로 dll파일을 만들었습니다.
2. implib로 lnk파일을 만들고(implib 목적.lnk 원본.dll)
3. dll파일과 lnk파일을 builder debug폴더에 옮기고
4. builder 프로젝트에 lnk파일을 add 했습니다.
질문을 소스 아래에 적자고 했으나 소스가 길어서.. 여기에 적겠습니다.
일단 위의 1,2,3,4 순서로 하면 NewClass(CID_IClassA, (void **)&p);는 인식을 하고 제대로 작동을 합니다.
그런데PNew_IClassA New_IClassA = (PNew_IClassA)GetProcAddress(hInst,TEXT("New_IClassA"));요거는 NULL값이 나오네요
(hinst는 널값이 아닙니다)
이상하다 왜이럴가 하다가 인터넷을 뒤져 본후
1. vs에서 mfc확장dll로 dll파일을 만들었습니다.
2. impdef 목적.def 원본.dll
3. implib로 lnk파일을 만들고(implib 목적.lnk 원본.def)
4. dll파일과 lnk파일을 builder debug폴더에 옮기고
5. builder 프로젝트에 lnk파일을 add 했습니다.
그런데 이렇게 해도 결과가 위와 동일했습니다.
그래서 def파일을 열어보니
LIBRARY MFCTEST.DLL
EXPORTS
DeleteClass = _DeleteClass@8
Delete_IClassA = _Delete_IClassA@4
NewClass = _NewClass@8
New_IClassA = _New_IClassA@0
요렇게 나왔습니다.
흠.. 생각을 약간 바꿔서 오른쪽에 있는 함수이름으로 바꾸어 봤지요
PNew_IClassA New_IClassA = (PNew_IClassA)GetProcAddress(hInst,TEXT("New_IClassA")); 이것을
PNew_IClassA New_IClassA = (PNew_IClassA)GetProcAddress(hInst,TEXT(" _New_IClassA@0"));요렇게
하니 값을 가져오더군요..-_-;;;
이 상황을 이해할 수 없습니다.
NewClass = _NewClass@8 요기서는 왼쪽함수이름으로 호출이 되는데 왜
New_IClassA = _New_IClassA@0 요기서는 오른쪽 함수 이름으로 호출이 되는것인지.... 요것도 왼쪽이름으로 호출하려면 어떻게 해야 하나요?
최대한 질문을 자세히 쓸려고 했는데 질문이 이해가 가실지도 모르겠네요...-_-;;
//vs쪽은 (물론 c++builder도..) 잘몰라서 그냥 붙여 넣기만 했습니다.
여기부터가 실제 소스입니다.
요것이 vs에서 헤더 파일
#ifndef __STDDLL_H
#define __STDDLL_H
/*
DLL와 응용프로그램 간에 동시에 사용할 헤더파일
추상클래스 원형이나 상수 등을 정의.
Written by 김태성 (jsdkts@korea.com)
*/
//---------------------------------------------------------------------------
// 클래스 ID 선언
enum TCID
{
CID_IClassA = 1
};
//---------------------------------------------------------------------------
// 드러낼 클래스 인터페이스
class IClassA
{
public:
int nTemp;
int value;
int value2;
public:
// 멤버 함수는 모두 순수가상 함수로 만든다.
virtual int __stdcall Add(int a, int b, int c) = 0;
virtual int __stdcall Multi(int a, int b, int c) = 0;
};
//#endif
//---------------------------------------------------------------------------
// 클래스 생성
#define DLLAPI __declspec(dllexport)
extern "C"
{
DLLAPI IClassA* __stdcall New_IClassA();
DLLAPI void __stdcall Delete_IClassA(IClassA* p);
DLLAPI bool __stdcall NewClass(TCID cid, void **pClass);
DLLAPI void __stdcall DeleteClass(TCID cid, void *pClass);
}
// DLL 동적 로딩에서 사용하기 편하게. 함수형을 선언.
typedef IClassA* (__stdcall *PNew_IClassA)();
typedef void (__stdcall *PDelete_IClassA)(IClassA* p);
typedef bool (*PNewClass)(TCID cid, void **pClass);
//---------------------------------------------------------------------------
#endif
요것이 vs에서 cpp 파일
// mfctest.cpp : 해당 DLL의 초기화 루틴을 정의합니다.
//
#include "stdafx.h"
#include "mfctest.h"
#include
#ifdef _MANAGED
#error /clr를 사용하여 컴파일하려면 mfctest.cpp에 있는 지침을 따르십시오.
// 프로젝트에 /clr를 추가하려면 다음 단계를 따르십시오.
// 1. #include 지시문을 지웁니다.
// 2. /crl를 throw하지 않고 미리 컴파일된 헤더가 비활성화되어 있는
// 프로젝트에 다음 텍스트를 사용하여 .cpp 파일을 추가합니다.
// #include
// #include
#endif
#ifdef _DEBUG
#define new DEBUG_NEW
#endif
static AFX_EXTENSION_MODULE mfctestDLL = { NULL, NULL };
#ifdef _MANAGED
#pragma managed(push, off)
#endif
extern "C" int APIENTRY
DllMain(HINSTANCE hInstance, DWORD dwReason, LPVOID lpReserved)
{
// lpReserved를 사용하는 경우 다음을 제거하십시오.
UNREFERENCED_PARAMETER(lpReserved);
if (dwReason == DLL_PROCESS_ATTACH)
{
TRACE0("mfctest.DLL을 초기화하고 있습니다.\n");
// 확장 DLL을 한 번만 초기화합니다.
if (!AfxInitExtensionModule(mfctestDLL, hInstance))
return 0;
// 이 DLL을 리소스 체인에 삽입합니다.
// 참고: 이 확장 DLL이 MFC 응용 프로그램이
// 아닌 ActiveX 컨트롤과 같은 MFC 기본 DLL에
// 의해 명시적으로 링크되어 있는 경우에는
// DllMain에서 이 줄을 제거하고, 제거한 줄은 이 확장 DLL에서
// 내보낸 별도의 함수에 추가합니다.
// 그런 다음 이 확장 DLL을 사용하는 기본 DLL은
// 해당 함수를 명시적으로 호출하여 이 확장 DLL을 추가해야 합니다.
// 그렇지 않으면 CDynLinkLibrary 개체가
// 기본 DLL의 리소스 체인에 추가되지 않으므로
// 심각한 문제가 발생합니다.
new CDynLinkLibrary(mfctestDLL);
}
else if (dwReason == DLL_PROCESS_DETACH)
{
TRACE0("mfctest.DLL을 종료하고 있습니다.\n");
// 소멸자가 호출되기 전에 라이브러리를 종료합니다.
AfxTermExtensionModule(mfctestDLL);
}
return 1; // 확인
}
#ifdef _MANAGED
#pragma managed(pop)
#endif
// 구현 클래스
class TClassA : public IClassA
{
public:
TClassA()
{
value2 = 1;
//MessageBox(NULL, "생성됨", "알림", MB_OK);
}
~TClassA()
{
value2 = 2;
//MessageBox(NULL, "소멸됨", "알림", MB_OK);
}
int __stdcall Add(int a, int b, int c)
{
nTemp = (a + b + c) * 2;
value = c;
return a + b + c;
}
int __stdcall Multi(int a, int b, int c)
{
nTemp = (a * b * c) * 2;
value = c;
return a * b * c;
}
};
//---------------------------------------------------------------------------
// 한개의 여러개의 클래스 생성 대리 함수
// 이런식이 방법을 쓸수도 있고 아래처럼 한개의 클래스당 하나씩
extern "C" __declspec(dllexport)
bool __stdcall NewClass(TCID cid, void **pClass)
{
*pClass = NULL;
switch(cid)
{
case CID_IClassA :
*pClass = new TClassA;
return true;
default :
break;
}
return false;
}
// 위와 대응되게 소멸자도 필요함.
extern "C" __declspec(dllexport)
void __stdcall DeleteClass(TCID cid, void *pClass)
{
switch(cid)
{
case CID_IClassA :
{
TClassA* p = (TClassA*)pClass;
delete p;
break;
}
default :
break;
}
}
// 특정 클래스 생성
extern "C" __declspec(dllexport)
IClassA* __stdcall New_IClassA()
{
return new TClassA();
}
// 특정 클래스 소멸
extern "C" __declspec(dllexport)
void __stdcall Delete_IClassA(IClassA* p)
{
delete p;
}
//---------------------------------------------------------------------------
요것이 builder에서 인크루드한 헤더(Unit.h는 안넣겠습니다)
#ifndef __STDDLL_H
#define __STDDLL_H
/*
DLL와 응용프로그램 간에 동시에 사용할 헤더파일
추상클래스 원형이나 상수 등을 정의.
Written by 김태성 (jsdkts@korea.com)
*/
//---------------------------------------------------------------------------
// 클래스 ID 선언
enum TCID
{
CID_IClassA = 1
};
//---------------------------------------------------------------------------
// 드러낼 클래스 인터페이스
class IClassA
{
public:
int nTemp;
int value;
int value2;
public:
// 멤버 함수는 모두 순수가상 함수로 만든다.
virtual int __stdcall Add(int a, int b, int c) = 0;
virtual int __stdcall Multi(int a, int b, int c) = 0;
};
//#endif
//---------------------------------------------------------------------------
// 클래스 생성
#define DLLAPI __declspec(dllimport)
extern "C"
{
DLLAPI IClassA* __stdcall New_IClassA();
DLLAPI void __stdcall Delete_IClassA(IClassA* p);
DLLAPI bool __stdcall NewClass(TCID cid, void **pClass);
DLLAPI void __stdcall DeleteClass(TCID cid, void *pClass);
}
// DLL 동적 로딩에서 사용하기 편하게. 함수형을 선언.
typedef IClassA* (__stdcall *PNew_IClassA)();
typedef void (__stdcall *PDelete_IClassA)(IClassA* p);
//typedef bool (*PNewClass)(TCID cid, void **pClass);
//---------------------------------------------------------------------------
#endif
요것이 cpp파일
//---------------------------------------------------------------------------
#include
#pragma hdrstop
#include "StdDll.h"
#include "Unit1.h"
//---------------------------------------------------------------------------
#pragma package(smart_init)
#pragma resource "*.dfm"
TForm1 *Form1;
//---------------------------------------------------------------------------
__fastcall TForm1::TForm1(TComponent* Owner)
: TForm(Owner)
{
}
//---------------------------------------------------------------------------
//---------------------------------------------------------------------------
//---------------------------------------------------------------------------
void __fastcall TForm1::Button1Click(TObject *Sender)
{
IClassA *p;
NewClass(CID_IClassA, (void **)&p);
Memo1->Lines->Add(p->Add(1,2,3));
Memo1->Lines->Add(p->Multi(2,3,4));
DeleteClass(CID_IClassA,p);
// DLL 로드
HINSTANCE hInst = LoadLibrary ("mfctest.dll");
char* s = "New_IClassA";
// 클래스 적재 함수 받음
PNew_IClassA New_IClassA = (PNew_IClassA)GetProcAddress(hInst,s);
PDelete_IClassA Delete_IClassA = (PDelete_IClassA)GetProcAddress(hInst, TEXT("_Delete_IClassA@4"));
if(New_IClassA)
{
IClassA* p = New_IClassA();
int a = p->Add(1,2,3);
int b = p->Multi(2,3,4);
String s;
// s.printf("value: %d, %d, %d", a, b, p->value);
MessageBox(0, s.t_str(), "알림", MB_OK);
Delete_IClassA(p);
//delete p; // BCB에서는 이렇게 직접 free 해도 된다.
}
FreeLibrary(hInst);
}
//---------------------------------------------------------------------------