【COM範例】WM上獲取短信內容,AcitiveX控件調用頁面JS方法

    原文地址:http://www.cnblogs.com/hoodlum1980/archive/2009/12/30/1635702.html

 

      COM 是 WIN32 系統中最複雜和晦澀,最重要的技術。

 

  【備註:以下是個人看法】COM 是比傳統的 API 提供方式(*.h, *.lib, *.dll) 更”高“層次的服務標準, 從某種意義上說,COM也是一種”API“,但它的使用和實現都要比傳統API複雜的多。COM 的宗旨是提供這樣的一種標準,使操作系統,獨立軟件開發商之間以一種標準方式提供交互性服務。COM 相比傳統API合作方式相比:兩者本質上都提供的二進制代碼(編譯後產品),前者是基於有組織的,語言中立的 具有面向對象特性的標準。後者是零散的,語言相關,面向過程的方式。前者的顯著優點是,它的組件是共享式的安裝在系統上的一種服務,組件位置對用戶是透明的,因此它僅需要在每個客戶端部署一份(甚至部署在一臺遠程計算機)。而後者位置不透明因此未必能夠做到這一點。COM 技術使用推出新接口實現升級,以降低對客戶端的影響。而傳統 API 提供方式有可能在升級時導致DLL版本混亂。

 

  由於對語言中立性的追求以及COM的標準,COM 的代碼相比普通的 C/C++ 可讀性更低,但也具有一定規律性。ATL中提供了一些輔助類來降低 COM 代碼的使用難度。

 

  這裏是兩個例子。

 

  【例子1】: 在 Windows Mobile 上獲取收件箱中的短信內容。

 

  (1)每個帳戶對應的是一個MsgStore。每個MsgStore可包含一組文件夾。

  (2)提供EntryID,用 OpenEntry 方法打開MAPI對象。

  (3)MAPI容器 使用 GetContentTable 獲取容器內內容的描述表。

  (4)MAPI Table 使用SetColumns 設定要查詢的字段。

  (5)使用 MAPI 提供的函數釋放相關查詢信息分配的內存。

  更多細節參考 相關SDK 文檔。

 

  下面是代碼。當打開收件箱以後,短信是按照時間升序排列的,因此使用 SeekRow 方法把遊標遊動到最後一行讀出既是最近收到的短信。

 

Code_GetLastMessage
//獲取SMS的最近收到的短信,返回爲“發件人:內容”的格式。
bool GetLastMessage(TCHAR *buffer, int bufferSize)
{
    HRESULT hr 
= S_OK;
    ICEMAPISession 
*pSession = NULL;
    IMsgStore 
*pMsgStore = NULL;
    IMAPIFolder 
*pFolder = NULL;
    IMessage 
*pMsg = NULL;
    
    
//和Table有關的MAPI對象
    IMAPITable *pTable = NULL;
    SRowSet 
*pRows = NULL;    
    SPropValue 
*pSProps = NULL;
    ULONG ulObjType; 
//對象類型
    ULONG cCount = 0;
    
int remainLength, subjectLength; //字符串長度
    bool result = false;

    
//設置MsgStore表的要查詢字段
    SizedSPropTagArray(2 , Columns1) = 
    {
        
2,
        PR_ENTRYID, 
//Store的Entry ID
        PR_DISPLAY_NAME //Store的Display Name 
    };
    
//設置收件箱內容表要查詢的字段
    SizedSPropTagArray(1, Columns2) = 
    {
        
1,
        PR_ENTRYID  
//獲取每個消息的EntryID
    };


    CoInitializeEx(NULL, COINIT_MULTITHREADED);  
    hr 
= MAPIInitialize(NULL);
    
if(FAILED(hr))
    {
        wcscpy_s(buffer, bufferSize, _T(
"failed at: MAPIInitialize"));
        
goto TAG_CLEAR;
    }

    hr 
= MAPILogonEx(0, NULL, NULL, 0, (LPMAPISESSION *)&pSession);
    
if(FAILED(hr))
    {
        wcscpy_s(buffer, bufferSize, _T(
"failed at: MAPILogonEx"));
        
goto TAG_CLEAR;
    }
    hr 
= pSession->GetMsgStoresTable(0&pTable);
    
if(FAILED(hr))
    {
        wcscpy_s(buffer, bufferSize, _T(
"failed at: GetMsgStoresTable"));
        
goto TAG_CLEAR;
    }

    pTable
->SetColumns((LPSPropTagArray)&Columns1, 0); 

    
while(SUCCEEDED(pTable->QueryRows(10&pRows)))
    {
        
if (pRows == NULL || pRows->cRows != 1
            
break

        
//開始一條條記錄查詢,
        
//pRows->aRow[0].lpProps[0]代表了第一個查詢屬性,即Entry ID,
        
//pRows->aRow[0].lpProps[1]則表示Display Name。 
        if (_tcsicmp(pRows->aRow[0].lpProps[1].Value.lpszW, _T("SMS")) == 0
        { 
            
//如果是SMS,則獲取Store對象 
            pSession->OpenEntry(
                pRows
->aRow[0].lpProps[0].Value.bin.cb, (LPENTRYID)pRows->aRow[0].lpProps[0].Value.bin.lpb, 
                NULL, MAPI_BEST_ACCESS,    
&ulObjType,(LPUNKNOWN*)&pMsgStore
                );
            
break;
        }
        FreeProws(pRows);
        pRows 
= NULL;
    }
    
if(pRows != NULL)
    {
        FreeProws(pRows); 
    }
    
if(pTable != NULL)
    {
        pTable
->Release();
    }

    
if(pMsgStore == NULL)
    {
        wcscpy_s(buffer, bufferSize, _T(
"Cannot Open MsgStore of SMS."));
        
goto TAG_CLEAR;
    }

    
//查詢“收件箱”的ENTRYID
    ULONG STags[] = { 1,    PR_CE_IPM_INBOX_ENTRYID };
    hr 
= pMsgStore->GetProps((SPropTagArray*)&STags, 0&cCount, &pSProps);
    
if(FAILED(hr))
    {
        wcscpy_s(buffer, bufferSize, _T(
"Cannot Get Inbox's EntryID."));
      if(pSProps != NULL) MAPIFreeBuffer(pSProps);
        
goto TAG_CLEAR;
    }

    
//打開收件箱文件夾
    hr = pMsgStore->OpenEntry(pSProps[0].Value.bin.cb, (LPENTRYID)pSProps[0].Value.bin.lpb,NULL,0&ulObjType, (IUnknown **)&pFolder);
    if(pSProps != NULL)
        MAPIFreeBuffer(pSProps);

    
if (FAILED(hr))
    {
        wcscpy_s(buffer, bufferSize, _T(
"Cannot Open Folder Of Inbox."));
        
goto TAG_CLEAR;
    }

    
//獲取收件箱的內容表。
    pFolder->GetContentsTable(0&pTable);

    
//嘗試移動到最後一行(最近收到的短信)
    ULONG rowCount;
    hr 
= pTable->GetRowCount(0&rowCount);
    
if(FAILED(hr) || rowCount <= 0)
    {
        pTable
->Release();
        pTable 
= NULL;
        wcscpy_s(buffer, bufferSize, _T(
"收件箱:沒有新的消息。"));
        
goto TAG_CLEAR;
    }
    hr 
= pTable->SeekRow(BOOKMARK_BEGINNING, rowCount - 1, NULL);
    hr 
= pTable->SetColumns((LPSPropTagArray)&Columns2, 0);
    
    
//要查詢的消息屬性:發件人,主題(短信內容)
    ULONG pMsgPropTags[] = { 2, PR_SENDER_EMAIL_ADDRESS, PR_SUBJECT };

    
while(SUCCEEDED(pTable->QueryRows(10&pRows)))
    {
        
//通過OpenEntry獲取IMessage對象
        pFolder->OpenEntry(
            pRows
->aRow[0].lpProps[0].Value.bin.cb,
            (LPENTRYID)pRows
->aRow[0].lpProps[0].Value.bin.lpb, //Message's EntryID
            NULL, 
            MAPI_BEST_ACCESS, 
            
&ulObjType,
            (LPUNKNOWN
*)&pMsg
            );
        
//這裏拿到每個IMessage對象就代表了Folder裏面的每一條Message,可以通過對它的操作來獲取想要的信息。
        hr = pMsg->GetProps((SPropTagArray*)&pMsgPropTags, MAPI_UNICODE, &cCount, &pSProps);
        
if(SUCCEEDED(hr))
        {
            
if(pSProps[0].ulPropTag == PR_SENDER_EMAIL_ADDRESS)
            {
                wcscpy_s(buffer, bufferSize, pSProps[
0].Value.lpszW);
                wcscat_s(buffer, bufferSize, _T(
""));
            }
            
else
            {
                buffer[
0= '/0';
            }

            
if(pSProps[1].ulPropTag == PR_SUBJECT)
            {
                remainLength  
= bufferSize - wcslen(buffer) - 1;
                subjectLength 
= wcslen(pSProps[1].Value.lpszW);
                
if(remainLength >= subjectLength)
                    wcscat_s(buffer, bufferSize, pSProps[
1].Value.lpszW);
                
else
                {
                    
//截斷字符串,並用三個點表示省略。
                    wcsncpy_s(buffer + wcslen(buffer), bufferSize, pSProps[1].Value.lpszW, remainLength - 3);
                    wcscat_s(buffer, bufferSize, _T(
"..."));
                }
            }
            result 
= true;            
        }
        
else
        {
            wcscpy_s(buffer, bufferSize, _T(
"Cannot Get Message."));
        }
        
if(pSProps != NULL)
            MAPIFreeBuffer(pSProps);

        
//僅僅讀取一條Message
        break;
    }

    
if(pRows != NULL)
    {
        FreeProws(pRows); 
        pRows 
= NULL; 
    }
    
if(pTable != NULL)
    {
        pTable
->Release();
        pTable 
= NULL;
    }


TAG_CLEAR:

    
if(pMsg != NULL)
        pMsg
->Release();

    
if(pFolder != NULL)
        pFolder
->Release();

    
if(pMsgStore != NULL)
        pMsgStore
->Release();

    
if(pSession != NULL)
    {
        pSession
->Logoff(000);
        pSession
->Release();
    }
    MAPIUninitialize();
    CoUninitialize();
    
return result;
}

 

  本段代碼已運用於 LedScreen插件V2.0 中:  
  

    

 

  【例子2】:在ActiveX控件中調用頁面上的 JavaScrpt 方法。

 

  (1)IE調用ActiveX控件的方法,本質上是通過控件的 IDispatch 接口調用的,該技術也就是自動化技術。該技術綁定時間比自定義接口最晚,因此效率不如通過自定義接口調用,但是也最靈活。 當我們在ActiveX 控件中調用頁面的腳本時,我們同樣通過腳本對象的 IDispatch 接口調用。

 

  (2)假設 ActiveX 控件事先知道頁面上的方法的參數列表,(相當於我們已知一個C#委託或者C++函數指針定義),如果不這樣假定,則實在意義不大。

 

   (3)  我們在控件接口中添加一個方法,稱爲InvokeJS。在這個方法裏我們試圖調用 頁面上的一個指定名稱的javascript方法。並且我們給這個方法一個字符串參數,內容是”hello world“。代碼如下:

 

code_InvokeJS
//調用JS方法(無參數)
STDMETHODIMP CMyControl::InvokeJS(BSTR funName)
{
    
if(m_document == NULL)
        
return S_OK;

    CComPtr
<IHTMLDocument2> pHtmlDoc;
    CComPtr
<IDispatch> pScript; //CComPtr:會自動調用Release();
    
    HRESULT hr;
    CComBSTR bstrMember;
    DISPID dispid; 
//DISPID即LONG(int32),接口函數的數字序號。

    bstrMember.AssignBSTR(funName);
    hr 
= this->m_document->QueryInterface(IID_IHTMLDocument2, (void**)&pHtmlDoc);
    hr 
= pHtmlDoc->get_Script(&pScript);
    hr 
= pScript->GetIDsOfNames(IID_NULL, &bstrMember, 1, LOCALE_SYSTEM_DEFAULT, &dispid);


    
//調用腳本對象的Invoke方法執行腳本函數:
    DISPPARAMS dispparams;
    memset(
&dispparams, 0sizeof dispparams);
    dispparams.cArgs 
= 1;
    dispparams.rgvarg 
= new VARIANT[dispparams.cArgs];
  
    
forint i = 0; i < dispparams.cArgs; i++)
    {
        CComBSTR bstr 
= "hello world"// 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 = pScript->Invoke(dispid, IID_NULL, 0, DISPATCH_METHOD, &dispparams, &vaResult, &excepInfo, &nArgErr);

    return S_OK;
}

 

  瀏覽器對象實際上我們是在控件的 SetClientSite 方法中獲取的, 該方法在創建控件時由容器調用,以給ActiveX控件一個時機去獲取一些容器信息。則在MSDN文檔中有專門介紹。控件的SetClientSite代碼如下:

 

code_setClientSite
//以下是控件的兩個成員變量:
private:
    IWebBrowser2
* m_browser; //瀏覽器對象
    IDispatch* m_document; //文檔對象


STDMETHODIMP CMyControl::SetClientSite(IOleClientSite
* pClientSite)
{
    HRESULT hr 
= S_OK;
    IServiceProvider 
*isp, *isp2 = NULL;
    
//BSTR url;

    
if (!pClientSite)
    {
        COMRELEASE(m_browser);
    }
    
else
    {
        hr 
= 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 **>(&m_browser));
        
if (FAILED(hr))
        {
            hr 
= S_OK;
            
goto cleanup;
        }

        
//獲取文檔對象
        hr = m_browser->get_Document(&m_document);
        
if(FAILED(hr))
        {
            m_document 
= NULL;
        }
        
//m_browser->get_LocationURL(&url);
        
//MessageBox((LPCTSTR)url, _T(""), MB_OK);
cleanup:
        
// Free resources.
        COMRELEASE(isp);
        COMRELEASE(isp2);
        
return hr;
    }

    
return S_OK;
}

 

   (4)假設在頁面上具有這樣一個JS方法:當我們點擊按鈕時,頁面首先通過IDispatch接口找到控件的InvokeJS方法,然後在控件中又通過IDispatch接口找到腳本的AlertMessage方法,彈出一個MessageBox。

 

code_HTML
<HTML>
<HEAD>
<TITLE>對象測試頁</TITLE>
</HEAD>
<BODY>
<script type="text/javascript" >
function AlertMessage(info)
{
    alert(info);
}
</script>
<OBJECT ID="MyControl" CLASSID="CLSID:......"></OBJECT>
<button onclick="MyControl.InvokeJS('AlertMessage');">InvokeJavaScript</button>
</BODY>
</HTML>

 

   【例子3】:通過 ITaskBar 對象隱藏或顯示任務欄按鈕。

  

Code_ShowInTaskBar
void ShowInTaskbarList(HWND hWnd, BOOL bShow)
{
    HRESULT hr;
    ITaskbarList
* pTaskbarList;

    CoInitialize(NULL);
    hr 
= CoCreateInstance(CLSID_TaskbarList, NULL, CLSCTX_INPROC_SERVER, 
            IID_ITaskbarList, (
void**)&pTaskbarList);

    
//==============  HrInit();==================
    
//Initializes the taskbar list object. This method must be called before any other 
    
//ITaskbarList methods can be called. 
    pTaskbarList->HrInit();

    
if(bShow)
    {
        pTaskbarList
->AddTab(hWnd);
    }
    
else
    {
        pTaskbarList
->DeleteTab(hWnd);
    }
    pTaskbarList
->Release();

    CoUninitialize();
}

 

 

 

   參考內容:

  《Mapi實例 》:http://bingqingwu5799.blog.163.com/blog/static/338365512009320111114912/

  MSDN & Windows Mobile 6 SDK Documents;

  CSDN 關於 ActiveX 調用頁面腳本的文章。(原連接未記錄)

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章