Udostępnij za pośrednictwem


TN058: Wdrożenie stanu modułu MFC

[!UWAGA]

Następujące Uwaga techniczna została zaktualizowana, ponieważ najpierw została uwzględniona w dokumentacji online.W rezultacie niektóre procedur i tematów może być nieaktualne lub nieprawidłowe.Najnowsze informacje zaleca się wyszukać temat zainteresowanie Indeks dokumentacji online.

Ten Uwaga techniczna opisuje wykonania konstrukcje MFC "Państwo modułu".Zrozumienie realizacji Państwo moduł ma kluczowe znaczenie przy użyciu MFC współużytkowane biblioteki DLL z biblioteki DLL (lub serwer w trakcie OLE).

Przed odczytaniem tej notatki, odnoszą się do "Zarządzanie Państwa danych z MFC modułów" w Tworzenie nowych dokumentów, Windows i widoki.Ten artykuł zawiera informacje o sposobie użycia ważne i przegląd informacji na ten temat.

Omówienie

Istnieją trzy rodzaje informacji o stanie MFC: stan modułu, stanu procesu i stan wątku.Czasami można łączyć tych typów stanu.Na przykład mapy uchwyt MFC firmy są zarówno moduł lokalnych, jak i lokalnej wątku.Umożliwia to dwa różne moduły mają różne mapy w każdym ich wątków.

Stan procesu i stan wątku są podobne.Te elementy danych są rzeczy, które są tradycyjnie zmienne globalne, ale należy mieć specyficzne dla danego procesu lub wątku Win32s właściwego wsparcia lub dla właściwego wsparcia wielowątkowości.Element danych w danym mieści się w kategorii zależy od tego elementu i jego pożądanych semantykę w odniesieniu do granic procesów i wątków.

Stan modułu jest unikatowy, może zawierać prawdziwie globalne Państwo lub Państwa, które jest procesem lokalnym lub lokalnej wątku.Ponadto to mogą być szybko przełączane.

Przełączanie stan modułu

Każdy wątek zawiera wskaźnik do stanu modułu "bieżący" lub "aktywne" (nic dziwnego, wskaźnik jest częścią stan lokalnej wątku MFC firmy).Wskaźnik ten jest zmieniany podczas wątku wykonywania przejścia graniczne modułu, takich jak aplikacji wywołującej do formantu OLE lub biblioteki DLL lub formantu OLE oddzwanianie do aplikacji.

Bieżący stan modułu jest włączane przez wywołanie AfxSetModuleState.W większości nigdy nie będzie sobie bezpośrednio z interfejsem API.MFC, w wielu przypadkach będzie wywoływać go dla Ciebie (na WinMain, punkty wejścia OLE AfxWndProc, itp.).Ma to dowolny składnik zapisu łącząc statycznie w specjalnym WndProci specjalnego WinMain (lub DllMain), wie, które Państwo moduł powinno być bieżącego.Ten kod można zobaczyć patrząc na DLLMODUL.CPP lub APPMODUL.CPP w katalogu MFC\SRC.

Jest rzadko, że chcesz ustawić stan modułu i nie ustawić ją ponownie.W większości przypadków ma własny moduł "push" state bieżący i następnie, po zakończeniu "pop" pierwotnym kontekście Wstecz.Jest to realizowane przez makro AFX_MANAGE_STATE i specjalnej klasy AFX_MAINTAIN_STATE.

CCmdTargetudostępnia specjalne funkcje do obsługi przełączania stanu modułu.W szczególności CCmdTarget jest główna klasa używane do automatyzacji OLE i modelu COM OLE punkty wejścia.Podobnie jak inne punkty wejścia narażonych na system, te punkty wejścia należy ustawić stan prawidłowego modułu.W jaki sposób jest danej CCmdTarget wiedzieć, jakie powinny być stanu "poprawny" moduł?Odpowiedź jest, że to "pamięta" co to jest "bieżący" Stan modułu, gdy jest skonstruowany, takie, aby można go bieżący stan modułu do tego "zapamiętane" wartość po późniejszym zestaw o nazwie.W rezultacie moduł Państwa danego CCmdTarget skojarzony jest obiekt jest aktualna, kiedy obiekt został skonstruowany stan modułu.Podjąć prosty przykład ładowanie serwera INPROC, tworzenia obiektu i wywołanie jej metody.

  1. Biblioteka DLL jest ładowany przez OLE przy użyciu LoadLibrary.

  2. RawDllMain jest wywoływana pierwszy.Ustawia stan modułu Państwo znane statycznych modułu dla biblioteki DLL.Z tego powodu RawDllMain łączony statycznie do biblioteki DLL.

  3. Konstruktor dla fabryki klasy skojarzone z obiektem naszych nazywa się.COleObjectFactorypochodzi z CCmdTarget i w rezultacie pamięta Państwa, w którym moduł został skonkretyzowany.Jest to ważne — zapytany Fabryczna klasa do tworzenia obiektów teraz wie jakie Państwo moduł ma być bieżący.

  4. DllGetClassObjectnazywa się uzyskać fabryki klas.MFC przeszukuje listy fabryki klas skojarzonych z tym modułem i zwraca go.

  5. COleObjectFactory::XClassFactory2::CreateInstance jest wywoływana.Przed utworzeniem obiektu i zwracania, ta funkcja ustawia stan modułu do stanu modułu, które były aktualne w kroku 3 (jeden, które były aktualne, gdy COleObjectFactory został skonkretyzowany).Ma to wewnątrz METHOD_PROLOGUE.

  6. Podczas tworzenia obiektu zbyt jest CCmdTarget pochodnych i w ten sam sposób COleObjectFactory zapamiętane, które Państwo moduł był aktywny, co powoduje to nowy obiekt.Teraz wie, które Państwo modułu, aby przełączyć się do obiektu ilekroć jest wywoływana.

  7. Klient wywołuje funkcję na obiekcie OLE COM otrzymała z jego CoCreateInstance wywołania.Kiedy obiekt jest nazywany korzysta z METHOD_PROLOGUE do przełączania stanu modułu, podobnie jak COleObjectFactory jest.

Jak widać, stan modułu są propagowane z obiektu do obiektu przy ich tworzeniu.Należy odpowiednio ustawić stanu modułu.Jeśli nie jest ustawiona, obiekt biblioteki DLL lub COM może źle interakcji z aplikacji MFC, która wywołuje, może być nie można odnaleźć zasobów własnych lub mogą nie działać w inny sposób miserable.

Uwaga niektóre rodzaje biblioteki DLL, w szczególności "MFC rozszerzenie" dll nie włączaj stanu modułu w ich RawDllMain (w rzeczywistości, zwykle nie nawet posiadają RawDllMain).Jest tak, ponieważ mają one zachowywać się "tak" były faktycznie znajduje się w aplikacji, która korzysta z nich.Są one bardzo częścią aplikacji, która działa i jest ich zamiar zmiany stanu globalnego tej aplikacji.

Formanty OLE i inne biblioteki DLL są bardzo różne.Nie chcą zmodyfikować stan aplikacji wywołującej; Aplikacja, która je wywołuje może być nawet aplikacji MFC i tak może istnieć żadne Państwo do modyfikowania.Jest to powód, że została wynaleziona przełączania stanu modułu.

Eksportowanych funkcji z biblioteki DLL, który uruchamia okno dialogowe w bibliotece DLL należy dodać następujący kod do początku funkcji:

AFX_MANAGE_STATE(AfxGetStaticModuleState( ))

To zamienia bieżący stan modułu z Państwem zwrócony z AfxGetStaticModuleState do końca bieżącego zakresu.

Jeśli będzie wystąpić problemy z biblioteki DLL zasobów AFX_MODULE_STATE makro nie jest używany.Domyślnie MFC używa dojścia do zasobu aplikacji głównej załadować szablonu zasobu.Ten szablon jest faktycznie przechowywane w bibliotece DLL.Głównym powodem jest informacji o stanie modułu MFC firmy nie został przełączony przez AFX_MODULE_STATE makro.Dojścia do zasobu jest odbierana od stanu modułu MFC firmy.Nie przełączenie stanu modułu powoduje dojścia do zasobu niewłaściwy ma być używany.

AFX_MODULE_STATEnie trzeba w każdej funkcji w bibliotece DLL.Na przykład InitInstance może być wywoływana przez kod MFC w aplikacji bez AFX_MODULE_STATE ponieważ MFC jest automatycznie przenoszony stanu modułu przed InitInstance , a następnie przełącza go ponownie po InitInstance zwraca.To samo dotyczy wszystkich programów obsługi mapę wiadomości.Regularne dll faktycznie mają procedury specjalne okno, która automatycznie przełącza stan modułu przed przesłaniem wiadomości.

Proces danych lokalnych

Dane lokalne proces nie byłoby takie głębokiej troski, gdyby nie został do trudności w modelu Win32s DLL.W Win32s wszystkich bibliotek DLL udostępnianie danych globalnych, nawet jeśli ładowany przez wiele aplikacji.Jest to bardzo różni się od "real" model danych biblioteki DLL systemu Win32, gdzie każdej biblioteki DLL pobiera oddzielną kopię swojej przestrzeni danych w każdym procesie, który dołącza do biblioteki DLL.Aby dodać do złożoności, danych na stercie w bibliotece DLL Win32s przydzielana jest w rzeczywistości proces (co najmniej tak dalece jak własności przechodzi).Należy wziąć pod uwagę następujące dane i kod:

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);
}

Należy rozważyć, co się dzieje, jeśli powyższy kod w znajduje w bibliotece DLL i że biblioteka DLL jest ładowany przez dwa procesy a i B (może w rzeczywistości być dwa wystąpienia tej samej aplikacji).A calls SetGlobalString("Hello from A").W efekcie pamięć została przydzielona na CString danych w kontekście procesu A.Należy pamiętać, że CString jest globalnym i jest widoczna zarówno a i B.Teraz b wywołuje GetGlobalString(sz, sizeof(sz)).B będą mogli zobaczyć dane, A ustawiona.Wynika to z Win32s nie oferuje żadnej ochrony między procesami, tak jak robi Win32.To pierwszy problem; w wielu przypadkach nie jest pożądane, aby jednej aplikacji wpływają na danych globalnych, uważany własnością przez inną aplikację.

Istnieją dodatkowe problemy.Załóżmy, że obecnie kończy pracę.Kiedy wyjścia A, pamięć używaną przez "strGlobal" ciąg staje się dostępny dla systemu — czyli wszystkie pamięci przez proces a jest zwalniane automatycznie przez system operacyjny.Jest nie zwolniona, ponieważ CString jest wywoływana destruktora; nie została wywołana jeszcze.Jest zwalniany po prostu ponieważ przydzielonych jej stosowania opuścił sceny.Teraz, jeśli b o nazwie GetGlobalString(sz, sizeof(sz)), mogą nie być poprawne dane.Inna aplikacja może użyto tej pamięci do czegoś innego.

Wyraźnie istnieje problem.MFC 3.x używana technika o nazwie pamięci lokalnej wątku (TLS).MFC 3.x byłoby przydzielić indeks TLS, że w ramach Win32s naprawdę działa jako indeks Magazyn lokalny procesu, mimo że nie jest wywoływana i będzie następnie odwołać wszystkie dane oparte na tym indeksie TLS.Jest to podobne do indeksu TLS, używany do przechowywania danych lokalnej wątku na Win32 (patrz poniżej Aby uzyskać więcej informacji na ten temat).Przyczyną każdej biblioteki DLL MFC wykorzystać co najmniej dwa indeksy TLS na proces.Gdy uwzględnić ładowanie wiele OLE formantu dll (formanty ocx), szybko uruchamiać z indeksów TLS (Brak dostępnych tylko 64).Ponadto MFC musiał umieścić te dane w jednym miejscu, w jednej strukturze.Nie został bardzo extensible i nie jest idealny w odniesieniu do jego użycie indeksów TLS.

MFC 4.x adresów to zbiór szablonów klasy użytkownik może "oblewać" dane, które powinny być procesem lokalnym.Na przykład problem, wymienionych powyżej może ustala pisania:

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 implementuje ten w dwóch etapach.Pierwszy z nich to warstwy na górze Win32 Tls * API (TlsAlloc, TlsSetValue, TlsGetValue, itp) używających tylko dwa indeksy TLS na proces, niezależnie od tego, ile dll masz.Drugi, CProcessLocal dostępu do danych to znajduje się szablon.Zastępuje ona operator - > co jest, co pozwala intuicyjny Składnia zobacz powyżej.Wszystkie obiekty, które są pakowane przez CProcessLocal musi pochodzić z CNoTrackObject.CNoTrackObjectzapewnia alokatora niższego poziomu (LocalAlloc/funkcji LocalFree) i wirtualnych destruktora takie, że MFC mogą niszczyć lokalnych obiektów procesu automatycznie, gdy proces jest zakończony.Takie obiekty mogą mieć niestandardowe destruktor, jeśli wymagane jest dodatkowe oczyszczanie.Powyższy przykład nie wymaga jednego, ponieważ kompilator wygeneruje destruktora domyślny do zniszczenia osadzonego CString obiektu.

Istnieją inne interesujące zalet tego podejścia.Są nie tylko wszystkie CProcessLocal zniszczone automatycznie, obiekty nie są skonstruowane do momentu są one potrzebne.CProcessLocal::operator->będzie tworzenia wystąpienia obiektu skojarzonego nazywa się po raz pierwszy, a nie wcześniej.W powyższym przykładzie, oznacza to, że "strGlobal" ciąg nie zostanie zbudowana do czasu pierwszego SetGlobalString lub GetGlobalString jest wywoływana.W niektórych przypadkach może to pomóc zmniejszyć czas uruchamiania biblioteki DLL.

Dane lokalne wątku

Podobne do przetwarzania danych lokalnych, dane lokalne wątku służy dane muszą być lokalne dla danego wątku.Oznacza to, że musisz osobne wystąpienie dane dla każdego wątku, który uzyskuje dostęp do danych.To wielokrotnie można zamiast mechanizmów rozległe synchronizacji.Jeśli dane nie musi być współużytkowane przez wiele wątków, takie mechanizmy może być kosztowne i niepotrzebne.Załóżmy, że mieliśmy CString obiektu (podobnie jak próbki powyżej).Możliwe wątku lokalnych zawijania z CThreadLocal szablonu:

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
      }
   }
}

Jeśli MakeRandomString została wywołana z dwóch różnych wątków, każdy będzie "losowo" ciąg w różny sposób bez zakłócania drugiej.Ponieważ jest faktycznie strThread instancji na wątku, zamiast tylko jedno wystąpienie globalnego.

Jak odwołanie jest wykorzystywany do przechwytywania CString adres raz zamiast raz na iteracji pętli.Kod pętla może napisane z threadData->strThread wszędzie "str" jest używany, ale kod byłby znacznie wolniej w realizacji.Najlepiej pamięci podręcznej odwołanie do danych, gdy wystąpią takie odniesienie w pętli.

CThreadLocal Szablonu klasy używa tych samych mechanizmów, CProcessLocal wykonuje i tych samych metod implementacji.

Zobacz też

Inne zasoby

Uwagi techniczne przez liczbę

Uwagi techniczne według kategorii