参考:

  • 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. Windows编译安装OpenSSL

    windows下使用vs2008中的nmake编译安装openssl的脚本build.bat: echo off & color 0A :: 项目名称 set PROJECT=openssl ...

  2. 不遗留问题-menu数据拼装-2

    $res = array(); foreach($idlist_1 as $id1) { $tmp = array(); $tmp1 = array(); $tmp1[] = $id1; foreac ...

  3. 迷宫bfs POJ3984

    #include<stdio.h> int map[5][5]={0,1,0,0,0,       0,1,0,1,0,       0,0,0,0,0,       0,1,1,1,0, ...

  4. Java Map遍历方式的选择

    [原文] 1. 阐述 对于Java中Map的遍历方式,很多文章都推荐使用entrySet,认为其比keySet的效率高很多.理由是:entrySet方法一次拿到所有key和value的集合:而keyS ...

  5. APP 上架苹果应用商城

    http://www.360doc.com/content/15/0203/15/19663521_445974056.shtml http://jingyan.baidu.com/article/4 ...

  6. C# .Net实现URL绝对路径和相对路径之间互相转换

    网站制作开发中,URL的绝对路径和相对路径之间互相转换,是经常需要用到的.以下是在C#.Net下一种实现二者互相转化的方法: [DllImport("shlwapi.dll", C ...

  7. 数据库里any 和 all 的区别

    any 是任意一个all 是所有 比如select * from student where 班级='01' and age > all (select age from student whe ...

  8. json 增删改 加 排序

    <script type="text/javascript"> var json = { "age":24, "name":&q ...

  9. Redis学习笔记(2)-String

    package cn.com; import java.util.List; import redis.clients.jedis.Jedis; public class Redis_String { ...

  10. JVM 常用配置

    JVM的配置,最常用的两个配置就是:-Xms512m –Xmx1024m -Xms设置JVM的初始化内存大小,-Xmx为最大内存大小,当突破这个值,将会报内存溢出,导致的原因有很多,主要是虚拟机的回收 ...