参考:

  • 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. poj 2186 tarjan求强连通分量

    蕾姐讲过的例题..玩了两天后才想起来做 貌似省赛之后确实变得好懒了...再努力两天就可以去北京玩了! 顺便借这个题记录一下求强连通分量的算法 1 只需要一次dfs 依靠stack来实现的tarjan算 ...

  2. laravel transaction : laravel 的事务是不支持eloquent的, 要用DB::的方式

    数据库事务处理# 你可以使用 transaction 方法,去执行一组数据库事务处理的操作: DB::transaction(function() { DB::table('users')->u ...

  3. Linux 计划任务 Crontab 笔记与总结(2)Crontab 的基本组成与配置

    [Crontab 的基本组成] ① 系统服务 CROND:每分钟都会从配置文件刷新定时任务 ② 配置文件 :文件方式设置定时任务 ③ 配置工具 crontab:用途调整定时任务 [配置文件的配置文件格 ...

  4. PHP笔记——java程序员看懂PHP程序

    PHP笔记——java程序员看懂PHP程序   php是一种服务器端脚本语言,类型松散的语言. <?php   ?>       xml风格 <script language=”ph ...

  5. 完美解决VS2003.Net fatal error LNK1201: 写入程序数据库“.pdb”时出错

    我的开发环境是Win7旗舰64位+VS2003.Net,经常卡pdb错误,文末给出一个完美的解决方案和一个懒人补丁包.问题描述如下:在重新编译的时候,经常报错: fatal error LNK1201 ...

  6. 放到u-boot/arch/arm/inlcude下面解压A20固件库制作笔记

    运行 build_dragonboard.sh,完成一次编译,首次编译需要消耗 20 分钟以上的时间.这里包括编译bootloader.kernel.rootfs. 修改 Linux 内核配置$ cd ...

  7. C#winform中ListView的使用

    使用ListView模仿Windows系统的资源管理器界面,实现文件(夹)的浏览.重命名.删除及查询等功能,主要功能界面展示如下: 1.MainForm.cs及MainForm.Designer.cs ...

  8. ubuntu下opencv在Qt中的使用

    1. 编译安装OpenCV2.4.9  本博已有文章描述 2. 安装Qt和QtCreator 从qt-project.org 下载Qt安装文件 qt-opensource-linux-x64-5.4. ...

  9. nrf51822裸机教程-IIC

    关于IIC总线的核心有以下几点: :时钟线高电平期间必须保持数据线不变. :时钟线低电平期间可以改变数据. :时钟线和数据线上都要接上拉电阻,以使总线不工作时,两根线的电平都处于高电平状态. :每个传 ...

  10. 【翻译】How To Tango With Django 1.5.4 第一章

    1.概览 这本书的目的就是为了给你提供Django实战开发的指导,这本书主要是为学生设计的,它提供了开发并运行第一个web应用程序的详细的指导步骤,并且指导你怎么将它发布到web服务器上. 本书就是为 ...