《Windwos API每日一练》12.1 剪贴板的简单用法

本节我们讲述剪贴板的简单实现方法。

本节必须掌握的知识点:

        剪贴板数据的标准格式

        内存分配

        把文本传到剪贴板

        从剪贴板中取得文本

        打开和关闭剪贴板

        第76练:剪贴板的简单用法

12.1.1 剪贴板数据的标准格式

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

分类

标准格式

说明

文本格式

CF_TEXT

NULL结尾的ANSI字符集,每行结尾含有回车换行符,最后的NULL表示整个数据的结束。

CF_OEMTEXT

NULL结尾的OEM字符集,供MS-DOS下的剪贴板使用

CF_UNICODETEXT

类似CF_TEXT,每行以回车换行结束,字符两个NULL标志着整个数据的结束。

CF_SYLK

含有微软件符号链接(Symbolic Link)格式数据的内存块。用与Excel程序交换,是一种ASCII格式,每行以回车换行符结束,但不一定以NULL结尾,现在很少用到。

位图相关

CF_BITMAP

设备相关位图

CF_DIB

设备无关位图的内存块(以位图信息结构开头,接着有可能是颜色表和位图的位)

CF_PALETTE

指向调色板的句柄,通常与CF_DIB一起使用

CF_TIFF

标签图像文件格式的内存块

图元

文件

格式

CF_METAFILEPICT

基于Windows过去支持的图元文件

CF_ENHMETAFILE

指向32Windows版本支持的增强型图元文件

其他

格式

CF_PENDATA

Windows画笔扩展一起使用

CF_WAVE

声音波形文件

CF_RIFF

资源交换文件格式的多媒体数据

CF_HDROP

和拖放服务一起使用的文件列表

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

● CF_TEXT 一种以NULL结尾的ANSI字符集字符串,字符串的每行结尾处含有一个回车换行符。这是最简单的剪贴板数据格式。要被传输到剪贴板的数据存储在内存块中,传输时使用指向此内存块的句柄。该内存块会成为剪贴板的属性,因此创建这个内存块的程序不应该继续使用它。

● CF_OEMTEXT包含文本数据(与CF_TEXT类似)但使用OEM字符集的内存块。 Windows程序通常不用担心这点;在窗口中运行MS-DOS程序时如果使用剪贴板则需要用到这种类型。

● CF_UNICODETEXT包含Unicode文本的内存块。类似于CF_TEXT,每行以回车换符结束,字符NULL(两个零字节)标志着整个数据的结束。 CF_UNICODETEXT 只在 WindowsNT 下支持。

● CF_LOCALE指向区域设置标识符的句柄,该标识符标明了与剪贴板相关的区域设置。

剪贴板和 Unicode

只需在调用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标识符, 则用CF_UNICODETEXT;如果没有,就用CF_TEXT。

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

● CF_SYLK含有微软符号链接(Symbolic Link)格式数据的内存块。这种格式被用来在微软的Multiplan、Chart和Excel程序之间交换数据。它是一种ASCII格式,每行以回车换行符结束。

● CF_DIF含有数据交换格式(DIF)数据的内存块。这是一种由Software Arts设计,用来把数据传输到VisiCalc电子表格程序的格式。它也是一种ASCII格式,每行以回车换行符结束。

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

● CF_BITMAP设备相关位图。此位图由位图句柄传输到剪贴板中。再强调一遍,程序把位图传给剪贴板之后不应继续使用此位图。

● CF_DIB定义了设备无关位图的内存块。设备无关位图在第十五章会给出描述。该内存块以位图信息结构开头,接着有可能是颜色表和位图的位。

● CF_PALETTE指向调色板的句柄。它通常和CF_DIB一起使用,用来定义在设备无关位图中使用的调色板。

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

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

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

● CF_METAFILEPICT基于Windows过去支持的图元文件的“图元文件图片”。

● CF_ENHMETAFILE指向32位Windows版本支持的增强型图元文件的句柄。

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

● CF_PENDATA和Windows画笔扩展一起使用。

● CF_WAVE声音(波形)文件。

● CF_RIFF资源交换文件格式(RIFF)的多媒体数据。

● CF_HDROP和拖放服务一起使用的文件列表。

12.1.2 内存分配

全局内存块的创建

当程序把一些数据传到剪贴板时,程序必须分配一个内存块并移交给剪贴板。当我们需要分配内存时,由于剪贴板中存储的内存块必须在Windows下运行的应用程序之间共享,所以malloc函数不足以完成此任务。取而代之的是Windows API函数GlobalAlloc。

●hGlobal = GlobalAlloc (uiFlags, dwSize) ;

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

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

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

#define GPTR (GMEM_FIXED I 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_MOVEABLE标志使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应用程序共享。

其它标准内存管理函数

●内存重分配函数

hGlobal = GlobalReAlloc(hGlobal,dwSize,uiFlags);

●获取内存块大小

dwSize = GlobalSize(hGlobal);

●获取内存块句柄

hGlobal = GlobalHandle(p); //p为指向内存块的地址(指可移动内存块)

●释放内存函数

GlobalFree(hGlobal);

12.1.3 把文本传到剪贴板

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

把文本复制到剪贴板的步骤

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

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

●如果内存块分配失败,GlobalAlloc的值为NULL。如果分配成功,则锁定这个内存块并得到指向它的指针:

pGlobal = GlobalLock (hGlobal);

●把字符串复制到全局内存块:

for (i = 0 ; i < wLength ; i++)

       *pGlobal++ = *pString++ ;

不需要加终止符NULL,因为GlobalAlloc的GHND标志在分配内存时把整个内存块置0。

●解锁内存:

GlobalUnlock(hGlobal);

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

OpenClipboard (hwnd) ;

EmptyClipboard () ;

●通过使用CF_TEXT标识符,把内存句柄传给剪贴板,并且关闭剪贴板:

SetClipboardData(CF_TEXT, hGlobal);

CloseClipboard();

就此完成。

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

●在处理单个消息的过程中调用OpenClipboard和CloseClipboard。避免不必要地长期打开剪贴板。

●不要把一个锁定的内存句柄传给剪贴板。

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

       ■示例

#include <stdio.h>

#include <stdlib.h>

#include <string.h>

#include <stdbool.h>

#ifdef _WIN32

    #include <Windows.h>

#endif

bool CopyTextToClipboard(const char* text) {

    bool success = false;

#ifdef _WIN32

    // 打开剪贴板

    if (OpenClipboard(NULL)) {

        // 清空剪贴板内容

        if (EmptyClipboard()) {

            // 计算文本长度

            size_t textLength = strlen(text);

           

            // 分配全局内存来存储文本数据

            HGLOBAL hGlobalMemory = GlobalAlloc(GMEM_MOVEABLE,

 (textLength + 1) * sizeof(char));

            if (hGlobalMemory != NULL) {

                // 锁定全局内存并获取指向内存区域的指针

                char* lpData = (char*)GlobalLock(hGlobalMemory);

                if (lpData != NULL) {

                    // 将文本复制到全局内存中

                    strcpy(lpData, text);

                   

                    // 解锁全局内存

                    GlobalUnlock(hGlobalMemory);

                   

                    // 设置剪贴板数据

                    if (SetClipboardData(CF_TEXT, hGlobalMemory) != NULL) {

                        success = true;

                    }

                }

            }

        }

       

        // 关闭剪贴板

        CloseClipboard();

    }

#endif

   

    return success;

}

int main() {

    const char* text = "Hello, World!";

   

    if (CopyTextToClipboard(text)) {

        printf("文本已复制到剪贴板。\n");

    } else {

        printf("无法将文本复制到剪贴板。\n");

    }

   

    return 0;

}

12.1.4 从剪贴板中取得文本

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

bAvailable = IsClipboardFormatAvailable (CF_TEXT);

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

       ■从剪贴板取得文本步骤

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

OpenClipboard(hwnd);

●取得指向文本的全局内存块的句柄:

hGlobal = GetClipboardData (CF_TEXT);

如果剪贴板没有CF_TEXT格式的数据,此句柄为NULL。这是判断剪贴板是否有文本数据的另一个方法。如果GetClipboardData返回NULL,则应关闭剪贴板,其他什么都不要做。

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

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

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

●记住,hGlobal是调用GetaipboardData取得的全局句柄。现在锁定该句柄,得到一个指向剪贴板数据块的指针:

pGlobal = GlobalLock (hGlobal);

●现在复制数据:

strcpy(pText, pGlobal);

或者也可以用简单的C代码:

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

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

GlobalUnlock (hGlobal);

Closed ipboard ();

现在你有了一个pText指针,它指向你的程序所拥有的文本副本。

示例

#include <stdio.h>

#include <stdlib.h>

#include <stdbool.h>

#ifdef _WIN32

    #include <Windows.h>

#endif

char* GetTextFromClipboard() {

    char* text = NULL;

#ifdef _WIN32

    // 打开剪贴板

    if (OpenClipboard(NULL)) {

        // 获取剪贴板中的数据句柄

        HANDLE hData = GetClipboardData(CF_TEXT);

        if (hData != NULL) {

            // 锁定数据句柄并获取指向数据的指针

            char* pData = (char*)GlobalLock(hData);

            if (pData != NULL) {

                // 获取文本长度

                size_t textLength = strlen(pData);

                // 分配内存来存储文本数据

                text = (char*)malloc((textLength + 1) * sizeof(char));

                if (text != NULL) {

                    // 复制文本数据到分配的内存中

                    strcpy(text, pData);

                }

               

                // 解锁数据句柄

                GlobalUnlock(hData);

            }

        }

        // 关闭剪贴板

        CloseClipboard();

    }

#endif

    return text;

}

int main() {

    char* text = GetTextFromClipboard();

    if (text != NULL) {

        printf("剪贴板中的文本是:%s\n", text);

        free(text);

    } else {

        printf("无法获取剪贴板中的文本。\n");

    }

    return 0;

}

12.1.5 打开和关闭剪贴板

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

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

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

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

示例

       #include <stdio.h>

#include <stdbool.h>

#ifdef _WIN32

    #include <Windows.h>

#endif

bool OpenClipboard() {

    bool success = false;

#ifdef _WIN32

    success = (bool)OpenClipboard(NULL);

#endif

    return success;

}

bool CloseClipboard() {

    bool success = false;

#ifdef _WIN32

    success = (bool)CloseClipboard();

#endif

    return success;

}

int main() {

    if (OpenClipboard()) {

        printf("剪贴板已打开。\n");

        // 在此处进行剪贴板操作

        if (CloseClipboard()) {

            printf("剪贴板已关闭。\n");

        } else {

            printf("剪贴板关闭失败。\n");

        }

    } else {

        printf("无法打开剪贴板。\n");

    }

    return 0;

}

12.1.6 第76练:剪贴板的简单用法

/*------------------------------------------------------------------------

 076 WIN32 API 每日一练

     第76个例子CLIPTEXT.C:剪贴板的简单用法

     EnableMenuItem 函数 

     IsClipboardFormatAvailable函数

     OpenClipboard函数

     GetClipboardData函数

     GlobalSize函数

     GlobalLock函数

     CloseClipboard函数

     GlobalAlloc函数

     GlobalUnlock函数

     EmptyClipboard函数

     SetClipboardData函数

 (c) www.bcdaren.com 编程达人

-----------------------------------------------------------------------*/

#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, LPSTR lpCmdLine, int nShowCmd )

{

     static TCHAR szAppName[] = TEXT("ClipText");

    (略)

     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:

//发送一个WM_COMMAND消息,处理IDM_EDIT_RESET

          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))//获取剪贴板数据

               {

/*Windows系统内存管理器为了避免内存的碎片化,会通过调整页表的方式移动内存块,页表重新映射物理地址。锁定期间,Windows会修复虚拟内存地址,不会移动内存块,防止虚拟地址被改变。此时就可以得到一个确定的本进程所属的指针指向该内存块,其他进程的指针不可以访问。*/

//锁定内存块,获取指向剪贴板数据的指针

                    pGlobal = GlobalLock(hGlobal);

                    if (pText)

                    {

                         free(pText);//清空

                         pText = NULL;

                    }

                    pText = malloc(GlobalSize(hGlobal));

                    lstrcpy(pText, pGlobal);

/*当使用剪切板时,因为剪贴板的内存块是共享的,多个程序同时使用剪贴板时可能会发生冲突。锁定期间,表示剪贴板为私有,使用完后,应及时释放剪贴板*/

                    GlobalUnlock(hGlobal);//解锁内存块

                    InvalidateRect(hwnd, NULL, TRUE);

               }

               CloseClipboard();//关闭剪贴板

               return 0;

          case IDM_EDIT_CUT:

          case IDM_EDIT_COPY:

               if (!pText)    //为空退出

                    return 0;

               //分配可移动的初始化为0的内存并拷贝数据到全局内存块里

//GHND默认堆内分配可移动内存

               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();//关闭剪贴板

          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;

          }

     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);

}

/******************************************************************************

EnableMenuItem 函数:启用,禁用或显示指定的菜单项

BOOL EnableMenuItem(

  HMENU hMenu,           //菜单句柄

  UINT  uIDEnableItem,   //指定菜单栏,菜单或子菜单中的项目

  UINT  uEnable          //启用,禁用还是灰色

);

*******************************************************************************

IsClipboardFormatAvailable函数:确定剪贴板是否包含指定格式的数据

BOOL IsClipboardFormatAvailable(

  UINT format

);

*******************************************************************************

OpenClipboard函数:开剪贴板进行检查,并阻止其他应用程序修改剪贴板内容。

BOOL OpenClipboard(

  HWND hWndNewOwner//与打开的剪贴板关联的窗口的句柄。如果此参数为NULL,则打开的剪贴板与当前任务关联。

);

*******************************************************************************

GetClipboardData函数:以指定的格式从剪贴板检索数据。剪贴板必须先前已打开。

HANDLE GetClipboardData(

  UINT uFormat//剪贴板格式

);

*******************************************************************************

GlobalSize函数:检索指定的全局内存对象的当前大小,以字节为单位

SIZE_T GlobalSize(

  HGLOBAL hMem//全局内存对象的句柄

);

*******************************************************************************

GlobalLock函数:锁定全局内存对象,并返回一个指向该对象内存块的第一个字节的指针

LPVOID GlobalLock(

  HGLOBAL hMem//全局内存对象的句柄

);

*******************************************************************************

CloseClipboard函数:关闭剪贴板

BOOL CloseClipboard();

*******************************************************************************

GlobalAlloc函数:从堆中分配指定的字节数

DECLSPEC_ALLOCATOR HGLOBAL GlobalAlloc(

  UINT   uFlags,//内存分配属性。如果指定零,则默认值为GMEM_FIXED。

  SIZE_T dwBytes//字节数

);

*******************************************************************************

GlobalUnlock函数:减少与GMEM_MOVEABLE分配的内存对象关联的锁计数。该函数对使用GMEM_FIXED分配的内存对象无效。

BOOL GlobalUnlock(

  HGLOBAL hMem//全局内存对象的句柄

);

*******************************************************************************

EmptyClipboard函数:清空剪贴板并释放对剪贴板中数据的句柄。然后,该函数将剪贴板的所有权分配给当前打开剪贴板的窗口。

BOOL EmptyClipboard();

*******************************************************************************

SetClipboardData函数:以指定的剪贴板格式将数据放置在剪贴板上。

HANDLE SetClipboardData(

  UINT   uFormat,//剪贴板格式

  HANDLE hMem//指定格式的数据句柄

);

*/

       ●资源脚本文件CLIPTEXT.rc包含一个菜单资源和一个快捷键资源:

/

//

// 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

//

IDR_ACCELERATOR1 ACCELERATORS

BEGIN

    "X",            IDM_EDIT_CUT,           VIRTKEY, CONTROL, NOINVERT

    "C",            IDM_EDIT_COPY,          VIRTKEY, CONTROL, NOINVERT

    "V",            IDM_EDIT_PASTE,         VIRTKEY, CONTROL, NOINVERT

    VK_DELETE,      IDM_EDIT_CLEAR,         VIRTKEY, NOINVERT

END

●运行结果:

图12-1 剪贴板的简单用法

 

总结

       实例CLIPTEXT.C的主窗口添加了菜单EDIT和快捷键,演示了对剪贴板的操作。

       窗口过程:

       ●WM_CREATE消息:调用SendMessage函数向窗口发送一个WM_COMMAND消息,处理RESET菜单。

       ●WM_INITMENUPOPUP消息:对菜单项进行初始化工作。调用IsClipboardFormatAvailable函数判断当前剪贴板是否有内容。如果有,则启用PASTE菜单,否则禁用。同时,判断pText缓冲区是否有字符串。如果有,则启用CUT、COPY和CLEAR菜单,如果没有,则禁用。

       ●WM_COMMAND消息:处理菜单消息。

       PASTE粘贴:打开剪贴板获取剪贴板数据,参考12.1.4小节,此处不再赘述。

       CUT剪贴和COPY复制:把数据复制到剪贴板,参考12.1.3小节,此处不再赘述。

       CLEAR清空:pText = NULL;调用InvalidateRect函数重绘窗口客户区。

RESET重置:清空pText缓冲区,将默认字符串拷贝到pText缓冲区,重绘窗口客户区。

●WM_PAINT消息:调用DrawText将pText缓冲区字符串绘制到窗口客户区。

●WM_DESTROY消息:是否pText缓冲区,销毁窗口。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/pingmian/48522.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Google Chrome 浏览器在链接上点右键的快捷键

如今&#xff0c;越来越多的软件都懒得设个快捷键&#xff0c;就算设置了连个下划线也懒得加了。 谷歌浏览器右键 > 链接另存为... 和 复制链接地址 的快捷键 (如图)

爬虫自己做的

1.urllib 1.1基本使用 1.2 下载&#xff08;图片&#xff0c;页面&#xff0c;视频&#xff09; 1.3 get 1.3.1 quote 中文变成对应uncode编码 当url 的wd中文时 quote是将中文变成对应uncode编码 然后拼接成完整的url 1.3.2urlencode方法 wd有多个参数 1.3.3ajas get实例 …

Connecting weaviate with langflow across docker containers

题意&#xff1a;在Docker容器之间连接Weaviate与Langflow 问题背景&#xff1a; I am trying to build a local RAG application using Langflow. For my vectore store, I want to use a local Weaviate instance, hosted in a separate docker container on the same netwo…

KAFKA搭建教程

KAFKA搭建教程 期待您的关注 KAFKA学习笔记 帮助更多人 目录 KAFKA搭建教程 1.下载Kafka并解压 2.添加环境变量 3.修改 server.properties 文件 4.将kafka复制到其它节点 5.修改node1、node2节点的broker.id 6.将master的环境变量同步到node1、 node2 7.启动zookeeper…

昇思25天学习打卡营第21天|RNN实现情感分类

Mindspore框架循环神经网络RNN模型实现情感分类 Mindspore框架循环神经网络RNN模型实现情感分类|&#xff08;一&#xff09;数据集准备 Mindspore框架循环神经网络RNN模型实现情感分类|&#xff08;二&#xff09;RNN模型 Mindspore框架循环神经网络RNN模型实现情感分类|&…

PostgreSQL 中如何解决因大量并发删除和插入操作导致的索引抖动?

&#x1f345;关注博主&#x1f397;️ 带你畅游技术世界&#xff0c;不错过每一次成长机会&#xff01;&#x1f4da;领书&#xff1a;PostgreSQL 入门到精通.pdf 文章目录 PostgreSQL 中如何解决因大量并发删除和插入操作导致的索引抖动一、理解索引抖动二、索引抖动的影响三…

nginx通过nginx_upstream_check_module实现后端健康检查

1、简介说明 nginx是常用的反向代理和负载均衡服务&#xff0c;具有强大并发能力、稳定性、丰富的功能集、低资源的消耗。 nginx自身是没有针对后端节点健康检查的&#xff0c;但是可以通过默认自带的ngx_http_proxy_module 模块和ngx_http_upstream_module模块中的相关指令来完…

【Langchain大语言模型开发教程】基于文档问答

&#x1f517; LangChain for LLM Application Development - DeepLearning.AI Embedding&#xff1a; https://huggingface.co/BAAI/bge-large-en-v1.5/tree/main 学习目标 1、Embedding and Vector Store 2、RetrievalQA 引包、加载环境变量 import osfrom dotenv import…

【BUG】已解决:OSError: [Errno 22] Invalid argument

已解决&#xff1a;OSError: [Errno 22] Invalid argument 目录 已解决&#xff1a;OSError: [Errno 22] Invalid argument 【常见模块错误】 错误原因&#xff1a; 解决方法如下&#xff1a; 欢迎来到英杰社区https://bbs.csdn.net/topics/617804998 欢迎来到我的主页&…

快速认识EA(Enterprise Architecture)

前言 企业架构&#xff0c;英文是&#xff1a;Enterprise Architecture&#xff0c;简称&#xff1a;EA&#xff0c;是承接企业战略规划与IT建设之间的桥梁&#xff0c;是企业信息化的核心&#xff0c;主要包括业务架构和IT架构。 架构的本质是管理和解决系统的复杂性&#x…

06. 截断文本 选择任何链接 :root 和 html 有什么区别

截断文本 对超过一行的文本进行截断,在末尾添加省略号(…)。 使用 overflow: hidden 防止文本超出其尺寸。使用 white-space: nowrap 防止文本超过一行高度。使用 text-overflow: ellipsis 使得如果文本超出其尺寸,将以省略号结尾。为元素指定固定的 width,以确定何时显示省略…

mysql无法启动

总是报错&#xff1a; 1、Job for mysql.service failed because the control process exited with error code. See "systemctl status mysql.service" and "journalctl -xeu mysql.service" for details. 2、ERROR 2002 (HY000): Cant connect to local …

linux中常见的协议、服务端口整理汇总

&#x1f341;博主简介&#xff1a; &#x1f3c5;云计算领域优质创作者 &#x1f3c5;2022年CSDN新星计划python赛道第一名 &#x1f3c5;2022年CSDN原力计划优质作者 ​ &#x1f3c5;阿里云ACE认证高级工程师 ​ &#x1f3c5;阿里云开发者社区专家博主 &#x1f48a;交流社…

【机器学习实战】数据增强基础

文章目录 1. 数据增强2. 数据增强技巧torchvision2.1 图像大小调整2.2 图像旋转2.3 图像转换为张量2.4 归一化2.5 图像裁剪2.6 仿射变换2.7 透视变换 3. 自动增强4. Mixup增强 1. 数据增强 什么是数据增强&#xff1f;数据增强是优化数据吗&#xff1f;这种说法并不尽然。首先…

Golang | Leetcode Golang题解之第239题滑动窗口最大值

题目&#xff1a; 题解&#xff1a; func maxSlidingWindow(nums []int, k int) []int {n : len(nums)prefixMax : make([]int, n)suffixMax : make([]int, n)for i, v : range nums {if i%k 0 {prefixMax[i] v} else {prefixMax[i] max(prefixMax[i-1], v)}}for i : n - 1…

将github上的项目导入到vscode并创建虚拟环境

1、将github上的项目导入到vscode 直接从github上下载到本地&#xff0c;用vscode打开&#xff08;Open file&#xff09; 2、创建虚拟环境 python -m venv <name> <name>\Scripts\activate ps: 1、退出虚拟环境 deactivate 2、如果运行python -m venv <…

Unity格斗游戏,两个角色之间互相锁定对方,做圆周运动

1&#xff0c;灵感来源 今天手头的工作忙完了&#xff0c;就等着服务器那边完活&#xff0c;于是开始研究同步问题。 正好想到之前想做的&#xff0c;两个小人对线PK&#xff0c;便有了这篇文章。 2&#xff0c;要实现的效果 如图所示&#xff0c;两个小人可以互相锁定&…

CARLA源码编译避坑指南

文章目录 前言一、CARLA官方编译教程二、无法拉取Unreal Engine Carla分支三、无法下载CARLA地图包四、osm2odr安装错误五、bdist_wheel错误六、编译结果 前言 之前也搭建过这个CARLA源码编译环境&#xff0c;很多坑&#xff0c;但是没记录下来&#xff0c;这次再搭建还是一样…

Spring Web MVC(一篇带你了解并入门,附带常用注解)

一&#xff0c;什么是Spring Web MVC 先看一下官网怎么说&#xff1a; 也就是Spring Web MVC一开始就是包含在Spring框架里面的&#xff0c;但通常叫做Spring MVC。 也可以总结出一个信息&#xff0c;这是一个Web框架。后面我就简称为Spring MVC了。 1.1MVC MVC也就是Mode…

防火墙--内容安全

目录 概述 IAE引擎流程 DPI深度包检测 基于特征字的检测技术 基于应用网关的检测技术 基于行为模式的检测技术 DFI深度流检测 基于数据流进行识别检测的技术 DPI和DFI对比 IDS&#xff08;入侵检测&#xff09; IPS&#xff08;入侵防御&#xff09; 优势 入侵检测…