旅途

如果想飞得高,就该把地平线忘掉

结构或大内存块打包的办法

结构或大内存块打包的办法

Revision History:

Version Date Creator Description
 

Implementation Scope:

继续阅读之前,我们假设您熟悉以下知识:

  • SAFEARRAY
  • ISTREAM
  • Microsoft MSMQ

目录:

1:概述

2:借用SAFEARRAY打包把结构写入MSMQ队列

3:借用IStream流打包传递数据到MSMQ队列

1.概述

通常我们建议通过MSMQ传递基于XML的字符串,但有时候也需要传递一些结构或者一些接口指针,那么如何打包传递呢?

这实际上可以转换为一个普适问题:

如何把一个结构体(structure object)或者巨大内存块(比如5MB左右)打包为PROPVARIANT-compatible的类型?

 

首先,IMSMQMessagePtr的Body属性接收_variant_t参数:

inline void IMSMQMessage::PutBody ( const _variant_t & pvarBody ) {

    HRESULT _hr = put_Body(pvarBody);

    if (FAILED(_hr)) _com_issue_errorex(_hr, this, __uuidof(this));

}

如果我们想把结构作为消息的Body写入MSMQ消息队列,我们需要把我们的结构、大内存块或接口指针转换为_variant_t。

2.借用SAFEARRAY打包把结构写入MSMQ队列

把一个结构体打包为PROPVARIANT-compatible的类型,需要用到SAFEARRAY,一个带有边界信息的数组。这是一个常用技巧,很多文章都有提及,我就不多解释了。

但是,注意这种方式一次只能打包65536字节以下的数据,这是由于

SAFEARRAY* SafeArrayCreateVector(

  VARTYPE      vt,            

  long          lLbound,          

  unsigned int  cElements);

的定义所限制的。

我们通常会用SafeArrayCreateVector API创建一个单维SAFEARRAY,分配一个sizeof(_DATA)大小的连续内存块,而这个函数的第三个参数是一个unsigned int类型,所以最大值就只能是65536了。

更多SAFEARRAY知识,参见使用SAFEARRAY传递对象。

 
2.借用SAFEARRAY打包把结构写入MSMQ队列

续上1.1篇的打包步骤(VC++代码):

// ChangeStruct2Var函数的定义:

// 第一个参数:

//   类型:CComVariant

//   作用:接收者

// 第二个参数:

//   类型:_DATA*

//   作用:源

HRESULT ChangeStruct2Variant (CComVariant &var, _DATA *pData)

{

HRESULT hr = S_OK;

 

// 使用SafeArrayCreateVector API创建一个单维SAFEARRAY,分配一个sizeof(_DATA)大小的连续内存块

// VT--UI1代表非负整形的变量类型,1个字节

// 常数'0'定义数组的下界

LPSAFEARRAY lpsa = SafeArrayCreateVector(VT_UI1, 0, sizeof(_DATA));

LPBYTE pbData = NULL;

 

if (lpsa)

{

     //在你访问SAFEARRAY数据之前,你必须调用SafeArrayAccessData。该函数锁定数据并且返回一个指针。在这里,锁定数组意味着增加该数组的内部计数器(cLocks)

    hr = SafeArrayAccessData(lpsa, (void **)&pbData);

}

else

    hr = HRESULT_FROM_WIN32(GetLastError());

 

if (SUCCEEDED(hr))

{

     // 使用safe array:

     // 将传入的_DATA指针指向的内存复制到pbData

    CopyMemory(pbData, pData, sizeof(*pData));

// 设置var的类型为数组

    var.vt = VT_ARRAYVT_UI1;

// 将var和我们的单维SAFEARRAY拉上关系:

    var.parray = lpsa;

}

 

if (pbData)

{

     //相应用来释放数据的函数是SafeArrayUnaccessData(),该功能释放该参数的计数。

    SafeArrayUnaccessData(var.parray);

}

if (FAILED(hr))

{

     // 销毁SAFEARRAY

    SafeArrayDestroy(lpsa);

}

 

return hr;

}

 

////////////////////////////////////////////////////////////

//Added Headers:

////////////////////////////////////////////////////////////

#include <comdef.h>

#include <atlbase.h>

///////////////////////////////////////////////////////////

//Added for MSMQ:

///////////////////////////////////////////////////////////

#import "mqoa.dll" no_namespace, named_guids

typedef    struct  _DATA 

       int    _n; 

       char   _str;

}_DATA;

//main:

{

.. ..

.. ..

IMSMQMessagePtr pisMsg = NULL;

hr = pisMsg.CreateInstance("MSMQ.MSMQMessage");

_DATA msg;

msg._n = 1;

msg._str = '1';

CComVariant var;

// 打包函数:

ChangeStruct2Variant(var, &msg);

// 打包后的CComVariant传递给MSMQMessege的Body属性:

pisMsg->Body= var;

pisMsg->AppSpecific=-1;

// 发送到消息队列:

pisMsg->Send(pisQueue);

.. ..

}
 

 

这样,就可以成功地把一个结构递交到MSMQ队列中了。

 

Implementation Scope:
继续阅读之前,我们假设您熟悉以下知识:

  • SAFEARRAY
  • ISTREAM
  • Microsoft MSMQ

目录:

1:概述

2:借用SAFEARRAY打包把结构写入MSMQ队列

3:借用IStream流打包传递数据到MSMQ队列

 

下面给出读取MSMQ消息时解析的步骤(VC++代码):

////////////////////////////////////////////////////////////

//Added Headers:

////////////////////////////////////////////////////////////

#include <comdef.h>

#include <atlbase.h>

///////////////////////////////////////////////////////////

//Added for MSMQ:

///////////////////////////////////////////////////////////

#import "mqoa.dll" no_namespace, named_guids

typedef    struct  _DATA 

       int    _n; 

       char   _str;

}_DATA;

//main:

{

.. ..

.. ..

hr = pisQI->raw_Open(MQ_PEEK_ACCESS,MQ_DENY_NONE,&pisQueue);

IMSMQMessagePtr piMessage;

// 获取MSMQ队列中的一个消息:

piMessage = pisQueue->PeekCurrent();

_DATA *msg = new _DATA();

// 解析函数:

ChangeVariant2Struct(CComVariant(piMessage->Body), msg);

.. ..

}

 

// ChangeVariant2Struct函数的定义:

// 第一个参数:

//   类型:CComVariant

//   作用:源

// 第二个参数:

//   类型:_DATA*

//   作用:接收者

HRESULT ChangeVariant2Struct (CComVariant &var, _DATA *DP)

{

SAFEARRAY* psa;

BYTE HUGEP *lpb;

psa = var.parray;

SafeArrayAccessData(psa, (void HUGEP **)&lpb);

CopyMemory((LPVOID)DP, (LPVOID)lpb, 8);

SafeArrayUnaccessData(psa);

 

return S_OK;

}
 


Writen by zhengyun.NoJunk(at)tomosoft.dot.com

Disclaimers:
 

本文档仅供参考。本文档所包含的信息代表了在发布之日,zhengyun对所讨论问题的当前看法,zhengyun不保证所给信息在发布之日以后的准确性。

用户应清楚本文档的准确性及其使用可能带来的全部风险。可以复制和传播本文档,但须遵守以下条款:

复制时不得修改原文,复制内容须包含所有页 ;
所有副本均须含有 zhengyun的版权声明以及所提供的其它声明 ;


Implementation Scope:
继续阅读之前,我们假设您熟悉以下知识:

  • SAFEARRAY
  • ISTREAM
  • Microsoft MSMQ

目录:

1:概述

2:借用SAFEARRAY打包把结构写入MSMQ队列

3:借用IStream流打包传递数据到MSMQ队列

 

3.借用IStream流传递数据
正如前面所述,当你有一块非常巨大的数据要传递给MSMQ队列时,而且你希望一次液压成型,那么把它打包入IStream流,也是一个很常用技巧,了解COM的人都知道,我也不多解释了。

我们研究了ATL中IPersistMemoryImpl接口Load方法的实现机理,来做我们的事情:

// 函数名:LoadStreamOnHugeMemory

// 功能:  同上

// 第一个参数pvMem指向一块要打包的内存,第二个参数指明这块内存的大小

HRESULT LoadStreamOnHugeMemory(void pvMem, ULONG cbSize)

{

     // Get Memory Handle:

     HGLOBAL h = GlobalAlloc(GMEM_MOVEABLE, cbSize);

     If(NULL == h) return E_OUTOFMEMORY;

     LPVOID pv = GlobalLock(h);

     If(!pv) return E_OUTOFMEMORY;

    

     // Copy to memory block

     CopyMemory(pv, pvMem, cbSize);

     CComPtr<IStream> spStream;

     // Create stream on Memory:

     HRESULT hr = CreateStreamOHGlobal(h, TRUE, &spStream);

     If(FAILED(hr))

     {

          GlobalUnlock(h);

          GlobalFree(h);

          return hr;

     }

     // stream now owns the memory

 

// unlock the data

GlobalUnlock(hGlobal);

 

// Create a stream holder. Load the stream holder from the global stream.

//  THIS STREAM HOLDER IS INTERITED FROM IPersistStream

//  And all virtual functions are Modified to handle the object....

CComPtr <IStreamHolder> pHolder = new CComObject <CStreamHolder>;

 

CComPtr <IPersistStream> pHolderStream;

hr = pHolder->QueryInterface (IID_IPersistStream, (void **)&pHolderStream);

 

pStream->Seek( zero, STREAM_SEEK_SET, NULL );

pHolderStream->Load(pStream);

 

CComVariant vComData = pHolder;

.. ..

 

//

// now, you have a big chunk of memory loaded into a ComVariant

//

// 现在你可以把打包后的CComVariant传递给MSMQMessege的Body属性了:

pisMsg->Body = vComData;

.. ..

}
 

 

其实,Aydin的实现和ATL中IPersistMemoryImpl接口Load方法实现异曲同工。我们不妨换一种方式实现。

 

Writen by zhengyun.NoJunk(at)tomosoft.dot.com

Disclaimers:
本文档仅供参考。本文档所包含的信息代表了在发布之日,zhengyun对所讨论问题的当前看法,zhengyun不保证所给信息在发布之日以后的准确性。

用户应清楚本文档的准确性及其使用可能带来的全部风险。可以复制和传播本文档,但须遵守以下条款:

复制时不得修改原文,复制内容须包含所有页 ;
所有副本均须含有 zhengyun的版权声明以及所提供的其它声明 ;
不得以赢利为目的对本文档进行传播 。

 

Implementation Scope:
继续阅读之前,我们假设您熟悉以下知识:

  • SAFEARRAY
  • ISTREAM
  • Microsoft MSMQ

目录:

1:概述

2:借用SAFEARRAY打包把结构写入MSMQ队列

3:借用IStream流打包传递数据到MSMQ队列

 

其实,Aydin的实现和ATL中IPersistMemoryImpl接口Load方法实现异曲同工。我们不妨换一种方式实现。

Aydin给出的VC++代码是:

// 智能流指针

CComPtr<IStream> pStream = NULL;

LARGE_INTEGER zero = {0,0};

// hGlobal是内存句柄:

LPBYTE pChunk = (BYTE *) GlobalLock(hGlobal);

 

// 创建一个空的stream:

HRESULT hr = CreateStreamOnHGlobal(NULL, TRUE, &pStream );

pStream->Seek( zero, STREAM_SEEK_SET, NULL );

 

ULONG pcbWritten = 0;

// pChunk现在已经指向我们的巨大内存块;

// 我们把这块内存写入IStream流中:

pStream->Write (pChunk, dwNumRead, &pcbWritten);

// 检查是否全部写入了:

ATLASSERT(pcbWritten==dwNumRead);

 

// unlock the data

GlobalUnlock(hGlobal);

 

// 剩下的一样,也是让CComPtr <IPersistStream>来调用Load方法加载IStream

 

// Create a stream holder. Load the stream holder from the global stream.

//  THIS STREAM HOLDER IS INTERITED FROM IPersistStream

//  And all virtual functions are Modified to handle the object....

CComPtr <IStreamHolder> pHolder = new CComObject <CStreamHolder>;

 

CComPtr <IPersistStream> pHolderStream;

hr = pHolder->QueryInterface (IID_IPersistStream, (void **)&pHolderStream);

 

pStream->Seek( zero, STREAM_SEEK_SET, NULL );

pHolderStream->Load(pStream);

 

CComVariant vComData = pHolder;

.. ..

 

//

// now, you have a big chunk of memory loaded into a ComVariant

//

// 现在你可以把打包后的CComVariant传递给MSMQMessege的Body属性了:

pisMsg->Body = vComData;

.. ..

 

//Coder: Aydin T.BAKIR
 


 全文完。

Writen by zhengyun.NoJunk(at)tomosoft.dot.com

Disclaimers:
 

本文档仅供参考。本文档所包含的信息代表了在发布之日,zhengyun对所讨论问题的当前看法,zhengyun不保证所给信息在发布之日以后的准确性。

用户应清楚本文档的准确性及其使用可能带来的全部风险。可以复制和传播本文档,但须遵守以下条款:

复制时不得修改原文,复制内容须包含所有页 ;
所有副本均须含有 zhengyun的版权声明以及所提供的其它声明 ;
不得以赢利为目的对本文档进行传播 。

posted on 2007-07-30 16:51 旅途 阅读(821) 评论(0)  编辑 收藏 引用 所属分类: COM+/DCOM


只有注册用户登录后才能发表评论。
网站导航: 博客园   IT新闻   BlogJava   博问   Chat2DB   管理