[전환] DLL 내보내기 클래스 및 고려 사항

4829 단어 프레임작업
글의 출처
함수를 여러 프로그램에서 재사용할 수 있도록 내보내고 DLL의 함수를 수정하여 DLL을 사용하는 응용 프로그램을 다시 컴파일하고 연결할 필요가 없습니다.대상을 대상으로 하는 프로그래머로서, DLL이 클래스의 차원에서 복원될 수 있도록 클래스를 내보낼 수 있기를 희망합니다.다행히 DLL도 클래스를 내보낼 수 있습니다.
그러나 사실은 이렇게 간단하지 않다. 내보내기 클래스의 DLL은 유지보수와 수정을 할 때 많은 부분을 조심해야 한다. 구성원 변수를 늘리고 내보내기 클래스의 기본 클래스를 수정하는 등 작업은 예상치 못한 결과를 초래할 수 있다. 아마도 사용자가 최신 버전의 DLL 라이브러리를 업데이트한 후에 응용 프로그램은 더 이상 일을 할 수 없을 것이다.이것이 바로 유명한 DLL 헬(DLL 지옥) 문제다.
DLL 지옥 문제는 어떻게 생긴 걸까요?다음 예제에서는 DLL에 내보내기 클래스 ClassD1이 있다고 가정합니다.
class ClassD
{
       public:
int GetInt();
              private:
int m_i;
};
int ClassD::GetInt()
{
       return m_i;
}
응용 프로그램은 현재 코드를 사용하여 이 클래스를 사용합니다.
ClassD d;
printf(“%d”, d.GetInt());
절차가 정상적으로 진행되어 아무런 문제가 없다.나중에 DLL을 업그레이드해야 했고 ClassD를 수정하여 다음과 같은 구성원 변수를 추가했습니다.
class ClassD//수정 후
{
       public:
int GetInt();
              private:
                     int m_i2;
int m_i;
};
새 DLL 컴파일 연결이 완료되면 프로그램 디렉터리로 복사합니다. 이 재수 없는 프로그램은 GetInt 방법을 사용해서 더 이상 정확한 값을 얻을 수 없을 것 같습니다.사실 GetInt의 실현이 다음과 같이 바뀌면, GetInt는 곧 오류를 범하고 퇴출될 것이다.
int ClassD::GetInt()//수정 후
{
       return m_i++;
}
이런 일은 지옥이라고 해도 과언이 아니다.왜 틀릴까요?우리는 먼저 클래스 실례의 창설부터 시작해서 하나의 클래스를 사용하는 작업 과정을 보아야 한다.
먼저, 프로그램 문구 "ClassD d;"이 종류를 위해 메모리를 신청합니다.이 메모리는 이 종류의 모든 구성원 변수와 허함수표를 저장합니다.메모리의 크기는 클래스의 성명에 의해 결정되며, 프로그램이 컴파일될 때 이미 결정됩니다.
그리고 'd.GetInt ()' 를 호출할 때 신청한 이 메모리를this 포인터로 GetInt 함수에 전달하고 GetInt 함수는this가 가리키는 위치에서 시작하여 m 를 추가합니다i 오프셋 허용, m 계산i가 있는 메모리 위치에서 데이터를 가져와 되돌려줍니다.m_i 상대this의 편이량은 mi가 클래스에서 정의한 위치가 결정되며, 정의된 구성원 변수는 메모리에서도 상위권에 있습니다.이 오프셋은 DLL을 컴파일할 때 결정됩니다.
ClassD의 정의가 수정된 상태로 바뀌었을 때 어떤 것들은 변했다.
첫 번째로 변하는 것은 메모리의 크기다.수정된 ClassD에 멤버 변수가 하나 더 생겨서 메모리도 커졌어요.그러나 이 점을 응용 프로그램은 모른다.
두 번째가 m 로 바뀌었어요.i 의 오프셋 주소입니다.왜냐하면 mi 이전에 m 을 정의했습니다i2,m_i의 실현 편향 주소는 실제적으로 이미 뒤에 있다.따라서 d.GetInt()는 원래 m 에 액세스합니다.i 뒤에 있는 그 위치, 이 위치는 원래 메모리의 뒷부분 범위를 넘어섰다.
DLL을 교체한 후에도 프로그램은 원래의 크기에 따라 메모리를 신청했지만, 호출하는 방법은 이 메모리보다 더 큰 구역에 접근했기 때문에 오류가 발생하는 것은 더욱 불가피하다.
이와 같은 상황은 다음과 같은 경우에도 발생합니다.
1) 응용 프로그램이 클래스에 직접 접근하는 공유 변수로 이 공유 변수가 새 DLL에서 정의한 위치에 변화가 생겼다.
2) 응용 프로그램이 클래스의 허함수를 호출하고 새로운 클래스에서 이 허함수의 앞에 허함수를 추가한다.
3) 새 클래스의 뒤에 구성원 변수가 추가되고 새 클래스의 구성원 함수는 이 변수를 방문하고 수정한다.
4) 새로운 종류의 기류를 수정하여 기류의 크기에 변화가 생겼다.
잠깐만, 한마디로 조심하지 않으면 너의 절차가 지옥에 떨어질 거야.이러한 오류를 일으킨 상황을 분석해 보면 사실 세 가지 변화만 오류를 일으킬 수 있다. 왜냐하면 이 세 가지는 이 DLL을 사용하는 응용 프로그램이 컴파일할 때 반드시 확인해야 할 내용이기 때문이다. 이 세 가지는 다음과 같다.
1) 클래스의 크기;
2) 클래스 구성원의 편이 주소;
3) 허함수의 순서.
업그레이드 가능한 DLL을 만들려면 위의 세 가지 문제를 피해야 합니다.그래서 다음 세 가지는 DLL을 지옥에서 멀어지게 하는 데 쓰인다.
1, 클래스를 직접 생성하지 않는 실례.클래스의 크기에 대해 클래스의 실례를 정의하거나 new 문장을 사용하여 실례를 만들 때 메모리의 크기는 컴파일할 때 결정됩니다.응용 프로그램이 클래스의 크기에 의존하지 않도록 하려면, 하나의 방법이 있습니다. 응용 프로그램은 클래스의 실례를 생성하지 않고 DLL의 함수를 사용하여 생성합니다.내보내기 클래스의 구조 함수를 개인 (privated) 으로 정의하고, 내보내기 클래스에 정적 (static) 구성원 함수 (예: New Instance () 를 제공하여 클래스를 만드는 실례를 제공합니다.새 DLL에서 NewInstance () 함수가 다시 컴파일되기 때문에 항상 정확한 크기의 실례 메모리를 되돌려줍니다.
2, 구성원 변수에 직접 액세스하지 않습니다.응용 프로그램이 클래스의 구성원 변수에 직접 접근할 때 이 변수의 오프셋 주소를 사용합니다.그래서 주소 의존을 피하는 방법은 구성원 변수에 직접 접근하지 않는 것이다.모든 구성원 변수의 접근 제어를 보호형(protected) 이상의 단계로 정의하고, 접근할 구성원 변수에 대해Get이나Set 방법을 정의합니다.Get 또는 Set 메서드는 새 DLL을 컴파일할 때 다시 컴파일되므로 항상 올바른 변수 위치에 액세스할 수 있습니다.
3, 허함수는 잊어버려라. 있어도 프로그램이 직접 접근하지 못하도록 해라.클래스의 구조 함수는 이미 사유(privated)이기 때문에 응용 프로그램도 이 클래스를 계승하지 않고 자신의 다태를 실현하지 못한다.클래스를 내보내는 부류에 허함수가 있거나, 클래스 공장 같은 프레임워크가 필요하다면, 이 함수를 보호된 (protected) 이상의 단계로 표시하고, 응용 프로그램에 이 함수를 호출하는 구성원 함수를 다시 설계해야 한다.이 점은 구성원 변수에 대한 처리와도 유사하다.
만약 내보낸 클래스가 상기 세 가지를 따를 수 있다면 앞으로 DLL에 대한 업그레이드는 안전하다고 볼 수 있을 것이다.
이미 존재하는 내보내기 클래스의 DLL을 유지보수하면, 정의된 순서든 수량이든 모든 구성원 변수를 바꾸지 마십시오.순서든 수량이든 모든 허함수를 건드리지 마라.
결론적으로 말하자면, 클래스를 내보내는 DLL은 함수를 제외한 어떤 내용도 내보내지 말아야 한다.좀 우습게 들리지 않아요!
또한 함수를 내보내는 데 있어서 LoadLibray+GetProcAddress의 유연한 로드 방식을 사용하려면 반드시 사용해야 한다
4
extern "C" { ... }
성명합니다.그렇지 않으면 함수가 C++를 통과한 컴파일러 함수 이름이 수식된 무작위 기호가 많은 C++ 방법명으로 바뀔 수 있습니다. 이 경우 GetProcAddress ("원 함수 이름") 를 사용하면 불러올 수 없습니다.
실제로, 내보내기 클래스의 DLL을 발표할 때, 클래스의 성명을 다시 정의하는 것을 권장합니다. 이 성명은 원래의 클래스의 구성원 변수와 상관없이 인터페이스 함수만 클래스의 성명에 열거할 수 있습니다. 예를 들어 다음과 같습니다.
class ClassInterface
{
       privated:
              ClassInterface();
       public:
              static ClassInterface * NewInstance();
              int GetXXX();
              void SetXXX();
              void Function();
};
이 DLL을 사용하는 프로그램은 위의 정의를 ClassInterface의 헤더 파일로 사용하면 보안 문제가 발생할 수 없습니다.
DLL 지옥문은 결국 DLL이 당초 함수급 공유 라이브러리로 설계되어 하나의 클래스에 필요한 정보를 제대로 제공하지 못했기 때문이다.클래스 레이어의 프로그램 재사용은 Java 및 C#에서 생성한 클래스 파일만 수행할 수 있습니다.

좋은 웹페이지 즐겨찾기