zoukankan      html  css  js  c++  java
  • 【COM范例】WM上获取短信内容,AcitiveX控件调用页面JS方法

      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 调用页面脚本的文章。(原连接未记录)

  • 相关阅读:
    类的加载顺序
    自定义形状类
    java的参数传递
    复数相加+equels、hashcode、clone<二>
    复数相加+equels、hashcode、clone<一>
    命令行程序
    计算阶乘
    控制程序的流程
    java运算符
    强制类型转换细节解析
  • 原文地址:https://www.cnblogs.com/hoodlum1980/p/1635702.html
Copyright © 2011-2022 走看看