获取RichEditCtrl图片及如何使用在你的程序中~

1. How To Get Image Out of RichEditCtrl (Plus writing to bitmap file):

//Suppose you are using MFC, You do
IRichEditOle* pReo = m_pRichEdit->GetIRichEditOle( );
//If you use Platform SDK directly
IRichEditOle* pReo;
::SendMessage(g_hYourRichEdit, EM_GETOLEINTERFACE,
  0, (LPARAM)(LPVOID*)&pReo);
//Note: in both cases, inside pReo's AddRef got called, so
//remember to release it later

LONG nNumber = pReo->GetObjectCount();  //Your Images' Number
//Handle Error yourself, code simplified for space limitation
for(int i = 0; i < nNumber; i++)
{
  REOBJECT* ro = new REOBJECT;
  ro->cbStruct = sizeof(REOBJECT);
  HRESULT hr = pReo->GetObject(i, ro, REO_GETOBJ_ALL_INTERFACES);
  if(FAILED(hr)) continue;

  //caller should released the inner object
  IDataObject* lpDataObject;
  hr = (ro->poleobj)->QueryInterface(IID_IDataObject,
       (void **)&lpDataObject);
  if(FAILED(hr)) continue;

  STGMEDIUM stgm;  //out
  FORMATETC fm;    //in

  fm.cfFormat = CF_DIB;  // Clipboard format
  fm.ptd = NULL;         // Target Device = Screen
  fm.dwAspect = DVASPECT_CONTENT;
                         // Level of detail = Full content
  fm.lindex = -1;        // Index = Not applicaple
  fm.tymed = TYMED_HGLOBAL ;
  hr = lpDataObject->GetData(&fm, &stgm);
  if(FAILED(hr)) continue;

  ASSERT(::GlobalSize(stgm.hGlobal));

  HANDLE hFile = ::CreateFile(_T("c:\\img.bmp"),
                 GENERIC_WRITE, 0, NULL, CREATE_ALWAYS,
                 FILE_ATTRIBUTE_NORMAL, NULL);
  if(hFile == INVALID_HANDLE_VALUE)
  { continie; }

  DWORD dwWritten;
  //Writing Bitmap File header
  BITMAPFILEHEADER bmfh;
  bmfh.bfType = 0x4d42;    //'BM'
  int nColorTableEntries = 0;
  int nSizeHdr = sizeof(BITMAPINFOHEADER) + sizeof(RGBQUAD) *
                 nColorTableEntries;
  bmfh.bfSize = 0;
  bmfh.bfReserved1 = bmfh.bfReserved2 = 0;
  bmfh.bfOffBits = sizeof(BITMAPFILEHEADER) +
                   sizeof(BITMAPINFOHEADER) +
                   sizeof(RGBQUAD) * nColorTableEntries;
  ::WriteFile(hFile, (LPVOID)&bmfh, sizeof(BITMAPFILEHEADER),
                             &dwWritten, NULL);

  DWORD dwGlobalSize = ::GlobalSize(stgm.hGlobal);
  LPVOID lpMem = ::VirtualAlloc(NULL, dwGlobalSize, MEM_COMMIT,
                                      PAGE_READWRITE);
  ::CopyMemory(lpMem, (LPVOID)::GlobalLock(stgm.hGlobal),
                                           dwGlobalSize);
  BITMAPINFOHEADER* pInfoHead = (BITMAPINFOHEADER*)lpMem;
  pInfoHead->biXPelsPerMeter = pInfoHead->biYPelsPerMeter
                                = 0;

  //Special Careful Here!!! Discard the Color Mask,
  //We need TRUE COLOR image
  //I bellieve you guys running Win2X/Xp use true color
  //screen setting
  if(pInfoHead->biCompression == BI_BITFIELDS)
  {
    pInfoHead->biCompression = BI_RGB;
    dwGlobalSize -= 3 * sizeof(RGBQUAD);  //delete the 3 DWORD
                                          //color mask
    LPBYTE pSrc, pDst;
    pSrc = (LPBYTE)lpMem;
    pSrc += sizeof(BITMAPINFOHEADER);
    pDst = pSrc;
    pSrc += 3 * sizeof(RGBQUAD);
    ::MoveMemory(pDst, pSrc, dwGlobalSize -
                 sizeof(BITMAPINFOHEADER));
  }

  //Write Image Data
  ::WriteFile(hFile, lpMem, dwGlobalSize, &dwWritten, NULL);
  ::GlobalUnlock(stgm.hGlobal);

  //You may find good if all image are same size, keeping the
  //memory for performance
  ::VirtualFree(lpMem, 0, MEM_RELEASE);
  ::CloseHandle(hFile);

  lpDataObject->Release();
  delete ro;
}
pReo->Release();

2. How Do You Transfer Data from Messenger to Your Own Program?

To achieve best performance, I use 2 Memory Map File and 4 Events to synchronize the RTF data and the image data respectively. All these kernel objects are named as a GUID to avoid name confliction. In the program side, when the frame starts, I make all initializations like these:

m_hMMF = CreateFileMapping(INVALID_HANDLE_VALUE, NULL,
  PAGE_READWRITE, 0, MsnPage, GUID_MMF_NAME);
if (m_hMMF == NULL)
{    // handle error here
}
LPVOID pView = MapViewOfFile(m_hMMF, FILE_MAP_WRITE, 0, 0, 0);
DWORD dwSize = YourMMFSize;
DWORD dwUsed = 0;
LPBYTE lpByte = (LPBYTE)pView;
::memcpy(lpByte, &dwSize, 4);
::memcpy(lpByte + 4, &dwUsed, 4);
  //Now both sides knows the MMF size
::UnmapViewOfFile(pView);

m_hReadEvent = ::CreateEvent(NULL, TRUE, FALSE,
                             GUID_READ_EVENT_NAME);
if(m_hReadEvent == NULL)
{    //handle error
}
m_hWriteEvent = ::CreateEvent(NULL, TRUE, FALSE,
                              GUID_WRITE_EVENT_NAME);
if(m_hWriteEvent == NULL)
{    //handle error
}
::ResetEvent(m_hReadEvent);
::SetEvent(m_hWriteEvent); //Important Here, If not set,
                           //you get deadlock forever
//Note: data is unidirectional from DLL to frame, so DLL
//can begin to write now

//create my frame thread ...

In frame side thread, it reads data sent from Hook DLL only:

//in thread part:
__try
{
  hMMF = OpenFileMapping(FILE_MAP_READ | FILE_MAP_WRITE, FALSE,
                         GUID_MSG_MMF_NAME);
  if (hMMF == NULL)    //error
    __leave;

  hWriteEvent = ::OpenEvent(EVENT_ALL_ACCESS,
                            FALSE,GUID_MSG_WRITE_EVENT_NAME);
  if(hWriteEvent == NULL)
    __leave;

  hReadEvent = ::OpenEvent(EVENT_ALL_ACCESS,
                           FALSE,g_MSG_READ_EVENT_NAME);
  if(hReadEvent == NULL) __leave;

  ::SetEvent(hWriteEvent);    //Just a reminder

  HANDLE hReadArr[2];
  hReadArr[0] = hKillThreadEvent;
  hReadArr[1] = hReadEvent;
  while(1)
  {
    DWORD dwRet = ::WaitForMultipleObjects(2, hReadArr, FALSE,
                                           1000);
    if(WAIT_OBJECT_0 == dwRet) __leave;
    if(dwRet == WAIT_TIMEOUT) continue;
    if(dwRet == WAIT_ABANDONED_0 || dwRet == WAIT_ABANDONED_0 + 1)
    __leave;
    ASSERT((WAIT_OBJECT_0 + 1) == dwRet);
    ::ResetEvent(hReadEvent);
    LPVOID pView = MapViewOfFile(hMMF, FILE_MAP_ALL_ACCESS,
                                 0, 0, 0);
    if(pView == NULL) __leave;
    LPBYTE lpByte = (LPBYTE)pView;

    //Enjoy Your Data here!!!!

    ::UnmapViewOfFile(pView);
    ::SetEvent(hWriteEvent);
  }
}
__finally {
  ::CloseHandle(hMMF);
  ::CloseHandle(hWriteEvent);
  ::CloseHandle(hReadEvent);    // and other stuff....
}

In the Hook DLL part, you just exchange the position of the read and write event, and you do not need the loop because the DLL is message driven; besides, you set the wait time in the DLL to zero because the data is sent passively from the DLL while the frame thread running in wait loop. This structure will ensure the MMF is accessed by only one thread at one time. And in my point of view, WM_COPYMESSAGE (Spy++ uses this method), though theoretically able to realize the data transferring functionality, should be avoided; the reason is compatibility and portablity. In my case, this program has been ported to an NT Service to monitor the Messenger, which has no window at all to pursue maximum stability and performance. Besides, even in a Windows application like this, considering the performance, if my frame thread is blocked temporarily by some GUI action, the DLL thread can still write data to the MMF until it is full, and once the frame thread gets a chance to run, it can fetch all the previous data and empty the MMF. Actually, in my last project, the DLL can make a larger MMF and copy the data there, free the old MMF, and wait the receiving thread to wake. All this means better fault tolerance, stability, and performance of the program.

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值