Udostępnij za pośrednictwem


TN041: migracja z MFC/OLE1 do MFC/OLE 2

[!UWAGA]

Następująca uwaga techniczna nie został zaktualizowana od pierwszego uwzględnienia jej w dokumentacji online.W rezultacie niektóre procedury i tematy może być nieaktualne lub nieprawidłowe.Aby uzyskać najnowsze informacje, zaleca się wyszukać temat w indeksie dokumentacji online.

Ogólne problemy odnoszące się do migracji

Jednym z celów projektu dla klas OLE 2 w MFC 2.5 (i wyższych) było zachować wiele z takiej samej architektury, wprowadzone w MFC 2.0 dla obsługi OLE 1.0.W rezultacie, wiele z tej samej klasy OLE 2.0 MFC nadal istnieją w tej wersji programu MFC (COleDocument, COleServerDoc, COleClientItem, COleServerItem).Ponadto wiele interfejsów API w tych klas są dokładnie takie same.Jednakże OLE 2 drastycznie różni się od OLE 1.0 można oczekiwać, że niektóre szczegóły zostały zmienione.Osoby zaznajomione z obsługą OLE1 MFC 2.0 będziesz czuć się w domu z obsługą 2.0 MFC.

Jeśli są przy istniejącej aplikacji MFC/OLE1 i dodanie do niej funkcjonalność OLE 2, najpierw należy przeczytać tę notatkę.Uwaga ta obejmuje niektóre zagadnienia ogólne mogą wystąpić w trakcie przenoszenia sieci funkcje OLE1 MFC/OLE 2 a następnie omówiono problemy odkryte podczas przenoszenia aplikacji dwóch włączonych MFC 2.0: próbki MFC OLE OCLIENT i HIERSVR.

Ważne jest architektura/widok dokumentu MFC

Jeśli aplikacja nie używa architektury/widok dokumentu MFC i chcesz dodać obsługę OLE 2 do aplikacji, nadszedł czas, aby przejść do widoku/dokumentu.Wiele korzyści klasy OLE 2 MFC firmy tylko są realizowane, gdy aplikacja używa architektury wbudowanych i składniki MFC.

Implementacja serwera lub kontenera bez korzystania z architekturą MFC jest możliwe, ale nie jest zalecane.

Zamiast wykonania MFC własne

Klasy MFC "puszkach wykonania" takie jak CToolBar, CStatusBar, i CScrollView mają wbudowany specjalny kod sprawy obsługi OLE 2.Tak więc jeśli używasz tych klas w aplikacji będziesz korzystać z wysiłku wkłada uświadomić OLE.Ponownie jest możliwość "roll samodzielnego" klasy w tym miejscu w tych celach, ale nie jest zalecane.Jeśli zachodzi potrzeba wdrożenia podobną funkcjonalność, kod źródłowy MFC jest doskonałą odniesienia za zajmowanie się kilka punktów drobniejsze OLE (szczególnie, jeśli chodzi o aktywacji w miejscu).

Sprawdź przykładowy kod MFC

Istnieje pewna liczba próbek MFC, które zawierają funkcje OLE.Każdy z tych aplikacji implementuje OLE z innej perspektywy:

  • HIERSVR przeznaczony głównie do użytku jako aplikacja serwera.Została uwzględniona w MFC 2.0 jako aplikacja MFC/OLE1 i został przeniesiony do 2 MFC/OLE i następnie rozszerzony tak, aby implementuje wiele funkcji OLE dostępne w OLE 2.

  • OCLIENT jest to aplikacji autonomicznych kontenera, służy do wykazania wiele funkcji OLE z punktu widzenia kontenera.Zbyt został on przeniesiony z MFC 2.0, a następnie rozszerzyć w celu obsługi wielu bardziej zaawansowanych funkcji OLE, takich jak formaty niestandardowe Schowka i łącza do elementów osadzonych.

  • DRAWCLI tej aplikacji implementuje obsługę kontenera OLE znacznie jak nie OCLIENT, chyba że robi to w ramach istniejącego programu rysunku zorientowanym obiektowo.Informuje natomiast, jak może zaimplementować obsługę kontenera OLE i zintegrować ją z istniejącej aplikacji.

  • SUPERPAD tej aplikacji, a także jako samodzielną aplikację grzywny, jest również serwerem OLE.Obsługa serwera, który implementuje jest dość stylu.Znaczący jest jak używa usługi Schowka OLE do kopiowania danych do Schowka, ale używa funkcji wbudowanych w kontroli systemu Windows "Edytuj" do implementowania Schowka Wklej.Pokazuje ciekawa mieszanka tradycyjny sposób użycia interfejsu API systemu Windows, a także integrację z nowych OLE interfejsów API.

Aby uzyskać więcej informacji, przykładowych aplikacji zobacz "MFC próbki pomoc".

Analiza przypadku: OCLIENT z MFC 2.0

Jak wspomniano powyżej, OCLIENT została uwzględniona w MFC 2.0 i wdrożone OLE z MFC/OLE1.Poniżej opisano kroki, za pomocą których ta aplikacja początkowo została przekonwertowana na skorzystać z klas MFC/OLE 2.Wiele funkcji dodanych po początkowej port został zakończony, aby lepiej zilustrować klas MFC/OLE.Funkcje te nie będą uwzględnione w tym miejscu; odnoszą się do próbki Aby uzyskać więcej informacji na temat tych zaawansowanych funkcji.

[!UWAGA]

Błędy kompilatora i krok po kroku proces został utworzony z programem Visual C++ 2.0.Określony błąd wiadomości oraz lokalizacje mogły ulec zmianie z Visual C++ 4.0, ale informacje o pojęciach pozostaje ważna.

Wstawanie go i uruchamianie

Podejście do portu próbki OCLIENT MFC/OLE ma się rozpocząć po utworzeniu go i poprawiania błędów kompilatora oczywiste, które spowoduje.Jeśli pobranie próbki OCLIENT z MFC 2.0 i skompiluj go w tej wersji MFC, znajdziesz się, że nie są to wiele błędów, aby rozwiązać.Poniżej opisano błędy w kolejności, w którym miały one miejsce.

Kompilowanie i napraw błędy

\oclient\mainview.cpp(104) : error C2660: 'Draw' : function does not take 4 parameters

Pierwsza z nich dotyczy błąd COleClientItem::Draw.W MFC/OLE1 zajęło więcej parametrów niż wersja biblioteki MFC/OLE ma.Dodatkowe parametry często nie były niezbędne i zwykle NULL (jak w przykładzie).Ta wersja biblioteki MFC automatycznie można określić wartości dla lpWBounds, kiedy ZOZ, który jest przygotowywana jest metaplik DC.Ponadto parametr pFormatDC nie jest już konieczne ponieważ budować jeden z "atrybut DC" kontrolera pDC przekazywane w ramach.Tak aby rozwiązać ten problem, po prostu usuń dwie dodatkowe NULL parametrów wywołania remis.

\oclient\mainview.cpp(273) : error C2065: 'OLE_MAXNAMESIZE' : undeclared identifier
\oclient\mainview.cpp(273) : error C2057: expected constant expression
\oclient\mainview.cpp(280) : error C2664: 'CreateLinkFromClipboard' : cannot convert parameter 1 from 'char [1]' to 'enum ::tagOLERENDER '
\oclient\mainview.cpp(286) : error C2664: 'CreateFromClipboard' : cannot convert parameter 1 from 'char [1]' to 'enum ::tagOLERENDER '
\oclient\mainview.cpp(288) : error C2664: 'CreateStaticFromClipboard' : cannot convert parameter 1 from 'char [1]' to 'enum ::tagOLERENDER '

Błędy powyżej wynikają z faktu, że wszystkie COleClientItem::CreateXXXX funkcje w MFC/OLE1 wymagane, że przekazywane unikatową nazwę reprezentującą dany element.Jest to wymóg podstawowej OLE interfejsu API.Nie jest to konieczne w MFC/OLE 2 ponieważ OLE 2 nie używa DDE jako podstawowy mechanizm komunikacji (nazwa była używana w konwersacji DDE).Aby rozwiązać ten problem, należy usunąć CreateNewName działać jak i wszystkie odwołania do niego.To proste dowiedzieć się, jakie każda funkcja MFC/OLE spodziewa się w tej wersji poprzez umieszczenie kursora na wezwanie i naciśnięcie klawisza F1.

Innego obszaru, który różni się znacząco jest obsługa Schowka OLE 2.Z OLE1 używane Schowka systemu Windows API interakcji ze Schowka.Z OLE 2 jest to z inny mechanizm.Interfejsy API MFC/OLE1 zakłada, że Schowka został otwarty przed skopiowaniem COleClientItem obiektu do Schowka.To nie jest już konieczne i spowoduje, że wszystkie operacje schowka MFC/OLE nie powiedzie się.Podczas edycji kodu w celu usunięcia zależności na CreateNewName, należy także usunąć kod, który otwiera i zamyka Schowka systemu Windows.

\oclient\mainview.cpp(332) : error C2065: 'AfxOleInsertDialog' : undeclared identifier
\oclient\mainview.cpp(332) : error C2064: term does not evaluate to a function
\oclient\mainview.cpp(344) : error C2057: expected constant expression
\oclient\mainview.cpp(347) : error C2039: 'CreateNewObject' : is not a member of 'CRectItem'

Błędy te wynikają z CMainView::OnInsertObject obsługi.Obsługa polecenia "Wstaw nowy obiekt" jest inny obszar gdzie zmieniło się trochę.W tym przypadku najłatwiej jest po prostu scalić oryginalna implementacja z usługami świadczonymi przez AppWizard dla nowej aplikacji kontenera OLE.W rzeczywistości jest to technika, który można stosować do przenoszenia innych aplikacji.W MFC/OLE1, okno dialogowe "Wstaw obiekt" wyświetlony po wywołaniu AfxOleInsertDialog funkcji.W tej wersji można skonstruować COleInsertObject obiektu okno dialogowe i wywołanie DoModal.Ponadto, tworzone są nowe zapasy OLE z CLSID zamiast ciągu classname.Wynik końcowy powinien wyglądać mniej więcej tak

COleInsertDialog dlg;
if (dlg.DoModal() != IDOK)
    return;

BeginWaitCursor();

CRectItem* pItem = NULL;
TRY
{
    // First create the C++ object
    pItem = GetDocument()->CreateItem();
    ASSERT_VALID(pItem);

    // Initialize the item from the dialog data.
    if (!dlg.CreateItem(pItem))
        AfxThrowMemoryException();
           // any exception will do
    ASSERT_VALID(pItem);
        
    // run the object if appropriate
    if (dlg.GetSelectionType() == 
            COleInsertDialog::createNewItem)
        pItem->DoVerb(OLEIVERB_SHOW, this);
        
    // update right away
    pItem->UpdateLink();
    pItem->UpdateItemRectFromServer();
        
    // set selection to newly inserted item
    SetSelection(pItem);
    pItem->Invalidate();
}
CATCH (CException, e)
{  
    // clean up item
    if (pItem != NULL)
        GetDocument()->DeleteItem(pItem);
            
    AfxMessageBox(IDP_FAILED_TO_CREATE);
}
END_CATCH
    
EndWaitCursor();

[!UWAGA]

Wstaw nowy obiekt może być inny dla aplikacji):

Należy również uwzględnić <afxodlgs.h>, który zawiera deklarację dla COleInsertObject okno dialogowe klasy, a także inne standardowe okna dialogowe świadczone przez MFC.

\oclient\mainview.cpp(367) : error C2065: 'OLEVERB_PRIMARY' : undeclared identifier
\oclient\mainview.cpp(367) : error C2660: 'DoVerb' : function does not take 1 parameters

Te błędy są spowodowane przez fakt, że niektóre stałe OLE1 zostały zmienione w OLE 2, mimo że w koncepcji są takie same.W tym przypadku OLEVERB_PRIMARY został zmieniony na OLEIVERB_PRIMARY.W OLE1 i OLE 2 zlecenia głównego jest zazwyczaj wykonywany przez kontener, gdy użytkownik kliknie element.

Dodatkowo DoVerb teraz ma dodatkowy parametr — wskaźnik do widoku (CView*).Ten parametr jest używany wyłącznie do realizacji "Edycja wizualna" (lub aktywacji w miejscu).Teraz ustawiamy tego parametru na wartość NULL, ponieważ ta funkcja nie realizują w tej chwili.

Aby upewnić się, że ramach nigdy nie próbuje w miejscu uaktywnić, należy zastąpić COleClientItem::CanActivate w następujący sposób:

BOOL CRectItem::CanActivate()
{
    return FALSE;
}

\oclient\rectitem.cpp(53) : error C2065: 'GetBounds' : undeclared identifier
\oclient\rectitem.cpp(53) : error C2064: term does not evaluate to a function
\oclient\rectitem.cpp(84) : error C2065: 'SetBounds' : undeclared identifier
\oclient\rectitem.cpp(84) : error C2064: term does not evaluate to a function

W MFC/OLE1 COleClientItem::GetBounds i SetBounds były używane do wyszukiwania i manipulowania w zakresie elementu ( lewej i góry członków były zawsze zero).W MFC/OLE 2 jest to bardziej bezpośrednio obsługiwane przez COleClientItem::GetExtent i SetExtent, które zajmują rozmiar lub CSize w zamian.

Kod dla swojej nowej SetItemRectToServer i wywołania UpdateItemRectFromServer wyglądać następująco:

BOOL CRectItem::UpdateItemRectFromServer()
{
   ASSERT(m_bTrackServerSize);
   CSize size;
   if (!GetExtent(&size))
      return FALSE;    // blank

   // map from HIMETRIC to screen coordinates
   {
      CClientDC screenDC(NULL);
      screenDC.SetMapMode(MM_HIMETRIC);
      screenDC.LPtoDP(&size);
   }
   // just set the item size
   if (m_rect.Size() != size)
   {
      // invalidate the old size/position
      Invalidate();
      m_rect.right = m_rect.left + size.cx;
      m_rect.bottom = m_rect.top + size.cy;
      // as well as the new size/position
      Invalidate();
   }
   return TRUE;
}

BOOL CRectItem::SetItemRectToServer()
{
   // set the official bounds for the embedded item
   CSize size = m_rect.Size();
   {
      CClientDC screenDC(NULL);
      screenDC.SetMapMode(MM_HIMETRIC);
      screenDC.DPtoLP(&size);
   }
   TRY
   {
      SetExtent(size);  // may do a wait
   }
   CATCH(CException, e)
   {
      return FALSE;  // links will not allow SetBounds
   }
   END_CATCH
   return TRUE;
}

\oclient\frame.cpp(50) : error C2039: 'InWaitForRelease' : is not a member of 'COleClientItem'
\oclient\frame.cpp(50) : error C2065: 'InWaitForRelease' : undeclared identifier
\oclient\frame.cpp(50) : error C2064: term does not evaluate to a function

W MFC/OLE1 API synchroniczne były wywołania z kontenera z serwerem symulowanego, ponieważ OLE1 był asynchroniczny z natury w wielu przypadkach.Konieczne było sprawdzić wywołanie asynchroniczne o wybitnych, w toku, przed rozpoczęciem przetwarzania polecenia od użytkownika.Świadczone usługi MFC/OLE1 COleClientItem::InWaitForRelease funkcji, w tym zakresie.W 2 MFC/OLE nie jest to konieczne, dzięki czemu można usunąć wszystkie razem zastępująca OnCommand w CMainFrame.

W tym momencie OCLIENT kompilowania i łączenia.

Inne niezbędne zmiany

Istnieje kilka rzeczy, które nie są wykonywane, które będą na bieżąco OCLIENT z systemem, jednak.To lepiej naprawić te problemy teraz zamiast później.

Po pierwsze należy zainicjować bibliotek OLE.Jest to realizowane przez wywołanie AfxOleInit z InitInstance:

if (!AfxOleInit())
{
  AfxMessageBox("Failed to initialize OLE libraries");
  return FALSE;
}

To jest również sprawdzić funkcje wirtualnych dla parametru Lista zmian.Jednej z takich funkcji jest COleClientItem::OnChange, przesłonięte w każdej aplikacji kontenera MFC/OLE.Patrząc na pomoc online, zobaczysz, że dodatkowe "DWORD dwParam" została dodana.Nowy CRectItem::OnChange wygląda następująco:

void 
CRectItem::OnChange(OLE_NOTIFICATION wNotification, DWORD dwParam)
{
  if (m_bTrackServerSize &&
        !UpdateItemRectFromServer())
  {
    // Blank object
    if (wNotification == OLE_CLOSED)
    {
      // no data received for the object - destroy it
      ASSERT(!IsVisible());
      GetDocument()->DeleteItem(this);
      return;   // no update (item is gone now)
    }
  }
  if (wNotification != OLE_CLOSED)
      Dirty();
  Invalidate();  // any change will cause a redraw
}

MFC/OLE1 aplikacji kontenera pochodna klasy dokumentu z COleClientDoc.W MFC/OLE 2 tej klasy zostały usunięte i zastąpione przez COleDocument (tej nowej organizacji ułatwia tworzenie aplikacji kontenera/server).Istnieje #define , który mapuje COleClientDoc do COleDocument celem ułatwienia przenoszenia aplikacji MFC/OLE1 2 MFC/OLE, takich jak OCLIENT.Jedną z cech nie dostarczonych przez COleDocument który został dostarczony przez COleClientDoc jest komunikat standardowe polecenia wpisów map.Jest to zrobione przez aplikacje serwera, które także wykorzystują COleDocument (bezpośrednio), nie pociąga za sobą obciążenie związane z tych programów obsługi polecenia chyba, że są one aplikację kontenera/serwer.Należy dodać następujące wpisy do mapy wiadomość CMainDoc:

ON_UPDATE_COMMAND_UI(ID_EDIT_PASTE, OnUpdatePasteMenu)
ON_UPDATE_COMMAND_UI(ID_EDIT_PASTE_LINK, OnUpdatePasteLinkMenu)
ON_UPDATE_COMMAND_UI(ID_OLE_EDIT_LINKS, OnUpdateEditLinksMenu)
ON_COMMAND(ID_OLE_EDIT_LINKS, COleDocument::OnEditLinks)
ON_UPDATE_COMMAND_UI(ID_OLE_VERB_FIRST, OnUpdateObjectVerbMenu)
ON_UPDATE_COMMAND_UI(ID_OLE_EDIT_CONVERT, OnUpdateObjectVerbMenu)
ON_COMMAND(ID_OLE_EDIT_CONVERT, OnEditConvert)

Wdrożenie wszystkich tych poleceń znajduje się w COleDocument, który jest klasą podstawową dla dokumentu.

OCLIENT jest w tym momencie funkcjonalności aplikacji kontenerowej OLE.Istnieje możliwość wstawiania elementów dowolnego typu (OLE1 lub OLE 2).Ponieważ potrzebny kod, aby umożliwić aktywację na miejscu nie jest zaimplementowane, elementy są edytowane w oddzielnym oknie, podobnie jak przy OLE1.Następna sekcja omawia odpowiednich zmian w celu umożliwienia edycji na miejscu (czasami nazywane "Edycja wizualna").

Dodanie "Edycja wizualna"

Jedną z najbardziej interesujących funkcji OLE jest aktywacja w miejscu (lub "Edycja wizualna").Ta funkcja umożliwia aplikacji serwera do przejęcia części interfejsu użytkownika kontenera dostarczyć bardziej płynne interfejs edytowania dla użytkownika.Do wykonania aktywacji w miejscu do OCLIENT, niektóre specjalne zasoby muszą zostać dodane, a także niektóre dodatkowy kod.Te zasoby i kod są normalnie dostarczane przez AppWizard — w rzeczywistości wiele tutaj kod został wypożyczony bezpośrednio z aplikacją AppWizard świeżego z obsługą "Kontener".

Po pierwsze jest konieczne dodanie zasobu menu ma być używany podczas znajduje się zapas, który jest aktywny w miejscu.Kopiowanie zasobów IDR_OCLITYPE i usuwając wszystkie oprócz pliku i okno wyskakujące okienka można utworzyć tego zasobu dodatkowe menu w programie Visual C++.Dwa słupki separator zostają wstawione między pliku i okno wyskakujące okienka do wskazania separacji grup (powinien wyglądać: plik || Okno).Aby uzyskać więcej informacji na te separatory oznacza i jak są scalane menu serwera i kontener zobacz "Menu i zasoby: Menu połączenia" w Klasy 2 OLE.

Po tych menu utworzone, należy powiadomić framework wiedzieć o nich.Jest to realizowane przez wywołanie CDocTemplate::SetContainerInfo w szablonie dokumentu przed dodaniem go do listy szablonów dokumentów w sieci InitInstance.Nowy kod, aby zarejestrować szablon dokumentu wygląda następująco:

CDocTemplate* pTemplate = new CMultiDocTemplate(
    IDR_OLECLITYPE,
    RUNTIME_CLASS(CMainDoc),
    RUNTIME_CLASS(CMDIChildWnd),    // standard MDI child frame
    RUNTIME_CLASS(CMainView));
pTemplate->SetContainerInfo(IDR_OLECLITYPE_INPLACE);
AddDocTemplate(pTemplate);

Zasób IDR_OLECLITYPE_INPLACE jest zasób w miejscu specjalne utworzone w programie Visual C++.

Aby umożliwić aktywację na miejscu, istnieje kilka rzeczy, które trzeba zmienić zarówno w CView klas pochodnych (CMainView), jak również COleClientItem klasy (CRectItem).Wszystkie te zastąpienia są dostarczane przez AppWizard i większości implementacji będą pochodzić bezpośrednio z domyślnej aplikacji AppWizard.

W pierwszym kroku tego portu, aktywacja w miejscu został wyłączony całkowicie przez zastąpienie COleClientItem::CanActivate.Aby umożliwić aktywację na miejscu powinny być usunięte ręcznej zmianie.Ponadto wartości NULL przekazano do wszystkich wywołań do DoVerb (istnieją dwa z nich), ponieważ zapewnia widok był tylko niezbędne do aktywacji w miejscu.Do pełnego wdrożenia aktywację na miejscu, jest niezbędne do przekazania odpowiednim widokiem w DoVerb zadzwonić.Jeden z tych połączeń jest w CMainView::OnInsertObject:

pItem->DoVerb(OLEIVERB_SHOW, this);

Inny jest w CMainView::OnLButtonDblClk:

m_pSelection->DoVerb(OLEIVERB_PRIMARY, this);

Należy zastąpić COleClientItem::OnGetItemPosition.To informuje serwer gdzie umieścić jego okna względem okna kontenera, gdy element jest aktywowany w miejscu.W przypadku OCLIENT wykonania jest trivial:

void CRectItem::OnGetItemPosition(CRect& rPosition)
{
    rPosition = m_rect;
}

Większość serwerów także implementować tak zwany "w miejscu zmiana rozmiaru." Pozwala to okno serwer o rozmiarze i przesuwa się, gdy użytkownik jest edytowany element.Kontener musi uczestniczyć w tej akcji, ponieważ przenoszenia lub zmiany rozmiaru okna, zazwyczaj wpływa na położenie i rozmiar w samym dokumencie kontenera.Wykonania na OCLIENT synchronizuje wewnętrznego prostokąta utrzymywane przez m_rect z nową pozycję i rozmiar.

BOOL CRectItem::OnChangeItemPosition(const CRect& rectPos)
{
    ASSERT_VALID(this);

    if (!COleClientItem::OnChangeItemPosition(rectPos))
        return FALSE;

    Invalidate();
    m_rect = rectPos;
    Invalidate();
    GetDocument()->SetModifiedFlag();

    return TRUE;
}

W tym momencie ma wystarczającej ilości kodu, aby umożliwić elementu być uaktywniony w miejscu i radzić sobie z rozmiary i położenie elementu, gdy jest aktywny, ale kod nie pozwoli użytkownikowi zakończyć sesji edycyjnej.Chociaż niektóre serwery będą tę funkcję udostępnia się poprzez obsługę klawisz escape, proponuje się pojemniki zdezaktywować elementu na dwa sposoby: (1) przez kliknięcie poza towaru i (2) naciskając klawisz ESC.

Klawisz ESCAPE dodawania akceleratora w programie Visual C++, mapująca klucz VK_ESCAPE do polecenia, ID_CANCEL_EDIT dodaje się do zasobów.Obsługa dla tego polecenia są następujące:

// The following command handler provides the standard
// keyboard user interface to cancel an in-place
// editing session.void CMainView::OnCancelEdit()
{
    // Close any in-place active item on this view.
    COleClientItem* pActiveItem = 
        GetDocument()->GetInPlaceActiveItem(this);
    if (pActiveItem != NULL)
        pActiveItem->Close();
    ASSERT(GetDocument()->GetInPlaceActiveItem(this) == NULL);
}

Do obsługi w przypadku, gdy użytkownik kliknie poza elementu, Dodaj następujący kod do początku CMainView::SetSelection:

if (pNewSel != m_pSelection || pNewSel == NULL)
{
    COleClientItem* pActiveItem = 
        GetDocument()->GetInPlaceActiveItem(this);
    if (pActiveItem != NULL && pActiveItem != pNewSel)
        pActiveItem->Close();
}
    

Gdy element jest aktywny w miejscu, powinien mieć fokus.Aby upewnić się, w przypadku obsługi OnSetFocus tak, aby fokus zawsze jest przenoszona do aktywnego elementu podczas widoku otrzymuje fokus:

// Special handling of OnSetFocus and OnSize are required 
// when an object is being edited in-place.
void CMainView::OnSetFocus(CWnd* pOldWnd)
{
    COleClientItem* pActiveItem = 
        GetDocument()->GetInPlaceActiveItem(this);
    if (pActiveItem != NULL &&
    pActiveItem->GetItemState() == COleClientItem::activeUIState)
    {
        // need to set focus to this item if it is same view
        CWnd* pWnd = pActiveItem->GetInPlaceWindow();
        if (pWnd != NULL)
        {
            pWnd->SetFocus();  // don't call the base class
            return;
        }
    }

    CView::OnSetFocus(pOldWnd);
}

Gdy zmieniany jest rozmiar widoku, należy powiadomić aktywnego elementu, który uległ zmianie prostokąta przycinania.W tym zapewnić obsługi dla OnSize:

void CMainView::OnSize(UINT nType, int cx, int cy)
{
    CView::OnSize(nType, cx, cy);
    COleClientItem* pActiveItem = 
        GetDocument()->GetInPlaceActiveItem(this);
    if (pActiveItem != NULL)
        pActiveItem->SetItemRects();
}

Analiza przypadku: HIERSVR z MFC 2.0

HIERSVR została również uwzględniona w MFC 2.0 i wdrożone OLE z MFC/OLE1.Uwaga ta krótko opisano kroki, według których ta aplikacja początkowo została przekonwertowana na skorzystać z klas MFC/OLE 2.Wiele funkcji dodanych po początkowej port został zakończony, aby lepiej zilustrować klas MFC/OLE 2.Funkcje te nie będą uwzględnione w tym miejscu; odnoszą się do próbki Aby uzyskać więcej informacji na temat tych zaawansowanych funkcji.

[!UWAGA]

Błędy kompilatora i krok po kroku proces został utworzony z programem Visual C++ 2.0.Określony błąd wiadomości oraz lokalizacje mogły ulec zmianie z Visual C++ 4.0, ale informacje o pojęciach pozostaje ważna.

Wstawanie go i uruchamianie

Podejście do portu próbki HIERSVR MFC/OLE ma się rozpocząć po utworzeniu go i poprawiania błędów kompilatora oczywiste, które spowoduje.Jeśli wziąć próbki HIERSVR z MFC 2.0 i skompiluj go w tej wersji MFC, znajdziesz się, że nie ma zbyt wiele błędów, aby rozwiązać (chociaż istnieją więcej niż z próbki OCLIENT).Błędy w kolejności, w której występują zwykle są opisane poniżej.

Kompilowanie i napraw błędy

\hiersvr\hiersvr.cpp(83) : error C2039: 'RunEmbedded' : is not a member of 'COleTemplateServer'

Ten pierwszy błąd wskazuje dużo większy problem z InitInstance funkcja dla serwerów.Inicjowanie wymagane dla serwera OLE prawdopodobnie jest jednym z największych zmian, które trzeba będzie wprowadzać w aplikacji MFC/OLE1 go do jazdy.Najlepszym rozwiązaniem jest spojrzeć na AppWizard tworzy dla serwera OLE i modyfikować kod w stosownych przypadkach.Oto kilka punktów należy pamiętać:

Należy zainicjować bibliotek OLE przez wywołanie AfxOleInit

Wywołanie obiektu szablonu dokumentu ustawić dojścia zasobów serwera i informacji klasy runtime, nie można ustawić z SetServerInfo CDocTemplate Konstruktor.

Nie pokazuj w głównym oknie aplikacji, jeśli osadzanie jest obecny w wierszu polecenia.

Musisz identyfikatora GUID dla dokumentu.Jest to identyfikator unikatowy dla typu dokumentu (128 bitów).AppWizard utworzy dla Ciebie — tak więc jeśli używasz techniki opisane tutaj kopiowania nowego kodu z nową aplikację serwera AppWizard generowane, można po prostu "wykraść" identyfikatora GUID z tej aplikacji.Jeśli tak nie jest, można użyć GUIDGEN.Narzędzie EXE w katalogu BIN.

Zachodzi konieczność "connect" z COleTemplateServer obiektu w szablonie dokumentu przez wywołanie COleTemplateServer::ConnectTemplate.

Aktualizacja rejestru systemu po uruchomieniu aplikacji autonomicznych.Dzięki temu, jeśli użytkownik przenosi.EXE aplikacji, uruchomienie go w nowym miejscu zaktualizuje bazy danych rejestracji systemu Windows wskaż nową lokalizację.

Po zastosowaniu wszystkich tych zmian na AppWizard tworzy na podstawie InitInstance, InitInstance (i związane z identyfikatorem GUID) dla HIERSVR otrzymuje brzmienie:

// this is the GUID for HIERSVR documents
static const GUID BASED_CODE clsid =
    { 0xA0A16360L, 0xC19B, 0x101A, { 0x8C, 0xE5, 0x00, 0xDD, 0x01, 0x11, 0x3F, 0x12 } };
    
/////////////////////////////////////////////////////////////////////////////
// COLEServerApp initialization

BOOL COLEServerApp::InitInstance()
{
    // OLE 2 initialization
    if (!AfxOleInit())
    {
        AfxMessageBox("Initialization of the OLE failed!");
        return FALSE;
    }

    // Standard initialization
    LoadStdProfileSettings(); // Load standard INI file options 

    // Register document templates
    CDocTemplate* pDocTemplate;
    pDocTemplate = new CMultiDocTemplate(IDR_HIERSVRTYPE,
        RUNTIME_CLASS(CServerDoc),   
        RUNTIME_CLASS(CMDIChildWnd),
        RUNTIME_CLASS(CServerView));
    pDocTemplate->SetServerInfo(IDR_HIERSVRTYPE_SRVR_EMB);
    AddDocTemplate(pDocTemplate);

    // create main MDI Frame window
    CMainFrame* pMainFrame = new CMainFrame;
    if (!pMainFrame->LoadFrame(IDR_MAINFRAME))
        return FALSE;
    m_pMainWnd = pMainFrame;

    SetDialogBkColor();   // gray look

    // enable file manager drag/drop and DDE Execute open
    m_pMainWnd->DragAcceptFiles();
    EnableShellOpen();
    
    m_server.ConnectTemplate(clsid, pDocTemplate, FALSE);
    COleTemplateServer::RegisterAll();

    // try to launch as an OLE server
    if (RunEmbedded())
    {
        // "short-circuit" initialization -- run as server!
        return TRUE;
    }
    m_server.UpdateRegistry();
    RegisterShellFileTypes();

    // not run as OLE server, so show the main window
    if (m_lpCmdLine[0] == '\0')
    {
        // create a new (empty) document
        OnFileNew();
    }
    else
    {
        // open an existing document
        OpenDocumentFile(m_lpCmdLine);
    }

    pMainFrame->ShowWindow(m_nCmdShow);
    pMainFrame->UpdateWindow();
    
    return TRUE;
}

Można zauważyć, że powyższy kod odnosi się do nowego Identyfikatora zasobu, IDR_HIERSVRTYPE_SRVR_EMB.Jest to zasób menu do użycia podczas edycji dokumentu, który jest osadzony w innym kontenerze.W MFC/OLE1 szczególne osadzony element do edycji elementów menu zostały zmienione w locie.Przy użyciu struktury menu zupełnie inny podczas edytowania osadzonego elementu zamiast edytować dokument oparty na pliku w znacznie ułatwia przewidują te dwa różne tryby różnych interfejsów użytkownika.Jak pojawi się później, zasób całkowicie oddzielone menu jest używana podczas edycji obiektu osadzonego w miejscu.

Aby utworzyć ten zasób, załadować zasobu skryptu Visual C++ i skopiować istniejący zasób IDR_HIERSVRTYPE w menu.Zmień nazwę nowego zasobu na IDR_HIERSVRTYPE_SRVR_EMB (jest to sam Konwencja nazewnicza, która używa AppWizard).Następna zmiana "Zapisz plik" do "Aktualizacja pliku"; Nadaj mu identyfikator polecenia ID_FILE_UPDATE.Również zmienić "Pliku Zapisz jako" na "Pliku Zapisz kopię jako"; Nadaj mu identyfikator polecenia ID_FILE_SAVE_COPY_AS.Środowisko framework zapewnia wykonania obu tych poleceń.

\hiersvr\svritem.h(60) : error C2433: 'OLESTATUS' : 'virtual' not permitted on data declarations
\hiersvr\svritem.h(60) : error C2501: 'OLESTATUS' : missing decl-specifiers
\hiersvr\svritem.h(60) : error C2146: syntax error : missing ';' before identifier 'OnSetData'
\hiersvr\svritem.h(60) : error C2061: syntax error : identifier 'OLECLIPFORMAT'
\hiersvr\svritem.h(60) : error C2501: 'OnSetData' : missing decl-specifiers

Istnieje wiele błędów wynikających z zastąpienia z OnSetData, ponieważ odwołuje się do OLESTATUS typu.OLESTATUS sposobem OLE1 zwrócone błędy.Ta została zmieniona na HRESULT 2 OLE, chociaż zazwyczaj konwertuje MFC HRESULT do COleException zawierające błąd.W tym szczególnym przypadku ręcznej zmiany z OnSetData nie jest już konieczne, najprostszym rozwiązaniem jest, aby ją usunąć.

\hiersvr\svritem.cpp(30) : error C2660: 'COleServerItem::COleServerItem' : function does not take 1 parameters

COleServerItem Konstruktor ma dodatkowy parametr "BOOL".Flaga ta określa, jak zarządzanie pamięcią odbywa się na COleServerItem obiektów.Przez ustawienie TRUE, ramach obsługuje zarządzanie pamięcią tych obiektów — ich usuwania, gdy nie są już niezbędne.Korzysta z HIERSVR CServerItem (pochodzące z COleServerItem) obiektów jako część jego danych w trybie macierzystym, więc ta flaga będzie równa FAŁSZ.Dzięki temu HIERSVR ustalić, kiedy element każdego serwera jest usuwany.

\hiersvr\svritem.cpp(44) : error C2259: 'CServerItem' : illegal attempt to instantiate abstract class
\hiersvr\svritem.cpp(44) : error C2259: 'CServerItem' : illegal attempt to instantiate abstract class

Jak oznaczać te błędy, istnieje "czysty wirtualny" funkcje, które nie zostały zastąpione w CServerItem.Najprawdopodobniej jest to spowodowane faktem, że zmienił się lista parametrów w OnDraw.Aby naprawić ten błąd, należy zmienić CServerItem::OnDraw w następujący sposób (jak również deklaracja w svritem.h):

BOOL CServerItem::OnDraw(CDC* pDC, CSize& rSize)
{
    // request from OLE to draw node
    pDC->SetMapMode(MM_TEXT); // always in pixels
    return DoDraw(pDC, CPoint(0,0), FALSE);
}

Nowy parametr jest "rSize".Dzięki temu można wypełnić rozmiar rysunku, jeśli jest to wygodne.Ten rozmiar musi znajdować się w HIMETRIC.W tym przypadku to nie jest wygodne wypełnienie tej wartości, więc wywołuje w ramach OnGetExtent do pobrania w zakresie.Za to do pracy, musisz zaimplementować OnGetExtent:

BOOL CServerItem::OnGetExtent(DVASPECT dwDrawAspect, CSize& rSize)
{
    if (dwDrawAspect != DVASPECT_CONTENT)
        return COleServerItem::OnGetExtent(dwDrawAspect, rSize);
        
    rSize = CalcNodeSize();
    return TRUE;
}

\hiersvr\svritem.cpp(104) : error C2065: 'm_rectBounds' : undeclared identifier
\hiersvr\svritem.cpp(104) : error C2228: left of '.SetRect' must have class/struct/union type
\hiersvr\svritem.cpp(106) : error C2664: 'void __pascal __far DPtoLP(struct ::tagPOINT __far *,int )__far const ' : cannot convert parameter 1 from 'int __far *' to 'struct ::tagPOINT __far *'

W funkcji CServerItem::CalcNodeSize rozmiar towaru jest konwertowany na HIMETRIC i przechowywane w m_rectBounds.Bez dokumentów "m_rectBounds" członek COleServerItem nie istnieje (został częściowo zastąpiony przez m_sizeExtent, ale w OLE 2 ten element członkowski ma nieco inny sposób użycia niż m_rectBounds w OLE1).Zamiast ustawienie HIMETRIC rozmiar do tej zmiennej Członkowskie wrócisz go.Zwrócona wartość jest używana w OnGetExtent, uprzednio wdrożone.

CSize CServerItem::CalcNodeSize()
{
    CClientDC dcScreen(NULL);

    m_sizeNode = dcScreen.GetTextExtent(m_strDescription,
      m_strDescription.GetLength());
    m_sizeNode += CSize(CX_INSET * 2, CY_INSET * 2);

    // set suggested HIMETRIC size
    CSize size(m_sizeNode.cx, m_sizeNode.cy);
    dcScreen.SetMapMode(MM_HIMETRIC);
    dcScreen.DPtoLP(&size);
    return size;
}

Zastępuje również CServerItem COleServerItem::OnGetTextData.Ta funkcja jest przestarzała w MFC/OLE i jest zastąpiony przez inny mechanizm.Wersja MFC 3.0 próbki MFC OLE HIERSVR implementuje tej funkcji przez zastąpienie COleServerItem::OnRenderFileData.Ta funkcja nie jest ważne dla tego portu podstawowego, więc można usunąć OnGetTextData override.

Istnieje wiele więcej błędów w svritem.cpp, które nie zostały rozwiązane.Nie są one "rzeczywiste" błędy — tylko błędy spowodowane przez poprzednie błędy.

\hiersvr\svrview.cpp(325) : error C2660: 'CopyToClipboard' : function does not take 2 parameters

COleServerItem::CopyToClipboardnie obsługuje już flagi "bIncludeNative".Rodzimych (dane zapisywane przez funkcję seryjne elementu serwera) jest zawsze kopiowany, więc usunąć pierwszy parametr.Dodatkowo CopyToClipboard spowoduje zgłoszenie wyjątku, po wystąpieniu błędu zamiast zwracać wartość FALSE.Zmień kod dla CServerView::OnEditCopy w następujący sposób:

void CServerView::OnEditCopy()
{
    if (m_pSelectedNode == NULL)
        AfxThrowNotSupportedException();
        
    TRY
    {
        m_pSelectedNode->CopyToClipboard(TRUE);
    }
    CATCH_ALL(e)
    {
        AfxMessageBox("Copy to clipboard failed");
    }
    END_CATCH_ALL   
}

Mimo, że było więcej błędów wynikających z kompilacją wersji MFC 2.0 HIERSVR niż było z tej samej wersji OCLIENT, było faktycznie mniejszą liczbę zmian.

W tym momencie HIERSVR będzie skompilować i połączyć i działać jako serwer OLE, ale bez funkcji edycji na miejscu, która realizuje się dalej.

Dodanie "Edycja wizualna"

Aby dodać do tej aplikacji serwera "Edycja wizualna" (lub aktywacji w miejscu), istnieje tylko kilka rzeczy, które trzeba zadbać o:

  • Musisz zasobów specjalne menu gdy element jest aktywny w miejscu.

  • Ta aplikacja ma pasku narzędzi, więc musisz pasek narzędzi o tylko podzbiór normalnych narzędzi, aby dopasować poleceń menu dostępne z serwera (pasuje do zasobu menu wymienionych powyżej).

  • Potrzebujesz nową klasę pochodną COleIPFrameWnd , udostępniająca interfejs użytkownika w miejscu (podobnie jak CMainFrame, pochodne CMDIFrameWnd, udostępnia interfejs użytkownika MDI).

  • Musisz powiedzieć ramach o tych specjalnych zasobów i klas.

Zasób menu jest łatwo stworzyć.Uruchom Visual C++, Kopiuj zasób menu IDR_HIERSVRTYPE do zasobu menu o nazwie IDR_HIERSVRTYPE_SRVR_IP.Modyfikowanie menu tak, że tylko lud menu Edycja i pomoc w lewo.Dodać dwa separatory do menu jednocześnie w menu Edycja i pomocy (powinien wyglądać: Edycja || Pomoc).Aby uzyskać więcej informacji na te separatory oznacza i jak są scalane menu serwera i kontener, zobacz "Menu i zasoby: Menu połączenia" w Klasy 2 OLE.

Mapa bitowa w pasku narzędzi podzbioru może być łatwo tworzone przez skopiowanie jednej z aplikacji świeżego AppWizard generowane przy zaznaczeniu opcji "Serwer".Następnie można zaimportować tę mapę bitową do programu Visual C++.Należy podać mapy bitowej Identyfikatora IDR_HIERSVRTYPE_SRVR_IP.

Klasa pochodna od COleIPFrameWnd mogą być kopiowane z aplikacji AppWizard generowane z także obsługę serwera.Skopiuj oba pliki IPFRAME.CPP i IPFRAME.H i dodać je do projektu.Upewnij się, że LoadBitmap wywołanie odwołuje się do IDR_HIERSVRTYPE_SRVR_IP, bitmapy, utworzony w poprzednim kroku.

Teraz, gdy są tworzone nowe zasoby i klas, dodać wymagany kod, tak aby ramach wie o tych (i wie, że teraz ta aplikacja obsługuje edycję w miejscu).Jest to realizowane przez dodanie więcej parametry, aby SetServerInfo wywołania w InitInstance funkcji:

pDocTemplate->SetServerInfo(IDR_HIERSVRTYPE_SRVR_EMB,
    IDR_HIERSVRTYPE_SRVR_IP, RUNTIME_CLASS(CInPlaceFrame));

Jest teraz gotowy do uruchomienia w miejscu w jakimkolwiek pojemniku, który też obsługuje aktywację na miejscu.Jednak jest jeden drobny błąd ukryte w kodzie.HIERSVR obsługuje menu kontekstowe wyświetlane, gdy użytkownik naciśnie przycisk prawym przyciskiem myszy.W tym menu działa, gdy HIERSVR jest w pełni otwarty, ale nie działa podczas edytowania osadzania w miejscu.Dlatego można przypinać do tej jednej linii kodu w CServerView::OnRButtonDown w dół:

pMenu->TrackPopupMenu(TPM_CENTERALIGN | TPM_RIGHTBUTTON,
    point.x, point.y, AfxGetApp()->m_pMainWnd);

Należy zauważyć, odniesienie do AfxGetApp() ->m_pMainWnd.Kiedy serwer jest aktywowana w miejscu, posiada okno główne i m_pMainWnd jest ustawiona, ale jest zazwyczaj niewidoczny.Ponadto, okno to odnosi się do główne okna aplikacji, okna MDI ramkę, która pojawia się, gdy serwer jest w pełni otworzyć lub uruchomić autonomicznych.Nie odnosi się do okna aktywnej ramki — gdy w miejscu aktywowany czyli ramki okna pochodzące z COleIPFrameWnd.Aby uzyskać poprawne aktywnego okna, nawet wtedy, gdy w miejscu edycji, ta wersja biblioteki MFC dodaje nową funkcję, AfxGetMainWnd.Ogólnie rzecz biorąc, należy użyć tej funkcji, a nie AfxGetApp() ->m_pMainWnd.Ten kod musi się zmienić w następujący sposób:

pMenu->TrackPopupMenu(TPM_CENTERALIGN | TPM_RIGHTBUTTON,
    point.x, point.y, AfxGetMainWnd());

Teraz masz minimalnie włączone dla aktywacji w miejscu funkcjonalności serwera OLE.Ale nadal są dostępne z 2 MFC/OLE, które nie były dostępne w MFC/OLE1 wiele funkcji.Zobacz przykładowe HIERSVR więcej pomysłów na temat funkcji, które można wdrożyć.Poniżej wymieniono niektóre funkcje, które implementuje w HIERSVR:

  • Powiększanie za prawdziwe zachowanie WYSISYG w odniesieniu do kontenera.

  • Przeciągnij / upuszczania i format schowka niestandardowych.

  • Przewijanie okna kontenera, jak zaznaczenie zostanie zmieniona.

Próbki HIERSVR w MFC 3.0 używa również nieco inny projekt dla jego elementów serwera.Pomaga zachować więcej wolnej pamięci i sprawia, że łącza jest bardziej elastyczne.W wersji 2.0 programu HIERSVR każdy węzeł w drzewie jest aCOleServerItemCOleServerItemprowadzi się nieco większe obciążenie niż przemieszczenie ściśle niezbędne dla każdego z tych węzłów, ale COleServerItem jest wymagane dla każdego aktywnego łącza.Ale w większości są bardzo niewielu łącza aktywne w danej chwili.Aby było to bardziej skuteczne, HIERSVR w tej wersji programu MFC oddziela węzła z COleServerItem.Ma on zarówno CServerNode i CServerItem klasy.CServerItem (pochodzące z COleServerItem) jest tylko w razie potrzeby utworzone.Gdy kontener (lub kontenery) należy zaprzestać używania danym link do dany węzeł, skreśla się obiekt CServerItem skojarzony z CServerNode.Ten projekt jest bardziej wydajne i bardziej elastyczne.Jego elastyczność pochodzi kontaktach z wieloma łączami zaznaczenia.Żadna z tych dwóch wersji HIERSVR obsługuje wybór wielokrotny, ale byłoby znacznie łatwiejsze, aby dodać (i obsługuje łączy do takich zaznaczeń) z wersji MFC 3.0 HIERSVR, ponieważ COleServerItem jest oddzielona od danych w trybie macierzystym.

Zobacz też

Inne zasoby

Uwagi techniczne według numerów

Uwagi techniczne według kategorii