在微软给的例子OClient中,有选中一个OLE对象然后Change Source的功能,但是会报错。分析了一下是这样的:
void CMainView::OnOleChangeSource() { ASSERT(m_pSelection != NULL && m_pSelection->GetType() == OT_LINK);
COleChangeSourceDialog dlg(m_pSelection); dlg.DoModal(); } |
然后找到COleChangeSourceDialog 的定义,在c:\Program Files (x86)\Microsoft Visual Studio 9.0\VC\atlmfc\include\afxodlgs.h中。
可以看到里面有个成员m_xLinkInfo.
然后找到COleChangeSourceDialog的实现文件,在C:\Program Files (x86)\Microsoft Visual Studio 9.0\VC\atlmfc\src\mfc\oledlgs3.cpp。
先看构造函数:
COleChangeSourceDialog::COleChangeSourceDialog(COleClientItem* pItem, CWnd* pParentWnd) : COleDialog(pParentWnd), m_xLinkInfo(NULL) { ASSERT_VALID(pItem);
memset(&m_cs, 0, sizeof(m_cs)); // initialize structure to 0/NULL
// fill in common part m_cs.cbStruct = sizeof(m_cs); m_cs.dwFlags = 0; if (AfxHelpEnabled()) m_cs.dwFlags |= CSF_SHOWHELP; m_cs.lpfnHook = AfxOleHookProc; m_nIDHelp = AFX_IDD_CHANGESOURCE;
// specific to this dialog m_cs.lpOleUILinkContainer = &m_xLinkInfo; COleDocument* pDoc = pItem->GetDocument(); DWORD dwItem = 0; if (pDoc != NULL) { POSITION posItem = pDoc->GetStartPosition(); ULONG iItem = 1; while ((posItem != NULL) && (dwItem == 0)) { COleClientItem* pSearchItem = pDoc->GetNextClientItem(posItem); if (pSearchItem == pItem) { dwItem = iItem; } iItem++; } } m_cs.dwLink = dwItem; } |
可以看到,这里面把成员m_xLinkInfo设置为NULL.
这里面用到了pItem,就是我们在CMainView::OnOleChangeSource传进来的m_pSelection, pItem->GetDocument()得到的就是我们主程序的CMainDoc指针,构造函数利用这个指针,获得当前文档中被选中的OLE对象的索引,比如第一个就是1.
m_xLinkInfo被赋给了m_cs.lpOleUILinkContainer,m_cs的定义如下:
OLEUICHANGESOURCE m_cs |
构造函数根据传入的pItem,初始化m_cs成员,以备它的其他函数调用。
现在我们回头看CMainView::OnOleChangeSource事件,这个事件中调用了COleChangeSourceDialog.DoModal函数。下面是这个函数的代码:
INT_PTR COleChangeSourceDialog::DoModal() { ASSERT_VALID(this); ASSERT(m_cs.lpfnHook != NULL); // can still be a user hook
m_cs.hWndOwner = PreModal(); INT_PTR iResult = MapResult(::OleUIChangeSource(&m_cs)); PostModal(); return iResult; } |
可以看到这里用到了m_cs变量。
然后执行这步的时候报错了,报错点在这个文件的这个函数:
C:\Program Files (x86)\Microsoft Visual Studio 9.0\VC\atlmfc\src\mfc\oledlgs1.cpp
COleClientItem* COleUILinkInfo::GetLinkItem( DWORD dwLink ) { POSITION posItem = m_pDocument->GetStartPosition(); COleClientItem* pItem = NULL; for (ULONG iLink = 0; iLink < dwLink; ++iLink) { pItem = m_pDocument->GetNextClientItem(posItem); }
return pItem; } |
调试了一下发现m_pDocument为NULL(0x00000000)
为什么那?
我们看COleUILinkInfo的构造函数:
COleUILinkInfo::COleUILinkInfo(COleDocument* pDocument) { ASSERT(pDocument == NULL || pDocument->IsKindOf(RUNTIME_CLASS(COleDocument))); m_pDocument = pDocument; m_pSelectedItem = NULL; m_pos = NULL; m_bUpdateLinks = FALSE; m_bUpdateEmbeddings = FALSE; } |
现在问题明确了,因为在COleChangeSourceDialog的构造函数中把成员m_xLinkInfo设置为NULL, 所以传给COleUILinkInfo构造函数的参数也是空,所以COleUILinkInfo的m_pDocument成员为空,所以m_pDocument->GetStartPosition()会报错。
继承COleChangeSourceDialog类自己写一个类来修复这个问题。
#pragma once #include "afxodlgs.h"
class MyOleChangeSourceDialog : public COleChangeSourceDialog { public: MyOleChangeSourceDialog(COleClientItem* pItem, CWnd* pParentWnd = NULL); }; |
#include "MyOleChangeSourceDialog.h"
MyOleChangeSourceDialog::MyOleChangeSourceDialog(COleClientItem* pItem, CWnd* pParentWnd) : COleChangeSourceDialog(pItem, pParentWnd) { m_xLinkInfo = COleUILinkInfo(pItem->GetDocument()); } |
void CMainView::OnOleChangeSource() { ASSERT(m_pSelection != NULL && m_pSelection->GetType() == OT_LINK);
MyOleChangeSourceDialog dlg(m_pSelection);
dlg.DoModal(); } |