天天看點

Win32程式設計點滴3 - 簡單ActiveX控件的使用

雖然這裡一片的.net氣氛,到處充斥着像MVC、WPF、WorkFlow、LINQ等各種niubility的術語。但我們使用的Windows還是由COM技術主宰着;我們在選擇日常使用的軟體時,也會避免使用.net開發的軟體。即便是.net的桌面程式,也會經常使用ActiveX控件。這篇文章就讓我們用最原始的方式來使用ActiveX,不使用任何MFC,ATL等架構,也不使用編譯器提供的#import之類的指令,也不使用任何ide提供的向導。

像OLE、ActiveX等COM的術語,即便是微軟也說不清它們的關系,是以下面說的我也這樣模棱兩可下去,隻要明白意思即可。

首先,要了解一下的是ActiveX技術是為了做“嵌入”這樣的功能而誕生的,比如:在Word中插入一張Bitmap圖檔,輕按兩下此圖檔,Word會調用畫筆程式的功能來編輯圖檔,整個Word的菜單欄也會變成畫筆程式的菜單欄。是以,ActiveX控件相當的複雜,有着幾十個相關的接口。簡單的說來,ActiveX控件的父視窗被稱為“容器”,是以作為ActiveX控件的使用者來說,要實作的接口基本上都是IOleXXXXContainer或IOleXXXXSite之類的;而ActiveX則實作了IOleXXXXObject等接口。在這些接口中,大多有“InPlace”這個術語,指的是“InPlace edit”,也就是Word通過輕按兩下圖檔調用畫筆編輯圖檔就稱為“InPlace edit”(僅僅了解一下,和這篇文章說的使用AcitveX控件無關)。

在這樣一篇文章中,我并不想講很多COM或者AcitveX的知識,隻是講使用ActiveX所必須涉及的接口,然後你就可以去查MSDN中的其他一些可選的接口來一步步對這個ActiveX加強控制。

作為一個最簡單的程式,我們需要實作的接口有:

IOleClientSite IOleInPlaceSite 。使用到的AcitveX提供的接口有: IOleObject IOleInPlaceObject 。建立ActiveX控件的步驟:

  1. 建立一個類,實作
  2. 使用CoCreateInstance建立相應ActiveX控件的執行個體,并擷取它的 接口指針。
  3. 調用 IOleObject::SetClientSite 傳入第一步中的類的指針。
  4. IOleObject::DoVerb 完成ActiveX控件的建立。
  5. 之後,可以調用ActiveX控件的 IOleInPlaceObject::SetObjectRects 調整控件的大小和位置。

根據以上步驟,建立如下函數:

HRESULT CreateAxControl(HWND hWnd,const wchar_t * ProgId,IUnknown ** ppControlUnknown,IUnknown ** ppContainerUnknown);      

第一個參數hWnd是父視窗句柄。ProgId是ActiveX控件的ProgId,因為我們不使用編譯器的#import,一般不知道所要建立控件的CLSID。ppControlUnknown是用來傳回ActiveX控件的IUnknown指針。ppContainerUnknown是用來傳回用來代表父視窗的IUnknown指針。

代碼如下(這裡的代碼去除了出錯的處理):

Win32程式設計點滴3 - 簡單ActiveX控件的使用
HRESULT CreateAxControl(HWND hWnd,const wchar_t * ProgId,IUnknown ** ppControlUnknown,IUnknown ** ppContainerUnknown)
{
    HRESULT hr;
    CLSID cls;
    IOleObject * pObject = NULL;
    CControlContainer * pContainer = NULL;

//通過ProgId得到CLSID
    CLSIDFromProgID(ProgId,&cls);
    
    //建立ActiveX控件的對象,順便得到IOleObject指針
    CoCreateInstance(cls,NULL,CLSCTX_INPROC_SERVER|CLSCTX_INPROC_HANDLER|CLSCTX_LOCAL_SERVER,
        IID_IOleObject,(void**)&pObject);
    
    //CControlContainer是實作了IOleClientSite和IOleInPlaceSite接口的類
    pContainer = new CControlContainer(hWnd);
    
    //調用IOleObject::SetClientSite,傳入容器指針
    pObject->SetClientSite(pContainer);
    
    //調用IOleObject::DoVerb,顯示控件
    pObject->DoVerb(OLEIVERB_SHOW,0,pContainer,0,hWnd,0);
    
    //一些傳回的參數
    pObject->QueryInterface(IID_IUnknown,(void**)ppControlUnknown);
    pContainer->QueryInterface(IID_IUnknown,(void**)ppContainerUnknown);

    if (pObject) pObject->Release();
    if (pContainer) pContainer->Release();
    return S_OK;
}
      
Win32程式設計點滴3 - 簡單ActiveX控件的使用

CControlContainer類的實作非常簡單,基本上IOleClientSite和IOleInPlaceSite接口大部分的方法都隻要簡單的傳回S_OK或E_NOTIMPLE即可。唯一需要實作的是IUnknown的方法,還有IOleWindow接口(IOleInPlaceSite繼承于IOleWindow)的GetWindow(傳回父視窗的句柄)。代碼如下:

Win32程式設計點滴3 - 簡單ActiveX控件的使用
class CControlContainer:public IOleClientSite,public IOleInPlaceSite
{
    HWND m_hWnd;
    ULONG m_refCnt;
public:
    CControlContainer(HWND hWnd)
    {
        m_hWnd = hWnd;
        m_refCnt = 1;
    }
    ~CControlContainer()
    {
    }
    .... IUnknown的實作
    //IOleControlSite
    STDMETHOD(SaveObject())
    {
        return E_NOTIMPL;
    }
    STDMETHOD(GetMoniker(DWORD,DWORD,IMoniker**))
    {
        return E_NOTIMPL;
    }
    STDMETHOD(GetContainer(IOleContainer **ppContainer))
    {
        return E_NOINTERFACE;
    }
    STDMETHOD(ShowObject())
    {
        return S_OK;
    }
    STDMETHOD(OnShowWindow(BOOL bShow))
    {
        return S_OK;
    }
    STDMETHOD(RequestNewObjectLayout())
    {
        return E_NOTIMPL;
    }
    //IOleWindow
    STDMETHOD(GetWindow(HWND * pHwnd))
    {
        *pHwnd = m_hWnd;
        return S_OK;
    }
    STDMETHOD(ContextSensitiveHelp(BOOL bEnterMode))
    {
        return S_OK;
    }
    //IOleInPlaceSite
    STDMETHOD(CanInPlaceActivate())
    {
        return S_OK;
    }
    STDMETHOD(OnInPlaceActivate())
    {
        return S_OK;
    }
    STDMETHOD(OnUIActivate())
    {
        return S_OK;
    }
    STDMETHOD(GetWindowContext(/* [out] */ IOleInPlaceFrame **ppFrame,
/* [out] */ IOleInPlaceUIWindow **ppDoc,
/* [out] */ LPRECT lprcPosRect,
/* [out] */ LPRECT lprcClipRect,
/* [out][in] */ LPOLEINPLACEFRAMEINFO lpFrameInfo))
    {
        return E_NOTIMPL;
    }
    STDMETHOD(Scroll(SIZE scrollSize))
    {
        return S_OK;
    }
    STDMETHOD(OnUIDeactivate(BOOL bUndoable))
    {
        return S_OK;
    }
    STDMETHOD(OnInPlaceDeactivate())
    {
        return S_OK;
    }
    STDMETHOD(DiscardUndoState())
    {
        return S_OK;
    }
    STDMETHOD(DeactivateAndUndo())
    {
        return S_OK;
    }
    STDMETHOD(OnPosRectChange(LPCRECT lprcPosRect))
    {
        return S_OK;
    }
};      
Win32程式設計點滴3 - 簡單ActiveX控件的使用

接下來,在父視窗的視窗過程中,調用上面實作的函數,建立一個Flash控件:

Win32程式設計點滴3 - 簡單ActiveX控件的使用
IUnknown * g_pControl = NULL;//控件的指針
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
    ....
    switch(message)
    {
    case WM_CREATE:
        //Flash控件
        hr = CreateAxControl(hWnd,L"ShockwaveFlash.ShockwaveFlash",&pControl,&pContainer);
        if (SUCCESSED(hr))
        {
            VARIANT src;
            src.vt = VT_BSTR;
            src.bstrVal = SysAllocString(L"http://www.google.com/intl/en_ALL/images/logo.gif");
            DispSetProperty(pControl,L"movie",&src);//這個函數的實作,請下載下傳源代碼
        }
        break;
    case WM_SIZE:
        {   
            //調整控件的大小            
            RECT rcClient;
            GetClientRect(hWnd,&rcClient);
            IOleInPlaceObject * pInPlaceObject;
            if (g_pControl && 
                SUCCEEDED(g_pControl->QueryInterface(IID_IOleInPlaceObject,(void**)&pInPlaceObject)))
            {
                pInPlaceObject->SetObjectRects(&rcClient,&rcClient);
                pInPlaceObject->Release();
            }
        }
        break;
    }
    ....
}      
Win32程式設計點滴3 - 簡單ActiveX控件的使用
下載下傳源代碼

在下一篇,可能會寫如何響應ActiveX的Dispatch事件。

繼續閱讀