1. Rick Domogalik
  2. PowerBuilder
  3. Wednesday, 22 July 2020 12:48 PM UTC

I am trying to copy files into the clipboard so they can be pasted to windows explorer or as an attachment into outlook from my application.  I found the below referenced method for doing this.  the code compiles and run with no error, but nothing is placed into the clipboard.  My assumption is there is a problem with the format of the file string I am sending to the function (yellow highlight).  This function should allow for multiple files to be selected and placed into the clipboard, but I am just testing with a single file.  Also wondering if the Green Highlight - Preferred drop effect should be a value not a text string.  The MS documentation on this argument was not easy to understand how to format this item.  

 

This code was also very old.  Not sure if something has changed with newer Windows OS.

 

Any help is greatly appreciated.

 

Rick

 

ulong format
boolean lb_return
ulong hdf,h2
ulong df,D2
ulong drop[]
ulong closing
blob{1000} buf1
blob{4} buf2
long ll_pos, ll_len
string files
string CFSTR_PREFERREDDROPEFFECT  = 'Preferred DropEffect';
ulong DROPEFFECT_COPY = 1;
uint copyformat
 
files =  "c:\pdf\1.pdf"

ll_len = len(files) + 1
drop = {ll_len,0,0,0,0 }
closing = 0
format = 15
ll_pos = blobedit(buf1,1," ")
ll_pos = blobedit(buf1,1,drop[1])
ll_pos = blobedit(buf1,5,drop[2])
ll_pos = blobedit(buf1,9,drop[3])
ll_pos = blobedit(buf1,13,drop[4])
ll_pos = blobedit(buf1,17,drop[5])
ll_pos = blobedit(buf1,21,files)
ll_pos = blobedit(buf1,ll_pos,closing)
hdf = GlobalAlloc(2, ll_pos)
//hdf = GlobalAlloc(66, ll_pos)
df = GlobalLock(hdf);
CopyMemory(df, buf1 , ll_pos)
GlobalUnlock(hdf)
blobedit(buf2,1,DROPEFFECT_COPY)
h2 = GlobalAlloc(66,4)
d2 = GlobalLock(h2);
CopyMemory(d2, buf2 , 4)
GlobalUnlock(h2)
lb_return = OpenClipboard(closing);
if lb_return then
 lb_return = EmptyClipboard();
 messagebox("test",lb_return);
 copyformat = RegisterClipboardFormatA (CFSTR_PREFERREDDROPEFFECT);
 messagebox("test",copyformat);
 SetClipboardData(copyformat,d2);
 SetClipboardData(format,df);
 lb_return = CloseClipboard();
else
 messagebox("test","Copy to clipboard failed!!!");
end if
if lb_return then
 return 1
else
 return -1
end if
 
 
 
Rick Domogalik Accepted Answer Pending Moderation
  1. Friday, 24 July 2020 19:22 PM UTC
  2. PowerBuilder
  3. # 1

I was able to get the code to work.  The clipboard viewer was pretty instrumental to getting to the bottom of the issue. Thanks John for all the help getting this resolved.  There were 3 issues with the original posters code.

1. wrong format specified.  was set to 1, changed to 5
2. Extra nulls were being added to the blob.  I did a very unorthodox way of getting rid of it, just by using blobmid to truncate the blob and get rid of it.  Still not sure why blobedit was adding the extra null.
3. The blob needed a "1" right before the start of the first filename.  Found no documentation on why this is needed, but with comparing a windows file copy to my code noticed the windows copy put a one right before.  I just modified the last blobedit to place a 1 in there and everything started working.

Here is a copy of the modified script that runs

ulong format
boolean lb_return
ulong hdf,h2
ulong df,D2
ulong drop[]
ulong closing, ul_null
blob{1000} buf1
blob{4} buf2
long ll_pos
string files
string CFSTR_PREFERREDDROPEFFECT  = 'Preferred DropEffect';
ulong DROPEFFECT_COPY = 5;
uint copyformat
setnull(ul_null)
files =  "C:\pdf\1.pdf"
drop = {20,0,0,0,0}
closing = 0
format = 15
ll_pos = blobedit(buf1,1," ")
ll_pos = blobedit(buf1,1,drop[1])
ll_pos = blobedit(buf1,5,drop[2])
ll_pos = blobedit(buf1,9,drop[3])
ll_pos = blobedit(buf1,13,drop[4])
//ll_pos = blobedit(buf1,17,drop[5])
//put in to add a "1" to blob before start of first file.
ll_pos = blobedit(buf1,17,1)
ll_pos = blobedit(buf1,21,files)
ll_pos = blobedit(buf1,ll_pos,closing)
//put in to get rid of extra null at end of string
ll_pos = ll_pos - 3
buf1 = blobmid(buf1, 1, ll_pos)
hdf = GlobalAlloc(66, ll_pos)
df = GlobalLock(hdf);
CopyMemory(df, buf1 , ll_pos)
GlobalUnlock(hdf)
blobedit(buf2,1,DROPEFFECT_COPY)
h2 = GlobalAlloc(66,4)
d2 = GlobalLock(h2);
CopyMemory(d2, buf2 , 4)
GlobalUnlock(h2)
lb_return = OpenClipboard(closing);
if lb_return then
 lb_return = EmptyClipboard();
 //messagebox("test",lb_return);
 copyformat = RegisterClipboardFormatA (CFSTR_PREFERREDDROPEFFECT);
 //messagebox("test",copyformat);
 SetClipboardData(copyformat,d2);
 SetClipboardData(format,df);
 lb_return = CloseClipboard();
else
 messagebox("test","Copy to clipboard failed!!!");
end if
if lb_return then
 return 1
else
 return -1
end if
Comment
  1. Armeen Mazda @Appeon
  2. Friday, 24 July 2020 20:49 PM UTC
Thanks for sharing the solution!
  1. Helpful
  1. John Fauss
  2. Friday, 24 July 2020 21:09 PM UTC
I'm really glad you were able to get this to work, Rick! Congrats!

The value 1 you had to add to get this to work is a Windows BOOL (a 4-byte boolean TRUE) that signifies that the file path/names are in Unicode. When this value is zero (FALSE) as in the original code, this tells Windows the info is ANSI encoded - The code was originally written pre-PB10, so ANSI was the norm. Not so, anymore.
  1. Helpful
There are no comments made yet.
John Fauss Accepted Answer Pending Moderation
  1. Friday, 24 July 2020 14:22 PM UTC
  2. PowerBuilder
  3. # 2

Thanks for the links, Rick. I think you're on the right track in regards to your observation about the in-memory list of files. I had seen mention in the WinAPI docs that this list needs to be terminated with a double-null and thought that might be the issue.

I found a free Clipboard Viewer utility (www.freeclipboardviewer.com) and have installed it to see if it would give some clues as to what Windows Explorer places in the Clipboard when you select and copy a small set of files. Here's a screenshot from that utility when I did exactly that:

You can clearly see how the list is terminated with an extra null character, which leads me to believe you're on the right track.

I have some experience manipulating memory contents on a byte-by-byte basis from PowerScript, so I will see what I can come up with that might help you out.

Comment
  1. Rick Domogalik
  2. Friday, 24 July 2020 15:14 PM UTC
The viewer was very helpful. It looks like the code is putting an extra null at the end of the string. And for the life of me, I can't see why and I can't make it go away.



If I remove the last blobedit

ll_pos = blobedit(buf1,ll_pos,closing)



It leaves it with just 1 null. I tried making a null character and putting it on at the end and it puts an extra space in there.



Also, I think there is an issue with the "preferred clipboard format", When I select a file from explorer and copy, it put the preview on 1 line, this code puts 1 character on a line and make a row for each character in the files string. When you copy a file, the format is in hex in the viewer and is 05 00 00 00, with this code it is 01 00 00 00. Guessing that might be part of the problem too.



Rick
  1. Helpful
  1. Rick Domogalik
  2. Friday, 24 July 2020 15:49 PM UTC
I was able to get rid of the extra null and fix the format issue to match what you got above on a normal file copy through windows. Still not working. My next thought was all the extra items that appear in the Clipboard viewer list. For example:

DataObject, Shill ISList array, FileName, FileContents…. and so on. There are line 13 more items. The code I am using only put in Preview(still messed up), List Of files and Preferred DropEffect. Guessing I will need some, if not all of those other objects to be set to make this work. But nowhere in the MS docs does it talk about setting anything else besides the CF_HDrop. It even says you don't have to set the format any longer.
  1. Helpful
There are no comments made yet.
John Fauss Accepted Answer Pending Moderation
  1. Wednesday, 22 July 2020 14:09 PM UTC
  2. PowerBuilder
  3. # 3

Greetings, Rick -

I understand you found this code posted elsewhere. I presume the posted code was for an older version of PB, possibly prior to PB 10? The reason I ask is because of the use of the ANSI version of the RegisterClipboardFormat API function, RegisterClipboardFormatA. It would be helpful to see the external function declaration (EFD) for this API function as it is coded in your PB app. Since PB began using Unicode internally starting with version 10, you typically should use the Unicode flavor of WinAPI functions, as in RegisterClipboardFormatW. In order to utilize the ANSI flavor, the EFD should include an "ALIAS FOR" clause that contains the ";ansi" keyword, as in:

 ... ALIAS FOR "RegisterClipboardFormatA;ansi"

Using the Unicode API eliminates the need to specify the ALIAS FOR clause in the EFD, and PB also does not have to translate character/string information from Unicode to ANSI when calling the API function. Much cleaner the whole way around.

I looked briefly at the WinAPI documentation for RegisterClipboardFormatA/W, and it takes a string argument value, as you have it coded.

Without being able to see the EFD, my guess is that PB is passing a Unicode string to an ANSI API function without the needed conversion because the ";ansi" has not been included in the EFD.

If you want, include the EFD for this API function (or for all of them) in your reply, and I can help you determine if any need to be tweaked.

Regards, John

Comment
  1. Rick Domogalik
  2. Wednesday, 22 July 2020 14:59 PM UTC
I see the uint change to ulong now with GlobalAlloc. Missed that when comparing. But I did put that in before and still have the same result of no error, but no file in the paste buffer.
  1. Helpful
  1. John Fauss
  2. Thursday, 23 July 2020 04:23 AM UTC
The external function declarations look right syntactically, but without delving deeper into it I cannot say what the problem is. I'd like to look at it more as I like the challenge, but that will have to happen on my own free time (evenings/weekends) so I am unable to commit as to when that will occur. You postulated that the way the Windows O/S works may have changed the original code was posted long ago and this may indeed be what is happening.

Can you supply the URL to the web page you found that contains the original code?

You might also search for example code in C++ or C# that does what you are wanting to accomplish. If you can find a working code sample, it may be easier to port that code to PowerScript.
  1. Helpful
  1. Rick Domogalik
  2. Friday, 24 July 2020 13:56 PM UTC
Here is the original post I found.



http://codeverge.com/sybase.powerbuilder.general/copy-file-to-clipboard/944553



I have done a lot of trial and error and I think the issue has to do with null characters needed at the end of the "file path" for the setclipboarddata cf_hdrop.



The MS documentation says the CF_HDrop format for the filename array should be

c:\temp1.txt'\0'c:\temp2.txt'\0''\0' Where '\0' is a null value. So one null after each file path and two nulls at the end. I don't think that is getting pushed into the blob with this code. I have tried many different methods of putting nulls in there and no luck. If I make a null character and put it on the end, it crashes on the copymemory call. Other methods cause explorer.exe to crash and restart, but no error or file paste.



Might need to not use this old code and look for the port you suggested. I have found several examples, but am struggling to convert to powerscript.

https://stackoverflow.com/questions/45347710/getclipboarddatacf-hdrop-fails-in-cut-and-paste

https://www.codeguru.com/cpp/w-p/clipboard/article.php/c2997/Copying-Files-into-Explorer.htm



Thanks for taking a look.



Rick
  1. Helpful
There are no comments made yet.
  • Page :
  • 1


There are no replies made for this question yet.
However, you are not allowed to reply to this question.