다음을 통해 공유


TN058: MFC 모듈 상태 구현

[!참고]

온라인 설명서의을 처음 포함 되었습니다 때문 다음 기술 참고 업데이트 되지 않았습니다.따라서 일부 절차 및 항목 오래 되었거나 잘못 된 수 있습니다.최신 정보는 온라인 설명서 색인에서 관심 있는 주제에 대해 검색 하는 것이 좋습니다.

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

참고 "관리 된 데이터 MFC 모듈의 상태를"이이 글을 읽기 전에 새 문서 만들기, 창과 보기.이 문서에서는 중요 한 사용 정보 및이 주제에 대 한 개요 정보를 포함합니다.

개요

세 종류의 MFC 상태 정보: 모듈 상태, 상태 프로세스 및 스레드 상태입니다.때로는 이러한 상태 형식은 결합할 수 있습니다.예를 들어, MFC의 핸들 맵에 모두 로컬 모듈 및 스레드 로컬입니다.이 있고 다른 맵을 각 스레드는 서로 다른 두 모듈을 수 있습니다.

프로세스 및 스레드 상태 비슷합니다.이러한 데이터 항목에는 일반적으로 전역 변수 있었지만 한 필요가 주어진된 프로세스에 특정 수 또는 적절 한 win32s에서 지원 하거나 적절 한 다중 스레딩 지원에 대 한 스레드 하는 것입니다.범주에 지정 된 데이터 항목이 적합 한지를 해당 항목 및 프로세스와 스레드 경계와 관련 하 여 필요한 의미에 따라 다릅니다.

진정한 글로벌 상태나 로컬 프로세스 또는 스레드 로컬 상태를 포함할 수 있는 모듈 상태와 다릅니다.또한이 신속 하 게 전환할 수 있습니다.

모듈 상태를 전환 합니다.

각 스레드에 모듈 "현재" 또는 "활성" 상태에 대 한 포인터를 포함 합니다. (당연히 포인터의 MFC 스레드 로컬 상태의 일부입니다).실행 스레드는 응용 프로그램에 OLE 컨트롤 또는 DLL에서 OLE 컨트롤을 응용 프로그램에 콜백 호출 모듈 경계를 전달 하면이 포인터가 변경 됩니다.

현재 모듈 상태를 호출 하 여 전환 될 AfxSetModuleState.대부분의 API에 직접 절대로 처리 됩니다.MFC, 대부분의 경우 호출이 합니다 (에 WinMain, OLE 진입점은 AfxWndProc등.).이렇게 작성에 특수 한 정적으로 연결 하 여 모든 구성 요소에서 WndProc, 하 고 특별 한 WinMain (또는 DllMain)는 현재 모듈 상태 수 알고.DLLMODUL에서 확인 하 여이 코드를 볼 수 있습니다.CPP 또는 APPMODUL입니다.CPP MFC\SRC 디렉터리에 있습니다.

모듈 상태를 설정 하 고 다음으로 다시 설정 하지 한다고 하는 드문 경우입니다.대부분의 자신의 모듈을 "푸시" 할 것으로 현재 상태 및 완료 되 면 다음, "원래 컨텍스트를 다시 pop".이렇게 하면 매크로 AFX_MANAGE_STATE 와 특수 클래스인 AFX_MAINTAIN_STATE.

CCmdTarget지원 모듈 상태 전환에 대 한 특별 한 기능이 있습니다.특히는 CCmdTarget 됩니다 진입점 OLE COM 및 OLE 자동화에 대 한 루트 클래스를 사용 합니다.같은 다른 진입점 시스템에 노출, 이러한 진입점은 올바른 모듈 상태로 설정 해야 합니다.어떻게 하지를 주어진 CCmdTarget "올바른" 모듈 상태를 알지?이 "생성할 때 모듈 상태를"현재"이란 기억", 그 수는 "나중에 있을 때 값에는 현재 모듈 상태를 기억" 집합 이라는 답은입니다.따라서 모듈 상태에 주어진 CCmdTarget 개체는 관련 개체를 생성할 때에 현재 모듈 상태입니다.INPROC 서버를 로드 개체를 만들고 해당 메서드를 호출 하는 간단한 예를 들자면

  1. OLE에서 DLL을 로드를 사용 하 여 LoadLibrary.

  2. RawDllMain 먼저 호출 됩니다.DLL 모듈 상태 정적 모듈을 알려진된 상태로 설정 합니다.이러한 이유로 RawDllMain DLL에 정적으로 링크.

  3. 클래스 팩터리는 개체와 연관 된에 대 한 생성자가 호출 됩니다.COleObjectFactory파생 된 CCmdTarget 및 결과적으로 인스턴스화된 모듈 상태를 기억 합니다.중요-클래스 팩터리 개체를 만들려면 묻는 메시지가 나타나면 지금 현재 상황에 어떤 모듈 상태를 알고.

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

  5. COleObjectFactory::XClassFactory2::CreateInstance 라고 합니다.개체를 만들고 반환 하기 전에이 함수 모듈 상태가 3 단계에서 현재 모듈 상태로 설정 (때 현재 했습니다 하는 것은 COleObjectFactory 인스턴스화된).이렇게 내부에 METHOD_PROLOGUE.

  6. 개체를 만들 때 너무 되는 CCmdTarget 파생 및 동일한 방식으로 COleObjectFactory 활성화 된 어떤 모듈 상태를 기억 하므로이 새 개체를 하지.개체는 모듈 상태를 전환 하려면 알고 이제 때마다이 호출 됩니다.

  7. 받은 OLE COM 개체에서 함수를 호출 하는 클라이언트의 CoCreateInstance 를 호출 합니다.개체를 호출할 때 사용 하 여 METHOD_PROLOGUE 마찬가지로 모듈 상태를 전환 하려면 COleObjectFactory 않습니다.

볼 수 있듯이 만들 때 모듈 상태 개체 로부터 개체에 전파 됩니다.모듈 상태를 적절 하 게 설정 하는 것이 중요 합니다.설정 되어 있지 않은 경우 DLL 또는 COM 개체를 MFC 응용 프로그램을 호출 하 고 자체 리소스를 찾으려면 못할 수 있습니다 또는 다른 불쾌 한 방식으로 실패할 수 있습니다 불완전 하 게 작용할 수 있습니다.

참고 특정 종류의 Dll, 특히 "MFC 확장" Dll의 모듈 상태를 전환 하지 마십시오 자신의 RawDllMain (실제로, 이들은 일반적으로 없는 한 RawDllMain).이들은 사용 하는 응용 프로그램에서 실제로 실행시 "처럼" 동작으로 사용할 때문입니다.아주 많이 실행 되는 응용 프로그램의 일부로 사용 되 고 의도 응용 프로그램의 전역 상태를 수정 하는 것.

OLE 컨트롤 및 기타 Dll 매우 다릅니다.이러한 호출 응용 프로그램의 상태를 수정 하지 않을; 호출 하 고 응용 프로그램 에서도 MFC 응용 프로그램 아닐 수도 있고 따라서 있을 수 없는 상태를 수정 하려면.이 모듈 상태 전환 발명 했다는 이유입니다.

같은 대화 상자에서 DLL 실행 하 여 DLL에서 내보낸된 함수에 대 한 함수의 시작 부분에 다음 코드를 추가 해야 합니다.

AFX_MANAGE_STATE(AfxGetStaticModuleState( ))

이 반환 된 상태와 현재 모듈 상태를 맞바꿉니다 AfxGetStaticModuleState 현재 범위가 끝날 때까지.

리소스 Dll에서 문제가 발생 하는 경우는 AFX_MODULE_STATE 매크로 사용 하지 않습니다.기본적으로 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에서 힙에 할당 된 데이터 실제로 특정 프로세스 (적어도 가장 소유권을 거치면 서)입니다.다음 코드와 데이터를 고려 하십시오.

static CString strGlobal; // at file scope

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

__declspec(dllexport)
void GetGlobalString(LPCTSTR lpsz, size_t cb)
{
   StringCbCopy(lpsz, cb, strGlobal);
}

어떻게 위의 코드에서 DLL에 있는지 여부와 A와 B (사실, 두 인스턴스는 동일한 응용 프로그램의 수 있습니다,)의 두 프로세스에서 DLL이 로드 되는 것이 좋습니다.A calls SetGlobalString("Hello from A").결과적으로 메모리 할당 됩니다 있는 CString A. 프로세스의 컨텍스트에서 데이터사실을 염두에 CString 자체 글로벌 지 며 모두 A로 표시와 B이제 B를 호출 GetGlobalString(sz, sizeof(sz)).B 집합 데이터를 볼 수 있습니다.Win32s 없습니다 w i n 32와 같이 프로세스 간 보호를 제공 하는 때문입니다.첫 번째 문제입니다. 대부분의 응용 프로그램이 다른 응용 프로그램에서 소유 하는 것으로 간주 됩니다 전역 데이터에 영향을 하는 것은 바람직하지 않습니다.

추가 문제 뿐입니다.지금 종료 되는 경우를 가정해 봅니다.때 A 종료에 의해 사용 되는 메모리의 'strGlobal' 시스템에서 사용할 수 있는 문자열 변경-즉, 운영 체제에서 프로세스에 의해 할당 된 모든 메모리가 자동으로 해제 됩니다.때문에 해제 되는 CString 소멸자가 호출 됩니다. 아직 호출 되지 않은.단순히 비워집니다 할당 된 응용 프로그램의 장면에 갔 기 때문에.이제 B 라는 GetGlobalString(sz, sizeof(sz)), 잘못 된 데이터를 가져올 수 있습니다.다른 응용 프로그램 메모리 다른 용도로 사용 되었습니다.

분명히 문제가 있습니다.MFC 3.x 스레드 로컬 저장소 (TLS) 이라는 기술을 사용 합니다.MFC 3.x는 호출 되지 않습니다 경우에 w i n 32에서 프로세스 로컬 저장소 인덱스 역할을 실제로 TLS 인덱스를 할당 하 고 해당 TLS 인덱스를 기준으로 하는 모든 데이터를 참조 합니다.이에 Win32 스레드 로컬 데이터를 저장 하는 데 사용 된 TLS 인덱스 유사 (해당 주제에 대 한 자세한 내용은 다음을 참조).원인은 MFC DLL 모든 프로세스 당 적어도 두 개의 TLS 인덱스를 활용할 수 있습니다.신속 하 게 많은 OLE 컨트롤 Dll (Ocx) 로드를 고려 하면 TLS 인덱스를 벗어났습니다 (가지 64만 사용할 수 있습니다)을 실행 합니다.또한 MFC 단일 구조의 한 곳에서이 모든 데이터를 배치 했습니다.매우 확장할 수 없습니다 하 고 관련 하 여 TLS 인덱스의 사용에 적합 하지 않습니다.

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

struct CMyGlobalData : public CNoTrackObject
{
   CString strGlobal;
};
CProcessLocal<CMyGlobalData> globalData;

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

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

MFC는이 두 단계를 구현합니다.먼저 되는 레이어 위에 Win32 Tls * Api (하면 TlsAlloc, TlsSetValue, TlsGetValue등) 얼마나 많은 Dll 경우에 관계 없이 프로세스 당 두 개의 TLS 인덱스를 사용 합니다.두 번째는 CProcessLocal 서식 파일이이 데이터에 액세스 하려면 제공 된.-연산자 재정의 > 어떤 직관적인 구문 위의 참조 수입니다.가 래핑하는 개체를 모두 CProcessLocal 에서 파생 되어야 합니다 CNoTrackObject.CNoTrackObject하위 수준 할당자 제공 (LocalAlloc/LocalFree)와 가상 소멸자는 프로세스가 종료 되 면 자동으로 MFC 프로세스 로컬 개체 파괴 수 있습니다.추가 정리 해야 하는 경우 이러한 개체에 사용자 정의 소멸자를 가질 수 있습니다.위의 예제에서는 파괴를 포함 하는 기본 소멸자 컴파일러 생성 되므로, 필요 하지 않습니다 CString 개체입니다.

다른 흥미로운 이점을이 방법입니다.뿐만 아니라 모두를 수 CProcessLocal 자동으로 소멸 되는 개체는 필요할 때까지 생성 된지 않습니다.CProcessLocal::operator->없음 빠르게 하 고 처음 호출 될 때 연결 된 개체를 인스턴스화합니다.위의 예제는 의미는 'strGlobal' 문자열 처음까지 생성할 수 없습니다 SetGlobalString 또는 GetGlobalString 라고 합니다.경우에 따라서는 DLL 시작 시간을 줄일 수 있습니다.

스레드 로컬 데이터

데이터는 지정 된 스레드에 대해 로컬 이어야 할 때 스레드 로컬 데이터 비슷한 로컬 데이터 처리를 위해이 사용 됩니다.즉, 해당 데이터에 액세스 하는 각 스레드에 대 한 데이터의 별도 인스턴스가 필요 합니다.이 광범위 한 동기화 메커니즘을 대신 여러 번 사용할 수 있습니다.데이터를 여러 스레드에서 공유 될 필요가 없는 경우 이러한 메커니즘은 비싸고 불필요 한 될 수 있습니다.우리 가정해 있는 CString 개체 (위의 예제 처럼).스레드 로컬에 래핑하여 해낼 수 있는 CThreadLocal 템플릿:

struct CMyThreadData : public CNoTrackObject
{
   CString strThread;
};
CThreadLocal<CMyThreadData> threadData;

void MakeRandomString()
{
   // a kind of card shuffle (not a great one)
   CString& str = threadData->strThread;
   str.Empty();
   while (str.GetLength() != 52)
   {
      unsigned int randomNumber;
      errno_t randErr;
      randErr = rand_s( &randomNumber );
      if ( randErr == 0 )
      {
         TCHAR ch = randomNumber % 52 + 1;
         if (str.Find(ch) < 0)
            str += ch; // not found, add it
      }
   }
}

경우 MakeRandomString 호출 된 두 개의 서로 다른 스레드에서 각 "문자열이 다른 방식으로 서로 방해 하지 않고 없었을 것".없기 때문에이 실제로 strThread 인스턴스 당 하나의 전역 인스턴스 대신 스레드.

방법에 대 한 참조를 사용 하 여 캡처를 CString 주소 한 번 대신 한 번 루프 반복 마다.루프 코드를 썼습니다 수 threadData->strThread 어디 'str' 사용 됩니다 있지만 코드 실행에서 훨씬 느리게 됩니다.이러한 참조 루프에서 발생 하는 경우 데이터에 대 한 참조를 캐시 하는 것이 좋습니다.

CThreadLocal 클래스 템플릿은 동일한 메커니즘을 사용 하는 CProcessLocal 합니까와 같은 구현 기술을 합니다.

참고 항목

기타 리소스

번호 기술 정보

범주별 기술 노트