|
|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Announcements
Chapters
Services
Feature Zones
|
IntroductionIn this first instalment of a four-part series of articles on programmatically transferring data to and from the Windows Clipboard, I'll explain the basic steps of using the Clipboard using the Clipboard API. After I've gone over the basics steps, which are used no matter what data format is on the Clipboard, I'll present a demo application that illustrates how to programmatically transfer simple (ANSI) text to and from the Clipboard. Using the Windows Clipboard APIThere are actually two distinct mechanisms for interfacing to the Clipboard. The first
involves using the Windows Clipboard API and the second uses OLE. Since the Clipboard API
is by far the most common method used, most of the demos in this little series will use this
technique. If you're familiar with the Windows API - especially the means by which memory
is allocated via the
Allocating Memory for Your DataThe verbiage "placing data on the Clipboard" is really a misnomer as the Clipboard is not
some sort of global data buffer. Actually, the Clipboard is little more than a handle to a
data buffer that is created and maintained by the application that is making this data available
for other applications. Since the data must be accessible from all processes, the Windows API
functions, As a quick refresher, here is the syntax for these two functions and how they work: The HGLOBAL GlobalAlloc(UINT uFlags, SIZE_T dwBytes) The Table 1 -
Note - Since specifying a value of The parameter The second function that you need to call to allocate the memory for your data is the Windows API function, LPVOID GlobalLock(HGLOBAL hMem) This function is very straight-forward and takes as its only parameter a handle returned from the Copy the Data into the Global MemoryOnce you've allocated the global memory and have obtained a pointer to it, you can copy the desired data into that global memory buffer. How you do this will depend on what type of data you're transferring so I'll go into more detail about this step in the three demos that follow this article. Unlock the Global MemoryThis is a very important, yet often overlooked step in the process. According to the Windows documentation once you've allocated the memory and inserted the data to be made public to other processes, you are not to touch this memory again. The reason is simple. Once you've made the Clipboard mechanism aware of the new data (next step), Windows will now have control over that memory and any further tampering with it on your end might very well invalidate the integrity of the data. To unlock the global memory block, make a call to the BOOL GlobalUnlock(HGLOBAL hMem) Open the ClipboardThe BOOL OpenClipboard(HWND hWndNewOwner) The Empty the ClipboardThis step is needed to initialized the Clipboard and is accomplished via the BOOL EmptyClipboard() Note that you do not need to call a function to determine if any data exists prior to calling this function as this function will only return a nonzero value if the function fails. If it succeeds or if the Clipboard doesn't contain any data, the function returns zero (representing success). Set the Clipboard DataFinally, at this step, you set the Clipboard data. As mentioned earlier, the Clipboard really doesn't
contain data, but simply maintains a global handle obtained (and populated) by your application. The function
to perform this step is the HANDLE SetClipboardData(UINT uFormat, HANDLE hMem) The value you specify for the Table 2 - Standard Clipboard Formats
Close the ClipboardWhen an application has finished examining or modifying the Clipboard data, the BOOL CloseClipboard() Demo to Transfer Simple TextThe first demo we'll start with will illustrate how to transfer text to and from the Clipboard. While this is not needed as much as it once was due to the Windows 2000/Me context menu (right-clicking over any edit control will result in a menu containing the standard Clipboard functions) there are still some very valid uses for this capability. One example is if you want to pass simple text-based data to another application. However, in order to keep the example as clean as possible this demo will consist of a dialog with two edit controls. One edit control will enable you to type text into it and copy that text to the Clipboard while the second edit control will be a read-only control. A button will enable you to paste text from the Clipboard into this second control. By keeping the demo relatively simple it's much easier for you to focus on the specifics of working with the Clipboard and it's also more convenient when you want to copy and paste the salient code from this demo into your own project files. Creating the SimpleTextTransfer Demo ProjectAt this time, create a dialog-based application called SimpleTextTransfer. Once you've done that modify the default dialog so that it looks like the one shown in Figure 2
After you've modified the dialog, you'll need to make the following changes to the dialog's controls.
Copying Text to the ClipboardOnce you've finished with the dialog controls' properties, add a event handler for the Copy button's
void CSimpleTextTransferDlg::OnBnClickedBtncopy() { if (UpdateData()) { CString strData; m_edtToClipboard.GetWindowText(strData); // test to see if we can open the clipboard first before // wasting any cycles with the memory allocation if (OpenClipboard()) { // Empty the Clipboard. This also has the effect // of allowing Windows to free the memory associated // with any data that is in the Clipboard EmptyClipboard(); // Ok. We have the Clipboard locked and it's empty. // Now let's allocate the global memory for our data. // Here I'm simply using the GlobalAlloc function to // allocate a block of data equal to the text in the // "to clipboard" edit control plus one character for the // terminating null character required when sending // ANSI text to the Clipboard. HGLOBAL hClipboardData; hClipboardData = GlobalAlloc(GMEM_DDESHARE, strData.GetLength()+1); // Calling GlobalLock returns to me a pointer to the // data associated with the handle returned from // GlobalAlloc char * pchData; pchData = (char*)GlobalLock(hClipboardData); // At this point, all I need to do is use the standard // C/C++ strcpy function to copy the data from the local // variable to the global memory. strcpy(pchData, LPCSTR(strData)); // Once done, I unlock the memory - remember you // don't call GlobalFree because Windows will free the // memory automatically when EmptyClipboard is next // called. GlobalUnlock(hClipboardData); // Now, set the Clipboard data by specifying that // ANSI text is being used and passing the handle to // the global memory. SetClipboardData(CF_TEXT,hClipboardData); // Finally, when finished I simply close the Clipboard // which has the effect of unlocking it so that other // applications can examine or modify its contents. CloseClipboard(); } } } Cutting Text to the ClipboardAs you might have guessed, the only difference between copying data to the Clipboard and cutting
it to the Clipboard is that in the latter case, the data is removed after the transfer takes place.
Therefore, at this point, add an event handler for the Cut button's void CSimpleTextTransferDlg::OnBnClickedBtncut() { OnBnClickedBtncopy(); m_edtToClipboard.SetWindowText(""); } Pasting Text from the ClipboardAt this point, the application can place data on the clipboard, but it doesn't yet allow for the pasting of that data to the dialog. Therefore, we'll take care of that now. As with the Copy and Cut buttons, add an event handler for the Paste button's void CSimpleTextTransferDlg::OnBnClickedBtnpaste() { // Test to see if we can open the clipboard first. if (OpenClipboard()) { // Retrieve the Clipboard data (specifying that // we want ANSI text (via the CF_TEXT value). HANDLE hClipboardData = GetClipboardData(CF_TEXT); // Call GlobalLock so that to retrieve a pointer // to the data associated with the handle returned // from GetClipboardData. char *pchData = (char*)GlobalLock(hClipboardData); // Set a local CString variable to the data // and then update the dialog with the Clipboard data CString strFromClipboard = pchData; m_edtFromClipboard.SetWindowText(strFromClipboard); // Unlock the global memory. GlobalUnlock(hClipboardData); // Finally, when finished I simply close the Clipboard // which has the effect of unlocking it so that other // applications can examine or modify its contents. CloseClipboard(); } } Testing the SimpleTextTransfer DemoAt this point, compile and test the application. Your results should be similar to what you see in Figure 3.
However, there is just one problem with our little test. If you click the <Alt><Print Screen> key combination, this copies a bitmap of the current window to the Clipboard. You can test this by opening the PaintBrush application and pasting the image into a new bitmap image. However, if you copy an image to the Clipboard and then click the Paste button on the demo application, the text in the "to clipboard" edit control is wiped out and nothing appears! This is because the demo is attempting to use the data on the Clipboard without first determining if the data is valid for this application. In order to check as to the format of the data on the Clipboard, simply use the BOOL IsClipboardFormatAvailable(UINT format) The void CSimpleTextTransferDlg::OnBnClickedBtnpaste() { // Test to see if we can open the clipboard first. if (OpenClipboard()) { if (::IsClipboardFormatAvailable(CF_TEXT) || ::IsClipboardFormatAvailable(CF_OEMTEXT)) { // Retrieve the Clipboard data (specifying that // we want ANSI text (via the CF_TEXT value). HANDLE hClipboardData = GetClipboardData(CF_TEXT); // Call GlobalLock so that to retrieve a pointer // to the data associated with the handle returned // from GetClipboardData. char *pchData = (char*)GlobalLock(hClipboardData); // Set a local CString variable to the data // and then update the dialog with the Clipboard data CString strFromClipboard = pchData; m_edtFromClipboard.SetWindowText(strFromClipboard); // Unlock the global memory. GlobalUnlock(hClipboardData); } else { AfxMessageBox("There is no text data (ANSI) on the Clipboard."); } // Finally, when finished I simply close the Clipboard // which has the effect of unlocking it so that other // applications can examine or modify its contents. CloseClipboard(); } } Now, if you run this application, copy a bitmap (or any data that is not defined as
SummaryIn this first of a four-part series on programming the Windows Clipboard, you learned the basics of the using the Windows API to transfer data to and from the Clipboard. You then put that new-found knowledge to work with a demo application using simple ANSI text. In the next instalment of this series, I'll go into sending and receiving bitmap images from and to the Clipboard. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||