October 7th, 2013

Printing the contents of the clipboard as text to stdout

The clip.exe takes its stdin and puts it on the clipboard. But how do you get it out? That’s today’s Little Program. (I guess we could call it clop.exe.)

#define UNICODE
#define _UNICODE
#define STRICT
#include <windows.h>
#include <stdio.h>
#include <tchar.h>
#include <strsafe.h>
void WriteToStdOut(const void *pvBuf, DWORD cbBuf)
{
 DWORD cbWritten;
 WriteFile(GetStdHandle(STD_OUTPUT_HANDLE), pvBuf, cbBuf,
           &cbWritten, nullptr);
}
int __cdecl _tmain(int argc, PTSTR *argv)
{
 if (OpenClipboard(nullptr)) {
  HANDLE h = GetClipboardData(CF_UNICODETEXT);
  if (h) {
   auto pwchText = static_cast<PCWSTR>(GlobalLock(h));
   if (pwchText) {
    SIZE_T cbMemory = GlobalSize(h);
    // arbitrary limit because I am lazy
    cbMemory = min(cbMemory, 0x10000000);
    size_t cbActual;
    if (SUCCEEDED(StringCbLengthW(pwchText, cbMemory,
                                  &cbActual))) {
     if (argc == 2 && _tcsicmp(argv[1], TEXT("/u")) == 0) {
      WriteToStdOut(pwchText, cbActual);
     } else {
      UINT cp = (argc == 2 &&
                _tcsicmp(argv[1], TEXT("/a")) == 0) ?
                     CP_ACP : CP_OEMCP;
      int cch = WideCharToMultiByte(cp, 0, pwchText,
               cbActual / 2, nullptr, 0, nullptr, nullptr);
      if (cch > 0) {
       auto psz = new(std::nothrow) char[cch];
       if (psz) {
        WideCharToMultiByte(cp, 0, pwchText, cbActual / 2,
                               psz, cch, nullptr, nullptr);
        WriteToStdOut(psz, cch);
        delete[] psz;
       }
      }
     }
    }
    GlobalUnlock(h);
   }
  }
  CloseClipboard();
 }
 return 0;
}

Okay, what do we have here?

We open the clipboard and try to get the Unicode text on it. We then look for the null terminator within the first 0x10000000 bytes. Why do I stop at 256MB? Because I’m lazy and this lets me avoid worrying about integer overflow. This is a Little Program, remember.

If you pass the /U command line switch, then the output is printed to stdout as the Unicode string itself.

If you pass the /A command line switch, then the output is converted to ANSI.

Otherwise the output is converted to the OEM code page.

Bonus chatter: You can get most of the same program above (no Unicode output) in much less code if you’re willing to use C#:

class Program {
  [System.STAThread]
  public static void Main(string[] args)
  {
    string text = System.Windows.Forms.Clipboard.GetText();
    if (args.Length == 1 && string.Compare(args[0], "/a", true) == 0) {
        System.Console.OutputEncoding = System.Text.Encoding.Default;
        System.Console.Write("changed encoding");
    }
   System.Console.Write(text);
  }
}

Or perl (ANSI output only):

use Win32::Clipboard;
print Win32::Clipboard()->GetText();
Topics
Code

Author

Raymond has been involved in the evolution of Windows for more than 30 years. In 2003, he began a Web site known as The Old New Thing which has grown in popularity far beyond his wildest imagination, a development which still gives him the heebie-jeebies. The Web site spawned a book, coincidentally also titled The Old New Thing (Addison Wesley 2007). He occasionally appears on the Windows Dev Docs Twitter account to tell stories which convey no useful information.

0 comments

Discussion are closed.

Feedback