【COM范例】WM上获取短信内容,AcitiveX控件调用页面JS方法

简介:   COM 是 WIN32 系统中最复杂和晦涩,最重要的技术。     【备注:以下是个人看法】COM 是比传统的 API 提供方式(*.h, *.lib, *.dll) 更”高“层次的服务标准, 从某种意义上说,COM也是一种”API“,但它的使用和实现都要比传统API复杂的多。

  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 方法把游标游动到最后一行读出既是最近收到的短信。

 

img_405b18b4b6584ae338e0f6ecaf736533.gif 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( 1 0 & 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( 1 0 & 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( 0 0 0 );
        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“。代码如下:

 

img_405b18b4b6584ae338e0f6ecaf736533.gif 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,  0 sizeof  dispparams);
    dispparams.cArgs 
=   1 ;
    dispparams.rgvarg 
=   new  VARIANT[dispparams.cArgs];
  
    
for int  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,  0 sizeof  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代码如下:

 

img_405b18b4b6584ae338e0f6ecaf736533.gif 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。

 

img_405b18b4b6584ae338e0f6ecaf736533.gif 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 对象隐藏或显示任务栏按钮。

  

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

目录
相关文章
|
7天前
|
存储 JavaScript 索引
JS中数组的相关方法介绍
JS中数组的相关方法介绍
|
9天前
|
JavaScript 前端开发 容器
AJAX载入外部JS文件到页面并让其执行的方法(附源码)
AJAX载入外部JS文件到页面并让其执行的方法(附源码)
13 0
|
7天前
|
JavaScript 前端开发 索引
JavaScript中与字符串相关的方法
JavaScript中与字符串相关的方法
|
1天前
|
JavaScript 前端开发
js绑定事件的方法
js绑定事件的方法
17 11
|
2天前
|
JavaScript
JS生成uuid的四种方法
JS生成uuid的四种方法
4 0
|
2天前
|
JavaScript 前端开发 iOS开发
js实用方法记录-动态加载css/js
js实用方法记录-动态加载css/js
9 0
|
Web App开发 JavaScript 前端开发
JavaScript将iframe中控件的值传到主页面控件中
主要是通过在主页面定义一个传输数据的函数GetData(data),然后在iframe嵌入页面中通过parent.GetData(data),这样即可在GetData实现将data进行处理即可。 现在来看一下代码的实现,首先来看一个主界面的代码 function GetData(data) { alert(data); document.
740 0
|
2月前
|
JavaScript
Node.js【GET/POST请求、http模块、路由、创建客户端、作为中间层、文件系统模块】(二)-全面详解(学习总结---从入门到深化)
Node.js【GET/POST请求、http模块、路由、创建客户端、作为中间层、文件系统模块】(二)-全面详解(学习总结---从入门到深化)
27 0
|
2月前
|
消息中间件 Web App开发 JavaScript
Node.js【简介、安装、运行 Node.js 脚本、事件循环、ES6 作业队列、Buffer(缓冲区)、Stream(流)】(一)-全面详解(学习总结---从入门到深化)
Node.js【简介、安装、运行 Node.js 脚本、事件循环、ES6 作业队列、Buffer(缓冲区)、Stream(流)】(一)-全面详解(学习总结---从入门到深化)
70 0
|
3天前
|
JavaScript 前端开发 应用服务中间件
node.js之第一天学习
node.js之第一天学习