Search code examples
c++textclipboardclipboarddata

C++ (VC): How to detect whether Windows clipboard contains CF_TEXT data?


I'm a complete beginner at C++, but I've managed to modify other people's code and finally put together a program that writes the Windows clipboard to a text file, with the text encoded in the codepage specified as a command line parameter for the program (for example, MyProg.exe 437) will write the text in codepage 437. If no codepage is specified, the program writes a text file in the standard Windows codepage 1252.

The trouble is that the program crashes if the clipboard contains (for example) a shortcut or file, not text. Can someone tell me how I can exit gracefully if there is no CF_TEXT data in the clipboard? (Or have I completely misunderstood the problem?)

I've searched for an answer with no success. Here is my VC2010 code (which I don't entirely understand, but it seems to work, when the clipboard contains CF_TEXT; it doesn't work with CF_UNICODETEXT, by the way - the output is simply a few bytes.):

#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;
} 

I'll be grateful for any suggestions on how to fix this last problem.


Solution

  • Before you get data from the clipboard using the GetClipboardData function, you have to check that the clipboard contains data in the format that you expect. This can be done using the IsClipboardFormatAvailable function as follows:

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

    Then you have to open the clipboard and obtain a handle to the clipboard buffer as follows (assuming the clipboard contains ANSI string):

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

    Then you have to obtain exclusive access to the data using the GlobalLock function . Only on success you can safely access the data

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

    The return type of GlobalLock is a void pointer. So depending on the data format which you have already determined using IsClipboardFormatAvailable, you have to cast it to the corresponding type. Windows data types are documented here.