0x01  Windows剪贴板
  

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

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

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

0x02  多数据项和延迟提交技术

  要把数据放入剪贴板,在打开剪贴板后一定要调用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等剪贴板延迟提交消息的处理。

  1.当另一个进程调用GetClipboardData()函数时,系统将会向延迟提交数据的剪贴板拥有者进程发送WM_RENDERFORMAT消息。剪贴板拥有者进程在此消息的响应函数中应使用相应的格式和实际的数据句柄来调用SetClipboardData()函数,但不必再调用OpenClipboard()和EmptyClipboard()去打开和清空剪贴板了。在设置完数据有也无须调用CloseClipboard()关闭剪贴板。

  2.如果其他进程打开了剪贴板并且调用EmptyClipboard()函数去清空剪贴板的内容,接管剪贴板的拥有权时,系统将向延迟提交的剪贴板拥有者进程发送WM_DESTROYCLIPBOARD消息,以通知该进程对剪贴板拥有权的丧失。而失去剪贴板拥有权的进程在收到该消息后则不会再向剪贴板提交数据。另外,在延迟提交进程在提交完所有要提交的数据后也会收到此消息。

  3.如果延迟提交剪贴板拥有者进程将要终止,系统将会为其发送一条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); // 将内存中的数据放置到剪贴板

0x03  API

 

  剪贴板的打开 – OpenClipboard

  要想把数据放置到剪贴板中,则必须先打开剪贴板,而这是通过 OpenClipboard 成员函数实现:

  BOOL  OpenClipboard(

    HWND  hWndNewOwner );  //指向一个与之关联的窗口句柄,即代表是这个窗口打开剪贴,如果这个参数设置为 NULL 的话,则以当前的任务或者说是进程来打开剪贴板。

  如果打开剪贴板成功,则该函数返回非 0 值,如果其他程序已经打开了剪贴板,那么当前这个程序就无法再打开剪贴板了,所以会致使打开剪贴板失败,从而该函数返回 0 值。

  剪贴板的清空 - EmptyClipboard

  这个函数将清空剪贴板,并释放剪贴板中数据的句柄,然后将剪贴板的所有权分配给当前打开剪贴板的窗口,这样做之后当前打开这个剪贴板的程序就拥有了剪贴板的所有权,因此这个程序就可以往剪贴板上放置数据了。

  BOOL EmptyClipboard(void);

  剪贴板的关闭 - CloseClipboard

  如果某个进程打开了剪贴板,则在这个进程没有调用 CloseClipboard 函数关闭剪贴板句柄之前,其他进程都是无法打开剪贴板的,所以我们每次使用完剪贴板之后都应该关闭剪贴板。注意,这里的关闭剪贴板并不代表当前打开剪贴板的这个程序失去了对剪贴板的所有权,只有在别的程序调用了 EmptyClipboard 函数之后,当前的这个程序才会失去对剪贴板的所有权,

  而那个调用 EmptyClipboard 函数的程序才能拥有剪贴板。

  BOOL CloseClipboard(void);

  数据发送到剪贴板 - SetClipboardData

  可以通过 SetClipboardData 函数来实现往剪贴板中放置数据,这个函数以指定的剪贴板格式向剪贴板中放置数据。

  HANDLE  SetClipboardData(
      UINT uFormat, //要放到剪贴板上的数据的格式,常见的有 CF_BITMAP ,CF_TEXT ,CF_DIB 等等(其他格式可以参考 MSDN)。
     HANDLE hMem ); //指定格式的数据的句柄,该参数可以是 NULL ,如果该参数为 NULL 则表明直到有程序对剪贴板中的数据进行请求时,该程序(也就是拥有剪贴板所有权的进程)才会将数据复制到剪贴板中,也就是提供指定剪贴板格式的数据,

  剪贴板中数据格式判断 – IsClipboardFormatAvaliable

 BOOL  IsClipboardFormatAvailable( UINT format ); 

  该函数用来判断剪贴板上的数据格式是否为 format 指定的格式。

  剪贴板中数据接收 - GetClipboardData

 HANDLE  GetClipboardData( UINT uFormat ); 

  该函数根据 uFormat 指定的格式,返回一个以指定格式存在于剪贴板中的剪贴板对象的句柄。

0x04  源代码  

自己用MFC实现的一个剪贴板程序,如图:

    

    

源代码:

 // ClipBoardDlg.cpp : 实现文件
// #include "stdafx.h"
#include "ClipBoard.h"
#include "ClipBoardDlg.h"
#include "afxdialogex.h" #ifdef _DEBUG
#define new DEBUG_NEW
#endif // 用于应用程序“关于”菜单项的 CAboutDlg 对话框
VOID ShowErrorMessage(DWORD ErrorCode);
class CAboutDlg : public CDialogEx
{
public:
CAboutDlg(); // 对话框数据
#ifdef AFX_DESIGN_TIME
enum { IDD = IDD_ABOUTBOX };
#endif protected:
virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV 支持 // 实现
protected:
DECLARE_MESSAGE_MAP()
}; CAboutDlg::CAboutDlg() : CDialogEx(IDD_ABOUTBOX)
{
} void CAboutDlg::DoDataExchange(CDataExchange* pDX)
{
CDialogEx::DoDataExchange(pDX);
} BEGIN_MESSAGE_MAP(CAboutDlg, CDialogEx)
END_MESSAGE_MAP() // CClipBoardDlg 对话框 CClipBoardDlg::CClipBoardDlg(CWnd* pParent /*=NULL*/)
: CDialogEx(IDD_CLIPBOARD_DIALOG, pParent)
{
m_hIcon = AfxGetApp()->LoadIcon(IDR_MAINFRAME);
} void CClipBoardDlg::DoDataExchange(CDataExchange* pDX)
{
CDialogEx::DoDataExchange(pDX);
DDX_Control(pDX, IDC_MESSAGE_LIST, m_CListBox_Message);
} BEGIN_MESSAGE_MAP(CClipBoardDlg, CDialogEx)
ON_WM_SYSCOMMAND()
ON_WM_PAINT()
ON_WM_QUERYDRAGICON()
ON_BN_CLICKED(IDC_TEXT, &CClipBoardDlg::OnBnClickedText)
ON_WM_DESTROYCLIPBOARD()
ON_BN_CLICKED(IDC_BITMAP, &CClipBoardDlg::OnBnClickedBitmap)
ON_BN_CLICKED(IDC_SELF_DEFINE, &CClipBoardDlg::OnBnClickedSelfDefine)
ON_BN_CLICKED(IDC_BUTTON4, &CClipBoardDlg::OnBnClickedButton4)
ON_WM_DESTROYCLIPBOARD()
END_MESSAGE_MAP() // CClipBoardDlg 消息处理程序 BOOL CClipBoardDlg::OnInitDialog()
{
CDialogEx::OnInitDialog(); // 将“关于...”菜单项添加到系统菜单中。 // IDM_ABOUTBOX 必须在系统命令范围内。
ASSERT((IDM_ABOUTBOX & 0xFFF0) == IDM_ABOUTBOX);
ASSERT(IDM_ABOUTBOX < 0xF000); CMenu* pSysMenu = GetSystemMenu(FALSE);
if (pSysMenu != NULL)
{
BOOL bNameValid;
CString strAboutMenu;
bNameValid = strAboutMenu.LoadString(IDS_ABOUTBOX);
ASSERT(bNameValid);
if (!strAboutMenu.IsEmpty())
{
pSysMenu->AppendMenu(MF_SEPARATOR);
pSysMenu->AppendMenu(MF_STRING, IDM_ABOUTBOX, strAboutMenu);
}
} // 设置此对话框的图标。 当应用程序主窗口不是对话框时,框架将自动
// 执行此操作
SetIcon(m_hIcon, TRUE); // 设置大图标
SetIcon(m_hIcon, FALSE); // 设置小图标 // TODO: 在此添加额外的初始化代码
this->m_DeskopPixelX = ::GetSystemMetrics(SM_CXSCREEN); //以像素为单位计算的屏幕尺寸
this->m_DeskopPixelY = ::GetSystemMetrics(SM_CYSCREEN); this->m_NewFormat = RegisterClipboardFormat(L"TEST_FORMAT"); //注册一个新的剪贴板格式,如果注册格式已经存在,将返回已经存在格式的值而不是注册新的格式。这样做的好处是可以在多个应用程序中使用同一种格式来复制和粘贴数据。
return TRUE; // 除非将焦点设置到控件,否则返回 TRUE
} void CClipBoardDlg::OnSysCommand(UINT nID, LPARAM lParam)
{
if ((nID & 0xFFF0) == IDM_ABOUTBOX)
{
CAboutDlg dlgAbout;
dlgAbout.DoModal();
}
else
{
CDialogEx::OnSysCommand(nID, lParam);
}
} // 如果向对话框添加最小化按钮,则需要下面的代码
// 来绘制该图标。 对于使用文档/视图模型的 MFC 应用程序,
// 这将由框架自动完成。 void CClipBoardDlg::OnPaint()
{
if (IsIconic())
{
CPaintDC dc(this); // 用于绘制的设备上下文 SendMessage(WM_ICONERASEBKGND, reinterpret_cast<WPARAM>(dc.GetSafeHdc()), ); // 使图标在工作区矩形中居中
int cxIcon = GetSystemMetrics(SM_CXICON);
int cyIcon = GetSystemMetrics(SM_CYICON);
CRect rect;
GetClientRect(&rect);
int x = (rect.Width() - cxIcon + ) / ;
int y = (rect.Height() - cyIcon + ) / ; // 绘制图标
dc.DrawIcon(x, y, m_hIcon);
}
else
{
CDialogEx::OnPaint();
}
} //当用户拖动最小化窗口时系统调用此函数取得光标
//显示。
HCURSOR CClipBoardDlg::OnQueryDragIcon()
{
return static_cast<HCURSOR>(m_hIcon);
} void CClipBoardDlg::OnBnClickedText()
{
// TODO: 在此添加控件通知处理程序代码
// 打开剪贴板
::OpenClipboard(this->GetSafeHwnd());
//OpenClipboard();
// 首先清空剪贴板
::EmptyClipboard();
// 设置剪贴板文本数据
this->WriteTextIntoClipboard();
// 关闭剪贴板
::CloseClipboard();
} VOID CClipBoardDlg::WriteTextIntoClipboard()
{
// 初始化文本信息
const char BufferData[] = "刘大大你可别是妖怪吧!";
DWORD BufferLength = strlen(BufferData);
HANDLE MemoryHandle = GlobalAlloc(GHND, BufferLength + );
if (MemoryHandle)
{
LPBYTE VirtualAddress = (LPBYTE)GlobalLock(MemoryHandle);
memcpy(VirtualAddress, BufferData, BufferLength);
//VirtualAddress[BufferLength] = 0; //调试看内存发现编译器已经做了优化了,这步没必要
GlobalUnlock(MemoryHandle);
// 设置剪贴板文本数据
if (::SetClipboardData(CF_TEXT, MemoryHandle) == NULL)
{
ShowErrorMessage(GetLastError());
}
}
else
this->MessageBox(L"WriteTextIntoClipboard() Error");
} void CClipBoardDlg::OnBnClickedBitmap()
{
// TODO: 在此添加控件通知处理程序代码
// 打开剪贴板
::OpenClipboard(this->GetSafeHwnd());
// 首先清空剪贴板
::EmptyClipboard();
// 设置剪贴板位图数据
this->WriteBitmapIntoClipboard();
// 关闭剪贴板
::CloseClipboard();
} // 写入位图数据
VOID CClipBoardDlg::WriteBitmapIntoClipboard()
{
// 取得当前屏幕位图
CDC DeviceContext, MemoryDeviceContext;
CBitmap ScreenBitmap;
DeviceContext.CreateDC(L"DISPLAY", NULL, NULL, NULL);
MemoryDeviceContext.CreateCompatibleDC(&DeviceContext);
ScreenBitmap.CreateCompatibleBitmap(&DeviceContext, m_DeskopPixelX, m_DeskopPixelY);
MemoryDeviceContext.SelectObject(&ScreenBitmap);
MemoryDeviceContext.BitBlt(, , m_DeskopPixelX, m_DeskopPixelY, &DeviceContext, , , SRCCOPY);
// 设置剪贴板文本数据
if (::SetClipboardData(CF_BITMAP, ScreenBitmap.GetSafeHandle()) == NULL)
{
ShowErrorMessage(GetLastError());
}
ScreenBitmap.DeleteObject();
MemoryDeviceContext.DeleteDC();
} void CClipBoardDlg::OnBnClickedSelfDefine()
{
// TODO: 在此添加控件通知处理程序代码
// TODO: 在此添加控件通知处理程序代码
::OpenClipboard(this->GetSafeHwnd());
// 首先清空剪贴板
::EmptyClipboard();
// 设置剪贴板位图数据
this->WriteSelfDefineDataIntoClipboard();
// 关闭剪贴板
::CloseClipboard();
} // 写入自定义数据
VOID CClipBoardDlg::WriteSelfDefineDataIntoClipboard()
{
PERSON_INFORMATION PersonInfo = { L"张飞", }; // 初始化自定义格式
DWORD PersonInfoLength = sizeof(PERSON_INFORMATION);
HANDLE MemoryHandle = GlobalAlloc(GHND, PersonInfoLength + );
if (MemoryHandle)
{
LPBYTE VirtualAddress = (LPBYTE)GlobalLock(MemoryHandle);
memcpy(VirtualAddress, &PersonInfo, PersonInfoLength);
VirtualAddress[PersonInfoLength] = ;
GlobalUnlock(MemoryHandle);
::SetClipboardData(this->m_NewFormat, MemoryHandle);
}
} void CClipBoardDlg::OnBnClickedButton4()
{
// TODO: 在此添加控件通知处理程序代码
::OpenClipboard(this->GetSafeHwnd());
//OpenClipboard();
// 首先清空剪贴板
::EmptyClipboard();
// 写入3种数据
this->WriteTextIntoClipboard();
this->WriteBitmapIntoClipboard();
this->WriteSelfDefineDataIntoClipboard();
// 关闭剪贴板
::CloseClipboard();
} VOID ShowErrorMessage(DWORD ErrorCode)
{
CHAR Message[] = { };
sprintf(Message, "(ErrorCode=%p) (ErrorCode=%d)", ErrorCode, ErrorCode);
MessageBoxA(, Message, , );
} void CClipBoardDlg::OnDestroyClipboard()
{
CDialogEx::OnDestroyClipboard(); // TODO: 在此处添加消息处理程序代码
this->m_CListBox_Message.AddString(L"WM_DESTORYCLIPBOARD");
this->m_CListBox_Message.SetCurSel(this->m_CListBox_Message.GetCount() - );
}
 // ClipBoardClientDlg.cpp : 实现文件
// #include "stdafx.h"
#include "ClipBoardClient.h"
#include "ClipBoardClientDlg.h"
#include "afxdialogex.h" #ifdef _DEBUG
#define new DEBUG_NEW
#endif // 用于应用程序“关于”菜单项的 CAboutDlg 对话框 class CAboutDlg : public CDialogEx
{
public:
CAboutDlg(); // 对话框数据
#ifdef AFX_DESIGN_TIME
enum { IDD = IDD_ABOUTBOX };
#endif protected:
virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV 支持 // 实现
protected:
DECLARE_MESSAGE_MAP()
}; CAboutDlg::CAboutDlg() : CDialogEx(IDD_ABOUTBOX)
{
} void CAboutDlg::DoDataExchange(CDataExchange* pDX)
{
CDialogEx::DoDataExchange(pDX);
} BEGIN_MESSAGE_MAP(CAboutDlg, CDialogEx)
END_MESSAGE_MAP() // CClipBoardClientDlg 对话框 CClipBoardClientDlg::CClipBoardClientDlg(CWnd* pParent /*=NULL*/)
: CDialogEx(IDD_CLIPBOARDCLIENT_DIALOG, pParent)
{
m_hIcon = AfxGetApp()->LoadIcon(IDR_MAINFRAME);
} void CClipBoardClientDlg::DoDataExchange(CDataExchange* pDX)
{
CDialogEx::DoDataExchange(pDX);
DDX_Control(pDX, IDC_LIST1, m_CListBox_Message);
} BEGIN_MESSAGE_MAP(CClipBoardClientDlg, CDialogEx)
ON_WM_SYSCOMMAND()
ON_WM_PAINT()
ON_WM_QUERYDRAGICON()
ON_BN_CLICKED(IDC_GET_TEXT, &CClipBoardClientDlg::OnBnClickedGetText)
ON_BN_CLICKED(IDC_GET_BITMAP, &CClipBoardClientDlg::OnBnClickedGetBitmap)
ON_BN_CLICKED(IDC_GET_SELF_DEFINE, &CClipBoardClientDlg::OnBnClickedGetSelfDefine)
ON_BN_CLICKED(IDC_GET_ALL, &CClipBoardClientDlg::OnBnClickedGetAll)
END_MESSAGE_MAP() // CClipBoardClientDlg 消息处理程序 BOOL CClipBoardClientDlg::OnInitDialog()
{
CDialogEx::OnInitDialog(); // 将“关于...”菜单项添加到系统菜单中。 // IDM_ABOUTBOX 必须在系统命令范围内。
ASSERT((IDM_ABOUTBOX & 0xFFF0) == IDM_ABOUTBOX);
ASSERT(IDM_ABOUTBOX < 0xF000); CMenu* pSysMenu = GetSystemMenu(FALSE);
if (pSysMenu != NULL)
{
BOOL bNameValid;
CString strAboutMenu;
bNameValid = strAboutMenu.LoadString(IDS_ABOUTBOX);
ASSERT(bNameValid);
if (!strAboutMenu.IsEmpty())
{
pSysMenu->AppendMenu(MF_SEPARATOR);
pSysMenu->AppendMenu(MF_STRING, IDM_ABOUTBOX, strAboutMenu);
}
} // 设置此对话框的图标。 当应用程序主窗口不是对话框时,框架将自动
// 执行此操作
SetIcon(m_hIcon, TRUE); // 设置大图标
SetIcon(m_hIcon, FALSE); // 设置小图标 // TODO: 在此添加额外的初始化代码
m_NewFormat = RegisterClipboardFormat(L"TEST_FORMAT"); return TRUE; // 除非将焦点设置到控件,否则返回 TRUE
} void CClipBoardClientDlg::OnSysCommand(UINT nID, LPARAM lParam)
{
if ((nID & 0xFFF0) == IDM_ABOUTBOX)
{
CAboutDlg dlgAbout;
dlgAbout.DoModal();
}
else
{
CDialogEx::OnSysCommand(nID, lParam);
}
} // 如果向对话框添加最小化按钮,则需要下面的代码
// 来绘制该图标。 对于使用文档/视图模型的 MFC 应用程序,
// 这将由框架自动完成。 void CClipBoardClientDlg::OnPaint()
{
if (IsIconic())
{
CPaintDC dc(this); // 用于绘制的设备上下文 SendMessage(WM_ICONERASEBKGND, reinterpret_cast<WPARAM>(dc.GetSafeHdc()), ); // 使图标在工作区矩形中居中
int cxIcon = GetSystemMetrics(SM_CXICON);
int cyIcon = GetSystemMetrics(SM_CYICON);
CRect rect;
GetClientRect(&rect);
int x = (rect.Width() - cxIcon + ) / ;
int y = (rect.Height() - cyIcon + ) / ; // 绘制图标
dc.DrawIcon(x, y, m_hIcon);
}
else
{
CDialogEx::OnPaint();
}
} //当用户拖动最小化窗口时系统调用此函数取得光标
//显示。
HCURSOR CClipBoardClientDlg::OnQueryDragIcon()
{
return static_cast<HCURSOR>(m_hIcon);
} void CClipBoardClientDlg::OnBnClickedGetText()
{
// TODO: 在此添加控件通知处理程序代码
CString BufferData;
this->m_CListBox_Message.ResetContent();
::OpenClipboard(this->GetSafeHwnd());
this->ReadTextFromClipboard();
::CloseClipboard();
} void CClipBoardClientDlg::OnBnClickedGetBitmap()
{
// TODO: 在此添加控件通知处理程序代码
CString BufferData;
this->m_CListBox_Message.ResetContent();
::OpenClipboard(this->GetSafeHwnd());
this->ReadBitmapFromClipboard();
::CloseClipboard();
} void CClipBoardClientDlg::OnBnClickedGetSelfDefine()
{
// TODO: 在此添加控件通知处理程序代码
CString BufferData = L"[自定义]";
this->m_CListBox_Message.ResetContent();
::OpenClipboard(this->GetSafeHwnd());
this->ReadSelfDefineDataFromClipboard();
::CloseClipboard();
} void CClipBoardClientDlg::OnBnClickedGetAll()
{
// TODO: 在此添加控件通知处理程序代码
this->m_CListBox_Message.ResetContent(); // 打开剪贴板
::OpenClipboard(this->GetSafeHwnd()); // 循环枚举
UINT v1 = ; // 从第一种格式值开始枚举 while (v1 = EnumClipboardFormats(v1))
{
// 这里只处理这3种格式
if (v1 == CF_TEXT)
this->ReadTextFromClipboard();
else if (v1 == CF_BITMAP)
this->ReadBitmapFromClipboard();
else if (v1 == m_NewFormat)
this->ReadSelfDefineDataFromClipboard();
}
::CloseClipboard();
} VOID CClipBoardClientDlg::ReadTextFromClipboard()
{
CString BufferData;
HANDLE MemoryHandle = ::GetClipboardData(CF_TEXT);
if (MemoryHandle)
{
BufferData = L"[文本]";
BufferData += (LPSTR)GlobalLock(MemoryHandle);
GlobalUnlock(MemoryHandle);
this->m_CListBox_Message.AddString(BufferData);
}
} VOID CClipBoardClientDlg::ReadBitmapFromClipboard()
{
CString BufferData;
HBITMAP BitmapHwnd = (HBITMAP)::GetClipboardData(CF_BITMAP);
if (BitmapHwnd)
{
BITMAP BitmapHeader;
::GetObject(BitmapHwnd, sizeof(BITMAP), &BitmapHeader);
BufferData.Format(L"[位图]Size=(%d,%d)", BitmapHeader.bmWidth, BitmapHeader.bmHeight);
this->m_CListBox_Message.AddString(BufferData);
}
} VOID CClipBoardClientDlg::ReadSelfDefineDataFromClipboard()
{
CString BufferData;
HANDLE MemoryHandle = ::GetClipboardData(m_NewFormat);
if (MemoryHandle)
{
PERSON_INFORMATION* PersonInfo = (PERSON_INFORMATION*)GlobalLock(MemoryHandle);
BufferData.Format(L"[自定义]Name=%s Age=%d", PersonInfo->Name, PersonInfo->Age);
GlobalUnlock(MemoryHandle);
this->m_CListBox_Message.AddString(BufferData);
}
}

  

windows剪贴板的更多相关文章

  1. 批处理将字符串输出到Windows剪贴板

    批处理将字符串输出到Windows剪贴板 2016-06-30 23:29 339人阅读 评论(0) 收藏 举报 版权声明:作者:N3verL4nd 出处:http://blog.csdn.net/x ...

  2. tmux复制到windows剪贴板/粘贴板的坑

    以下所有操作都是在windows下面用putty连接linux centos6的情景下. 一直很纳闷为什么在tmux模式下不能把复制到的文字放到系统的粘贴板里面呢?通过层层阻碍,终于找到了原因. 去掉 ...

  3. Ditto - Windows剪贴板增强小工具,方便复制粘贴多条记录

    在平时的工作中,好多地方需要重复的复制粘帖一些重复性的代码,频繁的Ctrl+C.Ctrl+V导致效率很低,而且时间长了会很烦,Windows自带的剪切板功能一次只能进行一条记录操作,单一的不行,与提高 ...

  4. Ditto —— windows 剪贴板增强小工具(复制粘贴多条记录)

    Windows 虽然不断在升级,但系统自带的剪贴板功能却仍然弱爆了 (只能保留一条记录). Ditto 下载地址:http://sourceforge.net/projects/ditto-cp/fi ...

  5. 【转】C++怎么读写windows剪贴板的内容?比如说自动把一个字符串复制.

    // 复制数据至剪切板BOOL CopyToClipboard(const char* pszData, const int nDataLen){ if(::OpenClipboard(NULL)) ...

  6. Windows剪贴板操作简单小例

    1.复制文字到剪贴板 CString strText = L"须要拷贝到剪贴板的文字"; if ( ::OpenClipboard(m_hWnd) ) { if ( ::Empty ...

  7. 【Windows编程】系列第九篇:剪贴板使用

    上一篇我们学习了常见的通用对话框,本篇来了解剪贴板的使用,它常用于复制粘贴功能. 剪贴板是Windows最早就加入的功能,由于该功能非常实用,我们几乎每天都会使用到.通过剪贴板,我们就可以将数据从一个 ...

  8. 【转】浅析Windows编程的剪贴板

    摘要: 本文对Windows剪贴板机制作了深入.全面的阐述,具体内容包括:文本.位图.DSP.自定义格式剪贴板的使用和多数据项和延迟提交技术. 关键词: VC++6.0: 剪贴板机制:数据格式:延迟提 ...

  9. Python Windows 快捷键自动给剪贴板(复制)图片添加水印

    编写一个能在windows上使用的按下快捷键自动给剪贴板(复制)的图片添加水印的小工具.plyer.PIL.pyinstaller.pynput.win32clipboard库.记录自己踩过的坑,部分 ...

随机推荐

  1. jmap -histo pid 输出的[C [B [I [S 的含义

    JMAP 输出 其中: [C is a char[][S is a short[][I is a int[][B is a byte[][[I is a int[][]

  2. Centos6.8 smokeping安装

    yum -y install rrdtool perl-rrdtool curl perl-core bind bind-chroot bind-utils httpd popt popt-devel ...

  3. ASP.NET发送电子邮件(转)

    原始地址:http://www.cnblogs.com/ForEvErNoME/archive/2012/06/05/2529259.html(有代码下载,博主真是有操守) 1.补充知识 (1)POP ...

  4. vue生命周期 钩子函数

    首先,1.x和2.x的生命周期钩子对比: 钩子函数的树状图,红色的是我们可以利用的函数,绿色的是函数解析,蓝色的是函数执行时机 <!DOCTYPE html> <html> & ...

  5. LOJ6072苹果树

    虽然结合了很多算法,但是一步一步地推一下还不算太难的一道题. 首先考虑枚举枚举有用的苹果的集合,然后去算生成树个数. 先考虑怎么计算生成树个数. 发现可以使用matrix-tree. 所有有用点可以和 ...

  6. 伪分布式&&完全分布式&&高可用(zookeeper)的配置文件内容

    [伪分布式] ①[core-site.xml] <configuration> <property> <name>fs.defaultFS</name> ...

  7. 配置tomcat允许跨域访问,cors跨域资源共享

    1.导入cors-filter-1.7.jar,java-property-utils-1.9.jar两个jar包 2.在web.xml里配上: <filter> <filter-n ...

  8. 如何引用GitHub的静态资源文件 js css

    参考:引用GitHub的静态资源文件 有些人说直接用 Github Raw 浏览器不执行是因为返回的 content-type 是 text/plain,这么说不准确.实际上浏览器对 MIME 类型并 ...

  9. CompareTo 基于的排序算法

    CompareTo 基于的排序算法(高级排序) 这个是今天学习MapReduce时发现的,自定义类后实现了WritableComparable<>接口后实现了接口中的compareTo方法 ...

  10. Tomcat修改用户名密码教程

    Tomcat安装教程见http://www.cnblogs.com/lsdb/p/6497964.html 启动tomcat后访问http://127.0.0.1/:8080,出现界面如下其右上角有三 ...