精华内容
下载资源
问答
  • 12.1 剪贴板的简单用法

    千次阅读 2015-11-19 13:43:55
     让我们先看看把数据传入剪贴板(剪切和复制)和从剪贴板中取得数据(粘贴)的代码。 12.1.1 剪贴板数据的标准格式  Windows 支持各种预定义的剪贴板格式,这些格式在 WINUSER.H 中定义并有前缀为 CF 的标识符。  ...

    摘录于《Windows程序(第5版,珍藏版).CHarles.Petzold 著》P447

            让我们先看看把数据传入剪贴板(剪切和复制)和从剪贴板中取得数据(粘贴)的代码。

    12.1.1  剪贴板数据的标准格式

            Windows 支持各种预定义的剪贴板格式,这些格式在 WINUSER.H 中定义并有前缀为 CF 的标识符

            首先,在剪贴板中可以存储三种文本数据类型,相应的有一种相关的剪贴板格式。

    • CF_TEXT    一种以 NULL 结尾的 ANSI 字符集字符串,字符串的每行结尾处含有一个回车换行符。这是最简单的剪贴板数据格式。要被传输到剪贴板的数据存储在内存块中,传输时使用指向此内存块的句柄。(我将很快介绍这个概念。)该内存块会成为剪贴板的属性,因此创建这个内存块的程序不应该继续使用它
    • CF_OEMTEXT    包含文本数据(与 CF_TEXT 类似)但使用 OEM 字符集的内存块。Windows 程序通常不用担心这点;在窗口中运行 MS-DOS 程序时如果使用剪贴板则需要用到这种类型。
    • CF_UNICODETEXT    包含 Unicode 文本的内存块。类似于 CF_TEXT,每行以回车换行符结束,字符 NULL(两个零字节)标志着整个数据的结束。CF_UNICODETEXT 只在 Windows NT 下支持。
    • CF_LOCALE    指向区域设置标识符的句柄,该标识符标明了与剪贴板相关的区域设置。

            此外还有两种剪贴板格式,它们在概念上和 CF_TEXT 格式类似(就是说,它们是基于文本的),但是它们不一定以 NULL 结尾,因为格式本身定义了数据的结尾。这些格式现在很少用到

    • CF_SYLK    含有微软符号链接(Symbolic Link)格式数据的内存块。这种格式被用来在微软的 Multiplan、Chart 和 Excel 程序之间交换数据。它是一种 ASCII 格式,每行以回车换行符结束。
    • CF_DIF    含有数据交换格式(DIF)数据的内存块。这是一种由 Software Arts 设计,用来把数据传输到 VisiCalc 电子表格程序的格式。它也是一种 ASCII 格式,每行以回城换行符结束。

            与位图相关的剪贴板格式有三种。位图即对应于输出设备像素的矩形位数组。位图和这些位图剪贴板格式将在第 14 章和第 15 章详细讨论。

    • CF_BITMAP    设备相关位图。此位图由位图句柄传输到剪贴板中。再强调一遍,程序把位图传给剪贴板之后不应继续使用此位图
    • CF_DIB    定义了设备无关位图的内存。设备无关位图在第 15 章会给出描述。该内存块以位图信息结构开头,接着有可能是颜色表和位图的位。
    • CF_PALETTE    指向调色板的句柄。它通常和 CF_DIB 一起使用,用来定义在设备相关位图中使用的调色板。

            剪贴板里的位图数据也可以采用行业标准的 TIFF 格式存储。

    • CF_TIFF    包含标签图像文件格式(Tag Image File Format, TIFF)数据的内存块。这是由微软、Aldus 和惠普公司联合一些硬件制造商共同设计的格式。这种格式从惠普网站上可以找到。

            还有两种图元文件格式,我会在第 18 章详细描述。图元文件是以二进制形式存储的绘图命令的集合

    • CF_METAFILEPICT    基于 Windows 过去支持的图元文件的“图元文件图片”。
    • CF_ENHMETAFILE    指向 32 位 Windows 版本支持的增强型图元文件的句柄。

            最后还有一些其他不同的剪贴板格式:

    • CF_PENDATA    和 Windows 画笔扩展一起使用。
    • CF_WAVE    声音(波形)文件。
    • CF_RIFF    资源交换文件格式(RIFF)的多媒体数据。
    • CF_HDROP    和拖放服务一起使用的文件列表。

    12.1.2  内存分配

            当程序把一些数据传到剪贴板时,程序必须分配一个内存块并移交给剪贴板。在本书先前的程序中,当我们需要分配内存时,只是调用了标准 C 运行时库支持的 malloc 函数。然而,由于剪贴板中存储的内存块必须在 Windows 下运行的应用程序之间共享,所以 malloc 函数不足以完成此任务。

            取而代之的是,我们必须研究在 Windows 早期设计的内存分配函数,那时的操作系统运行在 16 位实模式存储体系结构上。虽然这些函数通常没有什么用,但是现在仍然是受支持的,所以仍然可以用。

            可以用 Windows API 分配一块内存:

    hGlobal = GlobalAlloc(uiFlags, dwSize);
    这个函数有两个参数:一系列可能的标志和被分配内存块的字节大小。函数返回 HGLOBAL 类型的句柄,这个句柄被称为“指向全局内存块的句柄”(handle to a global memory block)或“全局句柄”。这个函数返回值为 NULL 时就表示没有足够的内存空间可以分配。

            尽管 GlobalAllo 的两个参数定义不同,但它们都是 32 位无符号整数。如果把第一个参数设为 0,那么使用的就是标志 GMEM_FIXED。这种情况下,GlobalAlloc 返回的全局句柄实际是一个指向被分配内存块的指针。

            如果想让内存块中的每个字节初始化为 0,则可以用标志 GMEM_ZEROINIT。在 Windows 头文件中,简短的 GPTR 标志把 GMEM_FIXED 和 GMEM_ZEROINIT 标志定义在一起:

    #define GPTR (GMEM_FIXED | GMEM_ZEROINIT)
    

            还有一个内存重分配函数:

    hGlobal = GlobalReAlloc(hGlobal, dwSize, uiFlags);
    如果内存块扩大了,可以用 GMEM_ZEROINIT  标志把新分配的字节初始化为 0。

            取得内存块大小的函数如下:

    dwSize = GlobalSize (hGlobal);
    
    释放内存的函数是:

    GlobalFree (hGlobal);

            早期的 16 位 Windows 版本会尽量避免 GMEM_FIXED 标志,因为 Windows 不能在物理内存中移动内存块。在 32 位 Windows 中,GMEM_FIXED 可以正常使用,因为它返回一个虚拟地址,操作系统可以通过改变页表移动物理内存里的内存块。在 16 位 Windows 上编程,推荐在 GlobalAlloc 中用 GMEM_MOVEABLE 标志。在 Windows 头文件中还定义了一个缩写标识符,用来把可移动内存初始化为 0:

    #define GHND (GMEM_MOVEABLE |  GMEM_ZEROINIT)
    
    GMEM_MOVEALBE 标志使 Windows 能在虚拟内存中移动内存块。这并不意味着内存块会在物理内存中移动,仅仅是此应用程序用来读/写内存块的地址可能会改变

            尽管 GMEM_MOVEABLE 经常用于 16 位 Windows 版本,但现在如今通常都不再使用了。虽然如此,如果应用程序频繁地分配、重分配、释放不同大小的内存块,应用程序虚拟地址空间就会变得七零八碎。你可能会把虚拟内存地址用完。如果这是个潜在的问题,就需要用可移动内存,具体用法如下。

            先定义一个指针(例如,指向整数类型的指针)和一个 GLOBALHANDLE 类型的变量:

    int *p;
    GLOBALHANDLE hGlobal;
    
    然后分配内存,例如:

    hGlobal = GlobalAlloc(GHND, 1024);

            和其他 Windows 句柄一样,不要太在意具体数字代表什么。只要存储下来就可以了。如果需要访问这个内存块,可调用以下函数把句柄转换为指针:

    p = (int *) GlobalLock (hGlobal);
    
    在内存块被锁定期间,Windows 会修复虚拟内存地址。内存块不会移动。访问完内存块之后,调用以下函数使 Windows 自由移动虚拟内存中的内存块:

    GlobalUnLock (hGlobal);
    为确保这个过程正确(并且体验早期 Windows 程序员所经历过的苦痛),应该在单个消息过程中锁定和解锁内存块。

            如果想释放内存,则调用 GlobalFree,传入句柄而不是指针。如果现在没有可访问的句柄,那么用以下函数即可:

    hGlobal = GlobalHandle(p);
    

            可以在释放一个内存块之前多次锁定它。Windows 有一个锁计数器,内存块被自由移动前,每次锁定都要有一个对应的解锁。Windows 在虚拟内存中移动内存块不需要把字节从一个位置复制到另一个位置——它只需要操作页表。一般来说,在 32 位版本的 Windows 上,为应用程序分配可移动内存的唯一原因就是防止虚拟内存变得零碎用剪贴板时,也应该用可移动内存

            为剪贴板分配内存时,应该用 GlobalAlloc 函数,并同时使用 GMEM_MOVEABLE 和 GMEM_SHARE 标志作为参数。GMEM_SHARE 标志使内存块能被其他 Windows 应用程序共享。

    12.1.3  把文本传到剪贴板

            假设你想把一个 ANSI 字符串传到剪贴板。你有一个指向这个字符串的指针(称作 pString),并且想传 iLength 个字符,它们也可能不是以 NULL 终止。

            首先得用 GlobalAlloc 来分配足够容纳这个字符串的内存块,并给终止符 NULL 留下空间:

    hGlobal = GlobalAlloc (GHND | GMEM_SHARE, iLength + 1);
    如果内存分配失败,hGlobal 的值为 NULL。如果分配成功,则锁定这个内存块并得到指向它的指针:

    pGlobal = GlobalLock (hGlobal);
    把字符串复制到全局内存块:

    for (i = 0; i < iLength; ++ i)
        *pGlobal++ = *pString++;
    
    不需要加终止符 NULL,因为 GlobalAlloc 的 GHND 标志在分配内存时把整个内存块置 0。解锁内存:

    GlobalUnLock (hGlobal);

            现在你有了一个全局内存句柄,它指向一块包含以 NULL 为终止符的文本的内存块。要把这个内存块传入剪贴板,应打开剪贴板并且清空它:

    OpenClipboard (hwnd);
    EmptyClipboard();
    
    通过使用 CF_TEXT 标识符,把内存句柄传给剪贴板,并且关闭剪贴板:

    SetClipboardData (CF_TEXT, hGlobal);
    CloseClipboard();http://write.blog.csdn.net/postedit/49906463
    
    这就万事大吉了。

            以下是关于这个过程的一些规则。

    • 在处理单个消息的过程中调用 OpenClipboard 和 CloseClipboard。避免不必要地长期打开剪贴板。
    • 不要把一个锁定的内存句柄传给剪贴板。
    • 调用 SetClipboardData 后,不要继续使用该内存块。它已不再属于程序,你应该把该句柄当作是无效的。如果需要继续使用数据,应再复制一份或者从剪贴板中读取(下一小节会讲到)。在调用 SetClipboardData 和 CloseClipboard 之间,也可以继续引用内存块,但是不要使用传给 SetClipboardData 函数的全局句柄。你可以使用由这个函数返回的全局句柄。锁定这个句柄来访问内存。在调用 CloseClipboard 之前解锁这个句柄。

    12.1.4  从剪贴板中取得文本

            从剪贴板中取得文本只比把文本传给剪贴板稍稍麻烦一点点。首先要确定剪贴板实际上 是否包含 CF_TEXT 格式的文本。最简单的方法之一是调用以下函数:

    bAvailable = IsClipboardFormatAvailable (CF_TEXT);
    
    如果剪贴板包含 CF_TEXT 数据,这个函数返回 TRUE(非 0)。在第 10 章的 POPPAD2 程序中,我们用这个函数检查 Edit 菜单上的 Paste 项是否可用。IsClipboardFormatAvailable 是剪贴板少数几个不需要打开剪贴板就能使用的函数之一。但是,在之后打开剪贴板以取得文本时,应该再次进行检查(用同样的函数或者其他某种方法),以确定 CF_TEXT 数据是否仍保存在剪贴板中。

            要把文本传出,先如下打开剪贴板:

    OpenClipboard(hwnd);
    
    取得指向文本的全局内存块的句柄;       
    hGlobal = GetClipboardData (CF_TEXT);
    
    如果剪贴板没有 CF_TEXT 格式的数据,此句柄为 NULL。这是判断剪贴板是否有文本数据的另一个方法。如果 GetClipboardData 返回 NULL,则应关闭剪贴板,其他什么都不要做。

            从 GetClipboardData 得到的句柄不属于你的程序,它属于剪贴板。这个句柄只在 GetClipboard 和 CloseClipboard 调用之间有效。你不能释放此句柄或是改变它应用的数据。如果想继续访问数据,应该复制内存块。

            以下是把数据复制到你程序里的方法。分配一个指向和剪贴板数据块一样大小的内存块的指针:

    pText = (char *) malloc (GlobalSize (hGlobal));
    
    记住,hGlobal 是调用 GetClipboardData 取得的全局句柄。现在锁定该句柄,得到一个指向剪贴板数据块的指针:

    pGlobal = GlobalLock (hGlobal);
    现在复制数据:

    strcpy (pText, pGlobal);
    
    或者也可以用简单的 C 代码:

    while (*pText++ = *pGlobal++);
    

            解锁内存块之后关闭剪贴板:

    GlobalUnlock (hGlobal);
    CloseClipboard();
    
    现在你有了一个 pText 指针,它指向你的程序所拥有的文本副本。

    12.1.5  打开和关闭剪贴板

            无论何时都只有一个程序可以打开剪贴板。调用 OpenClipboard 的目的是当某个程序正在使用剪贴板时,防止剪贴板内容改变。OpenClipboard 会返回一个表示剪贴板是否已被成功打开的 BOOL 值。如果有另一个应用程序尚未关闭剪贴板,便无法打开它。如果每个程序都根据用户命令尽快打开和关闭剪贴板,你可能就不会遇到无法打开剪贴板这个问题。

            如果程序之间不那么谦让或者碰到抢占式多任务,可能就会有些麻烦。即使你的程序在把数据放到剪贴板和用户调用粘贴项之间这段时间里没有丢失输入焦点,也不要假设你传入的数据还在剪贴板里。后台进程在这段时间里有可能已经访问过剪贴板。

            还要当心涉及消息框时的一个更微妙的问题:在不能分配足够的内存把数据复制到剪贴板时,你或者想显示一个消息框。但是,如果这个消息框不是系统模态,那么用户可以在消息框显示时切换到另一个应用程序。所以,要么把消息框变成系统模态,要么在显示消息框前关闭剪贴板。

            如果在打开剪贴板的同时又显示了对话框,也可能会碰到问题。对话框的编辑字段会使用剪贴板来剪切和粘贴文本。

    12.1.6  剪贴板和 Unicode

            到目前为止,我们仅仅讨论了用剪贴板传输 ANSI 文本(每个字符一个字节)。这是用 CF_TEXT 标识符时使用的格式。你可能还想了解 CF_OEMTEXT 和 CF_UNICODETEXT。

            我有个好消息:你只需在调用 SetClipboardData 和 GetClipboardData 时加上自己想要的文本格式,Windows 便会再剪贴板里处理所有的文本转换。例如,在 Windows NT 下,如果某程序用 CF_TEXT 剪贴板数据类型来使用 SetClipboardData,那么你也可以用 CF_OEMTEXT 来调用 GetClipboardData。同理,剪贴板也能把 CF_OEMTEXT 转换成 CF_TEXT。

            在 Windows NT 下,CF_UNICODETEXT、CF_TEXT 和 CF_OEMTEXT 之间可以互相转换。程序调用 SetClipboardData 时应该用它最方便的格式。同理,程序调用 GetClipboardData 时也应该用它何时使用的文本格式。如你所知,本书中的程序在有或没有 UNICODE 标识符的情况下都可以编译。如果你的程序是这样的,那么可能需要在调用 SetClipboardData 和 GetClipboardData 时实现这样的代码:如果定义了 UNICODE 标识符,则用 CF_UNICODETEXT;如果没有,就用 CF_TEXT。

            如下所示的 CLIPTEXT 程序演示了完成此任务的一种方法。

    /*---------------------------------------------
    	CLIPTEXT.C -- The Clipboard and Text
    		    (c) Charles Petzold, 1998
    ----------------------------------------------*/
    
    #include <windows.h>
    #include "resource.h"
    
    LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
    
    #ifdef UNICODE
    
    #define CF_TCHAR CF_UNICODETEXT
    TCHAR szDefaultText[] = TEXT("Default Text - Unicode Version");
    TCHAR szCaption[] = TEXT("Clipboard Text Transfers - Unicode Version");
    #else
    
    #define CF_TCHAR CF_TEXT
    TCHAR szDefaultText[] = TEXT(".Default Text - ANSI Version.");
    TCHAR szCaption[] = TEXT(".Clipboard Text Transfers - ANSI Version.");
    
    #endif
    
    int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
    				PSTR szCmdLine, int iCmdShow)
    {
    	static TCHAR szAppName[] = TEXT("ClipText");
    	HACCEL		 hAccel;
    	HWND		 hwnd;
    	MSG			 msg;
    	WNDCLASS	 wndclass;
    
    	wndclass.style				= CS_HREDRAW | CS_VREDRAW;
    	wndclass.lpfnWndProc		= WndProc;
    	wndclass.cbClsExtra			= 0;
    	wndclass.cbWndExtra			= 0;
    	wndclass.hInstance			= hInstance;
    	wndclass.hIcon				= LoadIcon(NULL, IDI_APPLICATION);
    	wndclass.hCursor			= LoadCursor(NULL, IDC_ARROW);
    	wndclass.hbrBackground		= (HBRUSH)GetStockObject(WHITE_BRUSH);
    	wndclass.lpszMenuName		= szAppName;
    	wndclass.lpszClassName		= szAppName;
    
    	if (!RegisterClass(&wndclass))
    	{
    		MessageBox(NULL, TEXT("This program requires Windows NT!"),
    			szAppName, MB_ICONERROR);
    		return 0;
    	}
    
    	hwnd = CreateWindow(szAppName, szCaption,
    		WS_OVERLAPPEDWINDOW,
    		CW_USEDEFAULT, CW_USEDEFAULT,
    		CW_USEDEFAULT, CW_USEDEFAULT,
    		NULL, NULL, hInstance, NULL);
    
    	ShowWindow(hwnd, iCmdShow);
    	UpdateWindow(hwnd);
    
    	hAccel = LoadAccelerators(hInstance, szAppName);
    
    	while (GetMessage(&msg, NULL, 0, 0))
    	{
    		if (!TranslateAccelerator(hwnd, hAccel, &msg))
    		{
    			TranslateMessage(&msg);
    			DispatchMessage(&msg);
    		}
    	}
    	return msg.wParam;
    }
    
    LRESULT CALLBACK WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
    {
    	static PTSTR pText;
    	BOOL		 bEnable;
    	HGLOBAL		 hGlobal;
    	HDC			 hdc;
    	PTSTR		 pGlobal;
    	PAINTSTRUCT  ps;
    	RECT		 rect;
    
    	switch (message)
    	{
    	case WM_CREATE:
    		SendMessage(hwnd, WM_COMMAND, IDM_EDIT_RESET, 0);
    		return 0;
    
    	case WM_INITMENUPOPUP:
    		EnableMenuItem((HMENU)wParam, IDM_EDIT_PASTE,
    			IsClipboardFormatAvailable(CF_TCHAR) ? MF_ENABLED : MF_GRAYED);
    
    		bEnable = pText ? MF_ENABLED : MF_GRAYED;
    
    		EnableMenuItem((HMENU)wParam, IDM_EDIT_CUT, bEnable);
    		EnableMenuItem((HMENU)wParam, IDM_EDIT_COPY, bEnable);
    		EnableMenuItem((HMENU)wParam, IDM_EDIT_CLEAR, bEnable);
    		break;
    
    	case WM_COMMAND:
    		switch (LOWORD(wParam))
    		{
    		case IDM_EDIT_PASTE:
    			OpenClipboard(hwnd);
    
    			if (hGlobal = GetClipboardData(CF_TCHAR))
    			{
    				pGlobal =(PTSTR) GlobalLock(hGlobal);
    				if (pText)
    				{
    					free(pText);
    					pText = NULL;
    				}
    				pText = (PTSTR) malloc(GlobalSize(hGlobal));
    				lstrcpy(pText, pGlobal);
    				InvalidateRect(hwnd, NULL, TRUE);
    			}
    			CloseClipboard();
    			return 0;
    
    		case IDM_EDIT_CUT:
    		case IDM_EDIT_COPY:
    			if (!pText)
    				return 0;
    
    			hGlobal = GlobalAlloc(GHND | GMEM_SHARE,
    				(lstrlen(pText) + 1) * sizeof(TCHAR));
    
    			pGlobal = (PTSTR)GlobalLock(hGlobal);
    			lstrcpy(pGlobal, pText);
    			GlobalUnlock(hGlobal);
    
    			OpenClipboard(hwnd);
    			EmptyClipboard();
    			SetClipboardData(CF_TCHAR, hGlobal);
    			CloseClipboard();
    
    			if (LOWORD(wParam) == IDM_EDIT_COPY)
    				return 0;
    										// fall through fro IDM_EDIT_CUT
    
    		case IDM_EDIT_CLEAR:
    			if (pText)
    			{
    				free(pText);
    				pText = NULL;
    			}
    			InvalidateRect(hwnd, NULL, TRUE);
    			return 0;
    
    		case IDM_EDIT_RESET:
    			if (pText)
    			{
    				free(pText);
    				pText = NULL;
    			}
    
    			pText = (PTSTR)malloc((lstrlen(szDefaultText) + 1) * sizeof(TCHAR));
    			lstrcpy(pText, szDefaultText);
    			InvalidateRect(hwnd, NULL, TRUE);
    			return 0;
    		}
    		break;
    
    	case WM_PAINT:
    		hdc = BeginPaint(hwnd, &ps);
    
    		GetClientRect(hwnd, &rect);
    
    		if (pText != NULL)
    			DrawText(hdc, pText, -1, &rect, DT_EXPANDTABS | DT_WORDBREAK);
    		EndPaint(hwnd, &ps);
    		return 0;
    
    	case WM_DESTROY:
    		if (pText)
    			free(pText);
    		PostQuitMessage(0);
    		return 0;
    	}
    	return DefWindowProc(hwnd, message, wParam, lParam);
    }
    CLIPTEXT.RC (excerpts)
    
    // Microsoft Visual C++ generated resource script.
    //
    #include "resource.h"
    
    /
    //
    // Menu
    //
    
    CLIPTEXT MENU
    BEGIN
        POPUP "&Edit"
        BEGIN
            MENUITEM "Cu&t\tCtrl+X",                IDM_EDIT_CUT
            MENUITEM "&Copy\tCtrl+C",               IDM_EDIT_COPY
            MENUITEM "&Paste\tCtrl+V",              IDM_EDIT_PASTE
            MENUITEM "De&lete\tDel",                IDM_EDIT_CLEAR
            MENUITEM SEPARATOR
            MENUITEM "&Reset",                      IDM_EDIT_RESET
        END
    END
    
    
    /
    //
    // Accelerator
    //
    
    CLIPTEXT ACCELERATORS
    BEGIN
        "C",            IDM_EDIT_COPY,          VIRTKEY, CONTROL, NOINVERT
        "V",            IDM_EDIT_PASTE,         VIRTKEY, CONTROL, NOINVERT
        VK_DELETE,      IDM_EDIT_CLEAR,         VIRTKEY, NOINVERT
        "X",            IDM_EDIT_CUT,           VIRTKEY, CONTROL, NOINVERT
    END
    
    RESOURCE.H (excerpts)
    
    // Microsoft Visual C++ 生成的包含文件。
    // 供 ClipText.rc 使用
    //
    #define IDM_EDIT_CUT                    40001
    #define IDM_EDIT_COPY                   40002
    #define IDM_EDIT_PASTE                  40003
    #define IDM_EDIT_CLEAR                  40004
    #define IDM_EDIT_RESET                  40005
    

            这个程序的目的是让你在 Windows NT 下既可以运行 Unicode 版本的程序,也可以运行 ANSI 版本的程序,并且了解剪贴板是怎样在这两个字符集之间进行转换的。请注意 CLIPTEXT.C 开头的#ifdef 语句。如果定义了 UNICODE 标识符,CF_TCHAR(我指定的通用剪贴板文本格式名)等于 CF_UNICODE;如果没有定义 UNICODE,CF_TCHAR 就等于 CF_TEXT,在程序后面,IsClipboardFormatAvailable、GetClipboardData 和 SetClipboardData 函数都用 CF_TCHAR 来指定数据类型。

            在程序开始处(或无论何时选择 Edit 菜单上的 Reset 选项时),静态变量 pText 包含一个指向字符串的指针,这个字符串在 Unicode 程序版本中是 Unicode 字符串“Default Text - Unicode version”;在非 Unicode 程序版本中是字符串“Default Text - ANSI version”的字符串。可以用 Cut 或 Copy 命令把这个文本字符串传输到剪贴板里,也可以用 Cut 或 Delete 命令从程序中删除这个字符串。Paste 命令把剪贴板里的文本内容复制到 pText。在处理 WM_PAINT 消息时,pText 字符串被显示在程序的客户区。

            如果首先从 Unicode 程序上选择 Copy 命令,然后又从非 Unicode 程序上选择 Paste 命令,你就能看到文本从 Unicode 转换成了 ANSI。同样,如果操作反过来,文本也会从 ANSI 转换成 Unicode。

    展开全文
  • OLE剪贴板学习心得

    2014-02-10 15:47:13
    OLE剪贴板学习心得 MFC对OLE剪贴板的支持主要集中在两个类上,分别是 COleDataSource,COleDataObject。 其中,COleDataSource作为操作者,而COleDataObject作为消费者,换句话说,用户需要使用COleDataSource把...
    OLE剪贴板学习心得
    MFC对OLE剪贴板的支持主要集中在两个类上,分别是
    COleDataSource,COleDataObject。
    其中,COleDataSource作为操作者,而COleDataObject作为消费者,换句话说,用户需要使用COleDataSource把数据放到OLE剪贴板上,而使用COleDataObject把它取回。


    将保存在全局内存中的项目放置在OLE剪贴板上,需要进行如下步骤:
    1.在堆上(而不是在堆栈上)创建COleDataSource对象。
    2.调用COleDataSource::CacheGlobalData将HGLOBAL递交给COleDataSource对象。
    3.调用COleDataSource::SetClipboard将对象放置在
       OLE剪贴板上。
    下列使用COleDataSource在OLE剪贴板上提供了ANSI文本字符串.
    char szText[]="Hello,world";
    HANDLE hData=::GlobalAlloc(CMEM_MOVEABLB,::lstrlen(szText)+1);
    ::lstrcpy(pData,szText);
    ::GlobalUnlock(hData);
    COleDataSource * pods=new COleDataSource;
    pods->CacheGlobalData(CF_TEXT,hData);
    pods->SetClipboard();


    MFC的COleDataObject提供了从OLE剪贴板获取项目的机制。
    1.创建COleDataObject对象。
    2.调用COleDataObject::AttachClipboard将   COleDataObject连接到OLE剪贴板。
    3.使用COleDataObject::GetGlobalData获取项目。
    4.释放由GetGlobalData返回的全局内存块。
    以下为例:
       char szTect[];
       COleDataObject pdo;
       pdo.AttachClipboard();
       HANDLE hData=pdo.GetGlobalData(CF_TEXT);
      
       if(hData!=NULL)
          LPCSTR pData=(LPCSTR)::Globallock(hData);
          if(::lstrlen(pData<BUFLEN))
             ::lstrcpy(szText,pData);
          ::GlobalUnlock(hData);
          ::GlobalFree(hData);
    注意,我们以上讨论的OLE剪贴板储存媒介都是内存,下面我们将要讨论储存媒介为非内存的情况。


    COleDataSource::CacheGlobalData和COleDataObject::GetGlobalData与全局内存密不可分。但是您可以使用更一般的COleDataSource::CacheData和COleDataObject::GetData函数在其他数据类型的媒介中传送数据。
    下面例子说明如何使用文件作为传送媒体,通过前贴板来传送文本字符串。字符串首先复制到临时文件中。然后用描述文件的信息和文件包含的数据初始化FORMATETC和STGMEDIUM结构。最后信息被传送给COleDataSource::CacheData,并用COleDataSource::SetClipboar将数据对象放置在剪贴板上。
    char szText[]="Hello,world"
    TCHAR szPath[Max_path],szFileName[Max_path];
    ::GetTempPath(sizeof(szPath)/sizeof(TCHAR),szPath);
    ::GetTempFileName(szPath,_T("tmp"),0,szFileName);//分配临时文件名称。
    CFile file;
    if(file.Open(szFileName,CFile::modeCreate|CFile::modeWrite))
    {
         file.Write(szText,::lstrlen(szText)+1);
         file.Close();
         LPWSTR pwszFileName=(LPWSTR)::CoTaskMemAlloc(MAX_PATH * sizeof(WCHAR));
         #ifdef UNICODE
               ::lstrcpy(pwszFileName,szFileName);
         #else
               ::MultiByteToWideChar(CP_ACP,MB_PRECOMPOSED,szFileName,-1,pwszFileName,MAX_PATE);
         #endif
         FORMATETC fe={CF_TEXT,NULL,DVASPECT_CONTENT,-1,TYMED_FILE};
         STGMEDIUM stgm;
         stgm.tymed=TYMED_FILE;
         stgm.lpszFileName=pwszFileName;
         stgm.pUnkForRelease=NULL;
       
         COleDataSource * pods=new COleDataSource;
         pods->CacheData(CF_TEXT,&stgm,&fe);
         pods->setClipboard();
    }


    在消费者端,您可以使用COleDataObject::GetData从剪贴板获取字符串
    char szText[BUFLEN];
    STGMEDIUM stgm;
    FORMATETC fe={CF_TEXT,NULL,DVASPECT_CONTENT,-1,TYMED_FILE;}
    COleDataObject pdo;
    pdo.AttachClipboard();
    if(pdo.GetData(CF_TEXT,&stgm,&fe)&&stgm.tymed==TYMED_FILE)
    {TCHAR szFileName|Max_Path;}
    #ifdef UNICODE
         ::lstrcpy(szFileName,stgm,lpszFileName);
    #else
         ::WideCharToMultiByte(CP_ACP,0,stgm,lpszf|leName,_T,szFileName,sizeof(szFileName)/sizeof(TCHAR),NULL,NULL);
    #endif
         CFile file;
         if(file.Open(szFileName,CFile::modeRead))|DWORD dwSize=file.GetLength();
         if(dwSize<BUFLEN)
               file.Read(szText,(UNIT)dwSize);
         file.Close();
       }
       ::ReleaseStgMedium(&stgm);
    }
     
    在读取OLE剪贴板内容是,用户可以直接使用COleDataObject::GetFileData取代GetData函数。
    __________________________________________
    一)ChangeClipboardChain
    将剪贴的连接从一个句柄转到下一个句柄。
    BOOL ChangeClipboardChain(
    HWND hWndRemove, // handle to window to remove
    HWND hWndNewNext // handle to next window
    );
    (1)hWndRemove表示第一个窗口的句柄(断开)。
    (2)hWndNewNext表示第二个窗口的句柄(连接)。
    注意,在使用之前应该使用SetClipboardViewer事先进行窗口句柄的连接。
    (二)CloseClipboard
    关闭剪贴板。
    BOOL CloseClipboard(VOID)//VOID意思是空白。
    本函数没有参数,事先应该用OpenClipboard函数打开过剪贴板。
    (三)CountClipboardFormats
    不管剪贴板是什么格式,全部转化为数据格式。
    int CountClipboardFormats(VOID)
    本函数没有参数。
    (四)EmptyClipboard
    清空剪贴板。
    BOOL EmptyClipboard(VOID)
    本函数没有参数。
    (五)EnumClipboardFormats
    使剪贴板内的格式转变成指定格式。
    UINT EnumClipboardFormats(
    UINT format // specifies a known available clipboard format
    );
    其中format表示的是将要转化成的格式。该参数的意义可参照后面。
    (六)GetClipboardData
    获取剪贴板内的数据。
    HANDLE GetClipboardData(
    UINT uFormat // clipboard format
    );
    其中format表示的是剪贴板内数据的格式。该参数的意义可参照后面。
    (七)GetClipboardFormatName
    获取剪贴板内数据格式的名称。
    int GetClipboardFormatName(
    UINT format, // clipboard format to retrieve
    LPTSTR lpszFormatName, // address of buffer for name
    int cchMaxCount // length of name string in characters
    );
    (1)format表示的意义同前,应该是不事先规定格式;
    (2)lpszFormatName表示的是格式名称地址;
    (3)cchMaxCount剪贴板内数据的长度。
    (八)GetClipboardOwner
    获取当前剪贴板是属于哪一个窗口的句柄。
    HWND GetClipboardOwner(VOID)
    返回那个窗口的句柄。
    (九)GetClipboardSequenceNumber
    返回剪贴板序号。
    DWORD GetClipboardSequenceNumber(VOID)
    (十)GetClipboardViewer
    返回剪贴板属于窗口的句柄。
    HWND GetClipboardViewer(VOID)
    (十一)GetOpenClipboardWindow
    返回打开剪贴板的那个窗口句柄。
    HWND GetOpenClipboardWindow(VOID)
    (十二)GetPriorityClipboardFormat
    int GetPriorityClipboardFormat(
    UINT *paFormatPriorityList, // address of priority list
    int cFormats // number of entries in list
    );
    (十三)IsClipboardFormatAvailable
    判断剪贴板的格式。
    BOOL IsClipboardFormatAvailable(
    UINT format // clipboard format
    );
    其中format表示的是剪贴板内数据的格式。该参数的意义可参照后面。
    (十四)OpenClipboard
    打开剪贴板。
    BOOL OpenClipboard(
    HWND hWndNewOwner // handle to window opening clipboard
    );
    返回剪贴板的句柄。
    (十五)RegisterClipboardFormat
    注册新的剪贴板格式。
    UINT RegisterClipboardFormat(
    LPCTSTR lpszFormat // address of name string
    );
    lpszFormat新的剪贴板格式名称。
    (十六)SetClipboardData
    设置剪贴板内的数据。
    HANDLE SetClipboardData(
    UINT uFormat, // clipboard format
    HANDLE hMem // data handle
    );
    uFormat表示的是要放进剪贴板数据的格式;
    hMem表示数据的地址指针。
    (十七)SetClipboardViewer
    将剪贴板内容连接到窗口。
    HWND SetClipboardViewer(
    HWND hWndNewViewer // handle to clipboard viewer window
    );
    hWndNewViewer表示要连接到的那个窗口句柄。
    上文中剪贴板格式Format的可选参数如下:
    CF_BITMAP位图格式;
    CF_DIB
    CF_DIBV5
    CF_DIF
    CF_DSPBITMAP
    CF_DSPENHMETAFILE
    CF_DSPMETAFILEPICT
    CF_DSPTEXT
    CF_ENHMETAFILE
    CF_GDIOBJFIRST
    CF_GDIOBJLAST
    CF_HDROP
    CF_LOCALE
    CF_METAFILEPICT
    CF_OEMTEXT
    CF_OWNERDISPLAY
    CF_PALETTE
    CF_PENDATA
    CF_PRIVATEFIRST
    CF_PRIVATELAST
    CF_RIFF
    CF_SYLK
    CF_TEXT文本格式;
    CF_WAVE音乐格式;
    CF_TIFF
    CF_UNICODETEXT




    Windows剪贴板


       Windows剪贴板是一种比较简单同时也是开销比较小的IPC(InterProcess Communication,进程间通讯)机制。Windows系统支持剪贴板IPC的基本机制是由系统预留的一块全局共享内存,用来暂存在各进程间进行交换的数据:提供数据的进程创建一个全局内存块,并将要传送的数据移到或复制到该内存块;接受数据的进程(也可以是提供数据的进程本身)获取此内存块的句柄,并完成对该内存块数据的读取。


      为使剪贴板的这种IPC机制更加完善和便于使用,需要解决好如下三个问题:提供数据的进程在结束时 Windows系统将删除其创建的全局内存块,而接受数据的进程则希望在其退出后剪贴板中的数据仍然存在,可以继续为其他进程所获取;能方便地管理和传送剪贴板数据句柄;能方便设置和确定剪贴板数据格式。为完善上述功能,Windows提供了存在于USER32.dll中的一组API函数、消息和预定义数据格式等,并通过对这些函数、消息的使用来管理在进程间进行的剪贴板数据交换。


      Windows系统为剪贴板提供了一组API函数和多种消息,基本可以满足编程的需要。而且Windows还为剪贴板预定义了多种数据格式。通过这些预定义的格式,可以使接收方正确再现数据提供方放置于剪贴板中的数据内容。


      文本剪贴板和位图剪贴板的使用


      这两种剪贴板是比较常用的。其中,文本剪贴板是包含具有格式CF_TEXT的字符串的剪贴板,是最经常使用的剪贴板之一。在文本剪贴板中传递的数据是不带任何格式信息的ASCII字符。若要将文本传送到剪贴板,可以先分配一个可移动全局内存块,然后将要复制的文本内容写入到此内存区域。最后调用剪贴板函数将数据放置到剪贴板:


    注意:
        下面代码中:
        1、HANDLE hGlobalMemory = GlobalAlloc(GHND, dwLength + 1);
    分配内存长度必须是dwLength + 1,否则在调用SetClipboardData时会出错;可以通过调用 int i2 = ::GlobalSize(hGlobalMemory );来查看分配的长度。


        2、hGlobalMemory不能释放,即不能调用::GlobalFree(hGlobalMemory),因为如果在一个程序中还要粘贴的话就必须不能释放,否则在同一程序中粘贴时获得的指针为NULL,但如果只在别的程序中粘贴则可以释放。


        以上两条是在编制HsfBrowserCtl(HOOPS三维浏览控件)时总结出来的。可参看原码中的复制、粘贴部分。
      DWORD dwLength = 100; // 要复制的字串长度
    HANDLE hGlobalMemory = GlobalAlloc(GHND, dwLength + 1); // 分配内存
    LPBYTE lpGlobalMemory = (LPBYTE)GlobalLock(hGlobalMemory); // 锁定内存
    for (int i = 0; i 〈 dwLength; i++) // 将"*"复制到全局内存块
     *lpGlobalMemory++ = '*';
     GlobalUnlock(hGlobalMemory); // 锁定内存块解锁
     HWND hWnd = GetSafeHwnd(); // 获取安全窗口句柄
     ::OpenClipboard(hWnd); // 打开剪贴板
     ::EmptyClipboard(); // 清空剪贴板
     ::SetClipboardData(CF_TEXT, hGlobalMemory); // 将内存中的数据放置到剪贴板
     ::CloseClipboard(); // 关闭剪贴板






      这里以OpenClipboard()打开剪贴板,并在调用了EmptyClipboard()后使hWnd指向的窗口成为剪贴板的拥有者,一直持续到 CloseClipboard()函数的调用。在此期间,剪贴板为拥有者所独占,其他进程将无法对剪贴板内容进行修改。


      从剪贴板获取文本的过程与之类似,首先打开剪贴板并获取剪贴板的数据句柄,如果数据存在就拷贝其数据到程序变量。由于GetClipboardData()获取的数据句柄是属于剪贴板的,因此用户程序必须在调用CloseClipboard()函数之前使用它:






      HWND hWnd = GetSafeHwnd(); // 获取安全窗口句柄
    ::OpenClipboard(hWnd); // 打开剪贴板
    HANDLE hClipMemory = ::GetClipboardData(CF_TEXT);// 获取剪贴板数据句柄
    DWORD dwLength = GlobalSize(hClipMemory); // 返回指定内存区域的当前大小
    LPBYTE lpClipMemory = (LPBYTE)GlobalLock(hClipMemory); // 锁定内存
    m_sMessage = CString(lpClipMemory); // 保存得到的文本数据
    GlobalUnlock(hClipMemory); // 内存解锁
    ::CloseClipboard(); // 关闭剪贴板






      大多数应用程序对图形数据采取的是位图的剪贴板数据格式。位图剪贴板的使用与文本剪贴板的使用是类似的,只是数据格式要指明为CF_BITMAP,而且在使用SetClipboardData()或GetClipboardData()函数时交给剪贴板或从剪贴板返回的是设备相关位图句柄。下面这段示例代码将把存在于剪贴板中的位图数据显示到程序的客户区:






      HWND hWnd = GetSafeHwnd(); // 获取安全窗口句柄
    ::OpenClipboard(hWnd); // 打开剪贴板
    HANDLE hBitmap = ::GetClipboardData(CF_BITMAP); // 获取剪贴板数据句柄
    HDC hDC = ::GetDC(hWnd); // 获取设备环境句柄
    HDC hdcMem = CreateCompatibleDC(hDC); // 创建与设备相关的内存环境
    SelectObject(hdcMem, hBitmap); // 选择对象
    SetMapMode(hdcMem, GetMapMode(hDC)); // 设置映射模式
    BITMAP bm; // 得到位图对象
    GetObject(hBitmap, sizeof(BITMAP), &bm);
    BitBlt(hDC, 0, 0, bm.bmWidth, bm.bmHeight, hdcMem, 0, 0, SRCCOPY); //位图复制
    ::ReleaseDC(hWnd, hDC); // 释放设备环境句柄
    DeleteDC(hdcMem); // 删除内存环境
    ::CloseClipboard(); // 关闭剪贴板










    多数据项和延迟提交技术


      要把数据放入剪贴板,在打开剪贴板后一定要调用EmptyClipboard()函数清除当前剪贴板中的内容,而不可以在原有数据项基础上追加新的数据项。但是,可以在EmptyClipboard()和CloseClipboard()调用之间多次调用SetClipboardData()函数来放置多个不同格式的数据项。例如:






      OpenClipboard(hWnd);
    EmptyClipboardData();
    SetClipboardData(CF_TEXT, hGMemText);
    SetClipboardData(CF_BITMAP, hBitmap);
    CloseClipboard();






      这时如果用CF_TEXT或CF_BITMAP等格式标记去调用IsClipboardFormatAvailable()都将返回TRUE,表明这几种格式的数据同时存在于剪贴板中。以不同的格式标记去调用GetClipboardData()函数可以得到相应的数据句柄。


      对于多数据项的剪贴板数据,还可以用CountClipboardFormats()和EnumClipboardFormats()函数得到当前剪贴板中存在的数据格式数目和具体的数据格式。EnumClipboardFormats()的函数原型为:






      UINT EnumClipboardFormats(UINT format);






      参数format指定了剪贴板的数据格式。如果成功执行将返回format指定的格式的下一个数据格式值,如果format为最后的数据格式值,那么将返回0。由此不难写出处理剪贴板中所有格式数据项的程序段代码:






      UINT format = 0; // 从第一种格式值开始枚举
    OpenClipboard(hWnd);
    while(format = EnumClipboardFormats(format))
    {
    …… // 对相关格式数据的处理
    }
    CloseClipboard();






      在数据提供进程创建了剪贴板数据后,一直到有其他进程获取剪贴板数据前,这些数据都要占据内存空间。如在剪贴板放置的数据量过大,就会浪费内存空间,降低对资源的利用率。为避免这种浪费,可以采取延迟提交(Delayed rendering)技术,即由数据提供进程先创建一个指定数据格式的空(NULL)剪贴板数据块,直到有其他进程需要数据或自身进程要终止运行时才真正提交数据。


      延迟提交的实现并不复杂,只需剪贴板拥有者进程在调用SetClipboardData()将数据句柄参数设置为NULL 即可。延迟提交的拥有者进程需要做的主要工作是对WM_RENDERFORMAT、WM_DESTORYCLIPBOARD和 WM_RENDERALLFORMATS等剪贴板延迟提交消息的处理。


      当另一个进程调用GetClipboardData()函数时,系统将会向延迟提交数据的剪贴板拥有者进程发送WM_RENDERFORMAT消息。剪贴板拥有者进程在此消息的响应函数中应使用相应的格式和实际的数据句柄来调用SetClipboardData()函数,但不必再调用OpenClipboard()和EmptyClipboard()去打开和清空剪贴板了。在设置完数据有也无须调用CloseClipboard()关闭剪贴板。如果其他进程打开了剪贴板并且调用EmptyClipboard()函数去清空剪贴板的内容,接管剪贴板的拥有权时,系统将向延迟提交的剪贴板拥有者进程发送WM_DESTROYCLIPBOARD消息,以通知该进程对剪贴板拥有权的丧失。而失去剪贴板拥有权的进程在收到该消息后则不会再向剪贴板提交数据。另外,在延迟提交进程在提交完所有要提交的数据后也会收到此消息。如果延迟提交剪贴板拥有者进程将要终止,系统将会为其发送一条WM_RENDERALLFORMATS消息,通知其打开并清除剪贴板内容。在调用 SetClipboardData()设置各数据句柄后关闭剪贴板。


      下面这段代码将完成对数据的延迟提交,WM_RENDERFORMAT消息响应函数OnRenderFormat()并不会立即执行,当有进程调用GetClipboardData()函数从剪贴板读取数据时才会发出该消息。在消息处理函数中完成对数据的提交:


      进行延迟提交:






      HWND hWnd = GetSafeHwnd(); // 获取安全窗口句柄
    ::OpenClipboard(hWnd); // 打开剪贴板
    ::EmptyClipboard(); // 清空剪贴板
    ::SetClipboardData(CF_TEXT, NULL); // 进行剪贴板数据的延迟提交
    ::CloseClipboard(); // 关闭剪贴板






      在WM_RENDERFORMAT消息的响应函数中:






      DWORD dwLength = 100; // 要复制的字串长度
    HANDLE hGlobalMemory = GlobalAlloc(GHND, dwLength + 1); // 分配内存块
    LPBYTE lpGlobalMemory = (LPBYTE)GlobalLock(hGlobalMemory); // 锁定内存块
    for (int i = 0; i 〈 dwLength; i++) // 将"*"复制到全局内存块
    *lpGlobalMemory++ = '*';
    GlobalUnlock(hGlobalMemory); // 锁定内存块解锁
    ::SetClipboardData(CF_TEXT, hGlobalMemory); // 将内存中的数据放置到剪贴板
        DSP和自定义数据格式的使用


       Windows系统预定义了三个带“DSP”前缀的数据格式:CF_DSPTEXT、CF_DSPBITMAP和 CF_DSPMETAFILEPICT。这是一些伪标准格式,用于表示在程序中定义的私有剪贴板数据格式。对于不同的程序,这些格式的规定是不同的,因此这些格式只针对某一具体程序的不同实例才有意义。


      为使用DSP数据格式,必须确保进程本身与剪贴板拥有者进程同属一个程序。可以调用GetClipboardOwner()函数来获取剪贴板拥有者窗口句柄,并调用GetClassName()来获取窗口类名:


    HWND hClipOwner = GetClipboardOwner();
    GetClassName(hClipOwner, &ClassName, 255);


      如果剪贴板拥有者窗口类名同本进程的窗口类名一致,就可以使用带有DSP前缀的剪贴板数据格式了。
    除了使用Windows预定义的剪贴板数据格式外,也可以在程序中使用自定义的数据格式。对于自定义的数据格式lpszFormat,可以调用RegisterClipboardFormat()函数来登记,并获取其返回的格式标识值:


    UINT format = RegisterClipboardFormat(lpszFormat);


      对此返回的格式标识值的使用与系统预定义的格式标识是一样的。可以通过GetClipboardFormatName()函数来获取自定义格式的ASCII名。


      小结


      本文主要对Windows编程中的剪贴板机制作了较为深入的讨论,对其中常用的文本、位图、DSP和自定义数据格式的使用方法以及多数据项和延迟提交等重要技术一并做了阐述。并给出了具体的程序示例代码,使读者能够更好的掌握剪贴板机制的使用。




    功能实现:复制位图到剪切阪。


    今日看了下COleDataSource的原码和网上的文章。


    COleDataSource用内嵌类实现了IDataObject接口
    //COPY HBITMAP To CilpBoard
    void CMyView::OnEditCopy()
    {


        COLORREF BACKGROUND_COLOR = #ffffff;
        tagSTGMEDIUM * data;
        CBitmap * junk;
        COleDataSource* pData = new COleDataSource;
        data = new tagSTGMEDIUM;
        junk = new CBitmap();
        CClientDC cdc(this);
        CDC dc;
        dc.CreateCompatibleDC(&cdc);
        CRect client;
        //replace this with something that calculates
        //the proper rectangle size
        GetClientRect(client);
        junk->CreateCompatibleBitmap(&cdc,client.Width(),client.Height());
        dc.SelectObject(junk);
        CBrush fill(BACKGROUND_COLOR);
        dc.FillRect(client,&fill);
        //replace this with something that draws to a CDC
        OnDraw(&dc);
        data->tymed = TYMED_GDI;
        data->hBitmap = HBITMAP(*junk);
        pData->CacheData( CF_BITMAP, data );
        pData->SetClipboard();
        delete data;
        delete junk;
        //delete pData;
    }


    另转载一篇文章,讲的不怎样,但可参看一下


    利用MFC实现对象拖放


      对象拖放是指对某一指定的对象,利用鼠标拖动的方法,在不同应用的窗口
    之间、同一应用的不同窗口之间或同一应用的同一窗口内进行移动、复制(粘贴)
    等操作的技术。
      利用对象拖放,可以为用户提供方便、直观的操作界面。
    实现对象拖放技术,需要了解、使用MFC的CView、COleDataSource和COleDropTarget
    等类,并利用这些类协同工作。
    本文讨论了对象拖放技术,并研究了如何利用MFC实现该技术。
    利用MFC实现对象拖放,编程比较容易,代码可读性好。
    修改稿
    利用MFC实现对象拖放
    1.对象拖放概念
      对象拖放是指对某一指定的对象,利用鼠标拖动的方法,在不同应用的窗口
    之间、同一应用的不同窗口之间或同一应用的同一窗口内进行移动、复制(粘贴)
    等操作的技术。
      对象拖放是在操作系统的帮助下完成的。 要开始一次拖动, 首先需要指定
    或生成被拖动的对象,然后指定整个拖放操作过程所使用的数据格式,并按指定
    的数据格式提供数据,最后启动对象拖放操作;当对象在某一窗口内落下时,拖
    放过程结束,接收拖放对象的窗口按指定的数据格式提取有关数据,并根据提取
    的数据生成对象。
    2.MFC中用于对象拖放的类
    MFC(Microsoft Foundation ClassLibrary)为实现对象拖放提供了如下三个类。
    为便于后边的
    讨论我们先来熟悉一下这些类。
    2.1.COleDataSource。用于启动一次拖放操作,并向系统提供拖放对象的数据。
    类中的成员
    函数有如下三种:
    a.设定提供数据的方式和使用的数据格式。提供数据的方式有两种,一种是即时
    方式,另一种是延迟方式;即时方式需要在拖动开始之前提供数据;延迟方式不
    需要立即提供数据,当系统请求有关数据时,由OnRenderData()等虚函数提供所
    需的数据。
    可以用CacheGlobalData()等函数指定使用即时方式提供数据,也可以用
    DelayRenderData()等函数指定使用延时方式提供数据。
    b.响应请求,提供数据。应当重载OnRenderFileData()或其他相应的虚函数,以
    提供有关数据(后边将详细讨论)。
    c.实施拖放操作。调用函数DoDragDrop(),开始实施拖放操作。
    2.2.OleDataTarget。用于准备接收拖放对象的目标窗口;一个窗口要想能够接收
    拖放对象,必须包含一个COleDataTarget对象,并注册该对象。类中主要成员函数:
    a.注册。函数Register()注册该对象,以便使窗口能够接收拖放对象。
    b.响应拖放过程中的动作(虚成员函数) 当鼠标首次进入窗口时系统将调用
    OnDragEnter(),当鼠标移出窗口时系统将调用OnDragLeave(), 当鼠标在窗口内移动,
    系统将重复调用调用OnDragOver(),当对象在窗口内落下调用OnDrop()。
    2.3.OleDataObject.用于接收拖放对象,类中主要成员函数有两种:
    a.确定可以使用的数据格式。IsDataAvailable()等函数确定指定数据格式是否可用;
    b.获取数据。GetData()、GetFileData()等函数用于按指定数据格式获得数据。
    3.利用MFC实现对象拖放
       要实现一次对象拖放,需要做三方面的工作:对象所在的窗口准备拖放对象并启拖动操作,
    接受对象的窗口响应有关拖放消息并接受落下的对象,以及拖放完成时的后期处理。
    以下分别予以介绍。
    3.1. 拖动操作的启动。拖放操作一般是从单击鼠标左键开始。在消息WM_LBUTTONDOWN的响应
    函数OnLButtonDown(...)中,首先要判定是否选定了某一对象,如果未选定或选定多个,则不能进
    行拖放操作;如果选定了一个对象,则可以进行拖放操作。
    要启动一次拖放操作,需要先准备一个COleDataSource对象。注意到类COleClientIten和类
    COleServerItem都是从类COleDataSource上派生的,如果选定的是COleClientItem对象或者是
    COleServerItem对象,则可以直接使用;否则,需要生成一个COleDataSource对象,值得注意的
    是:需要象上文中所说的,应该指定使用的数据格式,并按指定格式提供对象的有关数据。
    下面给出准备数据源的例子:
    class myDataSource: public COleDataSource
    public:
    COLORREF color;
    CString str;
    protected:
    virtual BOOL OnRenderFileData(LPFORMATETC,CFile*);
    //......
    };
    BOOL myDataSource::OnRenderFileData(LPFORMATETC lpFormatEtc,CFile* pFile)
    if(lpFormatEtc->cfFormat==CF_TEXT)
    pFile.Write("Test DragDrop",13); //Magic String
    pFile.Write(&color,sizeof(COLORREF));
    int len= str.GetLength();
    pFile.Write(&len,sizeof(int));
    pFile.Write(str,len);
    return TRUE;
    COleDataSource::OnRenderFileData(lpFormatEtc,pFile);
    return FALSE;
      有了以上数据源之后,就可以在消息WM_LBUTTON的响应函数OnLButtonDown()中,按如下方式,指定使用的数据格式:
    myDataSource* pItemDragDrop=new myDataSource;
    pItemDragDrop->str="This string will dragdrop to another place";
    pItemDragDrop->DelayRenderFileData(CF_TEXT,NULL);
      指定好使用的数据格式之后,调用此对象的成员函数DoDragDrop(...),启动对象拖放操作。
    需要注意的是,函数DoDragDrop(...)并不立即返回,而是要等到鼠标按钮弹起之后。
    3.2. 拖放对象的接收。缺省情况下,一般的窗口是不能接收拖放对象的;要使窗口可以接收拖
    放对象,需要在窗口类定义中加入成员对象COleDropTarget,并在生成窗口时调用函数
    COleDataTarget::Register()。例如:
    Class myView : public CScrollView
    private:
    COleDropTarget oleTarget;
    protected:
    virtual int OnCreate(LPCREATESTRUCT);
    //......
    int myView::OnCreate(LPCREATESTRUCT lpCreateStruct)
      //......
      dropTarget.Register(this);
      return 0;
    为实现拖放对象的接收,还应重载CView或COleDropTarget的虚函数:COnDragMove()、
    OnDragEnter()和OnDrop()等。函数OnDragEnter()、OnDragMove()应根据鼠标在窗口中的位置,
    返回以下数值:
    DROPEFFECT_MOVE---表明可以把对象复制到现在的窗口、现在的位置;
    DROPEFFECT_COPY---表明可以把对象从原来的窗口、原来的位置移到现在的窗口、现在的位置;
    DROPEFFECT_NONE---表明不能在该窗口的该位置放下。
    下例只允许移动对象,而不允许复制对象:
    DROPEFFECT myView::OnDragEnter(......)
      return DROPEFFECT_MOVE;
    DROPEFFECT myView::OnDragOver(......)
    return DROPEFFECT_MOVE;
    函数OnDrop()应处理拖动对象放下后的工作。该函数的参数pDataObjec指向一个
    COleDataObject对象,利用指针,可以获取有关数据。该函数的一般实现是:
    a.检查对象的数据格式: 利用函数COleDataObject::IsDataAvailable();
    b.按指定的格式获取数据:利用COleDataObject::GetFileData()等函数;
    c.建立对象(可能与原对象相同,也可能不建立对象仅使用对象中的数据):利用以上步骤
    得到的数据建立对象。例如:
    char magic_string[13];
    COLORREF color;
    CString str;
    int len;
    myDataSource* pMyData;
    if(IsDataAvailable(CF_TEXT))
    CFile file=GetFileData(CF_TEXT);
    file.Read(magic_string,13);
    if(strncmp(magic_string,"Test DragDrop",13)==0)
    file.Read(&color,sizeof(COLORREF));
    file.Read(&len,sizeof(int));
    file.Read(str,len);
    CClientDC dc(this);
    dc.SetTextColor(color);
    dc.SetBkMode(TRANSPARENT);
    dc.TextOut(100,50,str,len);
    pMyData=new myDataSource;
    pMyData->color=color;
    pMyData->str=str;
    对于COleClientItem或COleServerItem对象,可以按以下方法很容易地重建对象:
    COleClient* pItem=GetDocument()->CreateNewItem();
    pItem->CreateFrom(pDataObject);
    3.3. 拖放操作的结束函数DoDragDrop()返回时,拖放过程结束。函数DoDragDrop()的返回值,
    表明了对象的拖放结果。
    DROPEFFECT_MOVE:对象被放到他处,需删除原对象
    DROPEFFECT_COPY:对象被复制到他处,不删除原对象
    DROPEFFECT_NONE:未能实现拖放,无需删除原对象
    例如:
    int DragEffect=pItemTracking->DoDragDrop(......);
    switch(DragEffect)
    case DROPEFFECT_MOVE:
    delete pItemTracking;
    GetDocument()->UpdateAllItems(NULL);
    GetDocument()->UpdateAllViews(NULL);
    break;
    case DROPEFFECT_COPY:
    case DROPEFFECT_NONE:
    default:
    break;
    *********************
    想构建一个自己的粘贴版
    需要Hook的函数
    写粘贴板
    EmptyClipboardData();//清空自己的粘贴板
    SetClipboardData(CF_TEXT, hGMemText);
    SetClipboardData(CF_BITMAP, hBitmap);


    读粘贴版
    GetClipboardData(CF_TEXT,);
    GetClipboardData(CF_BITMAP)
    展开全文
  • 12.2 剪贴板的高级用法

    千次阅读 2015-11-19 17:06:50
    摘录于《Windows程序(第5版,... 如前所述,在数据准备好后向剪贴板传输数据需要四个函数调用: OopnClipboard (hwnd); EmptyClipboard(); SetClipboardData(iFormat, hGlobal); CloseClipboard();  而要取得这些

    摘录于《Windows程序(第5版,珍藏版).CHarles.Petzold 著》P457

            如前所述,在数据准备好后向剪贴板传输数据需要四个函数调用:

    OopnClipboard (hwnd);
    EmptyClipboard();
    SetClipboardData(iFormat, hGlobal);
    CloseClipboard();
    

            而要取得这些数据则需要三个函数调用:

    OpenClipboard(hwnd);
    hGlobal = GetClipboardData(iFormat);
    [其他程序行]
    CloseClipboard();
    

            你可以复制一份剪贴板数据,或在 GetClipboardData 和 CloseClipboard 调用之间以其他方式使用数据。这种方法也许在大多数情况下对你就足够了,但你也可以用更复杂的方式使用剪贴板。

    12.2.1  使用多种数据项

            打开剪贴板把数据放入时,必须调用 EmptyClipboard 告诉 Windows 释放或删除剪贴板里的内容。不能在剪贴板现有内容后添加数据。所以在这种意义上,剪贴板每次只保存一个数据项。

            然而,在 EmptyClipboard 和 CloseClipboard 调用之间,你可以多次调用 SetClipboardData,并且每次都使用不同的剪贴板样式。比如,如果想在剪贴板里存储一个短的文本字符串,那么你可以把这段文本写到图元文件或位图里。这样,这个字符串不仅可以被能从剪贴板读文本的程序访问,也能被可以从剪贴板里读位图和图元文件的程序访问。当然,这些程序没法轻易判断出图元文件或位图里其实含有字符串。

            如果想把多个句柄写到剪贴板,则应该对每个句柄都调用 SetClipboardData:

    OopnClipboard (hwnd);
    EmptyClipboard();
    SetClipboardData(CF_TEXT, hGlobalText);
    SetClipboardData(CF_BITMAP, hBitmap);
    SetClipboardData(CF_METAFILEPIC, hGlobalMFP);
    CloseClipboard();
    当这三种数据类型都在剪贴板里时,如果调用 IsClipboardFormatAvailable,并传入 CF_TEXT、CF_BITMAP 或 CF_METAFILEPIC 参数,结果都将返回 TRUE。要取得这些句柄,程序可以调用

    hGlobalText = GetClipboardData(CF_TEXT);
    
    或者
    hBitmap = GetClipboardData(CF_BITMAP);
    或者

    hGlobalMFP = GetClipboardData (CF_METAFILEPICT);
    

            等程序下次调用 EmptyClipboard 时,Windows 会释放或删除剪贴板里的这三个句柄。

            不要用这个技巧向剪贴板里添加不同的文本、位图或图元文件格式。只应使用一种文本、位图和图元文件格式。就像我提到过的,Windows 会在 CF_TEXT、CF_OEMTEXT 和 CF_UNICODETEXT 之间转换。它也会在 CF_BITMAP 和 CF_DIB,以及在 CF_METAFILEPICT 和 CF_ENHMETAFILE 之间转换。

            程序可以打开剪贴板然后调用 EnumClipboardFormats 来判断剪贴板里存储的数据格式。一开始,先把变量 iFormat 设置成 0:

    iFormat = 0;
    OpenClipboard(hwnd);
    
    现在连续调用 EnumClipboardFormats,从 0 作为参数开始。对于剪贴板当前的每个数据格式,此函数都会返回一个正的 iFormat 值。当函数返回值为 0 时,枚举就结束了:

    while (iFormat = EnumClipboardFormats(iFormat))
    {
        [针对每个 iFormat 值的操作]
    }
    CloseClipboard();
    

            要得到当前剪贴板里不同格式的数目,可以调用:

    iCount = CountClipboardFormats();
    

    12.2.2  延迟呈现

            把数据放到剪贴板里时,一般应把数据复制一份并且给剪贴板一个句柄,这个句柄指向含有被复制数据的全局内存块。对于非常大的数据项,这种方法会造成内存浪费。如果用户从来不把数据粘贴到另一个程序,那么这块内存空间就会一直被占用,直到被其他数据代替。

            借助于“延迟呈现”技术,这个问题是可以避免的。这个问题是可以避免的。在这项技术中,程序实际上并不需要提供数据,直到另一个程序需要这些数据。你只需要简单地调用 SetClipboardData 并传入 NULL,而不是给 Windows 传数据句柄:

    OopnClipboard (hwnd);
    EmptyClipboard();
    SetClipboardData(iFormat, NULL);
    CloseClipboard();

            可以用 iFormat 的不同值多次调用 SetClipboardData。对于其中一些调用,可以用 NULL 当参数;而对另外一些,可以用实际的句柄。

            以上操作再简单不过,但是从现在开始,过程变得有些复杂。当另一个程序调用 GetClipboardData 时,Windows 将检查那个格式的句柄是否为 NULL。如果是,Windows 就会给“剪贴板所有者”(即你的程序)发送一条消息,请求一个实际的数据句柄。你的程序这时必须给出句柄。

            更具体来说,“剪贴板所有者”是把数据放到剪贴板里的最后一个窗口。当某个程序调用了 OpenClipboard 函数,Windows 就把此函数需要的窗口句柄存储下来。这个句柄标识了打开剪贴板的窗口。在收到 EmptyClipboard 调用时,Windows 会把这个窗口定为新的剪贴板所有者

            使用延迟呈现技术的程序必须在它的窗口过程中处理三个消息:WM_RENDERFORMAT、WM_RENDERALLFORMATS 和 WM_DESTROYCLIPBOARD。当另一个程序调用 GetClipboardData 时,Windows 给你的窗口过程发送WM_RENDRFORMAT 消息。该消息的 wParam 值是 Windows 需要的格式。你在处理 WM_RENDERFORMAT 消息时,不要打开并清空剪贴板。只需简单地按照 wParam 给出的格式创建一个全局内存块,把数据传给它,并且用正确的格式和全局句柄调用 SetClipboardData 即可。显然,在处理 WM_RENDERFORMAT 消息时,需要在程序里保留信息以正确地建立数据。当另一个程序调用 EmptyClipboard 时,Windows 给你的程序发送一个 WM_DESTROYCLIPBOARD 消息。这个消息指出不再需要用于建立剪贴板数据的信息了。你的程序不再是剪贴板所有者。

            如果你的程序在自己仍然是剪贴板所有者时终止,并且剪贴板仍然拥有程序用 SetClipboardData 设置的 NULL 数据句柄,那么你将收到 WM_RENDERALLFORMATS 消息。此时你应该打开剪贴板,清空它,把数据放入全局内存块,然后对每个格式调用 SetClipboardData。最后关闭剪贴板。WM_RENDERALLFORMATS 消息是你的窗口过程最后接收到的消息之一。它的后面会跟有一个 WM_DESTROYCLIPBOARD 消息——因为你已经呈现了所有数据——再之后是普通的 WM_DESTROY 消息。

            如果你的程序只能把一种数据格式传给剪贴板(比如文本),那么你可以一块处理 WM_RENDERALLFORMATS 和 WM_RENDERFORMAT 消息。代码如下所示:

    case WM_RENDERALLFORMATS:
        OpenClipboard(hwnd);
        EmptyClipboard();
                                        // fall through
    case WM_RENDERFORMAT:
        [把文本放入全局内存块]
        SetClipboardData (CF_TEXT, hGlobal);
    
        if (message == WM_RENDERALLFORMATS)
            CloseClipboard();
        return 0;
    

            如果你的程序使用了若干个剪贴板格式,你也许应该只针对 wParam 需求的格式处理 WM_RENDERFORMAT 消息。你无需处理 WM_DESTROYCLIPBOARD 消息,除非对于你的程序来说保留创建数据的信息时一种负担。

    12.2.3  私有数据类型

            到目前为止,我们只处理了 Windows 定义的标准剪贴板类型。然而,你也许想用剪贴板存储“私有数据类型”。许多字处理程序使用这种技术来存储含有字体和格式信息的文本。

            乍一看,这个概念也许没有意义。如果使用剪贴板的目的是在应用程序之间传输数据,为什么剪贴板应该含有只能被一个应用程序理解的数据呢?答案很简单:剪贴板存在的原因之一也是为了把数据从程序自身传入和传出(或者是在同一个程序的不同实例之间传输),显然,这些实例能理解同样的私有格式。

            使用私有数据格式有几种方法。最简单的方法涉及从表面上看符合标准剪贴板格式的数据(即文本、位图或图元文件),但是该数据包含只有你的程序才能理解的含义。这种情况下,在调用 SetClipboardData 和 GetClipboardData 时,应使用以下 iFormat 值之一:CF_DSPTEXT、CF_DSPBITMAP、CF_DSPMETAFILEPICT 或 CF_DSPENHMETAFILE。(字母 DSP 代表 “display”)。这些格式允许 Windows 剪贴板查看器把数据显示成文本、位图或图元文件。然而,使用普通的 CF_TEXT、CF_BITMAP、CF_DIB、CF_METAFILEPICT 或者 CF_ENHMETAFILE 格式来调用 GetClipboardData 的其他程序将不会得到这些数据。

            如果使用这些格式之一把数据放入剪贴板,则必须用同样的格式把数据取出。但是如何得知数据时来自于你的程序的另一个实例,或是来自于另一个使用这些格式之一的程序呢?以下是一种方法:首先取得剪贴板所有者,你可以调用以下函数:

    hwndClipOwner = GetClipboardOwner();
    
    然后取得这个窗口句柄的窗口类的名称。

    TCHAR szClassName[32];
    [其他程序行]
    GetClassName(hwndClipOwner, szClassName, 32);
    
    如果类名和你的程序名一样,那么数据是被你的程序的另一个实例放入剪贴板的。

            使用私有格式的第二种方法涉及 CF_OWNERDISPLAY 标志。SetClipboardData 的全局内存句柄是 NULL:

    SetClipboardData (CF_OWNERDISPLAY, NULL);
    
    这种方法被一些字处理程序用来在 Windows 自带的剪贴板查看其的客户区内显示格式化文本。很明显剪贴板查看其并不知道怎样显示这种格式化文本当字处理程序指明了 CF_OWNERDISPLAY 格式时,将由它来负责绘制剪贴板查看器的客户区

            由于全局内存句柄是 NULL,所以调用 SetClipboardData 并传入 CF_OWNERDISPLAY 格式的程序(剪贴板所有者)必须处理由 Windows 发送给它的延迟呈现消息,此外还要处理另外 5 个消息。下面的 5 个消息是由剪贴板查看器发送给剪贴板所有者的

    • WM_ASKCBFORMATNAME    剪贴板查看器把这个消息发送给剪贴板所有者以得到数据格式的名称。lParam 参数是一个指向缓冲区的指针,wParam 是这个缓冲区能容纳的字符的最大数据。剪贴板所有者必须把剪贴板格式的名称复制到这个缓冲区。
    • WM_SIZECLIPBOARD    这个消息告诉剪贴板所有者,剪贴板查看器客户区的大小改变了。wParam 参数是一个指向剪贴板查看器的句柄,lParam 是一个指向含有新的大小的 RECT 结构的指针。如果 RECT 结构包含的全是 0,则说明剪贴板查看器正在被销毁或最小化。还有,尽管 Windows 剪贴板查看器只允许它自己的一个实例在运行,但其他剪贴板查看器也可以向剪贴板所有者发送这个消息。对于剪贴板所有者来说,处理多个剪贴板查看器消息虽非不可能(鉴于 wParam 标识了特定的查看器),但是也绝非轻而易举。
    • WM_PAINTCLIPBOARD    这个消息告诉剪贴板所有者要更新剪贴板查看器的客户区。同样,wParam 是指向剪贴板查看器窗口的句柄。lParam 参数是指向 PAINTSTRUCT 结构的全局句柄。剪贴板所有者可以锁定此句柄,并从此结构的 hdc 字段取得一个指向剪贴板查看器设备环境的句柄。
    • WM_HSCROLLCLIPBOARD 和 WM_VSCROLLCLIPBOARD    这两个消息告诉剪贴板所有者用户移动了剪贴板查看器的滚动条。wParam 参数是指向剪贴板查看器窗口的句柄,lParam 的低位字是滚动请求;如果该低位字是 SB_THUMBPOSITION,那么 lParam 的高位字就是滚动条滑块的位置。

            处理这些消息也许看起来得不偿失。然而,这个过程给用户提供了一个好处:当把文本从字处理程序中复制到剪贴板时,用户会欣慰地发现在剪贴板查看器的客户区显示文本仍然保持其原有的格式。

            使用私有剪贴板格式的第三种方法是注册你自己的剪贴板格式名。你向 Windows 提供这个格式的名称,Windows 会给你的程序一个数值,在 SetClipboardData 和 GetClipboardData 里,可以把这个数值用作格式参数。使用这种方法的程序通常也是按照标准格式之一把数据复制到剪贴板。这种方法允许剪贴板查看器在其客户区里显示数据(无需像 CF_OWNERDISPLAY 那么费力)并且允许其他程序从剪贴板里复制数据。

            举个例子,假设我们写了一个绘制矢量的程序,它把数据以位图格式、图元文件格式和它自己注册的剪贴板格式复制到剪贴板里。剪贴板查看器将会显示图元文件或位图。其他可以从剪贴板里读取位图或图元文件的程序会相应获取这些格式。然而,当绘制矢量的程序本身需要从剪贴板中读数据时,它将以它自己注册的格式复制数据,因为该格式可能含有比位图或图元文件更多的信息。

            想要注册新的剪贴板格式,程序可以调用以下函数:

    iFormat = RegisterClipboardFormat (szFormatName);
    iFormat 值介于 0xC000 和 0xFFFF 之间。剪贴板查看器(或者通过调用 EnumClipboardFormats 取得当前剪贴板所有格式的程序)要获得此格式的 ASCII 名称,可以调用以下函数:

    GetClipboardFormatName (iFormat, psBuffer, iMaxCount);
    Windows 最多复制 iMaxCount 个字符到 psBuffer 中。

            用这种方法把数据复制到剪贴板中的程序员也许会公布格式名和数据的实际格式。如果程序变得受欢迎,其他程序就可以从剪贴板中以这种格式复制数据。

    展开全文
  • Windows剪贴板允许把数据从一个程序传送到另一个程序中。它的原理相对而言比较简单,把数据存放到剪贴板上的程序或从剪贴板上取出数据的程序都无须太多的负担。数据格式CF_TEXT以NULL结尾的ANSI字符集字符串。它在每...

    Windows剪贴板允许把数据从一个程序传送到另一个程序中。它的原理相对而言比较简单,把数据存放到剪贴板上的程序或从剪贴板上取出数据的程序都无须太多的负担。

    数据格式

    CF_TEXT以NULL结尾的ANSI字符集字符串。它在每行末尾包含一个carriage return和linefeed字符。
    CF_OEMTEXT含有文字数据(与CF_TEXT类似)的内存块。但是它使用的是OEM字符集。
    CF_LOCALE一个国家地区标识符的句柄。表示剪贴簿文字使用的国别地区设定。
    CF_BITMAP与设备相关的位图格式。
    CF_DIB定义一个设备无关位图的内存块。
    CF_PALETTE调色盘句柄。它通常与CF_DIB配合使用,以定义与设备相关的位图所使用的颜色调色盘。
    CF_TIFF含有标号图像文件格式(TIFF)数据的整体内存块。
    CF_METAFILEPICT以旧的metafile格式存放的图片。
    CF_ENHMETAFILE增强型metafile(32位Windows支持的)句柄
    CF_PENDATA与Windows的笔式输入扩充功能联合使用。
    CF_WAVE声音(波形)文件。
    CF_RIFF使用资源交换文件格式(Resource Interchange File Format)的多媒体数据。
    CF_HDROP与拖放服务相关的文件列表。

    内存配置

    程序向剪贴簿传输一些数据的时候,必须配置一个内存块,并且将这块内存交给剪贴簿处理。早期的程序中需要配置内存时,我们只需使用标准C执行时期链接库所支持的malloc函数。但是,由于在Windows中执行的应用程序之间必须要共享剪贴簿所储存的内存块,这时malloc函数就有些不适任这项任务了。

    要用Windows API来配置一个内存块,可以调用:

    hGlobal = GlobalAlloc (uiFlags, dwSize) ;

    此函数有两个参数:一系列可能的旗标和内存块的字节大小。函数传回一个HGLOBAL型态的句柄,称为整体内存块句柄整体句柄。传回值为NULL表示不能配置足够的内存。

    虽然GlobalAlloc的两个参数略有不同,但它们都是32位的无正负号整数。如果将第一个参数设定为0,那么您就可以更有效地使用旗标GMEM_FIXED。在这种情况下,GlobalAlloc传回的整体句柄实际是指向所配置内存块的指针。如 果 不 喜 欢 将 内 存 块 中 的 每 一 位 都 初 始 化 为 0 , 那 么 您 也 能 够 使 用 旗 标GMEM_ZEROINIT。在Windows表头文件中,简洁的GPTR旗标定义为GMEM_FIXED和GMEM_ZEROINIT旗标的组合:

    #define GPTR (GMEM_FIXED | GMEM_ZEROINIT)

    下面是一个重新配置函数:

    hGlobal = GlobalReAlloc (hGlobal, dwSize, uiFlags) ;

    如果内存块扩大了,您可以用GMEM_ZEROINIT旗标将新的字节设为0。
    下面是获得内存块大小的函数:

    dwSize = GlobalSize (hGlobal) ;

    释放内存块的函数:

    GlobalFree (hGlobal) ;

    简写标识符:

    #define GHND (GMEM_MOVEABLE | GMEM_ZEROINIT)

    GMEM_MOVEABLE旗标允许Windows在虚拟内存中移动一个内存块。这不是说将在物理内存中移动内存块,只是应用程序用于读写这块内存的地址可以被变动。尽管GMEM_MOVEABLE是16位Windows的通则,但是它的作用现在已经少得多了。如果您的应用程序频繁地配置、重新配置以及释放不同大小的内存块,应用程序的虚拟地址空间将会变得支离破碎。可以想象得到,最后虚拟内存地址空间就会被用完。如果这是个可能会发生的问题,那么您将希望内存是可移动的。下面就介绍如何让内存块成为可搬移位置的。
    首先定义一个指标(例如,一个int型态的)和一个GLOBALHANDLE型态的变量:

    int * p ;
    GLOBALHANDLE hGlobal ;

    然后配置内存。例如:

    hGlobal = GlobalAlloc (GHND, 1024) ;

    与处理其它Windows句柄一样,您不必担心数字的实际意义,只要照著作就好了。需要存取内存块时,可以呼叫:

    p = (int *) GlobalLock (hGlobal) ;

    此函数将句柄转换为指标。在内存块被锁定期间,Windows将固定虚拟内存中的地址,不再移动那块内存。存取结束后呼叫:

    GlobalUnlock (hGlobal) ;

    这将使Windows可以在虚拟内存中移动内存块。要真正确保此程序正常运作(体验早期Windows程序写作者的痛苦经历),您应该在单一个消息处理期间锁定和解锁内存块。
    在释放内存时,呼叫GlobalFree应使用句柄而不是指标。如果您现在不能存取句柄,可以使用下面的函数:

    hGlobal = GlobalHandle (p) ;

    在解锁之前,您能够多次锁定一个内存块。Windows保留一个锁定次数,而且在内存块可被自由移动之前,每次锁定都需要相对应的解锁。当Windows在虚拟内存中移动一个内存块时,不需要将字节从一个位置复制到另一个,只需巧妙地处理内存页映像表。通常,让32位Windows为您的程序配置可移动的内存块,其唯一确实的理由只是避免虚拟内存的空间碎裂出现。使用剪贴簿时,也应该使用可移动内存。
    为剪贴簿配置内存时,您应该以GMEM_MOVEABLE和GMEM_SHARE旗标呼叫GlobalAlloc函数。GMEM_SHARE旗标使得其它应用程序也可以使用那块内存。

    将文字传送到剪贴簿

    让我们想象把一个ANSI字符串传送到剪贴簿上,并且我们已经有了指向这个字符串的指针(pString)。现在希望传送这个字符串的iLength字符,这些字符可能以NULL结尾,也可能
    不以NULL结尾。
    首先,通过使用GlobalAlloc来配置一个足以储存字符串的内存块,其中还包括一个终止字符NULL:

    hGlobal = GlobalAlloc (GHND | GMEM_SHARE, iLength + 1) ;

    如果未能配置到内存块,hGlobal的值将为NULL 。如果配置成功,则锁定这块内存,并得到指向它的一个指标:

    pGlobal = GlobalLock (hGlobal) ;

    将字符串复制到内存块中:

    for (i = 0 ; i < wLength ; i++)
    *pGlobal++ = *pString++ ;

    由于GlobalAlloc的GHND旗标已使整个内存块在配置期间被清除为零,所以不需要增加结尾的NULL 。以下叙述为内存块解锁:

    GlobalUnlock (hGlobal) ;

    现在就有了表示以NULL结尾的文字所在内存块的内存句柄。为了把它送到剪贴簿中,打开剪贴簿并把它清空:

    OpenClipboard (hwnd) ;
    EmptyClipboard () ;

    利用CF_TEXT标识符把内存句柄交给剪贴簿,关闭剪贴簿:

    SetClipboardData (CF_TEXT, hGlobal) ;
    CloseClipboard () ;

    工作告一段落。
    下面是关于此过程的一些规则:

    在处理同一个消息的过程中呼叫OpenClipboard和CloseClipboard。不需要时,不要打开剪贴簿。
    不要把锁定的内存句柄交给剪贴簿。
    当呼叫SetClipboardData后,请不要再继续使用该内存块。它不再属于使用者程序,必须把句柄看成是无效的。如果需要继续存取数据,可以制作数据的副本,或从剪贴簿中读取它(如下节所述)。您也可以在SetClipboardData呼叫和CloseClipboard呼叫之间继续使用内存块,但是不要使用传递给SetClipboardData函数的整体句柄。事实上,此函数也传回一个整体句柄,必需锁定这些代码以存取内存。在呼叫CloseClipboard之前,应先为此句柄解锁。

    从剪贴簿上取得文字

    从剪贴簿上取得文字只比把文字传送到剪贴簿上稍微复杂一些。您必须首先确定剪贴簿是否含有CF_TEXT格式的数据,最简单的方法是呼叫

    bAvailable = IsClipboardFormatAvailable (CF_TEXT) ;

    如果剪贴簿上含有CF_TEXT数据,这个函数将传回TRUE(非零)。
    IsClipboardFormatAvailable是少数几个不需先打开剪贴簿就可以使用的剪贴簿函数之一。但是,如果您之后想再打开剪贴簿以取得这个文字,就应该再做一次检查(使用同样的函数或其它方法),以便确定CF_TEXT数据是否仍然留在剪贴簿中。

    为了传送出文字,首先打开剪贴簿:

    OpenClipboard (hwnd) ;

    会得到代表文字的内存块代号:

    hGlobal = GetClipboardData (CF_TEXT) ;

    如果剪贴簿不包含CF_TEXT格式的数据,此句柄就为NULL。这是确定剪贴簿是否含有文字的另一种方法。如果GetClipboardData传回NULL,则关闭剪贴簿,不做其它任何工作。
    从 GetClipboardData 得 到 的 句 柄 并 不 属 于 使 用 者 程 序 - 它 属 于 剪 贴 簿 。 仅 在GetClipboardData和CloseClipboard呼叫之间这个句柄才有效。您不能释放这个句柄或
    更改它所引用的数据。如果需要继续存取这些数据,必须制作这个内存块的副本。
    这里有一种将数据复制到使用者程序中的方法。首先,配置一块与剪贴簿数据块大小相同的内存块,并配置一个指向该块的指标:

    pText = (char *) malloc (GlobalSize (hGlobal)) ;

    再次呼叫hGlobal ,而hGlobal是从GetClipboardData呼叫传回的整体句柄。现在锁定句柄,获得一个指向剪贴簿块的指针:

    pGlobal = GlobalLock (hGlobal) ;

    现在就可以复制数据了:

    strcpy (pText, pGlobal) ;

    或者,您可以使用一些简单的C程序代码:

    while (*pText++ = *pGlobal++) ;

    在关闭剪贴簿之前先解锁内存块:

    GlobalUnlock (hGlobal) ;
    CloseClipboard () ;

    剪贴板的简单使用

    clipText

    ClipText.rc

    /
    //
    // Menu
    //
    
    CLIPTEXT MENU DISCARDABLE 
    BEGIN
        POPUP "&Edit"
        BEGIN
            MENUITEM "Cu&t\tCtrl+X",                IDM_EDIT_CUT
            MENUITEM "&Copy\tCtrl+C",               IDM_EDIT_COPY
            MENUITEM "&Paste\tCtrl+V",              IDM_EDIT_PASTE
            MENUITEM "De&lete\tDel",                IDM_EDIT_CLEAR
            MENUITEM SEPARATOR
            MENUITEM "&Reset",                      IDM_EDIT_RESET
        END
    END
    
    
    /
    //
    // Accelerator
    //
    
    CLIPTEXT ACCELERATORS DISCARDABLE 
    BEGIN
        "C",            IDM_EDIT_COPY,          VIRTKEY, CONTROL, NOINVERT
        "V",            IDM_EDIT_PASTE,         VIRTKEY, CONTROL, NOINVERT
        VK_DELETE,      IDM_EDIT_CLEAR,         VIRTKEY, NOINVERT
        "X",            IDM_EDIT_CUT,           VIRTKEY, CONTROL, NOINVERT
    END

    RESOURCE.H

    #define IDM_EDIT_CUT                    40001
    #define IDM_EDIT_COPY                   40002
    #define IDM_EDIT_PASTE                  40003
    #define IDM_EDIT_CLEAR                  40004
    #define IDM_EDIT_RESET                  40005

    ClipText.c

    /*-----------------------------------------
       CLIPTEXT.C -- The Clipboard and Text
                     (c) Charles Petzold, 1998
      -----------------------------------------*/
    
    #include <windows.h>
    #include "resource.h"
    
    LRESULT CALLBACK WndProc (HWND, UINT, WPARAM, LPARAM) ;
    
    // 根据是否是Unicode来设置默认内容和标题
    #ifdef UNICODE
    
    #define CF_TCHAR CF_UNICODETEXT
    TCHAR szDefaultText[] = TEXT ("Default Text - Unicode Version") ;
    TCHAR szCaption[]     = TEXT ("Clipboard Text Transfers - Unicode Version") ;
    
    #else
    
    #define CF_TCHAR CF_TEXT
    TCHAR szDefaultText[] = TEXT ("Default Text - ANSI Version") ;
    TCHAR szCaption[]     = TEXT ("Clipboard Text Transfers - ANSI Version") ;
    
    #endif
    
    int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance,
                        PSTR szCmdLine, int iCmdShow)
    {
         static TCHAR szAppName[] = TEXT ("ClipText") ;
         HACCEL       hAccel ;
         HWND         hwnd ;
         MSG          msg ;
         WNDCLASS     wndclass ;
    
         wndclass.style         = CS_HREDRAW | CS_VREDRAW ;
         wndclass.lpfnWndProc   = WndProc ;
         wndclass.cbClsExtra    = 0 ;
         wndclass.cbWndExtra    = 0 ;
         wndclass.hInstance     = hInstance ;
         wndclass.hIcon         = LoadIcon (NULL, IDI_APPLICATION) ;
         wndclass.hCursor       = LoadCursor (NULL, IDC_ARROW) ;
         wndclass.hbrBackground = (HBRUSH) GetStockObject (WHITE_BRUSH) ;
         wndclass.lpszMenuName  = szAppName ;
         wndclass.lpszClassName = szAppName ;
    
         if (!RegisterClass (&wndclass))
         {
              MessageBox (NULL, TEXT ("This program requires Windows NT!"),
                          szAppName, MB_ICONERROR) ;
              return 0 ;
         }
    
         hwnd = CreateWindow (szAppName, szCaption,
                              WS_OVERLAPPEDWINDOW,
                              CW_USEDEFAULT, CW_USEDEFAULT,
                              CW_USEDEFAULT, CW_USEDEFAULT,
                              NULL, NULL, hInstance, NULL) ;
    
         ShowWindow (hwnd, iCmdShow) ;
         UpdateWindow (hwnd) ;
    
         hAccel = LoadAccelerators (hInstance, szAppName) ;
    
         while (GetMessage (&msg, NULL, 0, 0))
         {
              if (!TranslateAccelerator (hwnd, hAccel, &msg))
              {
                   TranslateMessage (&msg) ;
                   DispatchMessage (&msg) ;
              }
         }
         return msg.wParam ;
    }
    
    LRESULT CALLBACK WndProc (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
    {
         static PTSTR pText ;
         BOOL         bEnable ;
         HGLOBAL      hGlobal ;
         HDC          hdc ;
         PTSTR        pGlobal ;
         PAINTSTRUCT  ps ;
         RECT         rect ;
    
         switch (message)
         {
         case WM_CREATE:
              SendMessage (hwnd, WM_COMMAND, IDM_EDIT_RESET, 0) ;
              return 0 ;
    
        case WM_INITMENUPOPUP:
            // IsClipboardFormatAvailable判断是否含有CF_TEXT格式的数据
              EnableMenuItem ((HMENU) wParam, IDM_EDIT_PASTE,
                   IsClipboardFormatAvailable (CF_TCHAR) ? MF_ENABLED : MF_GRAYED) ;
    
              bEnable = pText ? MF_ENABLED : MF_GRAYED ;
    
              EnableMenuItem ((HMENU) wParam, IDM_EDIT_CUT,   bEnable) ;
              EnableMenuItem ((HMENU) wParam, IDM_EDIT_COPY,  bEnable) ;
              EnableMenuItem ((HMENU) wParam, IDM_EDIT_CLEAR, bEnable) ;
              break ;
    
         case WM_COMMAND:
              switch (LOWORD (wParam))
              {
              case IDM_EDIT_PASTE:
                  // 打开剪贴板
                   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) ;
                   }
                   CloseClipboard () ;
                   return 0 ;
    
              case IDM_EDIT_CUT:
              case IDM_EDIT_COPY:
                   if (!pText)
                        return 0 ;
                   // 分配整体句柄足够大小的内存
                   hGlobal = GlobalAlloc (GHND | GMEM_SHARE, 
                                          (lstrlen (pText) + 1) * sizeof (TCHAR)) ;
                   // 锁定整体句柄,然后返回剪贴板指针
                   pGlobal = GlobalLock (hGlobal) ;
                   // 拷贝
                   lstrcpy (pGlobal, pText) ;
                   // 再次锁定
                   GlobalUnlock (hGlobal) ;
    
                   // 打开剪贴板
                   OpenClipboard (hwnd) ;
                   // 清空剪贴板
                   EmptyClipboard () ;
                   // 把整体句柄交还给剪贴板
                   SetClipboardData (CF_TCHAR, hGlobal) ;
                   // 关闭剪贴板
                   CloseClipboard () ;
    
                   if (LOWORD (wParam) == IDM_EDIT_COPY)
                        return 0 ;        
                                                 // fall through for IDM_EDIT_CUT
              case IDM_EDIT_CLEAR:
                   if (pText)
                   {
                        free (pText) ;
                        pText = NULL ;
                   }
                   InvalidateRect (hwnd, NULL, TRUE) ;
                   return 0 ;
    
              case IDM_EDIT_RESET:
                   if (pText)
                   {
                        free (pText) ;
                        pText = NULL ;
                   }
    
                   pText = malloc ((lstrlen (szDefaultText) + 1) * sizeof (TCHAR)) ;
                   lstrcpy (pText, szDefaultText) ;
                   InvalidateRect (hwnd, NULL, TRUE) ;
                   return 0 ;
              }
              break ;
    
         case WM_PAINT:
              hdc = BeginPaint (hwnd, &ps) ;
    
              GetClientRect (hwnd, &rect) ;
    
              if (pText != NULL)
                   DrawText (hdc, pText, -1, &rect, DT_EXPANDTABS | DT_WORDBREAK) ;
    
              EndPaint (hwnd, &ps) ;
              return 0 ;
    
         case WM_DESTROY:
              if (pText)
                   free (pText) ;
    
              PostQuitMessage (0) ;
              return 0 ;
         }
         return DefWindowProc (hwnd, message, wParam, lParam) ;
    }
    

    可以看到,在将数据准备好之后,从剪贴板传输数据时需要四个调用:

    复制

    OpenClipboard (hwnd) ;
    EmptyClipboard () ;
    SetClipboardData (iFormat, hGlobal) ;
    CloseClipboard () ;

    存取这些数据需要三个调用:

    粘贴

    OpenClipboard (hwnd) ;
    hGlobal = GetClipboardData (iFormat) ;
    // 其它行程序
    CloseClipboard () ;

    延迟提出

    当把数据放入剪贴簿中时,一般来说要制作一份数据的副本,并将包含这份副本的内存块句柄传给剪贴簿。对非常大的数据项来说,这种方法会浪费内存空间。如果使用者不想把数据粘贴到另一个程序里,那么,在被其它内容取代之前,它将一直占据着内存空间。通过使用一种叫做「延迟提出」的技术可以避免这个问题。实际上,直到另一个程序需要数据,程序才提供这份数据。为此,不将数据句柄传给Windows,而是在SetClipboardData呼叫中使用NULL:

    OpenClipboard (hwnd) ;
    EmptyClipboard () ;
    SetClipboardData (iFormat, NULL) ;
    CloseClipboard () ;

    自定义数据格式

    共三种方法:

    • 在SetClipboardData和GetClipboardData呼叫中可使用下列wFormat值:CF_DSPTEXT、CF_DSPBITMAP、CF_DSPMETAFILEPICT或CF_DSPENHMETAFILE(字母DSP代表「显示器」);
    • CF_OWNERDISPLAY旗标
    • 注册自己的剪贴簿格式名

    剪贴板浏览器

    clipview

    /*-----------------------------------------
       CLIPVIEW.C -- Simple Clipboard Viewer
                     (c) Charles Petzold, 1998
      -----------------------------------------*/
    
    #include <windows.h>
    
    LRESULT CALLBACK WndProc (HWND, UINT, WPARAM, LPARAM) ;
    
    int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance,
                        PSTR szCmdLine, int iCmdShow)
    {
         static TCHAR szAppName[] = TEXT ("ClipView") ;
         HWND         hwnd ;
         MSG          msg ;
         WNDCLASS     wndclass ;
    
         wndclass.style         = CS_HREDRAW | CS_VREDRAW ;
         wndclass.lpfnWndProc   = WndProc ;
         wndclass.cbClsExtra    = 0 ;
         wndclass.cbWndExtra    = 0 ;
         wndclass.hInstance     = hInstance ;
         wndclass.hIcon         = LoadIcon (NULL, IDI_APPLICATION) ;
         wndclass.hCursor       = LoadCursor (NULL, IDC_ARROW) ;
         wndclass.hbrBackground = (HBRUSH) GetStockObject (WHITE_BRUSH) ;
         wndclass.lpszMenuName  = NULL ;
         wndclass.lpszClassName = szAppName ;
    
         if (!RegisterClass (&wndclass))
         {
              MessageBox (NULL, TEXT ("This program requires Windows NT!"),
                          szAppName, MB_ICONERROR) ;
              return 0 ;
         }
    
         hwnd = CreateWindow (szAppName, 
                              TEXT ("Simple Clipboard Viewer (Text Only)"),
                              WS_OVERLAPPEDWINDOW,
                              CW_USEDEFAULT, CW_USEDEFAULT,
                              CW_USEDEFAULT, CW_USEDEFAULT,
                              NULL, NULL, hInstance, NULL) ;
    
         ShowWindow (hwnd, iCmdShow) ;
         UpdateWindow (hwnd) ;
    
         while (GetMessage (&msg, NULL, 0, 0))
         {
              TranslateMessage (&msg) ;
              DispatchMessage (&msg) ;
         }
         return msg.wParam ;
    }
    
    LRESULT CALLBACK WndProc (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
    {
         static HWND hwndNextViewer ;
         HGLOBAL     hGlobal ;
         HDC         hdc ;
         PTSTR       pGlobal ;
         PAINTSTRUCT ps ;
         RECT        rect ;
    
         switch (message)
         {
         case WM_CREATE:
             // 成为剪贴板浏览器链的一部分
             // 最后一个剪贴板浏览器返回hwndNextViewer为Null
              hwndNextViewer = SetClipboardViewer (hwnd) ;
              return 0 ;
    
         case WM_CHANGECBCHAIN:
              if ((HWND) wParam == hwndNextViewer)
                   hwndNextViewer = (HWND) lParam ;
    
              else if (hwndNextViewer)
                   SendMessage (hwndNextViewer, message, wParam, lParam) ;
    
              return 0 ;
    
        // windows发送给目前剪贴板浏览器
        // 区别:WM_PAINTCLIPBOARD是由剪贴簿浏览器发送给使用CF_OWNERDISPLAY
              // 剪贴板数据格式的程序
         case WM_DRAWCLIPBOARD:
              if (hwndNextViewer)
                  // 发送给下一个剪贴板浏览器
                   SendMessage (hwndNextViewer, message, wParam, lParam) ;
    
              InvalidateRect (hwnd, NULL, TRUE) ;
              return 0 ;
    
         case WM_PAINT:
              hdc = BeginPaint (hwnd, &ps) ;
              GetClientRect (hwnd, &rect) ;
              // 打开剪贴板
              OpenClipboard (hwnd) ;
    
    #ifdef UNICODE
              // 获取全局内存句柄
              hGlobal = GetClipboardData (CF_UNICODETEXT) ;
    #else
              hGlobal = GetClipboardData (CF_TEXT) ;
    #endif
    
              if (hGlobal != NULL)
              {
                   pGlobal = (PTSTR) GlobalLock (hGlobal) ;
                   DrawText (hdc, pGlobal, -1, &rect, DT_EXPANDTABS) ;
                   GlobalUnlock (hGlobal) ;
              }
              // 关闭剪贴板
              CloseClipboard () ;
              EndPaint (hwnd, &ps) ;
              return 0 ;
    
         case WM_DESTROY:
             // 从剪贴板链中删除自己,windows会发送WM_CHANGECBCHAIN
              ChangeClipboardChain (hwnd, hwndNextViewer) ;
              PostQuitMessage (0) ;
              return 0 ;
         }
         return DefWindowProc (hwnd, message, wParam, lParam) ;
    }
    

    处理标准格式(如Windows提供的那个剪贴簿一样)以外的数据格式的剪贴簿浏览器还需要完成一些其它工作,比如显示剪贴簿中目前所有数据格式的名称。使用者可以通过呼叫
    EnumClipboardFormats并使用GetClipboardFormatName得到非标准数据格式名称来完成这项工作。使用CF_OWNERDISPLAY数据格式的剪贴簿浏览器必须把下面四个消
    息送往剪贴簿数据的拥有者以显示该资料:

    WM_PAINTCLIPBOARD
    WM_SIZECLIPBOARD
    WM_VSCROLLCLIPBOARD
    WM_HSCROLLCLIPBOARD

    如果您想编写这样的剪贴簿浏览器,那么必须使用GetClipboardOwner获得剪贴簿所有者的窗口句柄,并当您需要修改剪贴簿的显示区域时,将这些消息发送给该窗口。

    展开全文
  • 剪贴板所有api函数

    千次阅读 2009-06-26 16:07:00
    剪贴板所有api函数 [转大富翁笔记]这是我在编写剪贴板相关程序是收集的相关API函数。(一)ChangeClipboardChain 将剪贴的连接从一个句柄转到下一个句柄。 BOOL ChangeClipboardChain( HWND hWndRemove, // handle to ...
  • 剪贴板所有api函数

    2009-11-14 16:26:00
    编写剪贴板相关程序是收集的相关API函数。(一)ChangeClipboardChain 将剪贴的连接从一个句柄转到下一个句柄。 BOOL ChangeClipboardChain( HWND hWndRemove, // handle to window to remove HWND hWndNewNext // ...
  • 剪贴的连接从一个句柄转到下一个句柄。 BOOL ChangeClipboardChain( HWND hWndRemove, // handle to window to remove HWND hWndNewNext // handle to next window ); (1)hWndRemove表示第一个窗口的...
  • 作为一种基于Windows的开发工具,Delphi支持如下四种数据交换方式:剪贴板、动态数据交换 ( DDE)、对象联接与嵌入(OLE)以及动态联接库(DLLs)。这中间前三种方式最为常用,OLE功能最为强大,DDE次之。而剪贴板使用...
  • 第七章 剪贴板和动态数据交换(一) 应用程序间的数据交换是象Windows 这样的多任务环境的重要特性。作为一种基于Windows的开发工具,Delphi支持如下四种数据交换方式:剪贴板、动态数据交换 ( DDE)、对象联接...
  • 判断DIV内容更改 模拟 DIV onchange
  • 激光波前的剪切测试

    2021-02-08 21:28:31
    考虑到染料激光器的出射波前基本质量、功率与光斑大小等特点,系统选择了三平板剪切干涉作为基本干涉光路和一阶微分法被面复原算法;讨论了优化和工程化方法——对单幅干涉图进行自动数字化采集的智能化判断技术,包括...
  • vb简单控制音量大小及静音的方法

    千次阅读 2012-07-26 01:55:37
    复制内容到剪贴板 程序代码'新建EXE工程,添加三个按钮.'按钮一是音量增加,按钮二是音量减少,按钮三是静音切换.Option Explicit Private Declare Function SendMessage Lib "user32.dll" Alias "...
  • 项目当中需要读取采集生成的AVI文件大小,特记录如下: '获得文件大小Public Function GetFileSizeByName(ByVal sFileName As String) As Double  On Error GoTo ErrHandle Dim FileHandle As Long Dim ...
  • 怎样修改PE文件头大小

    千次阅读 2011-05-23 16:49:00
    】,会弹出一个对话框,提示“剪切板数据将写入在偏移量 80 ”,我们点击确定即可,结果如 图 9 所示。  这里一定要注意,在按快捷键之前,一定要确定光标在所选择区域最前方,否则会导致程序不能运行,甚至...
  • 电路维修入门

    千次阅读 2014-10-26 14:40:40
    (一) 电容篇   ...1、电容在电路中一般用“C”加数字...电容容量的大小就是表示能贮存电能的大小,电容对交流信号的阻碍作用称为容抗,它与交流信号的频率和电容量有关。 容抗XC=1/2πf c (f表示交流信号的
  • 电路维修入门教程

    万次阅读 多人点赞 2014-12-29 20:52:03
    (一) 电容篇   ...1、电容在电路中一般用“C”加数字...电容容量的大小就是表示能贮存电能的大小,电容对交流信号的阻碍作用称为容抗,它与交流信号的频率和电容量有关。 容抗XC=1/2πf c (f表示交流信号的
  • 5第二部分常见故障判断... 7第一章加电类故障... 7第二章启动与关闭类故障... 10第三章磁盘类故障... 13第四章显示类故障... 17第五章安装类故障... 20第六章操作与应用类故障... 23第七章局域网类故障... 25第八
  • 5.0版本以上,varchar(20),指的是20字符,无论存放的是数字、字母还是UTF8汉字(每个汉字3字节),都可以存放20个,最大大小是65532字节 回复memo字段, 采用varchar类型. varchar在 mysql 5中, 最大...
  • windows消息大全,常数值查询用

    千次阅读 2019-04-24 08:22:41
    windows消息大全,常数值查询用 消息,就是指Windows发出的一个通知,告诉应用程序某个事情发生了。例如,单击鼠标、改变窗口尺寸、按下键盘上的一个键都会使Windows 发送一个消息给应用程序。消息本身是作为一个...
  • 验证码数字与字母识别

    千次阅读 2018-03-26 11:13:36
    *判断剪切后图片的数字或字母数量,判断方法(图片中所有有为1的列,挨着此列的右边那一列如果一个1都没有表示为一个数字的结束。因为没有连续) */ for(int j=0;j;j++){ if(j){ labe:for(int ...
  • javaScript数字认识

    2009-09-08 12:51:00
    认识Javascript数组作者:SK猫1.认识数组数组就是某类数据的集合,数据类型可以是整型、字符串、甚至是对象Javascript不支持多维数组,...复制内容到剪贴板代码:var a = new Array(10); 此时为a已经开辟了内存空间,包
  • BI Publisher 模板开发语法大全

    千次阅读 2018-08-14 14:58:54
    转自:https://blog.csdn.net/cai_xingyun/article/details/17094863 原文代码用颜色表出,看起来方便一些,建议看原文 一.组 定义一个组的目的是告诉XMLPublisher对重复的数据行进行循环显示,也就是说需要使用...
  • 如何利用MATLAB提供GUI界面实现...通过GUI用户界面实现了数字图像的读取、存储、剪切、反色、加噪、滤波去噪、边沿检测、旋转、撤销和还原等功能。完成了一个功能齐全,运行稳定的数字图像处理系统的设计。(附代码)
  • VC数字图像处理编程讲座之二

    千次阅读 2005-11-24 15:28:00
    VC数字图像处理编程讲座之二前 言 数字图像处理技术与理论是计算机应用的一个重要领域,许多工程应用都涉及到图像处理,一直有一个强烈的愿望,想系统的写一个关于数字图像处理的讲座,由于工作学习很忙,时至今日才...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 8,927
精华内容 3,570
关键字:

判断剪贴板数字大小