为什么使用剪切板时都用GlobalAlloc分配内存(历史遗留问题,其实没关系了)
我在使用剪切板时,发现通用的都是使用GlobalAlloc来分配内存,我就想不是说在Win32中GlobalAlloc和LocalAlloc是一样的那为什么不用LocalAlloc呢,原谅我的好奇心吧,对此我研究了一番,如果你也有此疑问,可以看一看。
先看一看关于GlobalAlloc和LocalAlloc的由来,这里有前人做了比较好的解释,这里为了保证博客连贯性,直接截取过来(原文链接)
16位windows用一个全局堆和局部堆来管理内存,每一个应用程序或dll装入内存时,代码段被装入全局堆,而系统又为每个实例从全局堆中分配了一个64kb的数据段作为该实例的局部堆,用来存放应用程序的堆栈和所有全局或静态变量。而LocalAlloc/GlobalAlloc就是分别用于在局部堆或全局堆中分配内存。
由于每个进程的局部堆很小,所以在局部堆中分配内存会受到空间的限制。但这个堆是每个进程私有的,相对而言分配数据较安全,数据访问出错不至于影响到整个系统。 而在全局堆中分配的内存是为各个进程共享的,每个进程只要拥有这个内存块的句柄都可以访问这块内存,但是每个全局内存空间需要额外的内存开销,造成分配浪费。而且一旦发生严重错误,可能会影响到整个系统的稳定。
不过在Win32中,每个进程都只拥有一个缺省的私有堆,它只能被当前进程访问。应用程序也不可能直接访问系统内存。所以在Win32中全局堆和局部堆都指向进程的省缺堆。用LocalAlloc/GlobalAlloc分配内存没有任何区别。甚至LocalAlloc分配的内存可以被GlobalFree释放掉。所以在Win32下编程,无需注意Local和Global的区别。
结合上面的解释可知道,在Windows早期中的跨进程共享数据是通过将数据内存分配在共享内存(全局堆)中,然后使Clipboard拥有它来保证跨进程通信的。
那么刚刚说道GlobalAlloc和LocalAlloc分配的内存都是在进程的堆上,那么他们是如何来完成进程间共享数据的呢。这个只能依靠Clipboard函数了。事实上这个涉及到虚拟内存的内容(如果你对虚拟内存不了解的话,可以看一下我的这篇博文中的第一部分内容),在《Windows核心编程》书中讲到进程的地址空间划分,其中提到进程的内核模式分区是操作系统代码的驻地,其中的所有东西为所有进程所有,再结合MSDN中SetClipboardData中描述"AfterSetClipboardData is called, the system owns the object identified by the hMem parameter. The application can read the data, but must not free the handle or leave it locked.If the hMem parameter identifies a memory object, the object must have been allocated using the GlobalAlloc function with the GMEM_MOVEABLE and GMEM_DDESHARE flags. "那么我们很自然会想到当调用SetClipboardData后,系统在虚拟内存中移动要共享的内存到进程的内核模式分区中托管,这也是为什么一定要指明GMEM_MOVABLE的原因。
聪明的小伙伴你一定会问那为什么一定要指定GMEM_DDESHARE 参数呢,我们查看MSDN中GlobalAlloc中关于GMEM_SHARE和GMEM_DDESHARE参数的描述,“This flag is provided primarily for compatibility with 16-bit Windows. However, this flag may be used by some applications to enhance the performance of DDE operations and therefore can be specified if the memory is to be used for DDE. .”显然这里提供GMEM_SHARE和GMEM_DDESHARE参数只是为了兼容以前的程序编译,事实上你尽管把SHARE参数去掉吧,没什么问题,一切运行如初。
那么,既然这样了,使用LocalAlloc应该没有什么问题吧。
事实上在如下的程序中测试GlobalAlloc和LocalAlloc完全正常
剪切和复制
- case IDM_EDIT_CUT:
- case IDM_EDIT_COPY:
- if (!pText)
- {
- return (0);
- }
- #ifdef GLOBALFUNC
- //将Text数据复制到Global分配的数据区
- hGlobal = GlobalAlloc(GMEM_MOVEABLE | GMEM_ZEROINIT, (lstrlen(pText) + 1) * sizeof(TCHAR));
- pGlobal = GlobalLock(hGlobal);
- lstrcpy(pGlobal, pText);
- GlobalUnlock(hGlobal);
- //将Global数据设为剪切板所有以实现跨进程使用
- OpenClipboard(hwnd);
- EmptyClipboard();
- SetClipboardData(CF_TCHAR, hGlobal);
- CloseClipboard();
- #else
- //将Text数据复制到Global分配的数据区
- hGlobal = LocalAlloc(LMEM_MOVEABLE | LMEM_ZEROINIT, (lstrlen(pText) + 1) * sizeof(TCHAR));
- pGlobal = LocalLock(hGlobal);
- lstrcpy(pGlobal, pText);
- LocalUnlock(hGlobal);
- //将Global数据设为剪切板所有以实现跨进程使用
- OpenClipboard(hwnd);
- EmptyClipboard();
- SetClipboardData(CF_TCHAR, hGlobal);
- CloseClipboard();
- #endif
- if(LOWORD(wParam) == IDM_EDIT_COPY)
- {
- return (0);
- }
- //对于剪切还要向下执行以清除Text数据
- case IDM_EDIT_CLEAR:
- if (pText)
- {
- free(pText);
- pText = NULL;
- }
- InvalidateRect(hwnd, NULL, TRUE);
- return (0);
粘贴
- case IDM_EDIT_PASTE:
- fdef GLOBALFUNC
- OpenClipboard(hwnd);
- if (hGlobal = GetClipboardData(CF_TCHAR))
- {
- pGlobal = GlobalLock(hGlobal);
- if (pText)
- {
- free(pText);
- pText = NULL;
- }
- pText = malloc(GlobalSize(hGlobal));
- lstrcpy(pText, pGlobal);
- InvalidateRect(hwnd, NULL, TRUE);
- GlobalUnlock(hGlobal);
- }
- CloseClipboard();
- lse
- OpenClipboard(hwnd);
- if (hGlobal = GetClipboardData(CF_TCHAR))
- {
- pGlobal = LocalLock(hGlobal);
- if (pText)
- {
- free(pText);
- pText = NULL;
- }
- pText = malloc(LocalSize(hGlobal));
- lstrcpy(pText, pGlobal);
- InvalidateRect(hwnd, NULL, TRUE);
- LocalUnlock(hGlobal);
- }
- CloseClipboard();
- ndif
- return (0);
那么,很明显这个问题的答案令人感到可笑,所谓剪切板必须使用GlobalAlloc的说法不过是前人遗留的写法而已,在现在的操作系统(至少的得是XP了吧)中使用GlobalAlloc和LocalAlloc什么差别也没有。
如果你问我要使用哪个函数,那还是使用GlobalAlloc吧,毕竟你拿不准是否有人还会在年代久远的windows系统上编译你的程序呢。但是有一点需要说明的是,千万不要再说必须使用GlobalAlloc了。
其实再大胆一点不用GMEM_MOVABLE而使用GMEM_FIXED参数分配内存,你会发现一切还是正常的,原来SetClipboardData对这个分配的内存类型都没有了检查,直接强制性的移动了。尽管是个古老的东西,在现在的Windows操作系统中剪切板还是非常的重要,但是微软将它的标准放的这么低来保证程序的兼容性,说不定这个里面藏有好几个漏洞呢,这个黑客们慢慢探讨吧。
博客测试源代码下载
原创,转载请注明来自http://blog.csdn.net/wenzhou1219
http://blog.csdn.net/wenzhou1219/article/details/17693241
为什么使用剪切板时都用GlobalAlloc分配内存(历史遗留问题,其实没关系了)的更多相关文章
- android利用剪切板来实现数据的传递
在Android开发中我们经常要遇到的一个问题就是数据在不同的Activity之间的共享.在Android开发中有很多种方法可以达到这个目地. 这里介绍一种比较常见.又常用的一种方法就是使用剪切板.我 ...
- MFC 剪切板的使用、线程介绍
一.MFC 剪切板 CListBox *pList = (CListBox*)GetDlgItem(IDC_LIST1); // 获取ListBox控件句柄 CString strTmp; pList ...
- iOS14剪切板探究,淘宝实现方法分析
随着iOS 14的发布,剪切板的滥用也被大家所知晓.只要是APP读取剪切板内容,系统都会在顶部弹出提醒,而且这个提醒不能够关闭.这样,大家在使用APP的过程中就能够看到哪些APP使用了剪切板. 正好我 ...
- 对c#剪切板Clipboard占用的问题一点解决方法
以前在百度写的文档,转移到此处 前几天做一个程序,其中有一个剪切板的操作,具体代码: Clipboard.SetText(“ABC”); 来完成一个复制字符串的操作. 自己调试通过,完全正常,然后就交 ...
- 【转载】VC操作剪切板
1.在剪切板上放置数据 if(OpenClipboard()) //打开剪切板{ EmptyClipboard(); //清空剪切板 CString str; //从控件 ...
- Java内存区域(运行时数据区域)和内存模型(JMM)
Java 内存区域和内存模型是不一样的东西,内存区域是指 Jvm 运行时将数据分区域存储,强调对内存空间的划分. 而内存模型(Java Memory Model,简称 JMM )是定义了线程和主内存之 ...
- chrome浏览器下JavaScript实现clipboard时无法访问剪切板解决方案
在用JavaScript实现某个简单的复制到剪切板功能的时候,会考虑一下浏览器兼容性,主要是重点在IE和FireFox,把这个两个浏览器搞定后,基本上其他浏览器也不用太操心了,Chrome也一样,没出 ...
- 小菜学习Winform(六)剪切板和拖放复制
前言 在做winform项目的时候有时候会用到复制粘贴,在.net中提供了Clipboard类来操作剪切板,我们来看下. clipbrd.exe clipbrd是系统剪切板程序,但是在vista及以上 ...
- 简单实现兼容各大浏览器的js复制内容到剪切板
因为网站文章需要提供几个按钮,单击后实现复制文章内容到剪贴板. 在网上搜索了很多内容,发现都比较乱这里自己整理下,分享给大家 效果图如下: 之前使用的是window.clipboardData.set ...
随机推荐
- php redis操作具体解释
phpredis是redis的php的一个扩展,效率是相当高有链表排序功能,对创建内存级的模块业务关系 非常实用;下面是redis官方提供的命令使用技巧: 下载地址例如以下: https://gith ...
- selenium + firefox登录空间
在网上看到的大部分都是Python版本的,于是写了个java版本的 环境: selenium-java 3.9.1 firefox 57.0 geckodriver 0.19.1 firefox与ge ...
- MongoDB小结
教程 MongoDB MongoDB 是由C++语言编写的,是一个基于分布式文件存储的开源数据库系统.在高负载的情况下,添加更多的节点,可以保证服务器性能.MongoDB 旨在为WEB应用提供可扩展的 ...
- APP压力測试新手教程
Daniel Knott 用过各种不同编程语言和软件质量保证工具.他在软件开发和測试方面干了七年,自2010年,他一直在德国汉堡的XING AG公司就职,几个项目里,比方XING调查和XING建议,他 ...
- [Spring Boot 系列] 集成maven和Spring boot的profile 专题
maven中配置profile节点: <project> .... <profiles> <profile> <!-- 生产环境 --> <id& ...
- 衡量镜头解像能力性能的指标-MTF曲线
MTF(Modulation Transfer Function,模量传递函数),是目前分析镜头解像能力的方法,可以用来评判镜头还原物体对比度的能力.说到MTF,不得不先提一下衡量镜头性能的两在重要指 ...
- 斯托克斯定理(Stokes' theorem)
1. 几种形式 ∮∂SPdx+Qdy+Rdz=∬S∣∣∣∣∣∣cosα∂∂xPcosβ∂∂yQcosγ∂∂zR∣∣∣∣∣∣dS ∮∂Ωw=∬Ωdw 左边是内积: 右边是外积: 物理上的应用: ∮∂SE ...
- Windows 窗体设计器(Windows Forms Designer)入门
Visual Studio 2010 更新:2010 年 9 月 Windows 窗体设计器提供多个用于生成 Windows 窗体应用程序的工具. 本演练阐释如何使用设计器提供的各种工具生成应用程 ...
- 编写可移植C/C++程序的要点(12条)
1.分层设计,隔离平台相关的代码.就像可测试性一样,可移植性也要从设计抓起.一般来说,最上层和最下层都不具有良好的可移植性.最上层是GUI,大多数GUI都不是跨平台的,如Win32 SDK和MFC.最 ...
- HQL链接查询
和SQL查询一样,HQL也支持各种各样的连接查询,如内连接.外连接.我们知道在SQL中可通过join字句实现多表之间的连接查询.HQL同样提供了连接查询机制,还允许显示指定迫切内连接和迫切左外连接.H ...