<menu id="w8yyk"><menu id="w8yyk"></menu></menu>
  • <dd id="w8yyk"><nav id="w8yyk"></nav></dd>
    <menu id="w8yyk"></menu>
    <menu id="w8yyk"><code id="w8yyk"></code></menu>
    <menu id="w8yyk"></menu>
    <xmp id="w8yyk">
    <xmp id="w8yyk"><nav id="w8yyk"></nav>
  • 網站首頁 > 物聯資訊 > 技術分享

    COM組件開發實踐(七)---多線程ActiveX控件和自動調整ActiveX控件大小(上)

    2016-09-28 00:00:00 廣州睿豐德信息科技有限公司 閱讀
    睿豐德科技 專注RFID識別技術和條碼識別技術與管理軟件的集成項目。質量追溯系統、MES系統、金蝶與條碼系統對接、用友與條碼系統對接

    聲明:本文代碼基于CodeProject的文章《A Complete ActiveX Web Control Tutorial》修改而來,因此同樣遵循Code Project Open License (CPOL)

          最近遇到兩個需求:1)在ActiveX控件中使用工作線程來完成底層的硬件設備掃描任務,并在工作線程中根據操作結果回調外部web頁面的JavaScript函數;2)能根據控件任務的不同自動調整控件大小。但在查閱了大量資料后,發現網上討論ActiveX中多線程開發的文章基本沒有,最后在csdn論壇里遇到一個高手幫忙后,摸索了幾天才解決這兩個問題,本文的目的就在于記錄下我解決這兩個問題的過程,也希望能幫助到以后有同樣需求的朋友。

          簡單抽象下第一個任務的模型:在AcitveX控件中開啟一個工作線程去執行特點任務后,然后根據工作線程的執行結果中去通知外部的web頁面的JavaScript。在進入到多線程之前,先來介紹下ActiveX中調用外部web頁面的JavaScript函數的兩種方式。

    ActiveX中調用JavaScript

           第一種方式是使用事件,這是最簡單方法。在類視圖中,右鍵CMyActiveXCtrl ,選擇添加事件,這種方式就不贅述了。

          第二種方式是利用IWebBrowser2IHTMLDocument2這兩個COM組件來訪問包含ActiveX控件的外部Web頁面上的所有元素。具體實現步驟如下:

    1, CMyActiveXCtrl類中加入兩個變量:

    復制代碼public:
        IWebBrowser2
    * pWebBrowser; //IE瀏覽器
        IHTMLDocument2* pHTMLDocument; //包含此控件的web頁面
    復制代碼

    2重載OnSetClientSite函數。

    復制代碼void CMyActiveXCtrl::OnSetClientSite()
    {
        HRESULT hr 
    = S_OK;
        IServiceProvider 
    *isp, *isp2 = NULL;
        
    if (!m_pClientSite)
        {
            COMRELEASE(pWebBrowser);
        }  
        
    else
        {
            hr 
    = m_pClientSite->QueryInterface(IID_IServiceProvider, reinterpret_cast<void **>(&isp));
            
    if (FAILED(hr)) 
            {
                hr 
    = S_OK;
                
    goto cleanup;
            }
            hr 
    = isp->QueryService(SID_STopLevelBrowser, IID_IServiceProvider, reinterpret_cast<void **>(&isp2));
            
    if (FAILED(hr))
            {
                hr 
    = S_OK;
                
    goto cleanup;
            }
            hr 
    = isp2->QueryService(SID_SWebBrowserApp, IID_IWebBrowser2, reinterpret_cast<void **>(&pWebBrowser)); //查詢IE瀏覽器接口
            if (FAILED(hr)) 
            {
                hr 
    = S_OK;
                
    goto cleanup;
            }
            hr   
    =   pWebBrowser->get_Document((IDispatch**)&pHTMLDocument); //查詢Web頁面接口  
            if(FAILED(hr))   
            {   
                hr 
    = S_OK;
                
    goto cleanup;
            }   
        cleanup:
            
    // Free resources.
            COMRELEASE(isp);
            COMRELEASE(isp2);
        }
    }
    復制代碼

    3,控件在加載后會調用OnSetClientSite函數的,因此就會查詢到對應包含控件的Web頁面,有了這個頁面后,就可以使用下述函數來調用Web頁面中的JavaScript函數了。下述代碼來自CodeGuru 的文章JavaScript Calls from C++》,感興趣的話可以細讀。

    復制代碼bool CMyActiveXCtrl::GetJScript(CComPtr<IDispatch>& spDisp)
    {
        CHECK_POINTER(pHTMLDocument);
        HRESULT hr 
    = pHTMLDocument->get_Script(&spDisp);
        ATLASSERT(SUCCEEDED(hr));
        
    return SUCCEEDED(hr);
    }

    bool CMyActiveXCtrl::GetJScripts(CComPtr<IHTMLElementCollection>& spColl)
    {
        CHECK_POINTER(pHTMLDocument);
        HRESULT hr 
    = pHTMLDocument->get_scripts(&spColl);
        ATLASSERT(SUCCEEDED(hr));
        
    return SUCCEEDED(hr);
    }

    bool CMyActiveXCtrl::CallJScript(const CString strFunc,CComVariant* pVarResult)
    {
        CStringArray paramArray;
        
    return CallJScript(strFunc,paramArray,pVarResult);
    }

    bool CMyActiveXCtrl::CallJScript(const CString strFunc,const CString strArg1,CComVariant* pVarResult)
    {
        CStringArray paramArray;
        paramArray.Add(strArg1);
        
    return CallJScript(strFunc,paramArray,pVarResult);
    }

    bool CMyActiveXCtrl::CallJScript(const CString strFunc,const CString strArg1,const CString strArg2,CComVariant* pVarResult)
    {
        CStringArray paramArray;
        paramArray.Add(strArg1);
        paramArray.Add(strArg2);
        
    return CallJScript(strFunc,paramArray,pVarResult);
    }

    bool CMyActiveXCtrl::CallJScript(const CString strFunc,const CString strArg1,const CString strArg2,const CString strArg3,CComVariant* pVarResult)
    {
        CStringArray paramArray;
        paramArray.Add(strArg1);
        paramArray.Add(strArg2);
        paramArray.Add(strArg3);
        
    return CallJScript(strFunc,paramArray,pVarResult);
    }

    bool CMyActiveXCtrl::CallJScript(const CString strFunc, const CStringArray& paramArray,CComVariant* pVarResult)
    {
        CComPtr
    <IDispatch> spScript;
        
    if(!GetJScript(spScript))
        {
            
    //ShowError("Cannot GetScript");
            return false;
        }
        CComBSTR bstrMember(strFunc);
        DISPID dispid 
    = NULL;
        HRESULT hr 
    = spScript->GetIDsOfNames(IID_NULL,&bstrMember,1,
            LOCALE_SYSTEM_DEFAULT,
    &dispid);
        
    if(FAILED(hr))
        {
            
    //ShowError(GetSystemErrorMessage(hr));
            return false;
        }
        
    const int arraySize = paramArray.GetSize();
        DISPPARAMS dispparams;
        memset(
    &dispparams, 0sizeof dispparams);
        dispparams.cArgs 
    = arraySize;
        dispparams.rgvarg 
    = new VARIANT[dispparams.cArgs];
        
    forint i = 0; i < arraySize; i++)
        {
            CComBSTR bstr 
    = paramArray.GetAt(arraySize - 1 - i); // back reading
            bstr.CopyTo(&dispparams.rgvarg[i].bstrVal);
            dispparams.rgvarg[i].vt 
    = VT_BSTR;
        }
        dispparams.cNamedArgs 
    = 0;
        EXCEPINFO excepInfo;
        memset(
    &excepInfo, 0sizeof excepInfo);
        CComVariant vaResult;
        UINT nArgErr 
    = (UINT)-1;  // initialize to invalid arg
        hr = spScript->Invoke(dispid,IID_NULL,0,
            DISPATCH_METHOD,
    &dispparams,&vaResult,&excepInfo,&nArgErr);
        delete [] dispparams.rgvarg;
        
    if(FAILED(hr))
        {
            
    //ShowError(GetSystemErrorMessage(hr));
            return false;
        }
        
    if(pVarResult)
        {
            
    *pVarResult = vaResult;
        }
        
    return true;
    }
    復制代碼

    4,現在就可以來測試上述兩種調用JavaScript函數的方式了,為了簡單起見,就在原文代碼的基礎上修改了下。

    復制代碼void CMyActiveXCtrl::LoadParameter(void)
    {
        AFX_MANAGE_STATE(AfxGetStaticModuleState());
        m_OutputParameter 
    = m_InputParameter;
        
    // Fire an event to notify web page
        FireParameterLoaded();
        CString strOnLoaded(
    "OnLoaded");
        
    this->CallJScript(strOnLoaded);
    }
    復制代碼

    并且在web頁面中加入了一個測試用的JavaScript函數

    復制代碼function OnLoaded()
    {
        alert(
    "phinecos");
    }
    復制代碼

    多線程ActiveX控件

           有了上面調用JavaScript函數的基礎,現在就來為控件加入工作線程,然后在線程中根據任務執行結果來通知外部Web頁面做出應有的響應。

          我的第一個思路就是在主線程中設置回調函數,等創建子線程時,讓子線程保存主線程的指針,然后在線程執行過程中根據執行的結果去回調主線程的回調函數。這種思路看上去很不錯,就先按這步走。

          首先創建一個回調函數接口,指明主線程應有的回調函數

    復制代碼class ICallBack
    {
    public:
        
    virtual void OnSuccesful() = 0;//操作成功
        virtual void OnFailed() = 0;//操作失敗
    };
    復制代碼

          然后讓CMyActiveXCtrl控件類繼承自這個虛基類,實現這些回調函數接口

    class CMyActiveXCtrl : public COleControl,public ICallBack

    線程基類

           為了處理線程方便,本文使用了CodeProjectTrafficWatcher這篇文章中的一個CThread類,稍作修改得到下面的CMyThread類,就是在其中加入了ICallBackpCallBack這個主線程的回調函數接口。

    復制代碼class CMyThread
    {
    public:
        CMyThread()
        { 
            m_pThreadFunction 
    = CMyThread::EntryPoint;
            m_runthread 
    = FALSE;
        }
        
    virtual ~CMyThread()
        {
            
    if ( m_hThread )
                Stop(
    true);                    //thread still running, so force the thread to stop!
        }
        DWORD Start(DWORD dwCreationFlags 
    = 0)
        {
            m_runthread 
    = true;
            m_hThread 
    = CreateThread(NULL, 0, m_pThreadFunction, this, dwCreationFlags,&m_dwTID);
            m_dwExitCode 
    = (DWORD)-1;
            
    return GetLastError();
        }
        
    /**//**
            * Stops the thread.
            *    
            * @param bForceKill        if true, the Thread is killed immediately
            
    */
        DWORD Stop ( 
    bool bForceKill = false )
        {
            
    if ( m_hThread )
            {
                
    //嘗試"溫柔地"結束線程
                if (m_runthread == TRUE)
                    m_runthread 
    = FALSE;        //first, try to stop the thread nice
                GetExitCodeThread(m_hThread, &m_dwExitCode);
                
    if ( m_dwExitCode == STILL_ACTIVE && bForceKill )
                {
    //強制殺死線程
                    TerminateThread(m_hThread, DWORD(-1));
                    m_hThread 
    = NULL;
                }
            }
            
    return m_dwExitCode;
        }
        
    /**//**
            * Stops the thread. first tell the thread to stop itself and wait for the thread to stop itself.
            * if timeout occurs and the thread hasn't stopped yet, then the thread is killed.
            * @param timeout    milliseconds to wait for the thread to stop itself
            
    */
        DWORD Stop ( WORD timeout )
        {
            Stop(
    false);
            WaitForSingleObject(m_hThread, timeout);
    //等待一段時間
            return Stop(true);
        }
        
    /**//**
            * suspends the thread. i.e. the thread is halted but not killed. To start a suspended thread call Resume().
            
    */
        DWORD Suspend()
        {
    //掛起線程
            return SuspendThread(m_hThread);
        }
        
    /**//*
            * resumes the thread. this method starts a created and suspended thread again.
            
    */
        DWORD Resume()
        {
    //恢復線程
            return ResumeThread(m_hThread);
        }
        
    /**//**
            * sets the priority of the thread.
            * @param priority    the priority. see SetThreadPriority() in windows sdk for possible values.
            * @return true if successful
            
    */
        BOOL SetPriority(
    int priority)
        {
    //設置線程優先級
            return SetThreadPriority(m_hThread, priority);
        }
        
    /**//**
            * gets the current priority value of the thread.
            * @return the current priority value
            
    */
        
    int GetPriority()
        {
    //獲取線程優先級
            return GetThreadPriority(m_hThread);
        }
        
    void SetICallBack(ICallBack* pCallBack)
        {
            
    this->pCallBack = pCallBack;
        }
    protected:
        
    /**
            * 子類應該重寫此方法,這個方法是實際的工作線程函數
            
    */
        
    virtual DWORD ThreadMethod() = 0;
    private:

        
    /**//**
            * DONT override this method.
            *
            * this method is the "function" used when creating the thread. it is static so that way
            * a pointer to it is available inside the class. this method calls then the virtual 
            * method of the parent class.
            
    */
        
    static DWORD WINAPI EntryPoint( LPVOID pArg)
        {
            CMyThread 
    *pParent = reinterpret_cast<CMyThread*>(pArg);
            pParent
    ->ThreadMethod();//多態性,調用子類的實際工作函數
            return 0;
        }
    private:
        HANDLE    m_hThread;                    
    //線程句柄
        DWORD    m_dwTID;                    //線程ID
        LPVOID    m_pParent;                    //this pointer of the parent CThread object
        DWORD    m_dwExitCode;                //線程退出碼
    protected:
        LPTHREAD_START_ROUTINE    m_pThreadFunction;   
    //工作線程指針
        BOOL    m_runthread;                //線程是否繼續運行的標志
        ICallBack* pCallBack; //主線程的回調函數接口
    };

    復制代碼

    具體的工作線程子類

           具體的工作線程子類只需要從CMyThread繼承下去,重載ThreadMethod方法即可,為了簡單起見,下面就只模擬了操作設備成功的情況,當然可以根據實際應用記入具體操作代碼。

    復制代碼class CMyTaskThread :public CMyThread

    DWORD CMyTaskThread::ThreadMethod()
    {
        
    while(m_runthread)   
        {   
            
    this->pCallBack->OnSuccesful();//模擬操作成功,回調主線程
            Sleep(5000); //休息會再模擬   
        } 
        
    return 0;
    }
    復制代碼

    回調函數

          按照最明顯的思路,結合第一部分的知識,很顯然回調函數應該是下面這樣,選擇事件或直接調用外部的JavaScript函數來通知外部web頁面響應。

    復制代碼    void CMyActiveXCtrl::OnSuccesful()
    {
    //操作成功
            
    //FireParameterLoaded();
            CString strOnLoaded("OnLoaded");
            
    this->CallJScript(strOnLoaded);

    }
    復制代碼

         但不幸的是,這樣做根本無效,外部的Web頁面無法收到響應,就這個問題折騰了好幾天,思路上看好像沒什么錯呀,怎么就回調不了呢?。。。

    那么正確的做法應該是怎樣的呢?限于本文篇幅,將在下一篇中給出解答,并放出完整源代碼。

     

    作者:phinecos(洞庭散人)
    出處:http://phinecos.cnblogs.com/
    本文版權歸作者和博客園共有,歡迎轉載,但請保留此段聲明,并在文章頁面明顯位置給出原文連接。

    RFID管理系統集成商 RFID中間件 條碼系統中間層 物聯網軟件集成
    最近免费观看高清韩国日本大全