15 мая 2023 года "Исходники.РУ" отмечают своё 23-летие!
Поздравляем всех причастных и неравнодушных с этим событием!
И огромное спасибо всем, кто был и остаётся с нами все эти годы!

Главная Форум Журнал Wiki DRKB Discuz!ML Помощь проекту


Вставляем HBITMAP (Bitmap) в элемент управления RichEdit


Автор: Hani Atassi.

Требования: VC6 SP5, Win2000 SP2

Существует множество примеров, объясняющих как вставить файл bitmap в RichEdit. Но недостаток этих примеров в том, что они непригодны в случае, если изображение находится в виде ресурса либо в виде дескриптора (HBITMAP).

Статья основывается на примере MSDN "HOWTO: Insert a Bitmap Into an RTF Document Using the RichEdit Control", и использует интерфейс IDataObject. В статье MSDN используется OLE API (OleCreateFromFile), которая использует имя файла в виде строки. Так же существует множество способов создания объекта OLE (IOleObject). Все эти OLE API начинаются с префикса OleCreate. Одну из них я использовал в данном примере (OleCreateStaticFromData), которая позволяет нам создавать объект Ole, который в свою очередь содержит только представление без каких либо ссылок на данные. Естевственно, что данные могут быть в виде значения HBITMAP, которое может быть представлением картинки.

Думаю не стоит объяснять - как создать OleObject (IOleObject). Этот процесс хорошо описан в MSDN. Однако, один составляющих OLE объектов требует некоторого пояснения. В данном случае наша HBITMAP будет храниться в IDataObject.

Существует два метода получения нашего HBITMAP из объекта. Первый использует COleDataSource, который включён в интерфейс IDataObject. Второй метод - это добавить свою собственную обработку данного процесса в IDataObject. Второй метод хорош тем, что нет необходимости использовать MFC в приложении.

Первый метод использует следующий код:

STGMEDIUM stgm;
stgm.tymed = TYMED_GDI;	   // Хранилище среднее = дескриптор HBITMAP
stgm.hBitmap = hBitmap;
stgm.pUnkForRelease = NULL; // Используем ReleaseStgMedium
 
COleDataSource *pDataSource = new COleDataSource;
pDataSource->CacheData(CF_BITMAP, &stgm);
LPDATAOBJECT lpDataObject = 
  (LPDATAOBJECT)pDataSource->GetInterface(&IID_IDataObject);

Используя возвращаемое lpDataObject а так же другие объекты, Вы можете добавить объект в RichEdit.

Далее показан второй метод:

class CImageDataObject : IDataObject
{
public: 
  // Это статическая функция передающая указатель на IRochEditOle 
  //   и дескриптор bitmap.
  // После добавления данной функции, изображение будет добавлено 
  //   в текущую позицию RichEdit
  //
  static void InsertBitmap(IRichEditOle* pRichEditOle, 
                           HBITMAP hBitmap); 
 
private:
  ULONG m_ulRefCnt;
  BOOL  m_bRelease;
 
  // Данные, которые будут помещены в richedit
  //
  STGMEDIUM m_stgmed;
  FORMATETC m_fromat;
 
public:
  CImageDataObject() : m_ulRefCnt(0) {
    m_bRelease = FALSE;
  }
 
  ~CImageDataObject() {
    if (m_bRelease)
      ::ReleaseStgMedium(&m_stgmed);
  }
 
  // Методы интерфейса IUnknown
  // 
  STDMETHOD(QueryInterface)(REFIID iid, void ** ppvObject)
  {
    if (iid == IID_IUnknown || iid == IID_IDataObject)
    {
      *ppvObject = this;
      AddRef();
      return S_OK;
    }
    else
      return E_NOINTERFACE;
  }
  STDMETHOD_(ULONG, AddRef)(void)
  {
    m_ulRefCnt++;
    return m_ulRefCnt;
  }
  STDMETHOD_(ULONG, Release)(void)
  {
    if (--m_ulRefCnt == 0)
    {
       delete this;
    }
 
    return m_ulRefCnt;
  }
 
  // Методы интерфейса IDataObject
  //
  STDMETHOD(GetData)(FORMATETC *pformatetcIn, 
                     STGMEDIUM *pmedium) {
    HANDLE hDst;
    hDst = ::OleDuplicateData(m_stgmed.hBitmap, 
                              CF_BITMAP, NULL);
    if (hDst == NULL)
    {
      return E_HANDLE;
    }
 
    pmedium->tymed = TYMED_GDI;
    pmedium->hBitmap = (HBITMAP)hDst;
    pmedium->pUnkForRelease = NULL;
 
    return S_OK;
  }
  STDMETHOD(GetDataHere)(FORMATETC* pformatetc, 
                         STGMEDIUM*  pmedium ) {
    return E_NOTIMPL;
  }
  STDMETHOD(QueryGetData)(FORMATETC*  pformatetc ) {
    return E_NOTIMPL;
  }
  STDMETHOD(GetCanonicalFormatEtc)(FORMATETC*  pformatectIn ,
                                   FORMATETC* pformatetcOut ) {
    return E_NOTIMPL;
  }
  STDMETHOD(SetData)(FORMATETC* pformatetc , 
                     STGMEDIUM*  pmedium , 
                     BOOL  fRelease ) {
  m_fromat = *pformatetc;
  m_stgmed = *pmedium;

  return S_OK;
  }
  STDMETHOD(EnumFormatEtc)(DWORD  dwDirection , 
                           IEnumFORMATETC**  ppenumFormatEtc ) {
    return E_NOTIMPL;
  }
  STDMETHOD(DAdvise)(FORMATETC *pformatetc, 
                     DWORD advf, 
                     IAdviseSink *pAdvSink,
                     DWORD *pdwConnection) {
    return E_NOTIMPL;
  }
  STDMETHOD(DUnadvise)(DWORD dwConnection) {
    return E_NOTIMPL;
  }
  STDMETHOD(EnumDAdvise)(IEnumSTATDATA **ppenumAdvise) {
    return E_NOTIMPL;
  }

  // Другие вспомогательные функции
  //
  void SetBitmap(HBITMAP hBitmap);
  IOleObject *GetOleObject(IOleClientSite *pOleClientSite, 
                           IStorage *pStorage);
};

Приведённая реализация IDataObject специально расчитана на хранение дескриптора HBITMAP.

Чтобы вставить картинку в RichEdit, достаточно вызвать функцию CImageDataObject::InsertBitmap. Функция имеет два параметра:

IRichEditOle* pRichEditOle : Указатель на интерфейс IRichEditOle для элемента управления RichEdit. Вы можете использовать метод GetRichEditOle() в MFC классе CRichEditCtrl для получения этого указателя или следующий код:

::SendMessage((HWND)m_ctlRichText.GetHwnd(), 
   EM_GETOLEINTERFACE, 
   0, 
   (LPARAM)&m_pRichEditOle);

HBITMAP hBitmap : дескриптор изображения. Класс отвечает за закрытие и освобождение ресурсов, занимаемы дескриптором, поэтому не закрывайте его самостоятельно.

Downloads

Download demo project - 55 Kb
Download source - 3 Kb