Udostępnij za pośrednictwem


TN041: MFC/OLE1 migracji do MFC/OLE 2

[!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.

Kwestie ogólne odnoszące się do migracji

Jednym z celów projektu dla klasy OLE 2 w MFC 2.5 (i wyższych) było zachować wiele takiej samej architekturze wprowadzone w celu wsparcia OLE 1.0 2.0 MFC.W efekcie, wiele z tej samej klasy OLE 2.0 MFC nadal istnieją w tej wersji 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 tak można się spodziewać, że niektóre szczegóły uległy zmianie.Osoby zaznajomione z obsługą OLE1 MFC 2.0 można będzie zapoznania się z 2.0 wsparcie MFC firmy.

Jeśli biorąc istniejących aplikacji MFC/OLE1 i dodanie funkcji OLE 2 najpierw należy przeczytać tę notatkę.Uwaga ta obejmuje niektóre zagadnienia ogólne mogą wystąpić podczas przenoszenia swoje funkcje OLE1 2 MFC/OLE i omówiono problemy nie wykryte podczas przenoszenia aplikacji dwóch zawarte w 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 firmy i chcesz dodać obsługę OLE 2 do aplikacji, teraz jest czas, aby przejść do widoku/dokumentu.Wiele korzyści klasy OLE 2 MFC firmy tylko są realizowane, gdy aplikacja korzysta z architektury wbudowane i składniki MFC.

Wdrażania serwera lub kontenera bez używania architektury MFC jest możliwe, ale nie jest zalecana.

Zamiast własnego użyć implementacji MFC

Klas MFC "puszkach wykonania", takich jak CToolBar, CStatusBar, i CScrollView mają wbudowany specjalny kod sprawa obsługę OLE 2.Tak Jeśli mogą korzystać z tych klas w aplikacji warto skorzystać z wysiłku wprowadzane do nich uświadomić sobie OLE.Ponownie jest możliwe "roll samodzielnego skręcania" klas w tym miejscu w tych celach, ale nie proponuje się.Jeśli trzeba zaimplementować podobną funkcjonalność, kod źródłowy MFC jest doskonałym odniesienia, za zajmowanie się niektóre punkty drobniejsze OLE (szczególnie, gdy chodzi o aktywacji w miejscu).

Sprawdzić kod przykładowy MFC

Istnieje wiele próbek MFC, które zawierają funkcje OLE.Każdy z tych aplikacji implementuje OLE z inną kąt:

  • HIERSVR przeznaczony głównie do użycia jako aplikacja serwera.Została uwzględniona w 2.0 MFC jako aplikacja MFC/OLE1 i został na 2 MFC/OLE i następnie rozszerzone, takie, że implementuje OLE 2 wiele funkcji OLE.

  • OCLIENT jest aplikacja kontenera autonomicznych, przeznaczona do wykazania wiele funkcji OLE z punktu widzenia kontenera.On zbyt została przeniesiona z MFC 2.0, a następnie rozszerzone do obsługi wielu bardziej zaawansowane funkcje OLE, takich jak formaty niestandardowe Schowka i łącza do elementów osadzonych.

  • DRAWCLI aplikacja implementuje wsparcia kontenera OLE znacznie tak jak robi to OCLIENT, chyba że robi to w ramach istniejącego zorientowane obiektowo rysunku programu.Wskazuje sposób może być implementują obsługę kontenera OLE i zintegrować istniejącej aplikacji.

  • SUPERPAD tej aplikacji, jak również fine aplikacji autonomicznych, jest również serwerem OLE.Obsługa serwera, który implementuje jest całkiem minimalistyczny.Szczególnie interesujące jest jak używa usługi OLE w Schowku do kopiowania danych do Schowka, ale używa funkcji wbudowanych w kontroli systemu Windows "edit" do Schowka Wklej funkcję.Pokazuje to interesujące mix, tradycyjne użycia interfejsu API systemu Windows, jak również integracji z nowych OLE API.

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

Analiza przypadku: OCLIENT z MFC 2.0

Jak powiedziano wcześniej, OCLIENT została uwzględniona w MFC 2.0 i wdrożone OLE z MFC/OLE1.Poniżej opisano kroki, które początkowo konwertowane tej aplikacji do używania klas MFC/OLE 2.Liczba funkcje zostały dodane po port początkowy została ukończona, aby lepiej zilustrować klas MFC/OLE.Funkcje te nie zostaną objęte odnoszą się do samego próbki więcej informacji na temat tych zaawansowanych funkcji.

[!UWAGA]

Visual C++ 2.0 został utworzony proces krok po kroku i błędy kompilatora.Visual C++ 4.0 mógł zmienić wiadomości błędu i lokalizacji, ale informacje koncepcyjne pozostają ważne.

Pobieranie go i z systemem

Podejście do portu OCLIENT próbki MFC/OLE jest rozpoczęcie konstruowania i ustalania błędy kompilatora oczywiste, które powodują.Jeśli pobranie próbki OCLIENT z MFC 2.0 i skompiluj go w tej wersji MFC, przekonasz się, że nie istnieją że wiele błędów, aby rozwiązać.Poniżej opisano błędy w kolejności ich wystąpienia.

Kompilacji i napraw błędy

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

Pierwszy dotyczy błąd COleClientItem::Draw.MFC/OLE1 zajęło więcej parametrów niż wersja MFC/OLE ma.Dodatkowe parametry często nie były konieczne i zwykle wartość NULL (jak w przykładzie).Ta wersja biblioteki MFC można automatycznie określają wartości dla lpWBounds, gdy ZOZ, który jest rysowana jest metapliku DC.Ponadto parametr pFormatDC nie jest już konieczne ponieważ zbuduje jeden z "atrybut DC" kontrolera pDC w ramach.Aby rozwiązać ten problem, wystarczy usunąć dwie dodatkowe NULL parametrów wywołania Draw.

\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 unikatową nazwę przekazywane do reprezentowania element.Było to wymóg interfejs OLE API.Nie jest to konieczne w MFC/OLE 2 ponieważ OLE 2 nie używa DDE jako podstawowego mechanizmu komunikacji (nazwa była używana w konwersacji DDE).Aby rozwiązać ten problem, można usunąć CreateNewName działać jak również wszystkie odwołania do niego.Jest łatwy dowiedzieć się, jakie każda funkcja MFC/OLE oczekuje w tej wersji po prostu przez umieszczenie kursora na wywołanie i naciskając klawisz F1.

Inny obszar, który różni się znacząco jest obsługa Schowka OLE 2.Z OLE1 używane są Schowka systemu Windows, które interfejsów API interakcji ze Schowka.OLE 2 to zrobić za pomocą innego mechanizmu.API MFC/OLE1 założyć, że Schowek był otwarty, przed skopiowaniem COleClientItem obiektu do Schowka.Nie jest już konieczne i spowoduje, że wszystkie operacje schowka MFC/OLE niepowodzenie.Podczas edycji kodu, aby usunąć 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 innego obszaru, w którym rzeczy uległy zmianie ulegnie.W tym przypadku jest najłatwiejszym po prostu scalić oryginalnej implementacji z przewidzianego przez AppWizard nowych aplikacji kontenera OLE.W rzeczywistości jest to technika, który można zastosować do przenoszenia innych aplikacji.W MFC/OLE1 wyświetlane okno dialogowe "Wstaw obiekt" przez wywołanie AfxOleInsertDialog funkcji.W tej wersji skonstruować COleInsertObject okno dialogowe obiekt i wywołanie DoModal.Ponadto, nowe elementy OLE są tworzone z CLSID zamiast ciągu classname.Wynik końcowy powinien wyglądać następująco

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ć różne dla aplikacji):

Również jest konieczne uwzględnienie z <afxodlgs.h>, która zawiera zgłoszenia do COleInsertObject klasy okna dialogowego, a także innych okien standardowe dostarczone 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ą powodowane przez fakt, że niektóre stałe OLE1 uległy zmianie w OLE 2, mimo że pojęcia są takie same.W tym przypadku OLEVERB_PRIMARY została zmieniona na OLEIVERB_PRIMARY.W OLE1 i OLE 2 zlecenia głównego jest zwykle wykonywane przez kontenera, gdy użytkownik kliknie element.

Ponadto DoVerb teraz ma dodatkowy parametr — wskaźnik do widoku (CView*).Ten parametr jest używany tylko do realizacji "Edycja wizualna" (lub aktywacji w miejscu).Teraz ten parametr zostanie ustawiony na wartość NULL, ponieważ nie stosuje się tę funkcję w tej chwili.

Aby upewnić się, że ramy nigdy próby w miejscu aktywacji, 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 wykonywania kwerend i manipulować zakres elementu ( lewej i u góry członków były zawsze zero).2 MFC/OLE bardziej bezpośrednio jest to obsługiwane przez COleClientItem::GetExtent i SetExtent, które zajmują się rozmiar lub CSize zamiast.

Kod użytkownika nowy 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 interfejsie API synchroniczne MFC/OLE1 były wywołania z kontenera do serwera symulowane, ponieważ asynchroniczny założenia w wielu przypadkach OLE1.Było wywołanie asynchroniczne pozostających do spłaty w toku przed Sprawdź, czy przetwarzanie polecenia użytkownika.Pod warunkiem MFC/OLE1 COleClientItem::InWaitForRelease funkcji w ten sposób.2 MFC/OLE nie jest to konieczne, więc można usunąć zastępująca OnCommand w CMainFrame wszystkie razem.

W tym momencie OCLIENT skompiluje się.

Inne niezbędne zmiany.

Istnieje kilka rzeczy, które nie są wykonywane będą jednak zachować OCLIENT uruchamianie.Aby rozwiązać te problemy, teraz zamiast później lepiej jest.

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ż Sprawdź, czy są nowe funkcje wirtualnych dla zmian listy parametrów.Taka funkcja jest COleClientItem::OnChange, przesłonięte w każdej aplikacji kontenera MFC/OLE.Patrząc na pomoc online, zobaczysz, dodano dodatkowe "DWORD dwParam".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
}

W MFC/OLE1 aplikacji kontenera uzyskane klasy dokumentu z COleClientDoc.2 MFC/OLE tej klasy została usunięta i zastąpiona przez COleDocument (tej nowej organizacji ułatwia tworzenie kontenera serwera aplikacji).Istnieje #define mapuje COleClientDoc do COleDocument Aby uprościć przenoszenie 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 aplikacje serwera, które wykorzystują również COleDocument (pośrednio), nie pociągają za sobą dodatkowe obciążenie związane z tych programów obsługi polecenia chyba, że aplikacja serwera kontenera.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ń jest w COleDocument, która jest klasą podstawową dla dokumentu.

W tym momencie OCLIENT jest funkcjonalności aplikacji kontenera OLE.Istnieje możliwość wstawiania elementów dowolnego typu (OLE1 lub OLE 2).Ponieważ niezbędne kodu, aby umożliwić aktywację na miejscu nie jest zaimplementowana, elementy są edytowane w oddzielnym oknie, bardzo podobnie, jak z OLE1.Następna sekcja omawia niezbędne zmiany w celu umożliwienia edycji na miejscu (czasami nazywane "Edycja wizualna").

Dodawanie "Edycja wizualna"

Jednym z najbardziej interesujących OLE jest aktywacja w miejscu (lub "Edycja wizualna").Ta funkcja umożliwia aplikacji serwera do przejęcia części interfejsu użytkownika kontenera przewidzianego bardziej edycji interfejs użytkownika.Do wykonania aktywacji w miejscu, do OCLIENT, niektóre specjalne zasoby trzeba dodać, jak również pewne dodatkowe kodu.Te zasoby i kod są normalnie dostarczane przez AppWizard — w rzeczywistości wiele tutaj kod został pożyczonych bezpośrednio ze świeżych aplikacji AppWizard z obsługą "Kontenera".

Po pierwsze jest konieczne dodanie zasobu menu, gdy istnieje element, 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 menu dodatkowych w Visual C++.Dodaje się dwóch pasków separatora między pliku i okno wyskakujące okienka do wskazania oddzielenie grup (powinna wyglądać: plik || Okno).Więcej informacji na temat tych separatory oznacza i jak scalania menu serwera i kontener zobacz "Menu i zasobów: scalania Menu" w Klasy 2 OLE.

Po uzyskaniu tych menu utworzone należy powiadomić w ramach je znać.Jest to realizowane przez wywołanie CDocTemplate::SetContainerInfo w szablonie dokumentu przed dodaniem go do listy szablonów dokumentu 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);

Zasoby IDR_OLECLITYPE_INPLACE to specjalne zasoby w miejscu, utworzone w programie Visual C++.

Aby umożliwić aktywację na miejscu, są niektóre rzeczy, które trzeba zmienić zarówno w CView (CMainView) pochodzących z klasy oraz COleClientItem klasy (CRectItem).Wszystkie te zastąpienia są dostarczane przez AppWizard i większość wykonania będą pochodzić bezpośrednio z domyślnej aplikacji AppWizard.

W pierwszym kroku tego portu, aktywacja w miejscu zostało wyłączone całkowicie nadrzędnym COleClientItem::CanActivate.Aby umożliwić aktywację na miejscu należy usunąć to override.Ponadto NULL został przekazany do wywołania do DoVerb (istnieją dwa z nich) ponieważ dostarczanie widok był tylko niezbędne dla aktywacji w miejscu.Do pełnego wykonania aktywacji w miejscu, jest niezbędne do prawidłowego widoku w DoVerb wywołania.Jeden z tych połączeń jest 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 aktywowana w miejscu.Dla OCLIENT wykonania jest trywialny:

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

Większość serwerów także implementować tak zwany "w miejscu zmiany rozmiaru." Umożliwia to okno serwer o rozmiarze i przeniesione, podczas gdy użytkownik edytuje element.Kontener muszą uczestniczyć w tej akcji, ponieważ przenoszenie lub zmiana rozmiaru okna, zwykle wpływa na położenie i rozmiar w samym dokumencie kontenera.Wykonania OCLIENT synchronizuje wewnętrznego prostokąt utrzymywane przez m_rect w nowe położenie 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 istnieje wystarczająco dużo kodu towaru i aktywowane w miejsce oraz przeciwdziałania rozmiary i położenie elementu, gdy jest on aktywny, ale żaden kod nie pozwoli użytkownikowi zakończyć sesję edycji.Chociaż niektóre serwery będą funkcjonalność ta same poprzez obsługę klawisz escape, jest zasugerował, że pojemniki zapewniają zdezaktywować elementu na dwa sposoby: (1), kliknij na zewnątrz elementu i (2) naciskając klawisz ESC.

Klawisz ESCAPE dodawania akceleratora w języku Visual C++, który mapuje klucza VK_ESCAPE polecenia, ID_CANCEL_EDIT dodaje się do zasobów.Program obsługi dla tego polecenia jest następujący:

// 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 przypadek, 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, powinna mieć fokus.Aby upewnić się, czy dotyczy to obsługi OnSetFocus tak, aby fokus jest przenoszony zawsze aktywny element, podczas otrzymuje fokus, widok:

// 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ć aktywny element, który zmienił prostokąta przycinania.W tym zapewnienia 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 opisuje kroki, które początkowo konwertowane tej aplikacji do używania klas MFC/OLE 2.Liczba funkcje zostały dodane po port początkowy została ukończona, aby lepiej zilustrować klas MFC/OLE 2.Funkcje te nie zostaną objęte odnoszą się do samego próbki więcej informacji na temat tych zaawansowanych funkcji.

[!UWAGA]

Visual C++ 2.0 został utworzony proces krok po kroku i błędy kompilatora.Visual C++ 4.0 mógł zmienić wiadomości błędu i lokalizacji, ale informacje koncepcyjne pozostają ważne.

Pobieranie go i z systemem

Podejście do portu HIERSVR próbki MFC/OLE jest rozpoczęcie konstruowania i ustalania błędy kompilatora oczywiste, które powodują.Jeśli pobranie próbki HIERSVR z MFC 2.0 i skompiluj go w tej wersji MFC, przekonasz się, że nie ma 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.

Kompilacji i napraw błędy

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

Ten pierwszy błąd wskazuje znacznie większych problemów z InitInstance funkcja dla serwerów.Inicjowanie wymaganych dla serwera OLE jest prawdopodobnie jednym z największych zmiany, które trzeba będzie wprowadzać aplikacji MFC/OLE1 go z systemem.Doskonała jest przyjrzeć się AppWizard tworzy dla serwera OLE i zmodyfikować kod jako właściwe.Oto niektóre pamiętać:

Należy zainicjować bibliotek OLE, wywołując AfxOleInit

Wywołanie SetServerInfo obiektu szablonu dokumentu ustawić uchwyty zasobów serwera i informacje klasy runtime, a nie z CDocTemplate konstruktora.

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

Konieczne będzie identyfikatora GUID dla dokumentu.Jest to identyfikator unikatowy dla typu dokumentu (128 bitów).AppWizard utworzy jedna — sposób użycia techniki opisane tutaj kopiowania nowego kodu z nową aplikację serwera AppWizard generowane, użytkownik może po prostu "ukraść" GUID z tej aplikacji.Jeśli nie można użyć GUIDGEN.Narzędzie EXE w katalogu BIN.

Jest "połączenie" your COleTemplateServer obiektu do szablonu dokumentu, wywołując COleTemplateServer::ConnectTemplate.

Po uruchomieniu aplikacji autonomicznych, należy zaktualizować rejestru systemowego.W ten sposób użytkownik przesuwa.EXE aplikacji uruchomienia go z nowej lokalizacji będzie aktualizacji bazy danych rejestracji systemu Windows wskaż nową lokalizację.

Po zastosowaniu, wszystkie te zmiany na podstawie AppWizard tworzy dla InitInstance, InitInstance (i związanych 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 kod powyżej odnosi się do nowych identyfikatorów zasobów, IDR_HIERSVRTYPE_SRVR_EMB.Jest to zasób menu ma być używane podczas edycji dokumentu, który jest osadzony w innym kontenerze.W MFC/OLE1 szczególnych edycji elementu osadzone elementy menu zostały zmienione w locie.Za pomocą menu całkowicie inną strukturę podczas edytowania osadzonego elementu zamiast edytować dokument oparty na pliku w znacznie ułatwia świadczenia różnych interfejsów użytkownika dla tych dwóch trybów obowiązują oddzielne.Jak zobaczymy później, zasób całkowicie oddzielnych menu jest używana podczas edycji obiektu osadzonego w miejscu.

Do tworzenia tego zasobu, załadować zasobu Skrypt w języku Visual C++ i skopiować istniejący zasób menu IDR_HIERSVRTYPE.Zmień nazwę nowego zasobu do IDR_HIERSVRTYPE_SRVR_EMB (jest to ten sam Konwencja nazewnictwa, która korzysta z AppWizard).Następnie zmień "Zapisz plik" na "Aktualizacja pliku"; Nadaj identyfikator polecenia ID_FILE_UPDATE.Również zmienić "Zapisz jako" do "Zapisz kopię jako"; Nadaj identyfikator polecenia ID_FILE_SAVE_COPY_AS.Ramy miejsce 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

Liczba błędów wynikających z override z OnSetData, ponieważ odwołuje się do OLESTATUS typu.OLESTATUS został sposób OLE1 zwrócił błędy.Ta została zmieniona na HRESULT OLE 2, chociaż MFC zazwyczaj konwertuje HRESULT do COleException zawierające błąd.W tym szczególnym przypadku override z OnSetData nie jest już konieczne, więc jest najłatwiejszym go 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 jej na wartość TRUE, ramach obsługuje zarządzanie pamięcią tych obiektów — ich usuwania, gdy są one już potrzebne.Używa HIERSVR CServerItem (pochodzące z COleServerItem) obiektów w ramach macierzystej danych, więc ta flaga będzie równa FAŁSZ.Pozwala to określić, kiedy skreśla się ppkt każdego serwera HIERSVR.

\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 są niektóre funkcje "czystego wirtualny", które nie zostały zastąpione w CServerItem.Najprawdopodobniej jest to spowodowane faktem, że lista parametrów OnDraw's została zmieniona.Aby naprawić ten błąd, należy zmienić CServerItem::OnDraw w następujący sposób (jak również deklarację 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".Umożliwia to wypełnienie rozmiar rysunku, jeśli jest to wygodne.Rozmiar ten musi być w HIMETRIC.W tym przypadku jest nie do wypełnienia tej wartości, więc wywołuje w ramach OnGetExtent do pobrania w zakresie.Dla tej pracy trzeba 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 elementu jest konwertowany na HIMETRIC i przechowywane w m_rectBounds.Poznane "m_rectBounds" członek COleServerItem nie istnieje (został częściowo zastąpiony przez m_sizeExtent, ale w OLE 2 ten użytkownik ma nieco obciążenie niż m_rectBounds w OLE1).Zamiast ustawienie HIMETRIC rozmiar do tej zmiennej Członkowskie będziesz zwrócić 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 zastępuje inny mechanizm.Wersja MFC 3.0 próbki MFC OLE HIERSVR implementuje tej funkcji nadrzędnym COleServerItem::OnRenderFileData.Nie jest to istotne dla tego portu podstawowego, aby usunąć OnGetTextData override.

Istnieje wiele więcej błędów w svritem.cpp, które nie zostały uwzględnione.Nie są "real" błędy — tylko błędy spowodowane przez błędy poprzedniego.

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

COleServerItem::CopyToClipboardjuż nie obsługuje flagi "bIncludeNative".Dane macierzysty (dane zapisane przez funkcję Serialize elementu serwera) są zawsze kopiowane, więc usunąć pierwszy parametr.Ponadto CopyToClipboard wygeneruje wyjątek błąd uniemożliwi 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   
}

Chociaż było więcej błędów wynikających z kompilacji HIERSVR w wersji MFC 2.0 nie istniały dla tej samej wersji OCLIENT, były rzeczywiście mniej zmian.

W tym momencie HIERSVR i łączenie i kompilować działać jako serwer OLE, ale bez funkcji edycji w miejscu, które zostanie następnie zastosowane.

Dodawanie "Edycja wizualna"

Aby dodać do tej aplikacji "Edycja wizualna" (lub aktywacji w miejscu), istnieją tylko kilka rzeczy, które należy uważać z:

  • Musisz zasobu specjalne menu, gdy element jest aktywny w miejscu.

  • Ta aplikacja ma narzędzi, więc musisz narzędzi z podzbioru normalnej narzędzi, aby dopasować poleceń menu dostępnych z serwera (pasuje do zasobu menu wymienionych powyżej).

  • Potrzebujesz nową klasę pochodną COleIPFrameWnd zapewnia interfejs użytkownika w miejscu (podobnie jak CMainFrame, pochodzące z CMDIFrameWnd, udostępnia interfejs użytkownika MDI).

  • Musisz powiedzieć ramy te specjalne zasoby i klas.

Zasób menu jest łatwo utworzyć.Uruchom Visual C++, Kopiuj zasób menu IDR_HIERSVRTYPE do zasobu menu o nazwie IDR_HIERSVRTYPE_SRVR_IP.Modyfikowanie menu tak, aby pozostało tylko Edycja i pomocy wyskakujących menu.Dodać dwa separatory menu między menu Edycja i pomocy (powinna wyglądać: Edycja || Pomoc).Aby uzyskać więcej informacji na te separatory oznacza i jak scalania menu serwera i kontenera, zobacz "Menu i zasobów: scalania Menu" w Klasy 2 OLE.

Mapę bitową dla narzędzi podzbiór łatwo tworzone są przez kopiowanie jednej z aplikacji świeżego AppWizard generowane z opcją "Serwer", sprawdzane.Następnie można zaimportować tę mapę bitową do programu Visual C++.Należy podać identyfikator IDR_HIERSVRTYPE_SRVR_IP mapy bitowej.

Klasa pochodna od COleIPFrameWnd mogą być kopiowane z aplikacji z obsługą serwera, jak również AppWizard generowane.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, tworzone są nowe zasoby i klasy, należy dodać kod niezbędne tak, aby w ramach wie o tych (i wie, że teraz tej aplikacji, obsługuje edycję w miejscu).Jest to realizowane przez dodanie niektórych więcej parametrów do SetServerInfo wywołania w InitInstance funkcji:

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

Jest teraz gotowa do uruchomienia w miejscu w jakimkolwiek pojemniku, obsługuje również aktywację na miejscu.Jednak jest jeden drobnych błędów ukryte w kodzie.HIERSVR obsługuje menu kontekstowe wyświetlane, gdy użytkownik naciśnie przycisk prawym przyciskiem myszy.Menu to działa, gdy HIERSVR jest całkowicie otwarte, ale nie działa podczas edycji osadzania w miejscu.Przyczyny można zakotwiczać w dół do jednego wiersza kodu w CServerView::OnRButtonDown:

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

Zawiadomienie odniesienie do AfxGetApp() - > m_pMainWnd.Gdy serwer jest aktywowana w miejscu, posiada głównego okna i m_pMainWnd jest ustawiona, ale jest zazwyczaj niewidoczny.Ponadto, okno to odnosi się do główne okno aplikacji, okna ramki MDI, który pojawia się, gdy serwer jest w pełni otwierane lub uruchamiane autonomicznych.Nie odnosi się do okna aktywnej ramki — który podczas w miejscu aktywowany jest ramki okna pochodzące z COleIPFrameWnd.Aby uzyskać poprawne aktywnego okna, nawet w przypadku, gdy w miejscu edycji tej wersji MFC dodaje nową funkcję, AfxGetMainWnd.Ogólnie, należy używać tej funkcji, zamiast AfxGetApp() - > m_pMainWnd.Kod ten musi zmienić w następujący sposób:

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

Teraz masz minimalny zestaw włączone dla aktywacji w miejscu funkcjonalności serwera OLE.Jednakże nadal istnieje wiele funkcji dostępnych z 2 MFC/OLE, które nie były dostępne w MFC/OLE1.Zobacz próbki HIERSVR więcej pomysłów na funkcje, które chcesz zaimplementować.Poniżej wymieniono niektóre funkcje, które implementuje HIERSVR:

  • Powiększanie do PRAWDA zachowanie WYSISYG w odniesieniu do kontenera.

  • Przeciągnij / upuszczanie i format schowka niestandardowe.

  • Przewijanie okno kontenera, jak zaznaczenie jest zmieniany.

Próbki HIERSVR 3.0 MFC również używa nieco inny projekt jej elementów serwera.To pozwala zachować więcej wolnej pamięci i sprawia, że łącza są bardziej elastyczne.W wersji 2.0 HIERSVR każdy węzeł w drzewie jest wCOleServerItem.COleServerItemPrzenosi nieco większe obciążenie niż jest to absolutnie niezbędne dla każdego z tych węzłów, ale COleServerItem jest wymagana dla każdego aktywnego łącza.Jednak w większości, są bardzo niewiele łącza aktywne w danej chwili.Aby zwiększyć efektywność, HIERSVR w tej wersji MFC oddziela węzła z COleServerItem.Posiada zarówno CServerNode i CServerItem klasy.CServerItem (pochodzące z COleServerItem) jest tworzony tylko w razie potrzeby.Gdy kontener (lub kontenery) zatrzymać określonego łącze do dany węzeł, skreśla się obiektu CServerItem skojarzonego 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 nie obsługuje wybór wielokrotny, ale byłoby znacznie łatwiejsze, dodawać (i obsługuje łączy do takich zaznaczeń) w wersji MFC 3.0 HIERSVR, ponieważ COleServerItem jest oddzielona od macierzystych danych.

Zobacz też

Inne zasoby

Uwagi techniczne przez liczbę

Uwagi techniczne według kategorii