为什么使用剪切板时都用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 ...
随机推荐
- scala 通过apply创建类的对象
package cn.scala_base.oop.scalaobject; class Boy(name: String) { private var age: Int = 0; println(n ...
- C#高效率复制对象
高效率复制对象 1.需求 在代码中经常会遇到需要把对象复制一遍,或者把属性名相同的值复制一遍. 比如: public class Student { public int Id { get; set; ...
- 【17.76%】【codeforces round 382C】Tennis Championship
time limit per test2 seconds memory limit per test256 megabytes inputstandard input outputstandard o ...
- WPF UserControl 的绑定事件、属性、附加属性
原文:WPF UserControl 的绑定事件.属性.附加属性 版权声明:本文为博主原创文章,未经博主允许不得转载. https://blog.csdn.net/Vblegend_2013/arti ...
- 关于FileZilla上传文件后服务器端文件与本地文件大小不一致的解决方法
最近在调试网站时发现,通过ftp上传工具FileZilla上传至服务器端的文件与本地文件大小不一致,虽然没有影响网站的最终显示效果,但仍让我困惑不解.后发现是传输类型的原因,解决方法如下: 中文版Fi ...
- 格式字符串分配stl::string
代码非常easy,不解释,直接在代码: #include <cstdio> #include <cstdarg> #include <iostream> using ...
- ANDROID 中设计模式的採用--结构型模式
结构型模式中的适配器模式.外观模式.装饰模式.代理模式都属于包装模式,都是对另外的类或对象的包装,仅仅是各自的意图不同. 适配器模式通过对另外的类或对象的包装,将其接口转换为用户期 ...
- 《Silk》(皇家律师)—— 英美海洋法系
Abortion Act:堕胎法: 1. 表达习惯 we employ him, not the other way round, Officially,-,官方的说法是,Unofficially,- ...
- ASP.NET Core 新建项目(Windows) - ASP.NET Core 基础教程 - 简单教程,简单编程
原文:ASP.NET Core 新建项目(Windows) - ASP.NET Core 基础教程 - 简单教程,简单编程 ASP.NET Core 新建项目(Windows) 对于任何语言和框架,都 ...
- WPF多点触摸放大缩小旋转
原文:WPF多点触摸放大缩小旋转 版权声明:本文为博主原创文章,需要转载尽管转载. https://blog.csdn.net/z5976749/article/details/40118437 如果 ...