TN058: MFC 모듈 상태 구현

이 기술 노트 MFC "모듈 상태"의 구현을 설명합니다. MFC를 사용 하 여 공유 DLL에서 Dll에 대 한 모듈 상태 구현에 대 한 이해는 중요 한 (또는 OLE in-process 서버)。

이 노트를 읽기 전에 참조 하십시오 "관리는 데이터 MFC 모듈의 상태"에 새 문서 만들기, 창과 보기에서 Visual c + + 프로그래머 가이드. 이 문서에서는이 주제에 대 한 개요 정보 및 중요 한 사용 정보。

개요

거기에 세 가지 종류의 MFC 상태 정보: 모듈 상태, 프로세스 상태, 및 스레드 상태. 때로는 이러한 상태 유형과 결합 될 수 있다. 예를 들어, MFC의 핸들 지도 로컬 모듈 및 스레드 로컬. 이렇게 하면 각 그들의 스레드에서 다른 지도를 서로 다른 두 모듈。

스레드 및 프로세스 상태는 유사 합니다. 이러한 데이터 항목은 전통적으로 전역 변수 왔지만 특정 지정된 된 프로세스 또는 스레드 또는 적절 한 Win32s 지원에 대 한 적절 한 다중 스레딩 지원에 대 한 필요가 있을 것입니다. 지정한 데이터 항목에 맞는 범주 항목 및 프로세스 및 스레드 경계에 관해서는 그 원하는 의미 체계에 따라 달라 집니다.

모듈 상태는 고유의 그것 진정한 글로벌 상태 또는 로컬 프로세스 또는 스레드 로컬 상태에 포함 될 수 있습니다. 그것은 또한 신속 하 게 전환 될 수 있다。

모듈 상태 전환

"현재" 또는 "활성" 모듈 상태에 대 한 포인터를 포함 하는 각 스레드 (아니나 다를까, 포인터의 일부인 MFC의 스레드 로컬 상태). 실행 스레드는 OLE 컨트롤 호출 다시 응용 프로그램으로, OLE 컨트롤 또는 DLL 호출 응용 프로그램으로 같은 모듈 경계를 통과할 때이 포인터 변경。

AfxSetModuleState를 호출 하 여 현재 모듈 상태가 전환 됩니다. 대부분의 부분을 들어, 당신은 결코 API와 직접 거래. MFC, 많은 경우에, 전화 그것 당신을 위해 (에서 WinMain, OLE 진입점 AfxWndProc, 등). 이 특별 한에 정적으로 연결 하 여 작성 하는 모든 구성 요소에서 이루어집니다 WndProc그리고 특별 한 WinMain (또는 DllMain)는 모듈 상태를 현재 해야한다 알고 있다. DLLMODUL 살펴보고이 코드를 볼 수 있습니다.CPP 또는 APPMODUL입니다.CPP MFC\SRC 디렉터리에。

그것은 드문 모듈 상태를 설정 하 고 다음 다시 설정 하지 합니다. "" 당신의 자신의 모듈을 밀어 시간의 대부분 현재 하나로 상태 및 다음, 후에 할 수 있습니다, "팝" 원래 컨텍스트를 다시. 이 AFX_MANAGE_STATE 매크로 및 특수 클래스 AFX_MAINTAIN_STATE 에 의해 이루어집니다.

CCmdTarget 모듈 상태 전환을 지원 하기 위한 특별 한 기능을 하고있다. 특히, CCmdTarget 루트 클래스 OLE 자동화 및 사용 되는 OLE COM 진입점입니다. 다른 시작 지점 처럼 시스템에 노출, 이러한 진입점 올바른 모듈 상태로 설정 해야 합니다. 주어진된 CCmdTarget 및 "올바른" 모듈 상태를 어떻게 해야 대 한 알고? 대답은 그것은 "기억" "현재" 모듈 상태 만들어질 때 이다, 그것을 설정할 수 있도록 현재 모듈 상태를 "기억" 값 나중 때 호출 합니다. 그 결과, 주어진된 CCmdTarget 개체와 관련 된 모듈 상태에서 개체를 생성할 때 현재 모듈 상태가입니다. INPROC 서버 로드 개체를 만들고 해당 메서드를 호출 하는 간단한 예제를 보자。

  1. LoadLibrary 를 사용 하 여 OLE DLL은 로드。

  2. RawDllMain 먼저 호출 됩니다. 그것은 알려진된 정적 모듈 상태를 해당 DLL에 대 한 모듈 상태를 설정합니다. 이러한 이유로 RawDllMain 정적으로 DLL에 연결 되어。

  3. 우리의 개체와 관련 된 클래스 공장에 대 한 생성자 호출 됩니다. COleObjectFactory CCmdTarget 에서 파생 하 고 그 결과, 그것은 인스턴스화된 모듈 상태에 기억. 이것은 중요 하다-클래스 팩터리 개체를 만드는 요청 하면 지금은 최신 모듈 상태로 알고。

  4. DllGetClassObject 클래스 팩터리를 얻기 위해 호출 됩니다. MFC이이 모듈 연관 클래스 공장 목록을 검색 하 고 반환。

  5. COleObjectFactory::XClassFactory2::CreateInstance 라고 합니다. 개체를 만들기 전에 그것을 반환이 함수는 단계 3 (은 COleObjectFactory 를 인스턴스화할 때 현재 하나)에서 현재 모듈 상태를 모듈 상태를 설정 합니다. 이 METHOD_PROLOGUE 의 내부 이루어집니다.

  6. 때 개체가 만들어진, 그것은 너무 CCmdTarget 파생 이며 동일한 방식으로 COleObjectFactory 기억 모듈 상태는 활성, 그래서이 새 개체를 않습니다. 개체를 전환 하는 모듈 상태를 알고 지금은 호출할 때마다。

  7. 클라이언트는 CoCreateInstance 호출 로부터 받은 OLE COM 개체에는 함수를 호출 합니다. 해당 개체를 호출 하는 경우 METHOD_PROLOGUE 를 사용 하 여 그냥 COleObjectFactory 처럼 모듈 상태를 전환 하려면。

보시 다시 피, 모듈 상태 전파 됩니다 개체에서 개체가 만들어지는 경우. 그것은 모듈 상태를 적절 하 게 설정 하는 것이 중요 합니다. 그것을 호출 하는 또는 자체 리소스를 찾을 수 있을 수 있습니다 비참 하 게 다른 방법으로 실패할 수 있습니다 하는 MFC 응용 프로그램 DLL 또는 COM 개체 수 있습니다 제대로 상호 작용 설정 되지 않은 경우。

참고 Dll, 특히 "MFC 확장" Dll의 특정 종류의 RawDllMain 에서 모듈 상태를 전환 하지 않습니다 (실제로, 그들은 일반적으로 심지어 없어 RawDllMain). 이 때문에 그들은 "마치" 그들은 그들을 사용 하는 응용 프로그램에 실제로 존재 행동 하기 위한 것입니다. 그들은 아주 많이 실행 되는 응용 프로그램의 일부 이며 그들의 의도 응용 프로그램의 전역 상태를 수정 하려면。

OLE 컨트롤 및 다른 Dll은 매우 다르다입니다. 호출 응용 프로그램의 상태; 수정 하지 않으려는 그들을 호출 하는 응용 프로그램에도 MFC 응용 프로그램 못할 고 그래서 수정 상태가 될 수 있습니다. 이것은 모듈 상태 전환 발명 했다 이유 이다。

DLL에서 대화 상자를 시작 하는 것과 같은 DLL에서 내보낸된 함수에 대 한 함수의 시작 부분에 다음 코드를 추가 해야:

AFX_MANAGE_STATE (AfxGetStaticModuleState ())

이 현재 범위 끝까지 AfxGetStaticModuleState 에서 반환 하는 상태와 현재 모듈 상태를 스왑。

AFX_MODULE_STATE 매크로 사용 하지 않으면 리소스 Dll에 문제가 발생 합니다. 기본적으로 MFC 리소스 템플릿을 로드 하는 주 응용 프로그램의 리소스 핸들을 사용 합니다. 이 템플릿은 실제로 DLL에 저장 됩니다. 근본 원인을 MFC의 모듈 상태 정보 AFX_MODULE_STATE 매크로로 전환 되지 않습니다. 리소스 핸들은 MFC의 모듈 상태에서 복구 됩니다. 잘못 된 리소스 핸들을 사용 하면 모듈 상태를 전환 하지。

AFX_MODULE_STATE DLL의 모든 함수에 넣이 필요는 없습니다. 예를 들어, InitInstance MFC는 전에 모듈 상태를 자동으로 이동 하기 때문에 AFX_MODULE_STATE 없이 응용 프로그램에서 MFC 코드에서 호출할 수 있습니다 InitInstance 및 다음 다시 후 스위치 InitInstance 반환 합니다. 모든 메시지 맵 처리기에 대 한 마찬가지입니다. 기본 Dll 실제로 메시지를 라우팅하기 전에 모듈 상태를 자동으로 전환 하는 특수 한 마스터 창 프로시저는。

로컬 데이터 처리

로컬 데이터 처리 등 관심사의 되지 않을 것 이라고 그것이 없 었 Win32s DLL 모델의 어려움에 대 한. Win32s에서 모든 Dll 여러 응용 프로그램에서 로드 하는 경우에 그들의 글로벌 데이터를 공유 합니다. 각 DLL DLL에 연결 하는 각 프로세스의 데이터 공간의 별도 복사본을 가져옵니다 "진짜" Win32 DLL 데이터 모델에서 완전히 다르다. 복잡성을 추가 하려면 Win32s DLL에서 힙에 할당 된 데이터는 사실 특정 프로세스 (적어도 지금까지 소유권 간다). 다음 데이터 및 코드를 고려:

정적 CString strGlobal; / / 파일 범위에서

__declspec (dllexport) void SetGlobalString(LPCTSTR lpsz)
{
   strGlobal = lpsz;
}

__declspec (dllexport)
void GetGlobalString (LPCTSTR lpsz, int cb)
{
   lstrcpyn (lpsz, strGlobal, cb);
}

일어나는 경우 위의 코드에 있는 dll에서과 DLL이 A와 B (그것은, 사실, 수 같은 응용 프로그램의 두 인스턴스가) 두 프로세스에 의해 로드 되는 것이 좋습니다. 호출 SetGlobalString("Hello from A") . A. 계속 염두에 두고 자체 CString 글로벌 이며 모두 A에 게 표시 됩니다 프로세스의 컨텍스트에서 CString 데이터에 대 한 메모리를 할당 하는 결과로,와 B B를 호출 하는 지금 GetGlobalString(sz, sizeof(sz)) . B 집합 데이터를 볼 수 있을 것입니다. 이 때문에 Win32s 처럼 Win32 프로세스 사이의 보호를 제공 합니다. 그래서 첫 번째 문제가; 많은 경우에 그것은 하나의 응용 프로그램을 다른 응용 프로그램에 의해 소유로 간주 되는 글로벌 데이터 영향을 바람직한。

그러나 기대-더 많은 문제가 있다. 그는 지금 종료 경우를 가정해 봅니다. 때 A 종료에 의해 사용 되는 메모리는 ' strGlobal ' 문자열 시스템에 대 한 사용할 수-프로세스에 의해 할당 된 모든 메모리는 운영 체제에 의해 자동으로 해제 되는 즉. CString 소멸자가 호출 되 고; 때문에 해제 되지 않습니다. 그것은 아직 호출 되지 않았습니다. 단순히 해제 하기 때문에 그것을 할당 하는 응용 프로그램 현장에 남아 있다. 이제 B 라는 GetGlobalString(sz, sizeof(sz)) , 유효한 데이터를 얻을 수 없습니다. 일부 다른 응용 프로그램이 사용 할 수 있습니다 메모리를 다른 용도로。

분명히 여기에 문제가 있다. MFC 3.x 스레드 로컬 저장소 (TLS) 이라는 기술을 사용 합니다. MFC 3.x 이라는 되지 않습니다 비록 win32s에서 정말 프로세스 로컬 저장소 인덱스 역할을 하는 TLS 인덱스를 할당할 것 이라고 하 고 TLS 인덱스를 기반으로 하는 모든 데이터를 참조 하는 것 이다. 이것은 win32에서 스레드 로컬 데이터를 저장 하는 데 사용 된 TLS 인덱스 비슷합니다 (그 주제에 대 한 자세한 내용은 아래 참조). 이 프로세스 당 적어도 두 개의 TLS 인덱스를 활용 하려면 모든 MFC DLL을 발생 합니다. 많은 OLE 컨트롤 Dll (OCXs)를 로드 하는 것에 대 한 계정 때 신속 하 게 (거기 64만 가능)는 TLS 인덱스의 밖으로 실행 합니다. 또한, MFC 단일 구조에서 한 곳에서이 모든 데이터를 배치 했다. 매우 확장 하지 않았고는 TLS 인덱스의 사용과 관련 하 여 이상적인。

MFC 4.x 클래스 템플릿은 있습니다 수 "래핑" 프로세스 로컬 되어야 하는 데이터 집합으로이 해결 합니다. 예를 들어, 위에서 언급 한 문제를 작성 하 여 해결 될 수 있습니다.:

구조체 CMyGlobalData: 공공 CNoTrackObject
{
   CString strGlobal;
};
CProcessLocallt;CMyGlobalData > globalData;

__declspec (dllexport) void SetGlobalString(LPCTSTR lpsz)
{
   globalData-> strGlobal = lpsz;
}

__declspec (dllexport)
void GetGlobalString (LPCTSTR lpsz, int cb)
{
   lstrcpyn (lpsz, globalData-> strGlobal, cb);
}

MFC는 두 단계에이 구현합니다. 첫째, 상단에는 Win32 Tls * Api (TlsAlloc, TlsSetValue, TlsGetValue, 등)에 상관 없이 당신이 얼마나 많은 Dll 프로세스 당 두 개의 TLS 인덱스를 사용 하는 계층이입니다. 두 번째는 CProcessLocal 서식 파일이이 데이터 액세스를 제공 합니다. 연산자-gt; 무시 어느 것이 어떤 직관적인 구문 위에서 볼 수 있습니다. 모든 개체에 의해 싸여 CProcessLocal 에서 파생 되어야 합니다 CNoTrackObject . CNoTrackObject 제공 하는 하위 수준 할당자 (LocalAlloc/LocalFree)와 가상 소멸자는 프로세스가 종료 될 때 MFC 프로세스 로컬 개체를 자동으로 파괴할 수 있다. 이러한 개체는 추가 정리 해야 하는 경우 사용자 정의 소멸자를 가질 수 있습니다. 때문에 컴파일러가 포함된 CString 개체 파괴 기본 소멸자 생성 됩니다 위의 예제, 필요 하지 않습니다(&G).

이 접근 방식에 다른 재미 있는 이점이 있다. 뿐만 아니라 모든 하는 CProcessLocal 개체를 자동으로 파괴 필요할 때까지 생성 되지 않습니다. CProcessLocal::operator-gt; 라고, 처음으로 연결 된 개체와 아무 빨리 인스턴스화합니다. 위의 예제에서 그 의미는 ' strGlobal ' 문자열 처음으로 SetGlobalString 또는 GetGlobalString 가 호출 될 때까지 생성 되지 않습니다. 어떤 경우에이 DLL 시작 시간을 줄일 수 있습니다(&G).

스레드 로컬 데이터

로컬 데이터 처리와 마찬가지로, 스레드 로컬 데이터 사용 됩니다 데이터 주어진된 주제에 로컬 이어야 합니다. 즉, 해당 데이터에 액세스 하는 각 스레드에 대 한 데이터의 별도 인스턴스가 필요 합니다. 이 여러 번 사용할 수 있습니다 광범위 한 동기화 메커니즘을 대신 합니다. 데이터를 여러 스레드에서 공유할 필요는 없습니다, 비싸고 불필요 한 같은 메커니즘이 될 수 있습니다. (위의 샘플) 처럼 CString 개체를 했습니다 가정해 봅시다. 우리가 그것을 만들 수 스레드 로컬와 그것을 포장 하 여는 CThreadLocal 서식 파일:

구조체 CMyThreadData: 공공 CNoTrackObject
{
   CString strThread;
};
CThreadLocallt;CMyThreadData > threadData;

void MakeRandomString()
{
   / / 카드 셔플 (큰 것)의 종류
   CString & str = threadData-> strThread;
   str입니다.Empty();
   동안 (str.GetLength()! = 52)
   {
      TCHAR ch = rand () %52 + 1;
      경우 (str.Find(ch) < 0)
         str + = 채널; / / 하지 발견, 그것을 추가
   }
}

만약 MakeRandomString 라는 두 개의 서로 다른 스레드에서 각 것 "셔플" 문자열 다른 방법으로 다른 방해 하지 않고. 이것은 없기 때문에 실제로 strThread 인스턴스 하나만 전역 인스턴스 대신 스레드 당。

Note 루프 반복 마다 한 번 대신 한 번 CString 주소를 캡처에 대 한 참조를 사용 하는 방법. 루프 코드와 작성 되었습니다 threadData-gt;strThread 사방 ' str '를 사용 하지만 코드는 훨씬 느리게 실행 될 것 이라고 합니다. 이러한 참조 루프에서 발생 하는 경우 데이터에 대 한 참조를 캐시 하는 것이 가장 좋습니다(&G).

CThreadLocal클래스 템플릿에서 동일한 메커니즘을 사용 하는 CProcessLocal 가 같은 구현 기법。

기술 노트 번호로 |nbsp; 카테고리별 기술 노트(&N)

Index