AFX 에서MANAGE_STATE(AfxGetStaticModuleState():

6892 단어 MFC
이전에 MFC의 DLL을 썼을 때, 항상 자동으로 생성된 코드 프레임워크에서 알림을 보았고, 출력된 함수마다 AFX 를 추가해야 했다.MANAGE_STATE(AfxGetStaticModuleState()).이렇게 하는 뜻을 몰랐고 이렇게 하지 않았으며 코드도 잘 작동했기 때문에 쓸데없는 말 같았다.
최근 프로젝트에서 DLL에서 MFC 생성 인터페이스를 사용해야 하기 때문에 자원이 서로 다른 동적 라이브러리에 놓여 있고 다중 루트와 섞여 있을 때 일이 이상하게 복잡해진다는 것을 알 수 있다. 이전에 MFC에 대한 반은 알고 대처하기에는 부족했다.프로그램의 알 수 없는 붕괴, 알 수 없는 ASSERT, 자원을 아무리 해도 불러올 수 없는 이유는 무엇일까요?매번, 항상 시도하고, 매 라인의 시작에서AFXMANAGE_STATE(Afx Get Static Module State())를 추가하거나 어딘가에 Afx Set Resource Handler()를 사용하면 문제가 해결되지만 도대체 어떻게 된 일인지 잘 알지 못하는지 항상 불안하다. 마치 다음 초에 문제가 또 갑자기 튀어나올 것 같다.
그저께 이 문제는 마침내 극도로 발휘되었다. 내가 몇 시간을 썼지만 어떤 시도도 성공하지 못했다. 프로젝트의 관건이 될 때 이런 일이 발생하여 나는 앞으로 다시는 MFC를 쓰지 않겠다고 암암리에 맹세했다.많은 영화 줄거리처럼 일이 결국 해결되었는데 이번에는 더 이상 이렇게 넘어갈 수 없다는 것을 분명히 이해하기로 했습니다.
여기서 내가 겪은 문제는 어떻게 DLL의 인터페이스 코드로 이 DLL의 자원 (Resource) 을 사용하게 하고, 어떻게 작업 라인에IE 컨트롤이 있는 대화상자를 불러오는가 하는 것이다.
동료에게 DLL 리소스 전환은 어떻게 이루어졌는지 물었습니다.AFX_MANAGE_STATE(Afx Get Static Module State())가 바로 그들의 답이다. 마이크로소프트의 추천과 같이 이렇게 간단하구나!이 코드가 도대체 무엇을 했는지 봅시다.
#define AFX_MANAGE_STATE§ AFX_MAINTAIN_STATE2 _ctlState§;
AFX_MAINTAIN_STATE2::AFX_MAINTAIN_STATE2(AFX_MODULE_STATE* pNewState) { m_pThreadState = _afxThreadState; m_pPrevModuleState = m_pThreadState->m_pModuleState; m_pThreadState->m_pModuleState = pNewState; }
_AFXWIN_INLINE AFX_MAINTAIN_STATE2::~AFX_MAINTAIN_STATE2() { m_pThreadState->m_pModuleState = m_pPrevModuleState; }
원래는 국부적인 대상을 정의하고 그 구조와 분석 함수를 이용하여 함수의 입구와 함수의 출구에서 State 상태를 전환하는 것이다. AfxGetStaticModuleState()는 현재 코드가 있는 DLL의 State를 가져오는 것이 틀림없다고 생각한다.
과연
static _AFX_DLL_MODULE_STATE afxModuleState;
AFX_MODULE_STATE* AFXAPI AfxGetStaticModuleState() { AFX_MODULE_STATE* pModuleState = &afxModuleState; return pModuleState; }
class _AFX_DLL_MODULE_STATE : public AFX_MODULE_STATE
//AFX_MODULE_STATE (global data for a module) class AFX_MODULE_STATE : public CNoTrackObject { … CWinApp* m_pCurrentWinApp; HINSTANCE m_hCurrentInstanceHandle; HINSTANCE m_hCurrentResourceHandle; LPCTSTR m_lpszCurrentAppName; BYTE m_bDLL;//TRUE if module is a DLL, FALSE if it is an EXE
… COccManager* m_pOccManager; …
여기에 MFC는 많은 데이터를 여기에 쌓아 놓았는데 매우 복잡하고 구조성이 매우 나쁘다고 말할 수 밖에 없다.
afxModule State는 dll의 정적 구성원으로 같은 dll의 코드에 접근할 수 있지만 언제 초기화됩니까?
extern “C” BOOL WINAPI DllMain(HINSTANCE hInstance, DWORD dwReason, LPVOID/lpReserved/) { …
    AfxWinInit(hInstance, NULL, _T(""), 0);

… }
BOOL AFXAPI AfxWinInit(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPTSTR lpCmdLine, int nCmdShow) { ASSERT(hPrevInstance == NULL);
// handle critical errors and avoid Windows message boxes
SetErrorMode(SetErrorMode(0) |
    SEM_FAILCRITICALERRORS|SEM_NOOPENFILEERRORBOX);

// set resource handles
AFX_MODULE_STATE* pModuleState = AfxGetModuleState();
pModuleState->m_hCurrentInstanceHandle = hInstance;
pModuleState->m_hCurrentResourceHandle = hInstance;


}
원래 DLL의 입구 함수에서 이 DLL의 hInstance로 구조를 초기화했습니다.
이때까지 우리는 왜 자원 전환을 해야 하는지 모르겠다.앞에서 시작된afxThreadState는 도대체 무엇입니까?Thread와 관련이 있는 것 같은데, 도대체 뭘까요?
THREAD_LOCAL(_AFX_THREAD_STATE, _afxThreadState)
#define THREAD_LOCAL(class_name, ident_name)/AFX_DATADEF CThreadLocal ident_name;
template class CThreadLocal : public CThreadLocalObject
다시 아래로 추적해 보면 사실 코드가 어색해질수록 이해하기 어렵지만 기본적인 기능은 현재 이 행 코드의 라인에 접근하는 개인 데이터이다.이른바 스레드의 개인 데이터란 서로 다른 스레드가 같은 코드를 실행하면 얻는 데이터가 다를 수 있다는 것이다.그러고 보니 MFC의 많은 문맥이 전역적인 맵에 저장되어 있고 라인의 개인 데이터 구역에 놓여 있기 때문에 라인을 뛰어넘어 MFC의 대상을 전달하는 것은 매우 안전하지 않다.그런데 MFC는 왜 그랬을까?이 문제는 지금까지도 나는 여전히 이해할 수 없다.
아니면 시작된 코드로 돌아가서 자원 전환은 도대체 어떻게 진행되는 것입니까?
int CDialog::DoModal() { …
HINSTANCE hInst = AfxGetResourceHandle();
if (m_lpszTemplateName != NULL)
{
    hInst = AfxFindResourceHandle(m_lpszTemplateName, RT_DIALOG);
    HRSRC hResource = ::FindResource(hInst, m_lpszTemplateName, RT_DIALOG);
    hDialogTemplate = LoadResource(hInst, hResource);

… }
_AFXWIN_INLINE HINSTANCE AFXAPI AfxGetResourceHandle() { ASSERT(afxCurrentResourceHandle != NULL); return afxCurrentResourceHandle; }
#define afxCurrentResourceHandle AfxGetModuleState()->m_hCurrentResourceHandle
AFX_MODULE_STATE* AFXAPI AfxGetModuleState() { _AFX_THREAD_STATE* pState = _afxThreadState; AFX_MODULE_STATE* pResult; if (pState->m_pModuleState != NULL) {//thread state’s module state serves as override pResult = pState->m_pModuleState; } else {//otherwise, use global app state pResult = _afxBaseModuleState.GetData(); } ASSERT(pResult != NULL); return pResult; }
원래 MFC의 대화상자에서 자원을 불러오는 것은 현재 라인에 대응하는 Module State에 저장된 ResourceHandler를 가져와서 자원을 불러옵니다.그래서 DLL의 코드는 함수의 입구에 있어야 합니다. 먼저 현재 실행 중인 라인의Module State를 이 Dll의 State로 바꿔야 이 dll의 자원을 불러올 수 있습니다!이때 나는 왜 라인의 개인 데이터에 의존해서 Module State를 저장해야 하는지 갑자기 깨달았다. 사실은 확실히 전달이다!이것은 사실 CDialog가 다른 DLL에 저장되어 있기 때문이다. 예를 들어 MFC40.dll, MFC 라이브러리를 공유 모드로 연결하면사용자가 직접 작성한 CDialog의 하위 클래스는 CDialog와 같은 Dll에 놓여 있지 않다. 그들은 어떻게 이 자원 핸들을 전달합니까?두 가지 해결 방법: 1, 매개 변수를 이용하여 전달한다.2. 공공장소에 보관한다.전자는 파라미터를 늘려야 하기 때문에 매우 번거롭다. 윈32의 API는 이렇게 실현된 것 같지?후자는 이 공공장소가 어디에 있는지 확인해야 합니까?이것은 사람들로 하여금 공용 동적 라이브러리를 세우는 것을 생각나게 한다.주 프로그램에서 제공됩니까?한마디만 더 하자면 J2EE에는 용기의 개념(COM+도 있는 것 같고.NET가 어떤지 모르겠다)이 있다. 구성 요소는 모두 용기에 생존한다. 이때 우리는 이 데이터를 용기에 저장할 것을 상상할 수 있다.어쨌든 MFC의 실현은 라인의 개인 데이터 구역에 놓고 공공적인 동적 라이브러리도 필요 없고 메인 프로그램도 필요 없다. 스스로 해결할 수 있다!그것은 스스로 좋은 해결 방식이고 완벽하다고 여기지만, 특히 내막을 모르는 사람들을 일련의 문제를 야기한다.
자원 마운트에 관한 문제는 이미 해결된 것 같지만, 내가 실현한 dll은 일반적인 출력 함수로 출력하는 것이 아니라 출력 클래스이다. 나는 모든 클래스의 구성원 함수에 AFX 를 추가하고 싶지 않다.MANAGE_STATE(AfxGetStaticModuleState()).어떡하지?자원 전환의 원리를 이미 알았으니, 우리는 두 개의 출력 함수를 추가하여 각각 AFX 에 대응한다MAINTAIN_STATE2의 구조와 분석 함수는 클래스의 사용 전후에 호출하면 된다.또는 클래스의 구조와 분석 함수에 각각 넣는다.또는 구성원 변수로 성명한다.어쨌든 리소스 전환이 올바르게 중첩되어 상호 교차할 수 없음을 보장해야 합니다. DLL 간 상호 호출이 발생할 수 있습니다.
자, 이제 DLL의 자원을 정확하게 호출할 수 있지만,Dialog에IE 컨트롤이 포함되어 있을 때, 우리는 여전히 실패했습니다. 왜요?ActiveX 컨트롤에 대해dialog는 특수한 처리를 해야 한다는 것을 알고 있습니다.Afx Enable Control Container (), 저도COM을 사용하려면CoInitialize () 가 필요하다는 것을 알고 있지만, 두 개를 같이 사용해야 IE를 꺼낼 수 있다고 생각해 본 적이 없습니다. 그러나 마지막에는 이렇습니다.이상하게도 작업 라인에 있지 않으면 코인티리어 () 가 필요하지 않아도 IE 컨트롤을 불러올 수 있는데, 이것은 잠시 상관하지 않는다.
PROCESS_LOCAL(COccManager, _afxOccManager)
void AFX_CDECL AfxEnableControlContainer(COccManager* pOccManager) { if (pOccManager == NULL) afxOccManager = _afxOccManager.GetData(); else afxOccManager = pOccManager; }
#define afxOccManager AfxGetModuleState()->m_pOccManager
그러고 보니 이게afxOccManager는 전체 프로세스에 속해야 합니다. 전체 프로세스는 그 프로세스를 정의하는 dll에 하나밖에 없습니다.단, 이 대상(또는 사용자 정의)을 Module State에 전달해야 합니다. (앞에 있는 AFX MODULE STATE에 이 속성이 포함되어 있음을 주의하십시오.) 즉, Afx Enable Control Container () 를 사용하면 특정한 Module State에 OccManager의 메시지가 있습니다.단, 대상 dll에서 리소스를 올바르게 전환한 후에야 다음을 수행할 수 있습니다.
AFX_MANAGE_STATE(AfxGetStaticModuleState()); CoInitialize(NULL); AfxEnableControlContainer();
이로써 나를 오랫동안 괴롭혔던 이 문제는 마침내 맥락이 분명해졌다.

좋은 웹페이지 즐겨찾기