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(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“。代码如下:

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代码如下:

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

转载于:https://www.cnblogs.com/hoodlum1980/archive/2009/12/30/1635702.html

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

  1. uniapp - app 获取短信内容

    首先载入软件的时候请求获取读取短信的权限 android.permission.READ_SMS onLoad(){this.getMessage()//有的手机可能不会弹出弹窗 }, methods ...

  2. Android 监听短信数据库过滤获取短信内容上传至服务器

    前言 Android 监听短信的方式有两种 1.监听短信数据库,数据库发生改变时回调. 2.监听短信广播 其中第二种方式由于国内各厂家的定制Android 可能导致无响应 目前测试 魅族 无法监听到短 ...

  3. vue element-ui实现获取短信验证码 ,60秒倒计时及页面

    cc废话不多说先上效果图  vue页面布局 <el-form-item prop="phoneCode" class="pr"><el-inp ...

  4. android拦截短信获取短信内容,《英雄联盟手游》先锋测试招募说明:仅安卓用户...

    招募时间:5月10日~5月17日 测试开始时间:预计5月下旬或6月上旬 招募(体验)要求: 1.测试期间有较长时间可投入游戏体验: 2.能够积极反馈和表达自己的游戏体验感受: 3.需提前完成招募问卷( ...

  5. Total Control脚本功能JS API之获取设备短信内容

    每一个设备对象都对应了 Total Control 中已连接的一台手机设备. 通过设备对象,用户可以获取到手机设备的各种属性,以及使用控制接口对设备进行各种操作,比如 打开或关闭指定APP,发送短信等 ...

  6. Android 获取通话记录和短信内容

    Android 获取通话记录和短信内容 一.获取通话记录 1.权限声明,需要在Manifest.xml文件中声明权限(读取通话记录权限): <uses-permission android:na ...

  7. android 获取短信消息,Android开发获取短信的内容并截取短信

    Android 首先我们要写一个广播接收器,当我们的手机收到短信时,系统会自动发送一个广播,我们只需要接收到这条广播就可以了 在广播里面,我们重写的onReceive()方法,通过里面的Intent写 ...

  8. python+appium自动化测试获取短信+图片验证码

    本篇文章主要讲述的是如何自动获取短信验证码和如何自动获取图片验证码,并写入到对应的输入框中(以下均使用微博的找回密码作为示例) 获取短信验证码的方法有三种,如下所示: 在手机的通知栏中获取短信内容 通 ...

  9. python+appium自动化测试-获取短信+图片验证码

    本篇文章主要讲述的是如何自动获取短信验证码和如何自动获取图片验证码,并写入到对应的输入框中(以下均使用微博的找回密码作为示例) 获取短信验证码的方法有三种,如下所示: 在手机的通知栏中获取短信内容 通 ...

  10. Android 获取短信验证码并自动填充(踩坑小米、荣耀、OPPO、华为)

    Android 获取短信验证码并自动填充(踩坑小米.荣耀.OPPO) 前言 最近弄了个短信自动填充功能,一开始觉得很简单,不就是动态注册个广播接收器去监听短信消息不就可以了吗?结果没这么简单,问题就出 ...

最新文章

  1. rhel-server-7.5-x86_64-dvd.iso镜像下载及rar压缩包的解压
  2. pytorch图像和张量的相互转换_[Pytorch]Pytorch的tensor变量类型转换
  3. java 有多少api_Java常用API(二)
  4. 购物中心潮--我的创业之路
  5. 抓取html中用到的css_如何使用HTML和CSS制作像《星球大战》一样的抓取文字
  6. Java 8“失宠”
  7. 为什么最近python很火_最近python挺火的,也来凑凑热闹。
  8. 电商设计提升水平,需要优秀的模板素材进行临摹练习!
  9. 设置element-iu中table滚动条位置
  10. Blender Python UV 学习
  11. 视频点播-上传视频状态异常
  12. redhat 复制文件夹及子文件夹_Docker COPY 复制文件夹的诡异行为
  13. oracle关闭和,ORACLE启动和关闭
  14. 新视野大学英语(第三版)读写教程4答案
  15. LeetCode知识点总结 - 1179
  16. 【南宋】【文天祥】正气歌
  17. Vue3 Hooks 模块化抽离
  18. 【微信小程序】深入学习小程序基本目录文件与代码结构
  19. python实现蜂鸣器演奏两只老虎
  20. 苹果手机在未激活的状况下待机时间长了会自动关机吗

热门文章

  1. AWS redshift-hdb pg(Greenplum), 内置函数、数据类型、字符集
  2. H3C路由器Hub-Spoke网络结构D×××配置案例(试读连载一二)
  3. PSTN ,PBX ,IP PBX,VoIP
  4. Exchange 2007 邮箱设置
  5. 每天学一点flash(6) FLASH 8 和 FLASH CS3 加载外部文本的区别 (转载)
  6. [翻译]Real-Time Correlative Scan Matching
  7. 【Java】15分钟快速体验阿里Java诊断工具Arthas
  8. 数据--第39课 - 二叉树课后练习
  9. pfSense book Html版下载!
  10. Java多线程(六)线程池