参考:

  • Programming Windows with MFC, 2nd. Chapter 18, 19. 建议把这两章学习完(至少到OLE drag-and-drop之前要学习完)再来尝试OLE Clipboard
  • Programming Windows 5th.
  • Chapter 12 - The Clipboard, Memory Allocation,

When your program transfers something to the clipboard, it must allocate a memory block and essentially hand it
over to the clipboard. When we've needed to allocate memory in earlier programs in this book, we've simply used
the malloc function that is supported by the standard C run-time library. However, because the memory blocks
stored by the clipboard must be shared among applications running under Windows, the malloc function is
inadequate for this task.

  • Chapter 14 - Bitmaps and Bitblts, after Figure 14-22,

For bitmaps, however, the clipboard items are not global handles but bitmap handles. When you use the
CF_BITMAP, the GetClipboardData function returns an HBITMAP object and the SetClipboardData function accepts
an HBITMAP object. If you want to transfer a bitmap to the clipboard but still have a copy of it for use by the
program itself, you must make a copy of the bitmap. Similarly, if you paste a bitmap from the clipboard, you
should also make a copy.

  • HBITMAP是怎么create, copy, destroy的?

    • Chapter 14 - Bitmaps and Bitblts, The GDI Bitmap Object
    • Creating a DDB
      •   You then obtain the handle by calling one of the DDB-creation functions: for example, CreateBitmap . These
        functions allocate and initialize some memory in GDI memory to store information about the bitmap as well as the
        actual bitmap bits. The application program does not have direct access to this memory. The bitmap is
        independent of any device context. When the program is finished using the bitmap, it should be deleted:                                      DeleteObject (hBitmap) ;

    • The Memory Device Context
      • This is the same function you use for selecting pens, brushes, fonts, regions, and palettes into device contexts.
        However, the memory device context is the only type of device context into which you can select a bitmap. (You
        can also select other GDI objects into a memory device context if you need to.)

什么是Memory Device Context?

[下面的所有引用都摘自Programming Windows 5th.]

先来看什么是Device Context

When you want to draw on a graphics output device such as the screen or printer, you must first obtain a handle
to a device context (or DC). In giving your program this handle, Windows is giving you permission to use the
device. You then include the handle as an argument to the GDI functions to identify to Windows the device on
which you wish to draw.

The device context (also called simply the "DC") is really just a data structure maintained internally by GDI. A
device context is associated with a particular display device, such as a video display or a printer. For a video
display, a device context is usually associated with a particular window on the display.

Some of the values in the device context are graphics "attributes." These attributes define some particulars of how
GDI drawing functions work. With TextOut , for instance, the attributes of the device context determine the color
of the text, the color of the text background, how the x-coordinate and y-coordinate in the TextOut function are
mapped to the client area of the window, and what font Windows uses when displaying the text.

上面作者说了一堆,意思就是:DC是Windows操作系统维护的Data Structure,DC描述了显示设备(例如屏幕、打印机)的一些属性,并被GDI functions所使用。

为什么需要DC?

原因很简单,

比如说TextOut这个GDI function,同样一个字符串,你往屏幕上TextOut和往打印机上TextOut,TextOut肯定要针对你当前的显示设备采用不同的implementation,对吧?而且不同的型号的显示器,不同型号的打印机,TextOut都要针对特定的设备提供不同的实现,对吧?你想想现在有多少款显示器,多少款打印机?如果Windows的开发者针对每一款设备都提供一个TextOut,那就不用玩了,TextOut_SamsungXXXX, TextOut_HPXXXX, TextOut_CanonXXXX,这尼玛得写多少个?更别提还有其他的N多GDI functions了!

所以,最好的方法就是Windows只提供一个版本的TextOut,这个TextOut只对DC进行操作。对,这里DC就扮演了一个虚拟的显示设备的角色,其实就是提供了一层抽象。采用的思想与JVM是一模一样的!(当然或许JVM出现的时间比DC要晚,不过不用纠结谁采用谁的思想,反正都是一个意思)。这样,由设备的驱动程序来提供设备信息,并由Windows填充到DC中,DC为所有的GDI functions提供统一的接口,GDI通过DC,再通过设备驱动程序,就实现输出了!这样一来,不管是张三家生产的显示屏(张三负责提供其驱动程序),还是李四家生产的打印机(李四负责提供其驱动程序),对Windows来说都一样:调用其驱动程序获取设备信息->填充DC->GDI对DC进行操作->操作指令送到驱动程序->设备完成绘制!(当然准确的过程不见得是这样,这只是我个人的理解,从大体上应该差不远的!)

言归正传,神马是Memory DC?

DC是虚拟的显示设备,那么Memory DC是啥意思?其实也很简单,屏幕的输出是在屏幕的LED(或别的什么材质)面板上,打印机输出是在纸张(或别的什么材料)上,这些我们可以形象地统称为“画布Canvas”。那么Memory DC的意思其实就是用memory来表示一个虚拟的Canvas,反正我用memory中的一个(或若干个)bit来表示你LED面板上的一个pixel或者纸上的一个墨点总可以吧?对,就是这个意思。当然,Memory DC本身并不是canvas,它本身还是个DC,memory才是canvas。那么就涉及到一个问题,我在memory里面绘图完毕,总要输出到实际的显示设备上吧?不是显示器就是打印机或者别的什么玩意儿。对,正因为如此,一个Memory DC必须与一个实际设备的DC兼容(compatible),因此你会发现,在创建Memory DC的时候,这个Memory DC总是和某个实际设备相关联的!正如下所述:

Normally, a device context refers to a particular graphics output device (such as a video display or a printer)
together with its device driver. A memory device context exists only in memory. It is not a real graphics output
device, but is said to be "compatible" with a particular real device.

注意:

下面的例子,在关闭程序之后Clipboard中还是有数据的,不会因为你的程序关闭就丢失,因为使用的是global memory(GlobalAlloc)

CImage::Load,然后CImage::Detach()得到一个HBITMAP hBitmap

拷贝到Windows Clipboard(因为CImage.Load进来的是DIB,而且经过测试,即便你在SetClipboardData用CF_DIB也没用,必须转换成DDB)

参考:http://stackoverflow.com/questions/32309180/why-cant-i-directly-send-an-hbimap-from-a-cimage-to-clipboard

 if (::OpenClipboard(this->GetSafeHwnd())) {
CImage img;
img.Load(_T("D:\\scc.bmp"));
HBITMAP hbitmap_dib = img.Detach();
if (!hbitmap_dib)
return; DIBSECTION ds;
::GetObject(hbitmap_dib, sizeof(DIBSECTION), &ds); //make sure compression is BI_RGB
ds.dsBmih.biCompression = BI_RGB; //Convert DIB to DDB
HDC hdc = ::GetDC(NULL);
HBITMAP hbitmap_ddb = ::CreateDIBitmap(
hdc, &ds.dsBmih, CBM_INIT, ds.dsBm.bmBits, (BITMAPINFO*)&ds.dsBmih, DIB_RGB_COLORS);
::ReleaseDC(NULL, hdc); ::EmptyClipboard();
::SetClipboardData(CF_BITMAP, hbitmap_ddb);
::CloseClipboard();
}

拷贝到OLE Clipboard

方法1(参考https://social.msdn.microsoft.com/Forums/vstudio/en-US/ba75bba4-6f4d-43a4-905c-2caa7b0ea548/copy-a-gdi-bitmap-to-clipboard?forum=vcgeneral):

if(::AfxOleInit()) {
COleDataSource* pods = new COleDataSource;
STGMEDIUM clipboardData;
::memset(&clipboardData, , sizeof tagSTGMEDIUM);
clipboardData.tymed = TYMED_GDI;
clipboardData.hBitmap = hImage;
pods->CacheData(CF_BITMAP, &clipboardData);
pods->SetClipboard();
}

方法2(使用HGLOBAL),注意CacheGlobalData传入的数据必须位于从GlobalAlloc分配的memory,这一点和CacheData不同

根据该hBitmap所指向的bitmap的大小,首先用GlobalAlloc申请相应大小的memory,得到其HGLOBAL hGlobal(这一步我目前不知道该怎么做);经测试CacheGlobalData要求用GlobalAlloc得到的memory(用HGLOBAL来表示)才能工作,所以你直接把hBitmap交给CacheGlobalData是不行的(就如下面的拷贝"Hello, world"的例子,尽管HANDLE的定义为void*,你把szText直接传给CacheGlobalData也会报异常,你可以测试,即便szText是new出来的也不行,必须是GlobalAlloc弄出来的才行,对HBITMAP也是一样的道理)

然后:

if(AfxOleInit()) {
COleDataSource* pods = new COleDataSource;
pods->CacheGlobalData(CF_BITMAP, hGlobal);
pods->SetClipboard();
}

另外,经过测试,Programming Windows With MFC 2nd上1240页的例子应该如下才能工作:

char szText[] = "Hello, world"; // ANSI characters
HANDLE hData = ::GlobalAlloc(GMEM_MOVEABLE, ::strlen(szText) + );
LPSTR pData = (LPSTR) ::GlobalLock(hData);
::strcpy_s(pData, ::strlen(szText) + , szText);
::GlobalUnlock(hData); if(::AfxOleInit()) {
COleDataSource* pods = new COleDataSource;
pods->CacheGlobalData(CF_TEXT, hData);
pods->SetClipboard();
}

注意,前面使用::SetClipboardData(CF_BITMAP, hImage)的时候,那个hImage所在的memory不是由GlobalAlloc分配的。但不知道为什么,还是能用。但是这个例子就必须用GlobalAlloc才行,即便你用::SetClipboardData也是一样。我只能说可能是因为::SetClipboardData对CF_BITMAP与CF_TEXT的处理不一样。

总结:

字符串必须用GlobalAlloc分配再放到clipboard

bitmap则不用GlobalAlloc,只要确保是clipboard能接收的DIB或者DDB即可

【关于HBITMAP, DC, MEM DC, Clipboard】将HBITMAP拷贝到Clipboard(Windows Clipboard & OLE Clipboard)的更多相关文章

  1. linux与windows共享剪贴板(clipboard)

    linux与windows共享剪贴板(clipboard)的方法 先说两句废话,其实linux和windows之间不需要共享剪贴板,直接在putty中,按住SHIFT+鼠标选择就可以了. 但是作为一种 ...

  2. Chrome拷贝插件的对比 zeroclipboard和clipboard插件

    1.zeroclipboard插件 实现原理:Zero Clipboard 利用 Flash 进行复制,用了一个透明的 Flash ,让其漂浮在按钮之上,这样其实点击的不是按钮而是 Flash ,也就 ...

  3. CBitmap、HBITMAP、BITMAP相互转换

    一:理解 BITMAP是C++中定义的位图结构体 HBITMAP是Windows中使用的位图句柄 CBitmap是MFC封装的位图类 二:相互转换 1.HBITMAP->CBitmap 方法一: ...

  4. IPicture、BITMAP、HBITMAP和CBitmap的关系

    1.有关IPicture加载图片后直接Render到内存DC的问题(HBITMAP转换IPicture)Picture的方法get_Handle可以直接得到图片的句柄 IPicture *pIPict ...

  5. WM_PAINT在微软官方定义中,wParam和lParam都没有使用,所以就被Delphi给重定义了这个消息,还增加了DC(Delphi可任意改写消息的结构)

    LRESULT CALLBACK WindowProc( HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam ); Parameters wParam ...

  6. LDAP 中 CN, OU, DC 的含义

    1. LDAP的存储规则 区分名(DN,Distinguished Name) 和自然界中的树不同,文件系统/LDAP/电话号码簿目录的每一片枝叶都至少有一个独一无二的属性,这一属性可以帮助我们来区别 ...

  7. AD域服务器|两台DC无法进行复制同步

    注:本文由Colin撰写,版权所有!转载请注明原文地址,谢谢合作! 说明:前段时间公司两台域控出现了一些问题导致数据无法相互进行同步,DC之间也无法进行共享访问,网络用户无法通过计算机名映射的共享访问 ...

  8. 导出DC列表

    $DomainName = (gwmi win32_computersystem).Domain$dn0 = $DomainName.Split(".")[0]$dn1 = $Do ...

  9. VC一些经验系列: 《分享泄漏检测工具:内存、DC、GDI、Handle... 》

    分享下自己工作中用到的一些用于泄漏检测的工具 后面的是DC的一些定义和注意事项.(不喜勿看) //=================================================== ...

随机推荐

  1. 1. Programming in C is fun!

    Programming in C is fun! #include <stdio.h> int main() { printf("Programming in C is fun! ...

  2. 《你不知道的JavaScript》读书笔记(二)词法作用域

    JavaScript 采用的是 词法作用域 的工作模型. 定义 词法化:大部分标准语言编译器的第一个工作阶段叫词法化(单词化),这个过程会对源代码中的字符进行检查,如果是有状态的解析过程,还会赋予单词 ...

  3. ecshop 完美解决动态ip登录超时和购物车清空问题

    ecshop 完美解决动态ip登录超时和购物车清空问题 ECSHOP模板/ecshop开发中心(www.68ecshop.com) / 2014-05-06 前一段时间,ECSHOP开发中心的一个客户 ...

  4. 页面静态化3 --- 伪静态技术之Apache的rewrite机制

      Apache的rewrite机制: 意思就是,你发送的地址,比如:http://localhost/news-id67.html会被Apache改写成http://localhost/news.p ...

  5. p::first-line { text-transform: uppercase }

    https://www.w3.org/TR/css3-selectors/ Note that the length of the first line depends on a number of ...

  6. Greedy_algorithm

    https://en.wikipedia.org/wiki/Greedy_algorithm

  7. PHP 加密 和 解密 方法

    关于Discuz的加密解密函数,相信大家都有所了解,该authcode函数可以说是对PHP界作出了重大的贡献,真的发觉discuz这个函数写的太精彩啦. 研究了一下这个算法,总的来说可以归纳为以下三点 ...

  8. linux 不能用clock 计算sleep的时间

    http://bbs.csdn.net/topics/390558707 在Windows Sleep()占用processor time,Linux下的sleep()不占用processor tim ...

  9. ADS报错 Warning : L6301W:Could not find file C:\Program Files . Error : L6218 : Undefined symbol ......

    ADS1.2编译时,出现找不到一个不存在目录下的目标文件(*.o) 编译一个COPY到硬盘上的一个工程,出现以下的fatal error message: Error: (Fatal)L6002: C ...

  10. java JDK8 学习笔记——第15章 通用API

    第十五章 通用API 15.1 日志 15.1.1 日志API简介 1.java.util.logging包提供了日志功能相关类与接口,不必额外配置日志组件,就可在标准Java平台使用是其好处.使用日 ...