【问题标题】:C++ (VC): How to detect whether Windows clipboard contains CF_TEXT data?C++(VC):如何检测Windows剪贴板是否包含CF_TEXT数据?
【发布时间】:2015-06-29 14:41:18
【问题描述】:

我是 C++ 的初学者,但我设法修改了其他人的代码,最后编写了一个程序,将 Windows 剪贴板写入文本文件,将代码页中编码的文本指定为命令行程序的参数(例如,MyProg.exe 437)将在代码页 437 中写入文本。如果未指定代码页,程序将在标准 Windows 代码页 1252 中写入文本文件。

问题在于,如果剪贴板包含(例如)快捷方式或文件,而不是文本,程序会崩溃。如果剪贴板中没有CF_TEXT 数据,有人可以告诉我如何优雅地退出吗? (还是我完全误解了这个问题?)

我一直在寻找没有成功的答案。这是我的 VC2010 代码(我不完全理解,但是当剪贴板包含 CF_TEXT 时它似乎可以工作;顺便说一下,它不适用于 CF_UNICODETEXT - 输出只是几个字节。 ):

#include <stdafx.h>
#include <windows.h>
#include <iostream>
#include <fstream>
#include <codecvt> // for wstring_convert
#include <locale>  // for codecvt_byname

using namespace std;

void BailOut(char *msg)
{
fprintf(stderr, "Exiting: %s\n", msg);
exit(1);
}

int main(int argc, char* argv[])
{
std::string codepage = ".1252";
if (argc > 1) {
    std::string cpnum = argv[1];
    codepage = "."+cpnum;   
}

HANDLE clip;
string clip_text = "";
// exit if clipboard not available
if (!OpenClipboard(NULL))
        BailOut("Can't open clipboard"); 

clip = GetClipboardData(CF_TEXT);
clip_text = (char*)clip;
CloseClipboard();

// create conversion routines
typedef std::codecvt_byname<wchar_t,char,std::mbstate_t> codecvt;
std::wstring_convert<codecvt> cp1252(new codecvt(".1252"));
std::wstring_convert<codecvt> outpage(new codecvt(codepage));

std::string OutFile = "#clip.txt"; // output file name
ofstream OutStream;  // open an output stream
OutStream.open(OutFile, ios::out | ios::trunc);

// make sure file is successfully opened
if(!OutStream)
{
    cout << "Error opening file " << OutFile << " for writing.\n";
    return 1;
}
// convert to DOS/Win codepage number in "outpage"
OutStream << outpage.to_bytes(cp1252.from_bytes(clip_text)).c_str();
OutStream << endl;
OutStream.close(); // close output stream
return 0;
} 

对于如何解决最后一个问题的任何建议,我将不胜感激。

【问题讨论】:

    标签: c++ text clipboard clipboarddata


    【解决方案1】:

    在使用GetClipboardData 函数从剪贴板获取数据之前,您必须检查剪贴板是否包含您期望格式的数据。这可以使用IsClipboardFormatAvailable 函数来完成,如下所示:

    if(IsClipboardFormatAvailable(CF_TEXT))
    {
        // The clipboard contains null-terminated ANSI string.
    }
    else if (IsClipboardFormatAvailable(CF_UNICODETEXT))
    {
        // The clipboard contains null-terminated Unicode string.
    }
    

    然后您必须打开剪贴板并获取剪贴板缓冲区的句柄,如下所示(假设剪贴板包含 ANSI 字符串):

    if(!OpenClipboard(NULL)) return;
    HGLOBAL hglb = GetClipboardData(CF_TEXT);
    

    然后您必须使用GlobalLock 函数获得对数据的独占访问权。只有成功,您才能安全地访问数据

    if (hglb != NULL)
    {
        LPTSTR lptstr = GlobalLock(hglb); 
        if (lptstr != NULL) 
        {
            // Read the contents of lptstr which just a pointer to the string.
    
            // Don't forget to release the lock after you are done.
            GlobalUnlock(hglb);
        }
    }
    CloseClipboard(); 
    

    GlobalLock 的返回类型是一个void 指针。因此,根据您已经使用IsClipboardFormatAvailable 确定的数据格式,您必须将其转换为相应的类型。 Windows 数据类型记录在 here

    【讨论】:

    • IsClipboardFormatAvailable 对于设置 UI 很有用(例如,确定下拉菜单是否应提供粘贴选项),但使用它来确保 GetClipboardData 成功是粗略的。除非您已经打开剪贴板,否则数据可能会在您检查的时间和您实际调用 GetClipboardData 的时间之间发生变化。真正的解决方法是在尝试取消引用之前检查 GetClipboardData 的返回值。
    • @AdrianMcCarthy 是的。这就是为什么必须在GetClipboardData 之前调用OpenClipboard 以防止其他程序在调用CloseClipboard 之前修改剪贴板。在这种情况下,GetClipboardData 只会在剪贴板中的数据格式与指定格式不同或无法隐式转换为指定格式时才会失败。
    • 我还想补充一点,如果从 GetClipboardData 返回的句柄本身是指向数据第一个字节的指针(例如当数据格式为 CF_BITMAP 时),那么 GlobalLock 不应该叫做。然而,该指针应与NULL 进行检查。
    • 如果您调用 (1) IsClipboardFormatAvailable、(2) OpenClipboard 和 (3) GetClipboardData(如您的回答所示),那么在 1 和 2 之间存在竞争条件,这就是我尝试的重点制作。
    • 如果你仔细阅读我原来的评论,你会发现这正是我所说的。
    【解决方案2】:

    感谢您的回答,这比我在看到您的帖子之前想出的要有用得多。这是我笨拙且不安全的解决方案(它替换了我问题中的相应代码)。我将改用您更好的解决方案!

    HANDLE clip;
    std::string clip_text = "";
    // exit if clipboard not available
    if (!OpenClipboard(NULL))
            BailOut("Can't open clipboard"); 
    
    UINT textOK = 0;
    UINT currentFormat = 0;
        while(currentFormat = EnumClipboardFormats(currentFormat)) 
    {
        if(currentFormat == 1) 
            textOK = 1;
    }
    // only get text if text exists in clipboard
    if (textOK == 1)
    {
        clip = GetClipboardData(CF_TEXT);
        clip_text = (char*)clip;
    }
    CloseClipboard();
    

    由于各种原因(启动它的程序希望找到一个#CLIP.TXT 文件),我让我的代码生成一个空文件而不是退出。再次感谢!

    【讨论】:

    • 现在,恐怕我太初学者了,无法弄清楚如何解决这个问题:“GlobalLock的返回类型是一个空指针。所以取决于你拥有的数据格式已经使用 isClipboardFormatAvailable 确定,您必须将其转换为相应的类型。”如果我使用 CF_TEXT,我应该在哪里添加类型到代码中,以及哪种类型?我很抱歉第二次回来...
    • 查看答案中的最后一个代码片段。 CF_TEXT 对应的类型是LPTSTR。另请查看答案末尾提到的链接,以获取所有内置 Windows 类型的列表。
    • 再次,请原谅我的笨拙;你已经帮了大忙了。我正在使用您的确切代码片段,但 VC2010 编译器给了我以下错误消息:错误 C2440:'initializing':无法从 'LPVOID' 转换为 'LPTSTR' 1> 从 'void*' 转换为指向非的指针' void' 需要显式转换 这指的是以下行: LPTSTR lptstr = GlobalLock(hglb);所以我在这里有点迷路了。再次为我的无知浪费您的时间道歉!
    • 请注意,LPTSTR 对应于 ANSI 字符串的 LPSTR 或 Unicode 字符串的 LPWSTR。也许你应该直接使用这些类型。也就是说,在这种情况下使用LPSTR
    • 用 C++ 试试这个LPSTR lptstr = (LPSTR)GlobalLock(hglb);
    猜你喜欢
    • 2019-01-16
    • 1970-01-01
    • 2016-06-06
    • 2010-09-24
    • 2011-03-06
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多