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
, , COleClientItem
COleServerItem
) 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 CToolBar
CStatusBar
CScrollView
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 CreateNewName
bağı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
SetExtent
daha 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::OnChange
her 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 COleClientDoc
tü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::CanActivate
tamamen 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::OnGetItemPosition
gerekir. 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::SetSelection
aş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 OnSize
bir 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::ConnectTemplate
nesnenizi 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 InitInstance
InitInstance
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 OnSetData
kaynaklanan 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ı OnGetExtent
uygulamanı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 OnGetExtent
kullanı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::OnRenderFileData
bu 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, öğesindenCMDIFrameWnd
tü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_pMainWnd
dikkat 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 COleIPFrameWnd
tü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_pMainWnd
kullanmanı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 COleServerItem
ayırır. Hem CServerNode CServerItem
hem de sınıfı vardır. CServerItem
(' den COleServerItem
tü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.