为什么使用剪切板时都用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 ...
随机推荐
- 第三方微信支付,WAP、H5、APP、公众号支付的区别
你说一个微信支付被腾讯搞了N个版本出来,是技术问题还收费原因不得而知.公众号支付,H5(wap)支付,APP支付.看得小编一头雾水. 带点N个疑问? 1.公众号支付是在公众号里支付,支众号里引入的三方 ...
- hbase 判断列族是否存在
public static boolean isExistColumnFamily(String tableName,String cf) throws IOException { if(isExis ...
- module.exports输出的属性被ES6如何引用的
阮一峰的ES6教程里有讲: import 命令加载 CommonJS 模块 Node 采用 CommonJS 模块格式,模块的输出都定义在module.exports这个属性上面.在 Node 环境中 ...
- .net程序运行流程
程序员用.net开发的程序要在计算机上运行,首先程序经过编译后,会生成机器指令,一般以一个文件的形式保存,这个文件在外存储器上(存储器分外存与内存.外存:硬盘,U盘等:) 然后cpu会把硬盘上的文件读 ...
- git命令评测
近日得知git命令在库进行操作,查找git尽管小命令(当然,也不能太小),但他们是一个非常强大的组合,更重要的是,它是非常的效果不同状态的命令是不一样的打.该博文总结git命令.. Git命令 命令小 ...
- 初探js
第一章 1.JS的位置 1-1.行间 1-2.内嵌 1-3.外联 2.JS的标签位置 页面中的代码在一般情况下会按从上到下的顺序,从左往右的顺序执行. 因此当JS放在了元素上面的时候,就不能正常执 ...
- Spring boot配置说明
要加“m”说明是MB,否则就是KB了. -Xms:初始值 -Xmx:最大值 -Xmn:最小值 java -Xms80m -Xmx80m -jar mod.jar 时区设置 java -jar -Dus ...
- Qt 自定义事件(三种方法:继承QEvent,然后Send Post就都可以了,也可以覆盖customEvent函数,也可覆盖event()函数)
Qt 自定义事件很简单,同其它类库的使用很相似,都是要继承一个类进行扩展.在 Qt 中,你需要继承的类是 QEvent. 继承QEvent类,你需要提供一个QEvent::Type类型的参数,作为自定 ...
- March 29th, 2015, Thread Name is odd by increasing 1
public class Fabric extends Thread{ public static void main(String args[]){ Thread t = new Thread(ne ...
- Xcode 4.5( iOS6 SDK)、旧版本号cocos2d,支持iPhone5解析度
支持iPhone5全屏 1假设没有支持iPhone5是否.直接运行程序可以准备提交.开放iPhone5模拟器,你会发现上面有黑色的程序.没有矩形. 2真正运行该程序时,.你会发现程序回程屏幕高度.它是 ...