Aracılığıyla paylaş


TN041: MFC/OLE 2'ye MFC/OLE1 Geçişi

Dekont

Aşağıdaki teknik not, çevrimiçi belgelere ilk kez eklendiğinden beri güncelleştirilmemiştir. Sonuç olarak, bazı yordamlar ve konular güncel olmayabilir veya yanlış olabilir. En son bilgiler için, çevrimiçi belge dizininde ilgilendiğiniz konuyu aramanız önerilir.

Geçişle İlgili Genel Sorunlar

MFC 2.5 (ve üzeri) içindeki OLE 2 sınıflarının tasarım hedeflerinden biri, OLE 1.0 desteği için MFC 2.0'da yer alan mimarinin büyük kısmını korumaktı. Sonuç olarak, MFC 2.0'daki aynı OLE sınıflarının çoğu MFC'nin (COleDocument, COleServerDoc, , COleClientItemCOleServerItem) bu sürümünde hala var olur. Buna ek olarak, bu sınıflardaki API'lerin çoğu tam olarak aynıdır. Ancak, OLE 2, OLE 1.0'dan önemli ölçüde farklıdır, bu nedenle bazı ayrıntıların değiştiğini bekleyebilirsiniz. MFC 2.0'ın OLE1 desteği hakkında bilginiz varsa, MFC'nin 2.0 desteğiyle kendinizi evinizde hissedeceksiniz.

Mevcut bir MFC/OLE1 uygulamasını alıyor ve buna OLE 2 işlevselliği ekliyorsanız, önce bu notu okumalısınız. Bu not, OLE1 işlevinizi MFC/OLE 2'ye taşıma sırasında karşılaşabileceğiniz bazı genel sorunları kapsar ve ardından MFC 2.0'da bulunan iki uygulamayı taşıma sırasında ortaya çıkan sorunları açıklar: MFC OLE örnekleri OCLIENT ve HIERSVR.

MFC Belge/Görünüm Mimarisi Önemlidir

Uygulamanız MFC'nin Belge/Görünüm mimarisini kullanmıyorsa ve uygulamanıza OLE 2 desteği eklemek istiyorsanız, şimdi Belge/Görünüm'e geçmenin tam zamanı. MFC'nin OLE 2 sınıflarının avantajlarının çoğu yalnızca uygulamanız MFC'nin yerleşik mimarisini ve bileşenlerini kullandığında gerçekleştirilir.

MFC mimarisini kullanmadan bir sunucu veya kapsayıcı uygulamak mümkündür, ancak önerilmez.

Kendi Uygulamanız Yerine MFC Uygulamasını Kullanma

, ve gibi CToolBarCStatusBarCScrollView MFC "canned implementation" sınıfları OLE 2 desteği için yerleşik özel durum koduna sahiptir. Bu nedenle, bu sınıfları uygulamanızda kullanabilirseniz, OLE'nin farkında olmalarını sağlamak için bu sınıflara harcanan çabadan yararlanabilirsiniz. Yine bu amaçlar için burada "kendi sınıflarınızı sarmak" mümkündür, ancak önerilmez. Benzer işlevleri uygulamanız gerekiyorsa, MFC kaynak kodu OLE'nin daha ince noktalarından bazılarıyla ilgilenmek için mükemmel bir başvurudur (özellikle yerinde etkinleştirme söz konusu olduğunda).

MFC Örnek Kodunu İnceleme

OLE işlevselliğini içeren bir dizi MFC örneği vardır. Bu uygulamaların her biri OLE'i farklı bir açıdan uygular:

  • HIERSVR Çoğunlukla sunucu uygulaması olarak kullanılmak üzere kullanılır. MFC 2.0'a bir MFC/OLE1 uygulaması olarak eklenmiştir ve MFC/OLE 2'ye geçirilmiştir ve ardından OLE 2'de kullanılabilen birçok OLE özelliğini uygulayacak şekilde genişletilmiştir.

  • OCLIENT Bu, kapsayıcı açısından OLE özelliklerinin birçoğunun gösterilmesini amaçlayan tek başına bir kapsayıcı uygulamasıdır. Ayrıca MFC 2.0'dan da taşınabilir ve daha sonra özel pano biçimleri ve eklenmiş öğelere bağlantılar gibi daha gelişmiş OLE özelliklerinin çoğunu destekleyecek şekilde genişletilmiştir.

  • DRAWCLI Bu uygulama OLE kapsayıcı desteğini OCLIENT'ın yaptığı gibi uygular, ancak bunu mevcut nesne odaklı çizim programı çerçevesinde yapar. OLE kapsayıcısı desteğini nasıl uygulayabileceğinizi ve mevcut uygulamanızla nasıl tümleştirebileceğini gösterir.

  • SUPERPAD Bu uygulama, aynı zamanda ince bir tek başına uygulama olmanın yanı sıra bir OLE sunucusudur. Uyguladığı sunucu desteği oldukça minimalist. Özellikle ilgi çekici olan, verileri panoya kopyalamak için OLE pano hizmetlerini kullanma şeklidir, ancak pano yapıştırma işlevini uygulamak için Windows "düzenle" denetiminde yerleşik olarak bulunan işlevselliği kullanır. Bu, geleneksel Windows API kullanımının yanı sıra yeni OLE API'leriyle tümleştirmenin ilginç bir karışımını gösterir.

Örnek uygulamalar hakkında daha fazla bilgi için "MFC Örnek Yardımı" bölümüne bakın.

Örnek Olay İncelemesi: MFC 2.0'dan OCLIENT

Yukarıda açıklandığı gibi, OCLIENT MFC 2.0'a dahil edildi ve MFC/OLE1 ile OLE uygulandı. Bu uygulamanın başlangıçta MFC/OLE 2 sınıflarını kullanacak şekilde dönüştürüldüğü adımlar aşağıda açıklanmıştır. MFC/OLE sınıflarını daha iyi göstermek için ilk bağlantı noktası tamamlandıktan sonra bir dizi özellik eklendi. Bu özellikler burada ele alınmayacaktır; bu gelişmiş özellikler hakkında daha fazla bilgi için örneğin kendisine bakın.

Dekont

Derleyici hataları ve adım adım işlem Visual C++ 2.0 ile oluşturuldu. Visual C++ 4.0 ile belirli hata iletileri ve konumlar değişmiş olabilir, ancak kavramsal bilgiler geçerli kalır.

Çalışır Duruma Alma

OCLIENT örneğini MFC/OLE'ye taşıma yaklaşımı, bunu oluşturarak ve sonuçta ortaya çıkacak belirgin derleyici hatalarını düzelterek başlamaktır. MFC 2.0'dan OCLIENT örneğini alır ve MFC'nin bu sürümü altında derlerseniz, çözülecek çok fazla hata olmadığını görürsünüz. Hataların oluştuğu sırayla aşağıda açıklanmıştır.

Hataları Derleme ve Düzeltme

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

İlk hata ile ilgilidir COleClientItem::Draw. MFC/OLE1'de MFC/OLE sürümünün aldığından daha fazla parametre aldı. Ek parametreler genellikle gerekli değildi ve genellikle NULL (bu örnekte olduğu gibi). MFC'nin bu sürümü, çekilmekte olan CDC bir meta dosyası DC olduğunda lpWBound değerlerini otomatik olarak belirleyebilir. Ayrıca, çerçeve geçirilen pDC'nin "öznitelik DC"sinden bir tane oluşturacağı için pFormatDC parametresi artık gerekli değildir. Bu nedenle bu sorunu çözmek için Draw çağrısına fazladan iki NULL parametresini kaldırmanız yeterlidir.

\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 '

Yukarıdaki hatalar MFC/OLE1 içindeki tüm işlevlerin COleClientItem::CreateXXXX öğeyi temsil etmek için benzersiz bir ad geçirilmesini gerektirmesinden kaynaklanır. Bu, temel alınan OLE API'sinin bir gereksinimiydi. OLE 2, temel alınan iletişim mekanizması olarak DDE kullanmadığından (ad DDE konuşmalarında kullanılmıştır) MFC/OLE 2'de bu gerekli değildir. Bu sorunu çözmek için işlevin CreateNewName yanı sıra tüm başvurularını kaldırabilirsiniz. İmlecinizi aramaya yerleştirip F1 tuşuna basarak her MFC/OLE işlevinin bu sürümde ne beklediğini kolayca öğrenebilirsiniz.

Önemli ölçüde farklı olan bir diğer alan da OLE 2 pano işlemedir. OLE1 ile Windows pano API'lerinin panoyla etkileşim kurmasını kullandınız. OLE 2 ile bu farklı bir mekanizmayla yapılır. MFC/OLE1 API'leri, bir COleClientItem nesneyi panoya kopyalamadan önce panonun açık olduğunu varsayar. Bu artık gerekli değildir ve tüm MFC/OLE pano işlemlerinin başarısız olmasına neden olur. üzerindeki CreateNewNamebağımlılıkları kaldırmak için kodu düzenlerken, Windows panosunun açılmasını ve kapatılmasını sağlayan kodu da kaldırmanız gerekir.

\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'

Bu hatalar işleyiciden kaynaklanır CMainView::OnInsertObject . "Yeni Nesne Ekle" komutunun işlenmesi, işlerin biraz değiştiği başka bir alandır. Bu durumda, yeni bir OLE Kapsayıcısı uygulaması için AppWizard tarafından sağlanan özgün uygulamayı ile birleştirmek en kolayıdır. Aslında bu, diğer uygulamaları taşımaya uygulayabileceğiniz bir tekniktir. MFC/OLE1'de işlevi çağırarak AfxOleInsertDialog "Nesne Ekle" iletişim kutusunu görüntülemişsiniz. Bu sürümde bir COleInsertObject iletişim kutusu nesnesi oluşturup öğesini çağırırsınız DoModal. Ayrıca, yeni OLE öğeleri sınıf adı dizesi yerine CLSID ile oluşturulur. Sonuç şuna benzer olmalıdır:

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

Dekont

Yeni Nesne Ekle, uygulamanız için farklı olabilir:

Ayrıca, iletişim kutusu sınıfının bildirimini COleInsertObject ve MFC tarafından sağlanan diğer standart iletişim kutularını içeren afxodlgs.h> dosyasının da eklenmesi <gerekir.

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

Bu hatalar, kavramda aynı olsalar bile OLE 2'de bazı OLE1 sabitlerinin değişmesi nedeniyle oluşur. Bu durumda OLEVERB_PRIMARY olarak değiştirildi OLEIVERB_PRIMARY. Hem OLE1 hem de OLE 2'de, kullanıcı bir öğeye çift tıkladığında birincil fiil genellikle bir kapsayıcı tarafından yürütülür.

Buna ek olarak, DoVerb artık bir görünüm işaretçisi (CView*) olmak üzere ek bir parametre alır. Bu parametre yalnızca "Görsel Düzenleme" (veya yerinde etkinleştirme) uygulamak için kullanılır. Şimdilik bu özelliği şu anda uygulamadığınız için bu parametreyi NULL olarak ayarlarsınız.

Çerçevenin hiçbir zaman yerinde etkinleştirmeye çalışmadığından emin olmak için, aşağıdaki gibi geçersiz kılmalısınız COleClientItem::CanActivate :

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

MFC/OLE1'de COleClientItem::GetBounds ve SetBounds bir öğenin kapsamını sorgulamak ve işlemek için kullanıldı ( left ve top üyeleri her zaman sıfırdı). MFC/OLE 2'de bu, ve tarafından COleClientItem::GetExtent SetExtentdaha doğrudan desteklenir ve bunun yerine SIZE ile CSize ilgilenir.

Yeni SetItemRectToServer ve UpdateItemRectFromServer çağrılarınızın kodu şöyle görünür:

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

Birçok durumda OLE1 doğası gereği zaman uyumsuz olduğundan, MFC/OLE1'de bir kapsayıcıdan sunucuya zaman uyumlu API çağrılarının benzetimi yapıldı. Kullanıcıdan gelen komutları işlemeden önce bekleyen bir zaman uyumsuz çağrının devam ediyor olup olmadığını denetlemek gerekiyordu. MFC/OLE1 bunu yapmak için işlevi sağladı COleClientItem::InWaitForRelease . MFC/OLE 2'de bu gerekli değildir, bu nedenle CMainFrame'de OnCommand'ın geçersiz kılmasını hep birlikte kaldırabilirsiniz.

Bu noktada OCLIENT derlenir ve bağlanır.

Diğer Gerekli Değişiklikler

Ancak, OCLIENT'ın çalışmasını engelleyecek birkaç işlem yapılmaz. Bu sorunları daha sonra çözmek yerine şimdi düzeltmek daha iyidir.

İlk olarak, OLE kitaplıklarını başlatmak gerekir. Bu işlem, içinden InitInstanceçağrılarak AfxOleInit gerçekleştirilir:

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

Parametre listesi değişiklikleri için sanal işlevleri denetlemek de iyi bir fikirdir. Bu işlevlerden biri, COleClientItem::OnChangeher MFC/OLE kapsayıcı uygulamasında geçersiz kılınır. Çevrimiçi yardıma bakarak fazladan bir 'DWORD dwParam' eklendiğini göreceksiniz. Yeni CRectItem::OnChange aşağıdaki gibi görünür:

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'de kapsayıcı uygulamaları belge sınıfını öğesinden COleClientDoctüretdi. MFC/OLE 2'de bu sınıf tarafından kaldırıldı ve değiştirildi COleDocument (bu yeni kuruluş kapsayıcı/sunucu uygulamaları oluşturmayı kolaylaştırır). MFC/OLE1 uygulamalarınıN OCLIENT gibi MFC/OLE 2'ye taşımasını basitleştirmek için ile eşleyen COleClientDoc COleDocument bir #define vardır. tarafından COleDocument COleClientDoc sağlanmayan özelliklerden biri, standart komut iletisi eşleme girdileridir. Bu, aynı zamanda (dolaylı olarak) kullanan COleDocument sunucu uygulamalarının, kapsayıcı/sunucu uygulaması olmadığı sürece bu komut işleyicilerinin yükünü yanlarında taşımaması için yapılır. CMainDoc ileti eşlemesine aşağıdaki girdileri eklemeniz gerekir:

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)

Bu komutların tümünün uygulaması, belgenizin temel sınıfı olan içindedir COleDocument.

Bu noktada, OCLIENT işlevsel bir OLE kapsayıcı uygulamasıdır. Herhangi bir türde (OLE1 veya OLE 2) öğe eklemek mümkündür. Yerinde etkinleştirmeyi etkinleştirmek için gerekli kod uygulanmadığından, öğeler OLE1 ile olduğu gibi ayrı bir pencerede düzenlenir. Sonraki bölümde yerinde düzenlemeyi (bazen "Görsel Düzenleme" olarak da adlandırılır) etkinleştirmek için gerekli değişiklikler açıklanmıştır.

"Görsel Düzenleme" ekleme

OLE'nin en ilginç özelliklerinden biri yerinde etkinleştirmedir (veya "Görsel Düzenleme"). Bu özellik, sunucu uygulamasının kapsayıcının kullanıcı arabiriminin bazı bölümlerini devralarak kullanıcıya daha sorunsuz bir düzenleme arabirimi sağlamasına olanak tanır. OCLIENT'a yerinde etkinleştirme uygulamak için bazı özel kaynakların yanı sıra bazı ek kodlar eklenmesi gerekir. Bu kaynaklar ve kod normalde AppWizard tarafından sağlanır; aslında buradaki kodun çoğu doğrudan "Kapsayıcı" desteğine sahip yeni bir AppWizard uygulamasından ödünç alınıyordu.

Her şeyden önce, yerinde etkin olan bir öğe olduğunda kullanılacak bir menü kaynağı eklemek gerekir. IDR_OCLITYPE kaynağını kopyalayıp Dosya ve Pencere açılır pencereleri dışında tümünü kaldırarak Visual C++ uygulamasında bu ek menü kaynağını oluşturabilirsiniz. Grup ayrımını belirtmek için Dosya ve Pencere açılır pencereleri arasına iki ayırıcı çubuk eklenir (şöyle görünmelidir: File || Window). Bu ayırıcıların ne anlama geldiğini ve sunucu ve kapsayıcı menülerinin nasıl birleştirildiğinden daha fazla bilgi için bkz . Menüler ve Kaynaklar: Menü Birleştirme.

Bu menüleri oluşturduktan sonra, çerçeveye bunları bildirmeniz gerekir. Bu işlem, belge şablonunu InitInstance'ınızdaki belge şablonu listesine eklemeden önce çağırarak CDocTemplate::SetContainerInfo yapılır. Belge şablonunu kaydetmeye ilişkin yeni kod şöyle görünür:

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

IDR_OLECLITYPE_INPLACE kaynağı, Visual C++ içinde oluşturulan özel yerinde kaynaktır.

Yerinde etkinleştirmeyi etkinleştirmek için hem (CMainView) türetilmiş sınıfında hem de CView türetilmiş sınıfta COleClientItem (CRectItem) değiştirilmesi gereken bazı şeyler vardır. Bu geçersiz kılmaların tümü AppWizard tarafından sağlanır ve uygulamanın çoğu doğrudan varsayılan bir AppWizard uygulamasından gelir.

Bu bağlantı noktasının ilk adımında yerinde etkinleştirme, geçersiz kılınarak COleClientItem::CanActivatetamamen devre dışı bırakıldı. Yerinde etkinleştirmeye izin vermek için bu geçersiz kılma kaldırılmalıdır. Buna ek olarak, görünümün sağlanması yalnızca yerinde etkinleştirme için gerekli olduğundan, NULL tüm çağrılarına DoVerb geçirildi (bunlardan ikisi vardır). Yerinde etkinleştirmeyi tam olarak uygulamak için, çağrıda DoVerb doğru görünümü geçirmek gerekir. Bu çağrılardan biri içindedir CMainView::OnInsertObject:

pItem->DoVerb(OLEIVERB_SHOW, this);

Bir diğeri de içindedir CMainView::OnLButtonDblClk:

m_pSelection->DoVerb(OLEIVERB_PRIMARY, this);

öğesini geçersiz kılmak COleClientItem::OnGetItemPositiongerekir. Bu, öğe yerinde etkinleştirildiğinde sunucuya penceresini kapsayıcının penceresine göre nereye koyacağını bildirir. OCLIENT için uygulama önemsizdir:

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

Çoğu sunucu da "yerinde yeniden boyutlandırma" olarak adlandırılan işlemi uygular. Bu, kullanıcı öğeyi düzenlerken sunucu penceresinin boyutlandırılmasını ve taşınmasını sağlar. Pencereyi taşımak veya yeniden boyutlandırmak genellikle kapsayıcı belgesinin içindeki konumu ve boyutu etkilediğinden kapsayıcının bu eyleme katılması gerekir. OCLIENT uygulaması, m_rect tarafından tutulan iç dikdörtgeni yeni konum ve boyutla eşitler.

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

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

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

    return TRUE;
}

Bu noktada, bir öğenin yerinde etkinleştirilmesine ve etkin olduğunda öğenin boyutlandırılması ve taşınmasıyla başa çıkılması için yeterli kod vardır, ancak hiçbir kod kullanıcının düzenleme oturumundan çıkmasına izin vermez. Bazı sunucular kaçış anahtarını işleyerek bu işlevi kendileri sağlayacak olsa da, kapsayıcıların bir öğeyi devre dışı bırakmak için iki yol sağlaması önerilir: (1) öğenin dışına tıklayarak ve (2) ESCAPE tuşuna basarak.

ESCAPE anahtarı için Visual C++ ile VK_ESCAPE anahtarını bir komutla eşleyen bir hızlandırıcı ekleyin ID_CANCEL_EDIT kaynaklara eklenir. Bu komutun işleyicisi aşağıdaki gibidir:

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

Kullanıcının öğenin dışına tıkladığı durumu işlemek için, öğesinin başına CMainView::SetSelectionaşağıdaki kodu eklersiniz:

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

Bir öğe yerinde etkin olduğunda odak o olmalıdır. OnSetFocus'un işlendiğinden emin olmak için, görünümünüz odağı aldığında odağın her zaman etkin öğeye aktarılmasını sağlayın:

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

Görünüm yeniden boyutlandırıldığında, etkin öğeye kırpma dikdörtgeninin değiştiğini bildirmeniz gerekir. Bunu yapmak için için OnSizebir işleyici sağlarsınız:

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

Örnek Olay İncelemesi: MFC 2.0'dan HIERSVR

HIERSVR ayrıca MFC 2.0'a dahil edildi ve MFC/OLE1 ile OLE uygulandı. Bu notta, bu uygulamanın başlangıçta MFC/OLE 2 sınıflarını kullanacak şekilde dönüştürüldüğü adımlar kısaca açıklanmaktadır. MFC/OLE 2 sınıflarını daha iyi göstermek için ilk bağlantı noktası tamamlandıktan sonra bir dizi özellik eklendi. Bu özellikler burada ele alınmayacaktır; bu gelişmiş özellikler hakkında daha fazla bilgi için örneğin kendisine bakın.

Dekont

Derleyici hataları ve adım adım işlem Visual C++ 2.0 ile oluşturuldu. Visual C++ 4.0 ile belirli hata iletileri ve konumlar değişmiş olabilir, ancak kavramsal bilgiler geçerli kalır.

Çalışır Duruma Alma

HIERSVR örneğini MFC/OLE'ye taşıma yaklaşımı, bunu oluşturarak ve sonuçta ortaya çıkacak belirgin derleyici hatalarını düzelterek başlamaktır. MFC 2.0'dan HIERSVR örneğini alır ve MFC'nin bu sürümü altında derlerseniz, çözülecek çok fazla hata olmadığını fark edersiniz (OCLIENT örneğinden daha fazlası olsa da). Hatalar genellikle oluştuğu sırada aşağıda açıklanmıştır.

Hataları Derleme ve Düzeltme

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

Bu ilk hata, sunucular için işlevinde InitInstance çok daha büyük bir soruna işaret etti. Bir OLE sunucusu için gereken başlatma, büyük olasılıkla MFC/OLE1 uygulamanızda çalıştırmak için yapmanız gereken en büyük değişikliklerden biridir. Yapılacak en iyi şey, AppWizard'ın ole sunucusu için ne oluşturduğuna bakmak ve kodunuzu uygun şekilde değiştirmektir. Aklınızda bulundurmak gereken bazı noktalar şunlardır:

OLE kitaplıklarını çağırarak başlatmak gerekir AfxOleInit

Oluşturucuyla ayarlayabildiğiniz sunucu kaynak tanıtıcılarını ve çalışma zamanı sınıf bilgilerini ayarlamak için belge şablonu nesnesinde SetServerInfo öğesini çağırın CDocTemplate .

Komut satırında /Embedding varsa uygulamanızın ana penceresini göstermeyin.

Belgeniz için bir GUID gerekir. Bu, belgenizin türü (128 bit) için benzersiz bir tanımlayıcıdır. AppWizard sizin için bir tane oluşturur; bu nedenle, burada açıklanan yeni bir AppWizard tarafından oluşturulan sunucu uygulamasından yeni kodu kopyalama tekniğini kullanırsanız, bu uygulamadan GUID'yi "çalabilirsiniz". Aksi takdirde, BIN dizininde GUIDGEN.EXE yardımcı programını kullanabilirsiniz.

çağırarak COleTemplateServer::ConnectTemplatenesnenizi COleTemplateServer belge şablonuna "bağlamak" gerekir.

Uygulamanız tek başına çalıştırıldığında sistem kayıt defterini güncelleştirin. Bu şekilde, kullanıcı uygulamanız için .EXE dosyasını taşırsa yeni konumundan çalıştırıldığında Windows sistem kayıt veritabanı yeni konuma işaret edecek şekilde güncelleştirilir.

AppWizard'ın için InitInstanceInitInstance oluşturduğu öğeye göre bu değişikliklerin tümünü uyguladıktan sonra, HIERSVR için (ve ilgili GUID) şu şekilde okunmalıdır:

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

Yukarıdaki kodun IDR_HIERSVRTYPE_SRVR_EMB yeni bir kaynak kimliğine başvurduğunu fark edeceksiniz. Bu, başka bir kapsayıcıya eklenmiş bir belge düzenlendiğinde kullanılacak menü kaynağıdır. MFC/OLE1'de, katıştırılmış bir öğeyi düzenlemeye özgü menü öğeleri anında değiştirildi. Dosya tabanlı bir belgeyi düzenlemek yerine eklenmiş bir öğeyi düzenlerken tamamen farklı bir menü yapısı kullanmak, bu iki ayrı mod için farklı kullanıcı arabirimleri sağlamayı çok daha kolay hale getirir. Daha sonra göreceğiniz gibi, eklenmiş bir nesneyi yerinde düzenlerken tamamen ayrı bir menü kaynağı kullanılır.

Bu kaynağı oluşturmak için, kaynak betiğini Visual C++ içine yükleyin ve mevcut IDR_HIERSVRTYPE menü kaynağını kopyalayın. Yeni kaynağı IDR_HIERSVRTYPE_SRVR_EMB olarak yeniden adlandırın (bu, AppWizard'ın kullandığı adlandırma kuralıyla aynıdır). Ardından "Dosya Kaydet"i "Dosya Güncelleştirmesi" olarak değiştirin; komut kimliğini ID_FILE_UPDATE verin. Ayrıca "Dosya Farklı Kaydet" öğesini "Dosya Kaydetme Kopyasını Farklı Kaydet" olarak değiştirin; komut kimliğini ID_FILE_SAVE_COPY_AS verin. Çerçeve, bu komutların her ikisinin de uygulanmasını sağlar.

\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

OLESTATUS türüne başvurarak geçersiz kılınmasından OnSetDatakaynaklanan bir dizi hata vardır. OLESTATUS , OLE1'in hata döndürme yöntemiydi. Bu, OLE 2'de HRESULT olarak değiştirilmiştir, ancak MFC genellikle bir HRESULT'u hatayı içeren bir COleException değere dönüştürür. Bu özel durumda, geçersiz kılma OnSetData işlemi artık gerekli değildir, bu nedenle yapılması en kolay şey bunu kaldırmaktır.

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

COleServerItem Oluşturucu fazladan bir 'BOOL' parametresi alır. Bu bayrak, nesneler üzerinde bellek yönetiminin COleServerItem nasıl yapıldığını belirler. Bunu TRUE olarak ayarlayarak, çerçeve bu nesnelerin bellek yönetimini işler ve artık gerekli olmadığında bunları siler. HIERSVR, yerel verilerinin bir parçası olarak nesneleri kullanır CServerItem (türetilir COleServerItem), bu nedenle bu bayrağı FALSE olarak ayarlayacaksınız. Bu, HIERSVR'ın her sunucu öğesinin ne zaman silineceğini belirlemesini sağlar.

\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

Bu hataların da belirttiği gibi, CServerItem'da geçersiz kılınmayan bazı 'pure-virtual' işlevleri vardır. Bunun nedeni büyük olasılıkla OnDraw'un parametre listesinin değişmesidir. Bu hatayı düzeltmek için aşağıdaki gibi değiştirin CServerItem::OnDraw (svritem.h dosyasındaki bildirimin yanı sıra):

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

Yeni parametre :'rSize'. Bu, uygunsa çizimin boyutunu doldurmanızı sağlar. Bu boyut HIMETRIC içinde olmalıdır. Bu durumda, bu değeri doldurmak uygun değildir, bu nedenle çerçeve kapsamı almak için çağırır OnGetExtent . Bunun işe yarayabilmek için uygulamasını OnGetExtentuygulamanız gerekir:

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 *'

CServerItem::CalcNodeSize işlevinde, öğe boyutu HIMETRIC'e dönüştürülür ve m_rectBounds depolanır. Belgesiz 'm_rectBounds' üyesi COleServerItem yok (kısmen m_sizeExtent tarafından değiştirildi, ancak OLE 2'de bu üyenin ole1'de m_rectBounds biraz farklı bir kullanımı var). HIMETRIC boyutunu bu üye değişkenine ayarlamak yerine döndüreceksiniz. Bu dönüş değeri, daha önce uygulanan içinde OnGetExtentkullanılır.

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

CServerItem ayrıca öğesini geçersiz kılar COleServerItem::OnGetTextData. Bu işlev MFC/OLE'de kullanım dışıdır ve farklı bir mekanizmayla değiştirilir. MFC OLE örneği HIERSVR'ın MFC 3.0 sürümü, geçersiz kılarak COleServerItem::OnRenderFileDatabu işlevi uygular. Bu işlev bu temel bağlantı noktası için önemli olmadığından OnGetTextData geçersiz kılmasını kaldırabilirsiniz.

svritem.cpp dosyasında düzeltilmemiş daha birçok hata var. Bunlar "gerçek" hatalar değildir; yalnızca önceki hataların neden olduğu hatalardır.

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

COleServerItem::CopyToClipboard bayrağını artık desteklemez bIncludeNative . Yerel veriler (sunucu öğesinin Seri hale getirme işlevi tarafından yazılan veriler) her zaman kopyalanır, bu nedenle ilk parametreyi kaldırırsınız. Ayrıca, CopyToClipboard YANLIŞ döndürmek yerine bir hata oluştuğunda bir özel durum oluşturur. CServerView::OnEditCopy kodunu aşağıdaki gibi değiştirin:

void CServerView::OnEditCopy()
{
    if (m_pSelectedNode == NULL)
        AfxThrowNotSupportedException();

    TRY
    {
        m_pSelectedNode->CopyToClipboard(TRUE);
    }
    CATCH_ALL(e)
    {
        AfxMessageBox("Copy to clipboard failed");
    }
    END_CATCH_ALL
}

HIERSVR'ın MFC 2.0 sürümünün derlenmesinden kaynaklanan hatalar OCLIENT'ın aynı sürümü için olduğundan daha fazla hata olsa da, aslında daha az değişiklik yapıldı.

Bu noktada HIERSVR bir OLE sunucusu olarak derlenir ve bağlanır ve işlev görür, ancak bundan sonra uygulanacak yerinde düzenleme özelliği olmadan.

"Görsel Düzenleme" ekleme

Bu sunucu uygulamasına "Görsel Düzenleme" (veya yerinde etkinleştirme) eklemek için ilgilenmeniz gereken yalnızca birkaç şey vardır:

  • Öğe yerinde etkinken kullanılacak özel bir menü kaynağı gerekir.

  • Bu uygulamanın bir araç çubuğu vardır, bu nedenle sunucudan sağlanan menü komutlarıyla (yukarıda belirtilen menü kaynağıyla eşleşir) eşleşmesi için normal araç çubuğunun yalnızca bir alt kümesine sahip bir araç çubuğu gerekir.

  • Yerinde kullanıcı arabirimi sağlayan yeni bir sınıf türetilmiş COleIPFrameWnd olmanız gerekir (CMainFrame gibi, öğesinden CMDIFrameWndtüretilen MDI kullanıcı arabirimini sağlar).

  • Çerçeveye bu özel kaynaklar ve sınıflar hakkında bilgi vermeniz gerekir.

Menü kaynağını oluşturmak kolaydır. Visual C++ komutunu çalıştırın, IDR_HIERSVRTYPE menü kaynağını IDR_HIERSVRTYPE_SRVR_IP adlı bir menü kaynağına kopyalayın. Menüyü yalnızca Düzenle ve Yardım menüsü açılır pencerelerinin kalması için değiştirin. Düzenle ve Yardım menüleri arasındaki menüye iki ayırıcı ekleyin (şöyle görünmelidir: Edit || Help). Bu ayırıcıların ne anlama geldiğini ve sunucu ile kapsayıcı menülerinin nasıl birleştiği hakkında daha fazla bilgi için bkz . Menüler ve Kaynaklar: Menü Birleştirme.

Alt küme araç çubuğunun bit eşlemi, "Sunucu" seçeneği işaretli yeni bir AppWizard uygulamasından kopyalanarak kolayca oluşturulabilir. Bu bit eşlem daha sonra Visual C++ içine aktarılabilir. Bit eşlemine IDR_HIERSVRTYPE_SRVR_IP kimliğini verdiğinizden emin olun.

öğesinden COleIPFrameWnd türetilen sınıf, sunucu desteğine sahip AppWizard tarafından oluşturulan bir uygulamadan da kopyalanabilir. Her iki dosyayı da kopyalayın, IPFRAME. CPP ve IPFRAME. H ve bunları projeye ekleyin. Çağrının LoadBitmap önceki adımda oluşturulan bit eşlem olan IDR_HIERSVRTYPE_SRVR_IP başvurduğundan emin olun.

Artık tüm yeni kaynaklar ve sınıflar oluşturulduğuna göre, çerçevenin bunları bilmesi için gerekli kodu ekleyin (ve bu uygulamanın artık yerinde düzenlemeyi desteklediğini biliyor). Bu işlem, işlevindeki SetServerInfo InitInstance çağrıya bazı parametreler eklenerek gerçekleştirilir:

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

Artık yerinde etkinleştirmeyi de destekleyen herhangi bir kapsayıcıda yerinde çalışmaya hazırdır. Ancak kodda gizlenen küçük bir hata var. HIERSVR, kullanıcı sağ fare düğmesine bastığında görüntülenen bir bağlam menüsünü destekler. Bu menü HIERSVR tamamen açık olduğunda çalışır, ancak yerinde ekleme düzenlerken çalışmaz. Bunun nedeni CServerView::OnRButtonDown içindeki bu tek kod satırına sabitlenebilir:

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

başvurusuna AfxGetApp()->m_pMainWnddikkat edin. Sunucu yerinde etkinleştirildiğinde bir ana penceresi olur ve m_pMainWnd ayarlanır, ancak genellikle görünmez. Ayrıca, bu pencere uygulamanın ana penceresine, sunucu tamamen açık olduğunda veya tek başına çalıştırıldığında görüntülenen MDI çerçeve penceresine başvurur. Etkin çerçeve penceresine başvurmaz; yerinde etkinleştirildiğinde içinden COleIPFrameWndtüretilen bir çerçeve penceresidir. Yerinde düzenleme sırasında bile doğru etkin pencereyi almak için, MFC'nin bu sürümü yeni bir işlev ekler: AfxGetMainWnd. Genel olarak, yerine bu işlevi AfxGetApp()->m_pMainWndkullanmanız gerekir. Bu kodun aşağıdaki gibi değişmesi gerekir:

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

Artık işlevsel yerinde etkinleştirme için en düşük düzeyde etkinleştirilmiş bir OLE sunucunuz var. Ancak MFC/OLE 2 ile kullanılabilen ve MFC/OLE1'de bulunmayan birçok özellik vardır. Uygulamak isteyebileceğiniz özellikler hakkında daha fazla fikir edinmek için HIERSVR örneğine bakın. HIERSVR'ın uyguladığı özelliklerden bazıları aşağıda listelenmiştir:

  • Kapsayıcıya göre gerçek WYSIWYG davranışı için yakınlaştırma.

  • Sürükle / bırak ve özel pano biçimi.

  • Seçim değiştirilirken kapsayıcı penceresini kaydırma.

MFC 3.0'daki HIERSVR örneği, sunucu öğeleri için biraz farklı bir tasarım da kullanır. Bu, belleğin korunmasına yardımcı olur ve bağlantılarınızı daha esnek hale getirir. HIERSVR'ın 2.0 sürümüyle ağaçtaki her düğüm a'dırCOleServerItem. COleServerItem bu düğümlerin her biri için kesinlikle gerekli olandan biraz daha fazla ek yük taşır, ancak her etkin bağlantı için bir COleServerItem gereklidir. Ancak çoğu zaman, herhangi bir zamanda çok az etkin bağlantı vardır. Bunu daha verimli hale getirmek için, MFC'nin bu sürümündeki HIERSVR düğümünden COleServerItemayırır. Hem CServerNode CServerItem hem de sınıfı vardır. CServerItem (' den COleServerItemtüretilir) yalnızca gerektiği gibi oluşturulur. Kapsayıcı (veya kapsayıcılar) söz konusu düğüme belirli bir bağlantıyı kullanmayı bıraktığında, CServerNode ile ilişkili CServerItem nesnesi silinir. Bu tasarım daha verimli ve daha esnektir. Birden çok seçim bağlantısıyla ilgilenirken esnekliği devreye girer. HIERSVR'in bu iki sürümünün hiçbiri birden çok seçimi desteklemez, ancak HİERSVR'ın MFC 3.0 sürümüne eklenmesi (ve bu tür seçimlerin bağlantılarını desteklemek için) çok daha kolay olacaktır, çünkü COleServerItem yerel verilerden ayrılmıştır.

Ayrıca bkz.

Sayıya Göre Teknik Notlar
Kategoriye Göre Teknik Notlar