Modify

Opened 10 years ago

Closed 10 years ago

#1028 closed Bug (Fixed)

_ClipBoard_GetData does not work properly

Reported by: Ascend4nt Owned by: Jpm
Milestone: 3.3.1.2 Component: AutoIt
Version: 3.3.0.0 Severity: None
Keywords: Cc:

Description

In working with _ClipBoard_GetData() and looking at the code (in Clipboard.au3), I've noticed some major flaws with it (AutoIT version 3.3.0.0):

  1. Unicode text will not be returned (at all)
  2. An arbitrary limit is placed on the ANSI/OEM text to be returned (8192 chars)
  3. The Clipboard is closed before the data is transferred. (From the source code I've reviewed, this is not good form)
  4. The Global memory isn't locked for anything but ANSI/OEM text
  5. Returning any other data types then the 3 it handles will result in a Global memory handle being returned (*not* locked)

With my code, I went ahead and fixed all of the above, and used the _MemMoveMemory() function to transfer memory to an AutoIT structure for only the 'unknown' data types. (The rest can be obtained quite easily).

Also: one enhancement: The size of the data is returned in @extended, and is in chars for CF_TEXT, CF_OEMTEXT, and CF_UNICODETEXT, or in bytes for any other data type.

I don't know the method for submitting the fixed code or extra documentation (regarding @extended), so please let me know what else I should do.

Fixed code is attached.

Attachments (2)

_ClipBoard_GetData.[MyFix].au3 (5.1 KB) - added by Ascendant 10 years ago.
_ClipBoard_GetData (fixed) function
_ClipBoard_GetData.[Fix2].au3 (5.2 KB) - added by Ascend4nt 10 years ago.
_ClipBoard_GetData (fixed) function (2nd time)

Download all attachments as: .zip

Change History (14)

Changed 10 years ago by Ascendant

_ClipBoard_GetData (fixed) function

comment:1 Changed 10 years ago by Valik

This code is wrong:

$tData = DllStructCreate("ubyte[" & $iDataSize & "]",$pMemoryBlock)
$pMemoryDest = DllStructGetPtr($tData)
; Copy the memory over to our newly created structure so that we can unlock the memory
_MemMoveMemory($pMemoryBlock, $pMemoryDest , $iDataSize)
$vReturn = DllStructGetPtr($tData)

You initialized the structure with the pointer to the memory block. That means nothing was allocated. You are copying the data onto itself.

comment:2 Changed 10 years ago by Ascend4nt

Wow, you're totally right. I actually had messed around with a few ways of doing that, one of which was trying to copy between two DLLStructs using _MemMoveMemory(), but I wound up leaving a piece of that code in when reverting back to a regular memory move.

Here's the problem now: when changing the above back to this, I get a missing-data problem (memory isn't transferred):

$tData = DllStructCreate("ubyte[" & $iDataSize & "]")
$pMemoryDest = DllStructGetPtr($tData)
; Copy the memory over to our newly created structure so that we can unlock the memory
_MemMoveMemory($pMemoryBlock, $pMemoryDest , $iDataSize)
$vReturn = DllStructGetPtr($tData)

For example, try modifying that code, and doing this (with a small amount of HTML text copied from a browser):

Local $pPointer=_ClipBoard_GetDataFixed(49342)
Local $iDataSize=@extended
Local $stStruct=DllStructCreate("char["&$iDataSize&"]",$pPointer)
MsgBox(0,"HTML",DllStructGetData($stStruct,1))

It works only when I put that part I screwed up back into place. Hmm.. I wonder what's going on. I'll have to dig deeper. Thanks for catching that.

comment:3 Changed 10 years ago by Ascend4nt

From further testing, it appears that the actual _MemMoveMemory() copy does work, I've tested the data *inside* the function (after unlocking, closing the clipboard, and invalidating pointers) and it is perfectly fine. However, once the program exits, the DLLStructPtr that is returned becomes invalid.

I've resorted to returning the entire DLLStruct that was created for now, which works - but makes it a little more difficult for users to understand.

Is there a way to keep the DLLStruct memory active without resorting to Global variables? I thought perhaps you kept an internal reference count before you would destroy that memory.

Thanks

comment:4 Changed 10 years ago by Valik

A pointer is just a number. There is nothing special about it. You must return the DllStruct itself. You can't return the pointer and hope that does something because it can't and shouldn't.

comment:5 Changed 10 years ago by Ascend4nt

So do you recommend this method of returning a DLLStruct for non-text Clipboard types? This may break existing code, which I'd hate to do - but I already pointed out that the function is barely working as it is, and not doing the 'Get' properly. The change in return value would also confuse users, plus the Help info would need to be adjusted. Should I discuss this with GaFrost, since he appears to be the one handling UDF's? Or should I just post the modified code?

thanks

comment:6 Changed 10 years ago by Valik

Shouldn't the data be returned? Why does a pointer or a structure have to be returned at all? AutoIt supports binary data so the data returned will either be a string or a binary string.

comment:7 Changed 10 years ago by Ascen4nt

That would make sense, except I don't know an efficient way to grab the data and put it into a binary form. I might be not seeing something obvious though, because I have only gotten data into DLLStructs where you can only grab 1 byte at a time (for binary/unknown data) using DLLStructGetData, so it would require a type of For..Next loop to get each and every byte, which can be a long process for large amounts of data. Is there another way that I'm not seeing though?

comment:8 Changed 10 years ago by Valik

Case Else
	$tData = DllStructCreate("ubyte[" & $iDataSize & "]",$pMemoryBlock)
	$vReturn = DllStructGetData($tData)

comment:9 Changed 10 years ago by Ascend4nt

Dang, what the heck was I doing before that I was only getting one byte of data back at a time? I had that happen like last year or something, and have avoided trying to work with DLLStruct's with binary data as much as possible since then. Now that I just tested it however, it's fine. Live and learn! I'll attach a new version right away ('Fix2').

By the way, the HTML 'get' example I had above can now be changed to:

Local $vClipData=_ClipBoard_GetDataFixed(49342)
MsgBox(0,"HTML",BinaryToString($vClipData))

The other versions test fine as well:

MsgBox(0,"ANSI Text",_ClipBoard_GetDataFixed($CF_TEXT))
MsgBox(0,"OEM Text",_ClipBoard_GetDataFixed($CF_OEMTEXT))
MsgBox(0,"Unicode",_ClipBoard_GetDataFixed($CF_UNICODETEXT))

Thanks, Valik

Changed 10 years ago by Ascend4nt

_ClipBoard_GetData (fixed) function (2nd time)

comment:10 follow-up: Changed 10 years ago by anonymous

Darn, I forgot to remove one piece of code. Is it possible for someone to just pull this piece out:

; Reset Global structure, releast memory
$_stClipGetStruct = 0


comment:11 in reply to: ↑ 10 Changed 10 years ago by Jpm

Replying to anonymous:

Darn, I forgot to remove one piece of code. Is it possible for someone to just pull this piece out:

; Reset Global structure, releast memory
$_stClipGetStruct = 0


Done

comment:12 Changed 10 years ago by Jpm

  • Milestone set to 3.3.1.2
  • Owner set to Jpm
  • Resolution set to Fixed
  • Status changed from new to closed

Fixed in version: 3.3.1.2

Guidelines for posting comments:

  • You cannot re-open a ticket but you may still leave a comment if you have additional information to add.
  • In-depth discussions should take place on the forum.

For more information see the full version of the ticket guidelines here.

Add Comment

Modify Ticket

Action
as closed The owner will remain Jpm.
Author


E-mail address and user name can be saved in the Preferences.

 
Note: See TracTickets for help on using tickets.