This is a quick little C program which demonstrates how to feed data to the Windows clipboard. It also happens to be a useful little utility, if you use the Windows command line much. It works just fine with Cygwin.

See Reading from the Windows Clipboard for a symmetrical utility which pastes from the clipboard to standard output.

If you have any problems with this, please let me know. If you actually find it useful or informative, for God's sake let me know!


/*****************************************************************************
 *  ccopy.c
 *  Simple program to copy stdin to Win32 clipboard
 *
 *  You'll have to link this to user32.lib (or libuser32.a with Cygwin)
 *
 *  I've built this with MSVC++6 and EGCS 2.91.57
 *
 *  wharfinger  1/17/01
 *
 *  Version 0.1  1/17/01  Thanks to some comments by Eraser_, I took a second 
 *      look at streamtostr(), and noticed that it could be simplified.  I also 
 *      noticed a bug: If realloc() had failed, it would have tried to write 
 *      through a NULL pointer.  But it's fixed now.
 *****************************************************************************/

#include <stdio.h>
#include <windows.h>

/*  Arbitrary  */
#define BUFSIZE             1024

#define HASSLE_USER "\
 usage: Pipe text through it. The text will be copied to the clipboard.\n\
\n\
 This program was written by wharfinger@hotmail.com; no warranty blah\n\
 blah blah, public domain etc.\n"

/*  Copy string to clipboard  */
BOOL    strtocb( char *s, size_t size );

/*  Copy stream to string; allocate as needed  */
size_t  streamtostr( char **ps, size_t *size, FILE *stream );

/*---------------------------------------------------------------------------*/
int main( int argc, char *argv[] )
{
    /*  If somebody just types "ccopy", drop science on the poor dumb bastard.
     *  If there are any command line arguments at all, do the same.  Since 
     *  the only argument we could possibly have is "-?", anything else is 
     *  wrong, and we'd respond the same way in any case -- don't bother to
     *  check.
     */
    if ( argc > 1 || isatty( fileno( stdin ) ) ) {
        fprintf( stderr, HASSLE_USER );
    } else {
        char *  s    = NULL;
        size_t  size = 0;

        /*  Try to slurp stdin into a string.  */
        if ( streamtostr( &s, &size, stdin ) ) {
            /*  If that worked, try to stuff it in the clipboard.  */
            int rtn = strtocb( s, size );
            free( s );
            return ! rtn;
        }
    }

    return 1;
}


/*---------------------------------------------------------------------------*/
BOOL strtocb( char *s, size_t size )
{
    HGLOBAL glob    = NULL;
    char *  dest    = NULL;

    if ( ! s )
        return FALSE;
    else {
        OpenClipboard( NULL );  /*  Argument is HWND; we don't have one  */
        EmptyClipboard();       /*  Discard whatever was there.  */

        /*  "Global memory" is a relic of 16-bit Windows; all I did back then 
         *  was DOS, so I really don't grok.  In any case, if you use the 
         *  GMEM_MOVEABLE flag, you get a handle back from GlobalAlloc() 
         *  instead of a pointer.  You then call GlobalLock() to get an 
         *  pointer that you can write through.  SetClipboardData() expects a 
         *  handle, so we have to give it a handle; hence all the bullshit.
         */
        if (   NULL != ( glob = GlobalAlloc( GMEM_MOVEABLE, size + 1 ) )
            && NULL != ( dest = (char *)GlobalLock( glob ) ) )
        {
            strcpy( dest, s );
            GlobalUnlock( glob );

            /*  CF_TEXT is an integer constant which says what kind of data 
             *  we claim to be copying.  There are a dozen or so standard 
             *  ones, and you can define more with RegisterClipboardFormat().
             *  The clipboard can hold different data for each of an arbitrary
             *  number of different "formats", all at the same time.
             */
            SetClipboardData( CF_TEXT, glob );
        }

        /*  We're done with it.  When our process exits, it'll be released 
         *  anyway, and you and I both happen to know that we'll be doing that
         *  really soon.  So we're just playing nice not for any practical 
         *  reason, but merely not to make Jesus cry.  A normal Windows 
         *  program will keep on executing for an arbitrarily long time, of 
         *  course, and in that case it really would matter.
         *
         *  We never free glob because that belongs to the system now.  After
         *  we exit, it'll still persist.  That's the whole point.
         */
        CloseClipboard();

        return dest != NULL;
    }
}


/*---------------------------------------------------------------------------*/
size_t streamtostr( char **ps, size_t *psize, FILE *stream )
{
    int     c;
    size_t  copied = 0;     /*  Bytes copied since last realloc()  */

    /*  Aw, what the hell, start fresh.  */
    if ( *ps != NULL )
        free( *ps );

    /*  Try to allocate  */
    if ( ! ( (*ps) = (char *)malloc( BUFSIZE ) ) )
        return *psize = 0;

    if ( stream ) {
        /*  Loop, copy, etc.  */
        for ( *psize = 0; ( c = fgetc( stream ) ) != EOF; /**/ ) {
            /*  If we've used up all the allocated memory and we're still
             *  going, allocate some more.  If that fucks up, we're doomed.
             */
            if ( copied == BUFSIZE - 1 ) {
                copied = 0;
                if ( ! ( *ps = (char *)realloc( *ps, (*psize) + BUFSIZE ) ) )
                    return *psize = 0;
            }
            (*ps)[ (*psize)++ ] = c;
            copied++;
        }

        /*  Play nice, now.  */
        if ( *ps )
            (*ps)[ *psize ] = '\0';
    }

    return *psize;
}


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

Log in or register to write something here or to contact authors.