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

时间: 2024-11-02 20:21:01

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

android获取短信内容时,总是收到两次短信内容

问题描述 android获取短信内容时,总是收到两次短信内容 public String getSmsAndSendBack() { String[] projection = new String[] { ""address""person""body"" }; StringBuilder str = new StringBuilder(); try { ContentResolver cr = getContentReso

app-iOS怎么允许APP获取短信内容

问题描述 iOS怎么允许APP获取短信内容 想实现短信验证码自动填写功能,百度一说苹果不允许APP获取短信内容,但是我发现 有APP可以自动填写,想问问怎么实现的 解决方案 可以明确的说,ios是没有短信内容权限的,但是android可以哦. 解决方案二: ios对权限控制比较严格,所以... 解决方案三: 不可能获取到,别想了

中国建设银行获取网银盾序列号插件(控件)不可用解决方法

  1.在其他电脑上使用网银盾,如果存在同样的问题,则可以带上有效身份证件.网银签约账号和网银盾到购买网银盾网点由建行工作人员检测网银盾是否正常.如果其他电脑没有问题,遵照如下步骤逐一解决. 2.检测电脑接口是否正常,更换电脑USB接口.如电脑USB接口正常,建议卸载360.金山等安全软件,然后重新下载建行E路护航网银安全组件安装. 注意:安装时右键点击E路护航安全组件安装程序,然后选择"以管理员身份运行". 3.安装后,鼠标右键点击计算机(我的电脑)>管理>服务应用程序&

Android开发获取短信的内容并截取短信_Android

1.首先我们要写一个广播接收器,当我们的手机收到短信时,系统会自动发送一个广播,我们只需要接收到这条广播就可以了 2.在广播里面,我们重写的onReceive()方法,通过里面的Intent写到的Bundle就可以拿到短信的内容, 3.清单文件里面我们必须要添加权限,否则无法接收到. 4.为了防止我们的广播接收不到,我们自己写的广播接收器的权限必须要大,以防万一,我设置了1000. 下面上代码,里面的注释也比较详细.. <?xml version="." encoding=&qu

Android自动获取短信验证码

前言:android应用的自动化测试必然会涉及到注册登录功能,而许多的注册登录或修改密码功能常常需要输入短信验证码,因此有必要能够自动获得下发的短信验证码. 主要就是实时获取短信信息. android上获取短信信息主要有BroadcastReceiver方式与数据库方式,要实时的话就BroadcastReceiver比较方便 public class SMSReceiver extends BroadcastReceiver{    private String verifyCode="&quo

Android注册登录实时自动获取短信验证码_Android

android应用的自动化测试必然会涉及到注册登录功能,而许多的注册登录或修改密码功能常常需要输入短信验证码,因此有必要能够自动获得下发的短信验证码. 主要就是实时获取短信信息. android上获取短信信息主要有BroadcastReceiver方式与数据库方式,要实时的话就BroadcastReceiver比较方便,分享一篇文章大家可以查看一下,点击. public class SMSReceiver extends BroadcastReceiver{ private String ver

Android注册登录实时自动获取短信验证码

android应用的自动化测试必然会涉及到注册登录功能,而许多的注册登录或修改密码功能常常需要输入短信验证码,因此有必要能够自动获得下发的短信验证码. 主要就是实时获取短信信息. android上获取短信信息主要有BroadcastReceiver方式与数据库方式,要实时的话就BroadcastReceiver比较方便,分享一篇文章大家可以查看一下,点击. public class SMSReceiver extends BroadcastReceiver{ private String ver

获取手机短信内容

原理是通过,contentprovider获取系统短信数据库中的字段信息而达到获取内容目的 效果图如下: 具体代码如下: [html] view plaincopy package com.internal.message;      import java.text.SimpleDateFormat;   import java.util.ArrayList;   import java.util.Date;   import java.util.HashMap;   import java.

android ContentResolver获取手机电话号码和短信内容

通过ContentResolver 获取到手机的联系人跟手机短信信息并显示出来,供大家参考,具体内容如下 1.实现的代码: package com.example.phone; import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.Date; import java.util.HashMap; import java.util.List; import java.util.Map; impo