Click to See Complete Forum and Search --> : clipboard problem


Tiffany Chen
May 18th, 1999, 04:19 AM
I want to copy and paste a structure type of data (DRAGDATASTRUCT) to and from the clipboard.
I have registered the clipboard format in InitInstance and SetClipboardData in OnEditCopy, but when I try to get the data in OnEditPaste the data object doesn't contain the data copied.
An error(Unhandled exception in MFP.exe(MFC42D.Dll): 0xC0000005: Access Violation) occured when it ran up to the line
CString nailPath=pData->fileNameArray.GetAt(0);
in OnEditPaste().

If I appended all of the codes in OnEditPaste to the end of OnEditCopy, it works fine.
Any idea why it doesn't work in OnEditPaste? Thanks in advance.


BOOL CMFPApp::InitInstance()
{
...
m_EditClipFormat = ::RegisterClipboardFormat ("ThumNailList");
...
}



typedef struct tagDRAGDATASTRUCT {
CArray<int, int> selIndexArray; //index of the nail
CStringArray fileNameArray; //nail path
} DRAGDATASTRUCT;


void CMFPView::OnEditCopy()
{
HGLOBAL hgData=GlobalAlloc( GPTR, sizeof(DRAGDATASTRUCT));
ASSERT(hgData!=NULL);

DRAGDATASTRUCT* lpData=(DRAGDATASTRUCT*) GlobalLock(hgData);
ASSERT(lpData!=NULL);
//get Nail Path
LPCSTR pathName = GetNailPath(m_nCurNailIndex);
CStringArray stringArray;
stringArray.SetAtGrow(0, pathName);
memcpy(&lpData->fileNameArray,&stringArray, sizeof(stringArray)); //nail path
GlobalUnlock(hgData);

OpenClipboard();
EmptyClipboard();
::SetClipboardData(theApp.m_EditClipFormat, hgData );
CloseClipboard();


}


void CMFPView::OnEditPaste()
{
OpenClipboard();
if (!IsClipboardFormatAvailable(theApp.m_EditClipFormat))
return;
HGLOBAL hGlobal;
DRAGDATASTRUCT* pData;
hGlobal = ::GetClipboardData(theApp.m_EditClipFormat);
pData=(DRAGDATASTRUCT*)GlobalLock(hGlobal);
ASSERT(pData!=NULL);
CString nailPath = pData->fileNameArray.GetAt(0); //Access Violation occured here
GlobalUnlock(hGlobal);
CloseClipboard();

}

Rajaraman
May 18th, 1999, 04:39 AM
To me, there appears to be a small bug in your OnEditPaste() :
You are allocating global memory for the DRAGDATASTRUCT, which contains a CArray first. Then You are copying only the CStringArray into the global memory block. May be, you should try removing the CArray temporarily from the definition of the structure.

Rajaraman

Tiffany Chen
May 18th, 1999, 04:53 AM
I did copy CArray into the global memory block in OnEditCopy. Sorry, the code was not completed. Here is the complete code. Thanks.

void CMFPView::OnEditCopy()
{
HGLOBAL hgData=GlobalAlloc( GPTR, sizeof(DRAGDATASTRUCT));
ASSERT(hgData!=NULL);

DRAGDATASTRUCT* lpData=(DRAGDATASTRUCT*) GlobalLock(hgData);
ASSERT(lpData!=NULL);
//get index
CArray<int, int> tempArray;
tempArray.SetAtGrow(0, m_nCurNailIndex);
memcpy(&lpData->selIndexArray, &tempArray, sizeof(tempArray) );
//get Nail Path
LPCSTR pathName = GetNailPath(m_nCurNailIndex);
CStringArray stringArray;
stringArray.SetAtGrow(0, pathName);
memcpy(&lpData->fileNameArray,&stringArray, sizeof(stringArray));
GlobalUnlock(hgData);

OpenClipboard();
EmptyClipboard();
::SetClipboardData(theApp.m_EditClipFormat, hgData );
CloseClipboard();


}

Jason Teagle
May 18th, 1999, 06:33 AM
I believe the problem is in the GlobalAlloc() - for clipboard or OLE drag-and-drop operations, which are shared across the system, you should use GMEM_MOVEABLE|GMEM_DDESHARE, not GPTR. If you look at the help for SetClipboardData(), it confirms this.

Tiffany Chen
May 18th, 1999, 08:21 PM
Hi,

I tried GMEM_MOVEABLE|GMEM_DDESHARE, but I am still getting the same error. Any idea? Thanks.

Jason Teagle
May 19th, 1999, 02:52 AM
Now that I have had a chance to look at your code thoroughly, I see what the problem is.

The idea of using GMEM_DDESHARE is that clipboard data MUST be available to all processes on the machine; so, because one process can't access the memory given to another process, the memory used must be shareable.

Even though you used this flag as I suggested, you are storing an array in the lump of memory. You even copied the array memory map across (although this is not a good idea anyway - you should always use explicit object method calls, not copy the memory image even though it may achieve the same result).

CxxxArrays, CStrings, CxxxLists, etc., all use a pointer to store their data. This data, when the object is created and used, points into the ADDRESS SPACE OF THE PROCESS WHICH CREATED THE OBJECT.

So, when you copied the memory image, you also copied the pointer into your address space. Even though the place you wanted to get to it is also your address space, it is coming from a global lump of memory, which could have been locked anywhere - and is effectively a different process. So, when you tried to access the data which contained the pointer, you got an access violation.

To prove this theory, do the following: to your DRAGDATASTRUCT, add the following members:

UINT uTemp ;
char cTemp[32];

In your OnEditCopy(), before the GlobalUnlock() add:

lpData->uTemp = 13 ; // Unlucky for some.
lstrcpy(lpData->cTemp,"Hello!");

And in your OnEditPaste(), before the 'CString nailPath =' line, add:

UINT uTemp = pData->uTemp ;
CString strTemp = pData->cTemp ;

If you now run the code and step over the above two lines, you will find they work OK. The reason is that for simple data types such as UINT, the actual memory required to store the variable is WITHIN the global memory. For a char string, this is also true - all bytes allocated for the string are WITHIN the global memory. Hence, they can be accessed without problems because they do not point to another process' memory in any way.

Can you see the problem now? Believe me, I hit the same problem here not too long ago with CStrings.

The best solution I can think of is as follows: create a CMemFile to point to a block of memory, then create a CArchive on top of that C(Mem)File. Then, you can serialize your arrays into that archive (effectively serializing them into memory), and then you have an independent lump of memory which you can safely store in the clipboard. To access it, create a CMemFile and attach the locked global memory pointer to it, create the CArchive on top and serialize the object back in, just like serializing to and from a disk file.

I have never used CMemFile before, but if you want some help with it let me know (jteagle@solartron.com) and I'll help you get it working.

I hope this has explained what is wrong. Good luck.

Paul McKenzie
May 19th, 1999, 04:05 AM
If you're going to use a class such as CArray in your structure, you better guarantee that any memory allocated internally by the class is also of type GMEM_DDESHARE.
As Jason Teagle points out, types such as CString, CArray, etc. should only be used if variables declared of these types are used in the same process as the application. If you are going to share memory using the clipboard, the use of these MFC data types in the clipboard can cause problems, since they allocate memory internally that is *not* GMEM_DDESHARE.

Regards,

Paul McKenzie

Tiffany Chen
May 20th, 1999, 03:37 AM
Thanks for everyone's help. Yes, the problem is memory allocate CArray and CStringArrays. I have changed them to integer and char type and allocate memory for each int and string separately. It works now. Thanks again for all the suggestions.

Best Regards,
Tiffany

Tiffany Chen
May 20th, 1999, 03:39 AM
Thanks for everyone's help. Yes, the problem is memory allocate CArray and CStringArrays. I have changed them to integer and char type and allocate memory for each int and string separately. It works now. Thanks again for all the suggestions.

Best Regards,
Tiffany