例子:编写一个CyberArticle用于编辑文章的普通插件 (Visual C++)

在这里,我们继续使用例子:编写一个CyberArticle普通插件 (Visual C++)里面的工程,添加一个新的插件。

增加COM组件

同样,我们需要增加一个新的COM组件,命名为:CANormalEditPlugin1
 

找到并修改CANormalEditPlugin1.h文件

这是COM组件class定义文件。斜体部分为增加的内容:
 
// CANormalEditPlugin1.h : Declaration of the CCANormalEditPlugin1 

#pragma once 
#include "resource.h" // main symbols 

#include "MyCANormalPlugin1.h" 

/* 
* 包含必要的头文件。如果CyberArticle没有安装在C:\Program Files\Wizissoft\CyberArticle\,请修改下面相应的包含路径。 
*/ 
#include <commctrl.h> 
#include <exdisp.h> 
#include "C:/Program Files/Wizissoft/CyberArticle/developers/interface/include/CyberArticlePluginInterface.h" 

#if defined(_WIN32_WCE) && !defined(_CE_DCOM) && !defined(_CE_ALLOW_SINGLE_THREADED_OBJECTS_IN_MTA) 
#error "Single-threaded COM objects are not properly supported on Windows CE platform, such as the Windows Mobile platforms that do not include full DCOM support. Define _CE_ALLOW_SINGLE_THREADED_OBJECTS_IN_MTA to force ATL to support creating single-thread COM object's and allow use of it's single-threaded COM object implementations. The threading model in your rgs file was set to 'Free' as that is the only threading model supported in non DCOM Windows CE platforms." 
#endif 



// CCANormalEditPlugin1 

class ATL_NO_VTABLE CCANormalEditPlugin1 : 
    public CComObjectRootEx<CComSingleThreadModel>, 
    public CComCoClass<CCANormalEditPlugin1, &CLSID_CANormalEditPlugin1>, 
    public IDispatchImpl<ICANormalEditPlugin1, &IID_ICANormalEditPlugin1, &LIBID_MyCANormalPlugin1Lib, /*wMajor =*/ 1, /*wMinor =*/ 0> 
    , public ICAPlugin 
    , public ICAEditPlugin 
{ 
public: 
    CCANormalEditPlugin1() 
    { 
    } 

DECLARE_REGISTRY_RESOURCEID(IDR_CANORMALEDITPLUGIN1) 


BEGIN_COM_MAP(CCANormalEditPlugin1) 
    COM_INTERFACE_ENTRY(ICANormalEditPlugin1) 
    COM_INTERFACE_ENTRY(IDispatch) 
    COM_INTERFACE_ENTRY_IID(IID_ICAPlugin, ICAPlugin)             // ICAPlugin
    COM_INTERFACE_ENTRY_IID(IID_ICAEditPlugin, ICAEditPlugin)     // ICAEditPlugin
END_COM_MAP() 



    DECLARE_PROTECT_FINAL_CONSTRUCT() 

    HRESULT FinalConstruct() 
    { 
        return S_OK; 
    } 

    void FinalRelease() 
    { 
    } 

public: 
private: 
    CComPtr<ICAApp> m_spApp; 
    CComPtr<IWebBrowser2> m_spWebBrowser; 

public: 
    //ICAPlugin 
    virtual HRESULT STDMETHODCALLTYPE Init(ICAApp* pApp); 
    virtual HRESULT STDMETHODCALLTYPE Exit(); 
    virtual HRESULT STDMETHODCALLTYPE get_Flags(long* pnFlags); 
    virtual HRESULT STDMETHODCALLTYPE put_Flags(long nFlags); 
    virtual HRESULT STDMETHODCALLTYPE get_MenuItemText(BSTR* pbstrDescription); 
    virtual HRESULT STDMETHODCALLTYPE get_MenuItemStatus(long* pnStatus); 
    virtual HRESULT STDMETHODCALLTYPE get_MenuItemOrder(long* pnMenuItemOrder); 
    virtual HRESULT STDMETHODCALLTYPE Execute(); 
    //ICAEditPlugin 
    virtual HRESULT STDMETHODCALLTYPE put_WebBrowser(IWebBrowser2* pWebBrowser); 
    virtual HRESULT STDMETHODCALLTYPE put_Book(ICABook* pBook) { return S_OK; }    //不关心正在编辑的文章所在的书籍 
    virtual HRESULT STDMETHODCALLTYPE put_Node(ICANode* pNode) { return S_OK; }    //不关心正在编辑的文章 

public: 

}; 

OBJECT_ENTRY_AUTO(__uuidof(CANormalEditPlugin1), CCANormalEditPlugin1) 

找到并修改CANormalEditPlugin1.cpp文件

这是COM组件实现文件。(斜体部分为增加的内容)
// CANormalEditPlugin1.cpp : Implementation of CCANormalEditPlugin1 

#include "stdafx.h" 
#include "CANormalEditPlugin1.h" 

#include <atlstr.h> 
#include <mshtml.h> 
#include <mshtmhst.h> 
#include <mshtmcid.h> 
// CCANormalEditPlugin1 


HRESULT STDMETHODCALLTYPE CCANormalEditPlugin1::Init(ICAApp* pApp) 
{ 
    m_spApp = pApp; 
    ATLASSERT(m_spApp); 
    // 
    return S_OK; 
} 
HRESULT STDMETHODCALLTYPE CCANormalEditPlugin1::Exit() 
{ 
    m_spApp = NULL; 
    m_spWebBrowser = NULL; 
    // 
    return S_OK; 
} 
HRESULT STDMETHODCALLTYPE CCANormalEditPlugin1::get_Flags(long* pnFlags) 
{ 
    if (!pnFlags) 
        return E_INVALIDARG; 
    // 
    *pnFlags = CAPLUGIN_FLAG_TYPE_EDIT_TOOL;    //说明这是一个用来编辑文章的插件。 
    // 
    return S_OK; 
} 
HRESULT STDMETHODCALLTYPE CCANormalEditPlugin1::put_Flags(long nFlags) 
{ 
    return S_OK; 
} 
HRESULT STDMETHODCALLTYPE CCANormalEditPlugin1::get_MenuItemText(BSTR* pbstrDescription) 
{ 
    if (!pbstrDescription) 
        return E_INVALIDARG; 
    // 
    CString strDescription = _T("删除未选中的部分");        //返回菜单文字 
    // 
    *pbstrDescription = strDescription.AllocSysString(); 
    // 
    return S_OK; 
} 
HRESULT STDMETHODCALLTYPE CCANormalEditPlugin1::get_MenuItemStatus(long* pnStatus) 
{ 
    if (!pnStatus) 
        return E_INVALIDARG; 
    // 
    *pnStatus = 0; 
    // 
    ATLASSERT(m_spApp); 
    // 
    return S_OK; 
} 
HRESULT STDMETHODCALLTYPE CCANormalEditPlugin1::get_MenuItemOrder(long* pnMenuItemOrder) 
{ 
    ATLASSERT(pnMenuItemOrder); 
    *pnMenuItemOrder = 1001; 
    // 
    return S_OK; 
} 

HRESULT STDMETHODCALLTYPE CCANormalEditPlugin1::put_WebBrowser(IWebBrowser2* pWebBrowser) 
{ 
    m_spWebBrowser = pWebBrowser;    //保留正在编辑的浏览器接口 
    return S_OK; 
} 

BOOL WizHTMLDocumentGetSelection(IHTMLDocument2* pDoc, BOOL bHTML, CString& strText); 
BOOL WizHTMLDocumentSimplePasteHTML(IHTMLDocument2* pDoc, LPCTSTR lpszHtml); 
HRESULT WizHTMLDocumentExecHelperNN(IHTMLDocument2* pDoc, UINT nID, long nMinSupportLevel = OLECMDF_SUPPORTED|OLECMDF_ENABLED, long nExecOpt = OLECMDEXECOPT_DODEFAULT); 


HRESULT STDMETHODCALLTYPE CCANormalEditPlugin1::Execute() 
{ 
    ATLASSERT(m_spApp); 
    if (!m_spWebBrowser) 
        return E_FAIL;; 
    // 
    //获得IHTMLDocument2接口 
    CComPtr<IDispatch> spDocDisp; 
    if (FAILED(m_spWebBrowser->get_Document(&spDocDisp))) 
        return E_FAIL; 
    // 
    if (!spDocDisp) 
        return E_FAIL; 
    // 
    CComQIPtr<IHTMLDocument2> spDoc(spDocDisp); 
    if (!spDoc) 
        return E_FAIL; 
    // 
    //获得选中的HTML内容 
    CString strHTML; 
    if (!WizHTMLDocumentGetSelection(spDoc, TRUE, strHTML)) 
        return E_FAIL; 
    // 
    if (strHTML.IsEmpty()) 
        return S_FALSE; 
    // 
    //执行命令,首先全选文章,然后删除 
    //通过这种方式,可以进行undo/redo 
    WizHTMLDocumentExecHelperNN(spDoc, IDM_SELECTALL); 
    WizHTMLDocumentExecHelperNN(spDoc, IDM_DELETE); 
    // 
    //粘贴选中的部分HTML内容 
    WizHTMLDocumentSimplePasteHTML(spDoc, strHTML); 
    // 
    return S_OK; 
} 

/////////////////////////////////////////////////////////////////////////////////////////// 
//辅助函数 
// 

//获得选中的HTML内容 
BOOL WizHTMLDocumentGetSelection(IHTMLDocument2* pDoc, BOOL bHTML, CString& strText) 
{ 
    ATLASSERT(pDoc); 
    // 
    CComPtr<IHTMLSelectionObject> spSelection = NULL; 
    if (SUCCEEDED(pDoc->get_selection(&spSelection)) && spSelection) 
    { 
        CComPtr<IDispatch> spDisp = NULL; 
        if (SUCCEEDED(spSelection->createRange(&spDisp)) && spDisp) 
        { 
            CComQIPtr<IHTMLTxtRange, &IID_IHTMLTxtRange> spRange(spDisp); 
            if (spRange) 
            { 
                CComBSTR bstr; 
                if (bHTML) 
                { 
                    if (SUCCEEDED(spRange->get_htmlText(&bstr))) 
                    { 
                        strText = bstr; 
                        return TRUE; 
                    } 
                } 
                else 
                { 
                    if (SUCCEEDED(spRange->get_text(&bstr))) 
                    { 
                        strText = bstr; 
                        return TRUE; 
                    } 
                } 
            } 
        } 
    } 
    // 
    CComPtr<IHTMLFramesCollection2> spColl = NULL; 
    if (SUCCEEDED(pDoc->get_frames(&spColl)) && spColl) 
    { 
        long nCount = 0; 
        spColl->get_length(&nCount); 
        for (int i = 0; i < nCount; i++) 
        { 
            CComVariant vIn(i, VT_I4); 
            CComVariant vOut; 
            if (SUCCEEDED(spColl->item(&vIn, &vOut))) 
            { 
                CComPtr<IHTMLWindow2> spFrame = NULL; 
                if (SUCCEEDED(vOut.pdispVal->QueryInterface(IID_IHTMLWindow2, (void**)&spFrame))) 
                { 
                    CComPtr<IHTMLDocument2> spFrameDoc = NULL; 
                    if (SUCCEEDED(spFrame->get_document(&spFrameDoc))) 
                    { 
                        if (WizHTMLDocumentGetSelection(spFrameDoc, bHTML, strText)) 
                            return TRUE; 
                    } 
                } 
            } 
        } 
} 
return FALSE; 
} 


HRESULT WizHTMLDocumentExecCommand(IHTMLDocument2* pDoc, const GUID *pGuid, long cmdID, long cmdExecOpt, VARIANT* pInVar=NULL, VARIANT* pOutVar=NULL) 
{ 
    CComQIPtr<IOleCommandTarget> spCmdTarg = pDoc; 
    if (spCmdTarg) 
    { 
        return spCmdTarg->Exec(pGuid, cmdID, cmdExecOpt, pInVar , pOutVar); 
    } 
    else 
    { 
        return E_NOINTERFACE; 
    } 
} 
HRESULT WizHTMLDocumentExecCommand(IHTMLDocument2* pDoc, long cmdID, long cmdExecOpt, VARIANT* pInVar=NULL, VARIANT* pOutVar=NULL) 
{ 
    return WizHTMLDocumentExecCommand(pDoc, &CGID_MSHTML, cmdID, cmdExecOpt, pInVar, pOutVar ); 
} 

long WizHTMLDocumentQueryStatus(IHTMLDocument2* pDoc, long cmdID) 
{ 
    CComQIPtr<IOleCommandTarget> spCmdTarg = pDoc; 
    if (spCmdTarg) 
    { 
        OLECMD ocmd = {cmdID, 0}; 
        if (S_OK == spCmdTarg->QueryStatus(&CGID_MSHTML, 1, &ocmd, NULL)) 
            return ocmd.cmdf; 
    } 
    // if an error occurred, returning 0 means the command is disabled 
    return 0; 
} 

HRESULT WizHTMLDocumentExecHelperNN(IHTMLDocument2* pDoc, UINT nID, long nMinSupportLevel, long nExecOpt) 
{ 
    HRESULT hr = E_FAIL; 
    long lStatus = WizHTMLDocumentQueryStatus(pDoc, nID); 
    if ((lStatus & nMinSupportLevel) == nMinSupportLevel) 
        hr = WizHTMLDocumentExecCommand(pDoc, nID, nExecOpt); 
    return hr; 
} 

BOOL WizHTMLDocumentSelectionScrollInToView(IHTMLDocument2* pDoc) 
{ 
    CComPtr<IHTMLSelectionObject> spSel; 
    pDoc->get_selection(&spSel); 
    if (!spSel) 
        return FALSE; 
    // 
    CComPtr<IDispatch> spRange; 
    spSel->createRange(&spRange); 
    // 
    if (CComQIPtr<IHTMLControlRange, &IID_IHTMLControlRange> spCtrlRange = spRange) // the user has slected controls (e.g. an image) 
    { 
        spCtrlRange->scrollIntoView(CComVariant(true)); 
    } 
    if (CComQIPtr<IHTMLTxtRange, &IID_IHTMLTxtRange> spTxtRange = spRange) 
    { 
        spTxtRange->scrollIntoView(); 
    } 
    // 
    return TRUE; 
} 

//粘贴HTML内容 
BOOL WizHTMLDocumentSimplePasteHTML(IHTMLDocument2* pDoc, LPCTSTR lpszHtml) 
{ 
    CComPtr<IHTMLSelectionObject> spSel; 
    pDoc->get_selection(&spSel); 
    if (!spSel) 
        return FALSE; 

    CComPtr<IDispatch> spRange; 
    spSel->createRange(&spRange); 

    CComQIPtr<IHTMLControlRange, &IID_IHTMLControlRange> spCtrlRange = spRange; 
    if (spCtrlRange) // the user has slected controls (e.g. an image) 
    { 
        // remove all selected controls 
        CComPtr<IHTMLElement> spElem; 
        long nLen; 
        spCtrlRange->get_length(&nLen); 
        for (long i = nLen - 1; i >= 0; i--) 
        { 
            spCtrlRange->item(i, &spElem); 
            // 
            CComQIPtr<IHTMLDOMNode> spNode(spElem); 
            if (spNode) 
            { 
                CComPtr<IHTMLDOMNode> spOldNode; 
                VARIANT_BOOL vRemChilds(true); 
                spNode->removeNode(vRemChilds, &spOldNode); 
            } 
        } 

        // Now get the textrange after deleting all selected controls 
        spRange = 0; 
        spSel->createRange(&spRange); 
    } 

    CComQIPtr<IHTMLTxtRange, &IID_IHTMLTxtRange> spTxtRange = spRange; 
    if (spTxtRange) 
    { 
        // spTxtRange will be valid if nothing is selected or if text is selected 
        // or if multiple elements are seleted 
        CComBSTR bstrHtml(lpszHtml); 
        if (FAILED(spTxtRange->pasteHTML(bstrHtml))) 
            return FALSE; 
        // 
        WizHTMLDocumentSelectionScrollInToView(pDoc); 
        // 
        return TRUE; 
    } 
    return FALSE; 
} 

找到并修改MyCANormalPlugin1.cpp
这个文件主要用来注册COM组件(斜体部分为增加的内容)
 
// MyCANormalPlugin1.cpp : Implementation of DLL Exports. 


#include "stdafx.h" 
#include "resource.h" 
#include "MyCANormalPlugin1.h" 


/* 
* 包含必要的头文件。如果CyberArticle没有安装在C:\Program Files\Wizissoft\CyberArticle\,请修改下面相应的包含路径。 
*/ 
#include "C:/Program Files/Wizissoft/CyberArticle/developers/interface/include/CyberArticlePluginHelper.h" 


class CMyCANormalPlugin1Module : public CAtlDllModuleT< CMyCANormalPlugin1Module > 
{ 
public : 
    DECLARE_LIBID(LIBID_MyCANormalPlugin1Lib) 
    DECLARE_REGISTRY_APPID_RESOURCEID(IDR_MYCANORMALPLUGIN1, "{D1AF3660-89D7-4EBC-9549-50C4F6B8A1D7}") 
}; 

CMyCANormalPlugin1Module _AtlModule; 


#ifdef _MANAGED 
#pragma managed(push, off) 
#endif 

// DLL Entry Point 
extern "C" BOOL WINAPI DllMain(HINSTANCE hInstance, DWORD dwReason, LPVOID lpReserved) 
{ 
    hInstance; 
    return _AtlModule.DllMain(dwReason, lpReserved); 
} 

#ifdef _MANAGED 
#pragma managed(pop) 
#endif 




// Used to determine whether the DLL can be unloaded by OLE 
STDAPI DllCanUnloadNow(void) 
{ 
    return _AtlModule.DllCanUnloadNow(); 
} 


// Returns a class factory to create an object of the requested type 
STDAPI DllGetClassObject(REFCLSID rclsid, REFIID riid, LPVOID* ppv) 
{ 
    return _AtlModule.DllGetClassObject(rclsid, riid, ppv); 
} 


// DllRegisterServer - Adds entries to the system registry 
STDAPI DllRegisterServer(void) 
{ 
    // registers object, typelib and all interfaces in typelib 
    HRESULT hr = _AtlModule.DllRegisterServer(); 
    // 
    /* 
    注册CyberArticle插件 
    */ 
    CyberArticleRegisterPlugin(CLSID_CANormalPlugin1, _T("My CA Normal Plugin1")); 
    CyberArticleRegisterPlugin(CLSID_CANormalEditPlugin1, _T("删除为选中部分")); 
    // 
    return hr; 
} 


// DllUnregisterServer - Removes entries from the system registry 
STDAPI DllUnregisterServer(void) 
{ 
    HRESULT hr = _AtlModule.DllUnregisterServer(); 
    // 
    /* 
    取消注册CyberArticle插件 
    */ 
    CyberArticleUnregisterPlugin(CLSID_CANormalPlugin1); 
    CyberArticleUnregisterPlugin(CLSID_CANormalEditPlugin1); 
    // 
    return hr; 
}  
从例子中可以看到,要编写一个可以用于编辑文章的插件非常简单,只需要在实现ICAPlugin的基础上,增加实现ICAEditPlugin接口就可以了。
一旦获得了IWebBrowser2接口,插件就可以通过IHTMLDocument2来操作整个HTML文档,实现各种高级功能。