我在使用剪切板时,发现通用的都是使用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完全正常

剪切和复制

  1. case IDM_EDIT_CUT:
  2. case IDM_EDIT_COPY:
  3. if (!pText)
  4. {
  5. return (0);
  6. }
  7. #ifdef GLOBALFUNC
  8. //将Text数据复制到Global分配的数据区
  9. hGlobal = GlobalAlloc(GMEM_MOVEABLE | GMEM_ZEROINIT, (lstrlen(pText) + 1) * sizeof(TCHAR));
  10. pGlobal = GlobalLock(hGlobal);
  11. lstrcpy(pGlobal, pText);
  12. GlobalUnlock(hGlobal);
  13. //将Global数据设为剪切板所有以实现跨进程使用
  14. OpenClipboard(hwnd);
  15. EmptyClipboard();
  16. SetClipboardData(CF_TCHAR, hGlobal);
  17. CloseClipboard();
  18. #else
  19. //将Text数据复制到Global分配的数据区
  20. hGlobal = LocalAlloc(LMEM_MOVEABLE | LMEM_ZEROINIT, (lstrlen(pText) + 1) * sizeof(TCHAR));
  21. pGlobal = LocalLock(hGlobal);
  22. lstrcpy(pGlobal, pText);
  23. LocalUnlock(hGlobal);
  24. //将Global数据设为剪切板所有以实现跨进程使用
  25. OpenClipboard(hwnd);
  26. EmptyClipboard();
  27. SetClipboardData(CF_TCHAR, hGlobal);
  28. CloseClipboard();
  29. #endif
  30. if(LOWORD(wParam) == IDM_EDIT_COPY)
  31. {
  32. return (0);
  33. }
  34. //对于剪切还要向下执行以清除Text数据
  35. case IDM_EDIT_CLEAR:
  36. if (pText)
  37. {
  38. free(pText);
  39. pText = NULL;
  40. }
  41. InvalidateRect(hwnd, NULL, TRUE);
  42. return (0);

粘贴

  1. case IDM_EDIT_PASTE:
  2. fdef GLOBALFUNC
  3. OpenClipboard(hwnd);
  4. if (hGlobal = GetClipboardData(CF_TCHAR))
  5. {
  6. pGlobal = GlobalLock(hGlobal);
  7. if (pText)
  8. {
  9. free(pText);
  10. pText = NULL;
  11. }
  12. pText = malloc(GlobalSize(hGlobal));
  13. lstrcpy(pText, pGlobal);
  14. InvalidateRect(hwnd, NULL, TRUE);
  15. GlobalUnlock(hGlobal);
  16. }
  17. CloseClipboard();
  18. lse
  19. OpenClipboard(hwnd);
  20. if (hGlobal = GetClipboardData(CF_TCHAR))
  21. {
  22. pGlobal = LocalLock(hGlobal);
  23. if (pText)
  24. {
  25. free(pText);
  26. pText = NULL;
  27. }
  28. pText = malloc(LocalSize(hGlobal));
  29. lstrcpy(pText, pGlobal);
  30. InvalidateRect(hwnd, NULL, TRUE);
  31. LocalUnlock(hGlobal);
  32. }
  33. CloseClipboard();
  34. ndif
  35. 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分配内存(历史遗留问题,其实没关系了)的更多相关文章

  1. android利用剪切板来实现数据的传递

    在Android开发中我们经常要遇到的一个问题就是数据在不同的Activity之间的共享.在Android开发中有很多种方法可以达到这个目地. 这里介绍一种比较常见.又常用的一种方法就是使用剪切板.我 ...

  2. MFC 剪切板的使用、线程介绍

    一.MFC 剪切板 CListBox *pList = (CListBox*)GetDlgItem(IDC_LIST1); // 获取ListBox控件句柄 CString strTmp; pList ...

  3. iOS14剪切板探究,淘宝实现方法分析

    随着iOS 14的发布,剪切板的滥用也被大家所知晓.只要是APP读取剪切板内容,系统都会在顶部弹出提醒,而且这个提醒不能够关闭.这样,大家在使用APP的过程中就能够看到哪些APP使用了剪切板. 正好我 ...

  4. 对c#剪切板Clipboard占用的问题一点解决方法

    以前在百度写的文档,转移到此处 前几天做一个程序,其中有一个剪切板的操作,具体代码: Clipboard.SetText(“ABC”); 来完成一个复制字符串的操作. 自己调试通过,完全正常,然后就交 ...

  5. 【转载】VC操作剪切板

    1.在剪切板上放置数据 if(OpenClipboard())    //打开剪切板{    EmptyClipboard(); //清空剪切板    CString str;       //从控件 ...

  6. Java内存区域(运行时数据区域)和内存模型(JMM)

    Java 内存区域和内存模型是不一样的东西,内存区域是指 Jvm 运行时将数据分区域存储,强调对内存空间的划分. 而内存模型(Java Memory Model,简称 JMM )是定义了线程和主内存之 ...

  7. chrome浏览器下JavaScript实现clipboard时无法访问剪切板解决方案

    在用JavaScript实现某个简单的复制到剪切板功能的时候,会考虑一下浏览器兼容性,主要是重点在IE和FireFox,把这个两个浏览器搞定后,基本上其他浏览器也不用太操心了,Chrome也一样,没出 ...

  8. 小菜学习Winform(六)剪切板和拖放复制

    前言 在做winform项目的时候有时候会用到复制粘贴,在.net中提供了Clipboard类来操作剪切板,我们来看下. clipbrd.exe clipbrd是系统剪切板程序,但是在vista及以上 ...

  9. 简单实现兼容各大浏览器的js复制内容到剪切板

    因为网站文章需要提供几个按钮,单击后实现复制文章内容到剪贴板. 在网上搜索了很多内容,发现都比较乱这里自己整理下,分享给大家 效果图如下: 之前使用的是window.clipboardData.set ...

随机推荐

  1. JavaScript实现简单图片滚动 --9张图告诉你,C罗欲哭无泪

    源代码下载:http://download.csdn.net/detail/u011043843/7510425 昨晚德国和葡萄牙的焦点之战你看了吗?北京时间凌晨的比赛中.C罗领衔的葡萄牙0-4德国被 ...

  2. Windows安装Jekyll

    Run Jekyll on Windows 夹 Jekyll介绍 安装Ruby 安装DevKit 安装Jekyll 安装Python 安装pip 执行Jekyll Introduction Jekyl ...

  3. Qt的paint函数重写,以及QPaint给一条线绘制箭头

    直接代码: QPainter *painter; static const double Pi = 3.14159265358979323846264338327950288419717; stati ...

  4. 城市三级联动 AJAX-原生js封装

    话不多说我们先来一张效果图给大家看一下: html代码如下: <!DOCTYPE html><html lang="en"><head> < ...

  5. linux下仅仅有rman备份集的异机不同文件夹恢复

    昨天在客户那里做了一次rman异机的恢复,把生产库弄一份给測试库用,总库大概80G,总共花费了2个小时,当时客户的环境是windows 11.2.0.3,今天早晨在linux下又一次測试了一下,记录下 ...

  6. .NET 中使用 Mutex 进行跨越进程边界的同步 - walterlv

    原文:.NET 中使用 Mutex 进行跨越进程边界的同步 - walterlv .NET 中使用 Mutex 进行跨越进程边界的同步 2018-12-30 08:41 Mutex 是 Mutual ...

  7. node lesson2

    var express = require('express'); var utility = require('utility'); var app = express(); app.get('/' ...

  8. 微信公众平台通用接口API指南

    微信公众平台 通用接口 消息接口 开发模式 作者:方倍工作室原文:http://www.doucube.com/index.php?m=Article&a=show&id=5 微信公众 ...

  9. 对于ado.net dataProvider的介绍

    学习刘皓的 ADO.NET入门教程(二)了解.NET数据提供程序 而来 这篇文章很一般,主要是对dataProvider做了个简单的介绍.因为在该系列文章中提到,ado.net主要有两部分 dataP ...

  10. sql在单引号的声明标志着嵌套问题

    在sql声明,我们将不可避免地使用嵌套单引号什么时候.但它肯定不是一个直接嵌套,java使用反斜杠做到这一点是不够的.在sql这是做一个单引号逃逸. 例如,下面的例子是展示一个示例存储过程的语句进行查 ...