How to copy string to clipboard in C? - c

The SetClipboardData function requires a HANDLE reference; I'm having trouble converting my string for use in the function.
Here is my code:
char* output = "Test";
HLOCAL hMem = LocalAlloc( LHND,1024);
char* cptr = (char*) LocalLock(hMem);
memcpy( cptr, output, 500 );
SetClipboardData(CF_TEXT, hMem);
LocalUnlock( hMem );
LocalFree( hMem );
CloseClipboard();
What am I doing wrong here and what's the proper way to do it?
Thanks.

Read the MSDN documentation for the SetClipboardData function. It appears you are missing a few steps and releasing the memory prematurely. First of all, you must call
OpenClipboard before you can use SetClipboardData. Secondly, the system takes ownership of the memory passed to the clipboard and it must be unlocked. Also, the memory must be movable, which requires the GMEM_MOVEABLE flag as used with GlobalAlloc (instead of LocalAlloc).
const char* output = "Test";
const size_t len = strlen(output) + 1;
HGLOBAL hMem = GlobalAlloc(GMEM_MOVEABLE, len);
memcpy(GlobalLock(hMem), output, len);
GlobalUnlock(hMem);
OpenClipboard(0);
EmptyClipboard();
SetClipboardData(CF_TEXT, hMem);
CloseClipboard();

I wrote an open source command line tool to do this in Windows:
http://coffeeghost.net/2008/07/25/ccwdexe-copy-current-working-directory-command/
ccwd.exe copies the current working directory to the clipboard. It's handy when I'm several levels deep into a source repo and need to copy the path.
Here's the complete source:
#include "stdafx.h"
#include "windows.h"
#include "string.h"
#include <direct.h>
int main()
{
LPWSTR cwdBuffer;
// Get the current working directory:
if( (cwdBuffer = _wgetcwd( NULL, 0 )) == NULL )
return 1;
DWORD len = wcslen(cwdBuffer);
HGLOBAL hdst;
LPWSTR dst;
// Allocate string for cwd
hdst = GlobalAlloc(GMEM_MOVEABLE | GMEM_DDESHARE, (len + 1) * sizeof(WCHAR));
dst = (LPWSTR)GlobalLock(hdst);
memcpy(dst, cwdBuffer, len * sizeof(WCHAR));
dst[len] = 0;
GlobalUnlock(hdst);
// Set clipboard data
if (!OpenClipboard(NULL)) return GetLastError();
EmptyClipboard();
if (!SetClipboardData(CF_UNICODETEXT, hdst)) return GetLastError();
CloseClipboard();
free(cwdBuffer);
return 0;
}

Take a look at Microsoft's Documentation on using the clipboard. This requires that your using the WinAPI, but this shouldn't be a problem since your on Windows. Note that programming the Windows API is never simple unless you use a very high-level language.

Related

Is concatenating arbitrary number of strings with nested function calls in C undefined behavior?

I have an application that builds file path names through a series of string concatenations using pieces of text to create a complete file path name.
The question is whether an approach to handle concatenating a small but arbitrary number of strings of text together depends on Undefined Behavior for success.
Is the order of evaluation of a series of nested functions guaranteed or not?
I found this question Nested function calls order of evaluation however it seems to be more about multiple functions in the argument list rather than a sequence of nesting functions.
Please excuse the names in the following code samples. It is congruent with the rest of the source code and I am testing things out a bit first.
My first cut on the need to concatenate several strings was a function that looked like the following which would concatenate up to three text strings into a single string.
typedef wchar_t TCHAR;
TCHAR *RflCatFilePath(TCHAR *tszDest, int nDestLen, TCHAR *tszPath, TCHAR *tszPath2, TCHAR *tszFileName)
{
if (tszDest && nDestLen > 0) {
TCHAR *pDest = tszDest;
TCHAR *pLast = tszDest;
*pDest = 0; // ensure empty string if no path data provided.
if (tszPath) for (pDest = pLast; nDestLen > 0 && (*pDest++ = *tszPath++); nDestLen--) pLast = pDest;
if (tszPath2) for (pDest = pLast; nDestLen > 0 && (*pDest++ = *tszPath2++); nDestLen--) pLast = pDest;
if (tszFileName) for (pDest = pLast; nDestLen > 0 && (*pDest++ = *tszFileName++); nDestLen--) pLast = pDest;
}
return tszDest;
}
Then I ran into a case where I had four pieces of text to put together.
Thinking through this it seemed that most probably there would also be a case for five that would be uncovered shortly so I wondered if there was a different way for an arbitrary number of strings.
What I came up with is two functions as follows.
typedef wchar_t TCHAR;
typedef struct {
TCHAR *pDest;
TCHAR *pLast;
int destLen;
} RflCatStruct;
RflCatStruct RflCatFilePathX(const TCHAR *pPath, RflCatStruct x)
{
TCHAR *pDest = x.pLast;
if (pDest && pPath) for ( ; x.destLen > 0 && (*pDest++ = *pPath++); x.destLen--) x.pLast = pDest;
return x;
}
RflCatStruct RflCatFilePathY(TCHAR *buffDest, int nLen, const TCHAR *pPath)
{
RflCatStruct x = { 0 };
TCHAR *pDest = x.pDest = buffDest;
x.pLast = buffDest;
x.destLen = nLen;
if (buffDest && nLen > 0) { // ensure there is room for at least one character.
*pDest = 0; // ensure empty string if no path data provided.
if (pPath) for (pDest = x.pLast; x.destLen > 0 && (*pDest++ = *pPath++); x.destLen--) x.pLast = pDest;
}
return x;
}
Examples of using these two functions is as follows. This code with the two functions appears to work fine with Visual Studio 2013.
TCHAR buffDest[512] = { 0 };
TCHAR *pPath = L"C:\\flashdisk\\ncr\\database";
TCHAR *pPath2 = L"\\";
TCHAR *pFilename = L"filename.ext";
RflCatFilePathX(pFilename, RflCatFilePathX(pPath2, RflCatFilePathY(buffDest, 512, pPath)));
printf("dest t = \"%S\"\n", buffDest);
printf("dest t = \"%S\"\n", RflCatFilePathX(pFilename, RflCatFilePathX(pPath2, RflCatFilePathY(buffDest, 512, pFilename))).pDest);
RflCatStruct dStr = RflCatFilePathX(pPath2, RflCatFilePathY(buffDest, 512, pPath));
// other stuff then
printf("dest t = \"%S\"\n", RflCatFilePathX(pFilename, dStr).pDest);
Arguments to a function call are completely evaluated before the function is invoked. So the calls to RflCatFilePath* will be evaluated in the expected order. (This is guaranteed by ยง6.5.2.2/10: "There is a sequence point after the evaluations of the function designator and the actual arguments but before the actual call.")
As indicated in a comment, the snprintf function is likely to be a better choice for this problem. (asprintf would be even better, and there is a freely available shim for it which works on Windows.) The only problem with snprintf is that you may have to call it twice. It always returns the number of bytes which would have been stored in the buffer had there been enough space, so if the return value is not less than the size of the buffer, you will need to allocate a larger buffer (whose size you now know) and call snprintf again.
asprintf does that for you, but it is a BSD/Gnu extension to the standard library.
In the case of concatenating filepaths, there is a maximum string length supported by the operating system/file system, and you should be able to find out what it is (although it might require OS-specific calls on non-Posix systems). So it might well be reasonable to simply return an error indication if the concatenation does not fit into a 512-byte buffer.
Just for fun, I include a recursive varargs concatenator:
#include <stdarg.h>
#include <stdlib.h>
#include <string.h>
static char* concat_helper(size_t accum, char* chunk, va_list ap) {
if (chunk) {
size_t chunklen = strlen(chunk);
char* next_chunk = va_arg(ap, char*);
char* retval = concat_helper(accum + chunklen, next_chunk, ap);
memcpy(retval + accum, chunk, chunklen);
return retval;
} else {
char* retval = malloc(accum + 1);
retval[accum] = 0;
return retval;
}
}
char* concat_list(char* chunk, ...) {
va_list ap;
va_start(ap, chunk);
char* retval = concat_helper(0, chunk, ap);
va_end(ap);
return retval;
}
Since concat_list is a varargs function, you need to supply (char*)NULL at the end of the arguments. On the other hand, you don't need to repeat the function name for each new argument. So an example call might be:
concat_list(pPath, pPath2, pFilename, (char*)0);
(I suppose you need a wchar_t* version but the changes should be obvious. Watch out for the malloc.) For production purposes, the recursion should probably be replaced by an iterative version which traverses the argument list twice (see va_copy) but I've always been fond of the "there-and-back" recursion pattern.

How to return a string from GetOpenFileNameA

This is a function to open a file dialog in Windows and return a string with the file name:
#include <windows.h>
#include <commdlg.h>
#include <string.h>
char* openFileDlg(char FileTypes[]);
char* openFileDlg(char FileTypes[]){
OPENFILENAME ofn;
char szFile[260];
HWND hwnd;
HANDLE hf;
ZeroMemory(&ofn, sizeof(ofn));
ofn.lStructSize = sizeof(ofn);
ofn.hwndOwner = hwnd;
ofn.lpstrFile = szFile;
ofn.lpstrFile[0] = '\0';
ofn.nMaxFile = sizeof(szFile);
strcpy(ofn.lpstrFilter,FileTypes);
ofn.nFilterIndex = 1;
ofn.lpstrFileTitle = NULL;
ofn.nMaxFileTitle = 0;
ofn.lpstrInitialDir = NULL;
ofn.Flags = OFN_PATHMUSTEXIST | OFN_FILEMUSTEXIST;
if(GetOpenFileNameA(&ofn)){
char *toReturn;
sprintf(toReturn,"%s",ofn.lpstrFile);
return toReturn;
}
else{
return NULL;
}
}
When I call this function and open a file, the process ends and returns value 3 (which means there is an error). How can I do so that this function returns a string with the path of the selected file?
Edit: I've changed my code to this and it still doesn't work:
#include <windows.h>
#include <commdlg.h>
#include <string.h>
void openFileDlg(char *toReturn[],char FileTypes[]);
void openFileDlg(char *toReturn[],char FileTypes[]){
OPENFILENAME ofn;
/*
Code for the settings of the GetOpenFileNameA, irrelevant in this question.
If you really need to know what's here, look at the code above.
*/
if(GetOpenFileNameA(&ofn)){
strcpy(*toReturn,ofn.lpstrFile);
}
else{
sprintf(*toReturn,"");
}
}
I should also say that if I press the Cancel button in the open file dialog box instead of selecting a file, it works fine. After some tests, I've noticed that it's the line strcpy(*toReturn,ofn.lpstrFile); that causes the error.
The pointer variable toReturn doesn't point anywhere, using it in any way without initializing it (i.e. making it point somewhere valid and big enough) will lead to undefined behavior
You have two solutions really:
Allocate memory dynamically and return a pointer to that. This of course requires the caller to free the memory when done with it.
Have the function take another two arguments: A pointer to a buffer and the length of the buffer. Then copy the string into that buffer, and return a boolean "true" or "false" success/failure status.
I recommend solution number two.
On an unrelated note, there's no need to use the expensive sprintf function in your case, a simple strcpy (or strncpy if you go with the second solution) will do.
You also have to remember in both cases that strings in C have an actual length of one more than e.g. strlen reports, for the terminating '\0' character.
In general, if you want to return a string in C, I'd use one of the following methods:
1) pass in a string buffer for the method to write to:
int openFileDlg(char FileTypes[], char* toReturn, int bufLen) {
/* ... */
snprintf(toReturn, bufLen, /* what you want to print */);
return ERROR; // status-code
}
/* ... */
char errorBuf[80];
int result;
result = openFileDlg(..., errorBuf, sizeof(errorBuf));
2) allocate memory, expect caller to free it:
char* openFileDlg(char FileTypes[]) {
/* ... */
char *toReturn = malloc(/* big enough */);
sprintf(toReturn, /* what you want to print */);
return toReturn;
}
/* ... */
char* error = openFileDlg(...);
if (error) {
/* ... */
free(error);
}
personally, I'd prefer (1) because it's safer. Option (2) is nicer to the API of the function, but has a risk of memory leaks if you forget to free the returned buffer. In a bigger project (especially with multiple people working on it) this is a very real risk.
(I realise this is pretty much the same as Joachim's answer, but his went up as I was writing mine)
You did not allocate memory for your return value. If you know the length of ofn.lpstrFile you could do this:
char *toReturn = malloc( (sizeOfLpstrFile + 1) * sizeof(char)) ;
sprintf(toReturn,"%s",ofn.lpstrFile);
return toReturn;
Still I consider this a bad idea because the calling function will have to free the memory which is not obvious from the interface.

How do I embed and use resource files in a C program using VS11?

This is my first attempt at using a resource file. I have seen a lot of answers that apply to C# but not C. Any suggestions?
Assuming you mean the method used by Sysinternals and others to carry the drivers or needed DLLs (or even the x64 version of the program itself) in the resource section of a program (e.g. Sysinternals' Process Explorer), using Microsoft Visual C you can use this code:
BOOL ExtractResTo(HINSTANCE Instance, LPCTSTR BinResName, LPCTSTR NewPath, LPCTSTR ResType)
{
BOOL bResult = FALSE;
HRSRC hRsrc;
if(hRsrc = FindResource(HMODULE(Instance), BinResName, ResType))
{
HGLOBAL hGlob
if(HGLOBAL hGlob = LoadResource(Instance, hRsrc))
{
DWORD dwResSize = SizeofResource(Instance, hRsrc);
HANDLE hFileWrite = CreateFile(NewPath, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_ARCHIVE, 0);
if(hFileWrite != INVALID_HANDLE_VALUE)
__try
{
DWORD dwSizeWritten = 0;
bResult = (WriteFile(hFileWrite, LockResource(hGlob), dwResSize, &dwSizeWritten, NULL) && (dwSizeWritten == dwResSize));
}
__finally
{
CloseHandle(hFileWrite);
}
}
}
return bResult;
}
This saves the given resource (BinResName) of resource type (ResType) from the module (e.g. a DLL) Instance to file NewPath. Obviously if your C doesn't understand __try and __finally you'll have to adjust the code accordingly.
Taken from here (in SIDT.rar) and adjusted for C. The code is under the liberal BSD license according to the website.
Now if you wanted to get the pointer to the data (ppRes) and its size (pwdResSize):
BOOL GetResourcePointer(HINSTANCE Instance, LPCTSTR ResName, LPCTSTR ResType, LPVOID* ppRes, DWORD* pdwResSize)
{
// Check the pointers to which we want to write
if(ppRes && pdwResSize)
{
HRSRC hRsrc;
// Find the resource ResName of type ResType in the DLL/EXE described by Instance
if(hRsrc = FindResource((HMODULE)Instance, ResName, ResType))
{
HGLOBAL hGlob;
// Make sure it's in memory ...
if(hGlob = LoadResource(Instance, hRsrc))
{
// Now lock it to get a pointer
*ppRes = LockResource(hGlob);
// Also retrieve the size of the resource
*pdwResSize = SizeofResource(Instance, hRsrc);
// Return TRUE only if both succeeded
return (*ppRes && *pdwResSize);
}
}
}
// Failure means don't use the values in *ppRes and *pdwResSize
return FALSE;
}
Call like this:
LPVOID pResource;
DWORD pResourceSize;
if(GetResourcePointer(hInstance, _T("somename"), _T("sometype"), &pResource, &pResourceSize))
{
// use pResource and pResourceSize
// e.g. store into a string buffer or whatever you want to do with it ...
}
Note, this also works for resources with integer IDs. You can detect these by using the macro IS_INTRESOURCE.
The resource script, e.g. myresources.rc, itself is trivial:
#include <winnt.rh>
"somename" "sometype" "path\to\file\to\embed"
RCDATA instead of sometype is a reasonable choice
#include <winnt.rh> // for RCDATA to be known to rc.exe
"somename" RCDATA "path\to\file\to\embed"
... in which case you would adjust the above call to be:
GetResourcePointer(hInstance, _T("somename"), RT_RCDATA, &pResource, &pResourceSize)
Complete example making use of GetResourcePointer above. Let's say you have a pointer variable char* buf and you know your resource is actual text, then you need to make sure that it is zero-terminated when used as a string, that's all.
Resource script:
#include <winnt.rh>
"test" RCDATA "Test.txt"
The code accessing it
char* buf = NULL;
LPVOID pResource;
DWORD pResourceSize;
if(GetResourcePointer(hInstance, _T("test"), RT_RCDATA, &pResource, &pResourceSize))
{
if(buf = calloc(pResourceSize+1, sizeof(char)))
{
memcpy(buf, pResource, pResourceSize);
// Now use buf
free(buf);
}
}
Unless, of course you meant to simply link a .res file which works by passing it to the linker command line.

tmpfile() on windows 7 x64

Running the following code on Windows 7 x64
#include <stdio.h>
#include <errno.h>
int main() {
int i;
FILE *tmp;
for (i = 0; i < 10000; i++) {
errno = 0;
if(!(tmp = tmpfile())) printf("Fail %d, err %d\n", i, errno);
fclose(tmp);
}
return 0;
}
Gives errno 13 (Permission denied), on the 637th and 1004th call, it works fine on XP (haven't tried 7 x86). Am I missing something or is this a bug?
I've got similar problem on Windows 8 - tmpfile() was causing win32 ERROR_ACCESS_DENIED error code - and yes, if you run application with administrator privileges - then it works fine.
I guess problem is mentioned over here:
https://lists.gnu.org/archive/html/bug-gnulib/2007-02/msg00162.html
Under Windows, the tmpfile function is defined to always create
its temporary file in the root directory. Most users don't have
permission to do that, so it will often fail.
I would suspect that this is kinda incomplete windows port issue - so this should be an error reported to Microsoft. (Why to code tmpfile function if it's useless ?)
But who have time to fight with Microsoft wind mills ?! :-)
I've coded similar implementation using GetTempPathW / GetModuleFileNameW / _wfopen. Code where I've encountered this problem came from libjpeg - I'm attaching whole source code here, but you can pick up code from jpeg_open_backing_store.
jmemwin.cpp:
//
// Windows port for jpeg lib functions.
//
#define JPEG_INTERNALS
#include <Windows.h> // GetTempFileName
#undef FAR // Will be redefined - disable warning
#include "jinclude.h"
#include "jpeglib.h"
extern "C" {
#include "jmemsys.h" // jpeg_ api interface.
//
// Memory allocation and freeing are controlled by the regular library routines malloc() and free().
//
GLOBAL(void *) jpeg_get_small (j_common_ptr cinfo, size_t sizeofobject)
{
return (void *) malloc(sizeofobject);
}
GLOBAL(void) jpeg_free_small (j_common_ptr cinfo, void * object, size_t sizeofobject)
{
free(object);
}
/*
* "Large" objects are treated the same as "small" ones.
* NB: although we include FAR keywords in the routine declarations,
* this file won't actually work in 80x86 small/medium model; at least,
* you probably won't be able to process useful-size images in only 64KB.
*/
GLOBAL(void FAR *) jpeg_get_large (j_common_ptr cinfo, size_t sizeofobject)
{
return (void FAR *) malloc(sizeofobject);
}
GLOBAL(void) jpeg_free_large (j_common_ptr cinfo, void FAR * object, size_t sizeofobject)
{
free(object);
}
//
// Used only by command line applications, not by static library compilation
//
#ifndef DEFAULT_MAX_MEM /* so can override from makefile */
#define DEFAULT_MAX_MEM 1000000L /* default: one megabyte */
#endif
GLOBAL(long) jpeg_mem_available (j_common_ptr cinfo, long min_bytes_needed, long max_bytes_needed, long already_allocated)
{
// jmemansi.c's jpeg_mem_available implementation was insufficient for some of .jpg loads.
MEMORYSTATUSEX status = { 0 };
status.dwLength = sizeof(status);
GlobalMemoryStatusEx(&status);
if( status.ullAvailPhys > LONG_MAX )
// Normally goes here since new PC's have more than 4 Gb of ram.
return LONG_MAX;
return (long) status.ullAvailPhys;
}
/*
Backing store (temporary file) management.
Backing store objects are only used when the value returned by
jpeg_mem_available is less than the total space needed. You can dispense
with these routines if you have plenty of virtual memory; see jmemnobs.c.
*/
METHODDEF(void) read_backing_store (j_common_ptr cinfo, backing_store_ptr info, void FAR * buffer_address, long file_offset, long byte_count)
{
if (fseek(info->temp_file, file_offset, SEEK_SET))
ERREXIT(cinfo, JERR_TFILE_SEEK);
size_t readed = fread( buffer_address, 1, byte_count, info->temp_file);
if (readed != (size_t) byte_count)
ERREXIT(cinfo, JERR_TFILE_READ);
}
METHODDEF(void)
write_backing_store (j_common_ptr cinfo, backing_store_ptr info, void FAR * buffer_address, long file_offset, long byte_count)
{
if (fseek(info->temp_file, file_offset, SEEK_SET))
ERREXIT(cinfo, JERR_TFILE_SEEK);
if (JFWRITE(info->temp_file, buffer_address, byte_count) != (size_t) byte_count)
ERREXIT(cinfo, JERR_TFILE_WRITE);
// E.g. if you need to debug writes.
//if( fflush(info->temp_file) != 0 )
// ERREXIT(cinfo, JERR_TFILE_WRITE);
}
METHODDEF(void)
close_backing_store (j_common_ptr cinfo, backing_store_ptr info)
{
fclose(info->temp_file);
// File is deleted using 'D' flag on open.
}
static HMODULE DllHandle()
{
MEMORY_BASIC_INFORMATION info;
VirtualQuery(DllHandle, &info, sizeof(MEMORY_BASIC_INFORMATION));
return (HMODULE)info.AllocationBase;
}
GLOBAL(void) jpeg_open_backing_store(j_common_ptr cinfo, backing_store_ptr info, long total_bytes_needed)
{
// Generate unique filename.
wchar_t path[ MAX_PATH ] = { 0 };
wchar_t dllPath[ MAX_PATH ] = { 0 };
GetTempPathW( MAX_PATH, path );
// Based on .exe or .dll filename
GetModuleFileNameW( DllHandle(), dllPath, MAX_PATH );
wchar_t* p = wcsrchr( dllPath, L'\\');
wchar_t* ext = wcsrchr( p + 1, L'.');
if( ext ) *ext = 0;
wchar_t* outFile = path + wcslen(path);
static int iTempFileId = 1;
// Based on process id (so processes would not fight with each other)
// Based on some process global id.
wsprintfW(outFile, L"%s_%d_%d.tmp",p + 1, GetCurrentProcessId(), iTempFileId++ );
// 'D' - temporary file.
if ((info->temp_file = _wfopen(path, L"w+bD") ) == NULL)
ERREXITS(cinfo, JERR_TFILE_CREATE, "");
info->read_backing_store = read_backing_store;
info->write_backing_store = write_backing_store;
info->close_backing_store = close_backing_store;
} //jpeg_open_backing_store
/*
* These routines take care of any system-dependent initialization and
* cleanup required.
*/
GLOBAL(long)
jpeg_mem_init (j_common_ptr cinfo)
{
return DEFAULT_MAX_MEM; /* default for max_memory_to_use */
}
GLOBAL(void)
jpeg_mem_term (j_common_ptr cinfo)
{
/* no work */
}
}
I'm intentionally ignoring errors from some of functions - have you ever seen GetTempPathW or GetModuleFileNameW failing ?
A bit of a refresher from the manpage of on tmpfile(), which returns a FILE*:
The file will be automatically deleted when it is closed or the program terminates.
My verdict for this issue: Deleting a file on Windows is weird.
When you delete a file on Windows, for as long as something holds a handle, you can't call CreateFile on something with the same absolute path, otherwise it will fail with the NT error code STATUS_DELETE_PENDING, which gets mapped to the Win32 code ERROR_ACCESS_DENIED. This is probably where EPERM in errno is coming from. You can confirm this with a tool like Sysinternals Process Monitor.
My guess is that CRT somehow wound up creating a file that has the same name as something it's used before. I've sometimes witnessed that deleting files on Windows can appear asynchronous because some other process (sometimes even an antivirus product, in reaction to the fact that you've just closed a delete-on-close handle...) will leave a handle open to the file, so for some timing window you will see a visible file that you can't get a handle to without hitting delete pending/access denied. Or, it could be that tmpfile has simply chosen a filename that some other process is working on.
To avoid this sort of thing you might want to consider another mechanism for temp files... For example a function like Win32 GetTempFileName allows you to create your own prefix which might make a collision less likely. That function appears to resolve race conditions by retrying if a create fails with "already exists", so be careful about deleting the temp filenames that thing generates - deleting the file cancels your rights to use it concurrently with other processes/threads.

How to copy bitmap to clipboard using the win32 API?

How do I copy a buffer that would save to a ".BMP" file to the clipboard using the win32 API? I.e., I have a raw buffer of a Windows V3 Bitmap (including the header) that I can literally write() to a file and will result in a valid .BMP file, but I want to copy it to the clipboard instead.
On OS X, in plain C, the code would look something like this (which works as intended):
#include <ApplicationServices/ApplicationServices.h>
int copyBitmapToClipboard(char *bitmapBuffer, size_t buflen)
{
PasteboardRef clipboard;
CFDataRef data;
if (PasteboardCreate(kPasteboardClipboard, &clipboard) != noErr) {
return PASTE_OPEN_ERROR;
}
if (PasteboardClear(clipboard) != noErr) return PASTE_CLEAR_ERROR;
data = CFDataCreateWithBytesNoCopy(kCFAllocatorDefault, bitmapBuffer, buflen,
kCFAllocatorNull);
if (data == NULL) {
CFRelease(clipboard);
return PASTE_DATA_ERROR;
}
if (PasteboardPutItemFlavor(clipboard, 42, kUTTypeBMP, data, 0) != noErr) {
CFRelease(data);
CFRelease(clipboard);
return PASTE_PASTE_ERROR;
}
CFRelease(data);
CFRelease(clipboard);
return PASTE_WE_DID_IT_YAY;
}
I am unsure how to accomplish this with the win32 API. This is as far as I've gotten, but it seems to silently fail (that is, the function returns with a successful error code, but when attempting to paste, the menu item is disabled).
#include <windows/windows.h>
int copyBitmapToClipboard(char *bitmapBuffer, size_t buflen)
{
if (!OpenClipboard(NULL)) return PASTE_OPEN_ERROR;
if (!EmptyClipboard()) return PASTE_CLEAR_ERROR;
if (SetClipboardData(CF_DSPBITMAP, bitmapBuffer) == NULL) {
CloseClipboard();
return PASTE_PASTE_ERROR;
}
CloseClipboard();
return PASTE_WE_DID_IT_YAY;
}
Could anyone provide some insight as to how to fix this?
Edit
Per Aaron and martinr's suggestions, I've now modified the code to the following:
#include <windows/windows.h>
int copyBitmapToClipboard(char *bitmapBuffer, size_t buflen)
{
HGLOBAL hResult;
if (!OpenClipboard(NULL)) return PASTE_OPEN_ERROR;
if (!EmptyClipboard()) return PASTE_CLEAR_ERROR;
hResult = GlobalAlloc(GMEM_MOVEABLE, buflen);
if (hResult == NULL) return PASTE_DATA_ERROR;
memcpy(GlobalLock(hResult), bitmapBuffer, buflen);
GlobalUnlock(hResult);
if (SetClipboardData(CF_DSPBITMAP, hResult) == NULL) {
CloseClipboard();
return PASTE_PASTE_ERROR;
}
CloseClipboard();
return PASTE_WE_DID_IT_YAY;
}
But it still has the same result. What am I doing wrong?
Final Edit
The working code:
#include <windows/windows.h>
int copyBitmapToClipboard(char *bitmapBuffer, size_t buflen)
{
HGLOBAL hResult;
if (!OpenClipboard(NULL)) return PASTE_OPEN_ERROR;
if (!EmptyClipboard()) return PASTE_CLEAR_ERROR;
buflen -= sizeof(BITMAPFILEHEADER);
hResult = GlobalAlloc(GMEM_MOVEABLE, buflen);
if (hResult == NULL) return PASTE_DATA_ERROR;
memcpy(GlobalLock(hResult), bitmapBuffer + sizeof(BITMAPFILEHEADER), buflen);
GlobalUnlock(hResult);
if (SetClipboardData(CF_DIB, hResult) == NULL) {
CloseClipboard();
return PASTE_PASTE_ERROR;
}
CloseClipboard();
GlobalFree(hResult);
return PASTE_WE_DID_IT_YAY;
}
Thanks, martinr!
I think the hMem needs to be a return value from LocalAlloc, an HMEMORY rather than a pointer.
EDIT
Sorry yes, GlobalAlloc with GMEM_MOVEABLE is required, not LocalAlloc.
EDIT
I suggest you use CF_DIB clipboard data format type.
DIB is the same as BMP except it is without the BITMAPFILEHEADER, so copy the source bytes except for the first sizeof(BITMAPFILEHEADER) bytes.
EDIT
From OpenClipboard() documentation (http://msdn.microsoft.com/en-us/library/ms649048(VS.85).aspx):
"If an application calls OpenClipboard with hwnd set to NULL, EmptyClipboard sets the clipboard owner to NULL; this causes SetClipboardData to fail."
You need to set up a window; even if you're not doing WM_RENDERFORMAT type stuff.
I found this a lot with Windows APIs. I haven't used the Clipboard APIs per se but with other APIs I usually found that creating a hidden window and passing that handle to the relevant API was enough to keep it quiet. There's usually some notes on issues to do with this if you're creating a window from a DLL rather than an EXE; read whatever is the latest Microsoft word about DLLs, message loops and window creation.
As regardsBITMAPINFO, that's not the start of the stream the clipboard wants to see :- the buffer you give to SetClipboardData should start right after where the BITMAPFILEHEADER stops.
You need to pass a HANDLE to SetClipboard() (that is - memory allocated with GlobalAlloc()) rather than passing a straight pointer to your bitmap.
Echo Aaron and martinr. Example here, crucial section:
// Allocate a global memory object for the text.
hglbCopy = GlobalAlloc(GMEM_MOVEABLE,
(cch + 1) * sizeof(TCHAR));
if (hglbCopy == NULL)
{
CloseClipboard();
return FALSE;
}
// Lock the handle and copy the text to the buffer.
lptstrCopy = GlobalLock(hglbCopy);
memcpy(lptstrCopy, &pbox->atchLabel[ich1],
cch * sizeof(TCHAR));
lptstrCopy[cch] = (TCHAR) 0; // null character
GlobalUnlock(hglbCopy);
// Place the handle on the clipboard.
SetClipboardData(CF_TEXT, hglbCopy);

Resources