Tek-Tips is the largest IT community on the Internet today!

Members share and learn making Tek-Tips Forums the best source of peer-reviewed technical information on the Internet!

  • Congratulations Chriss Miller on being selected by the Tek-Tips community for having the most helpful posts in the forums last week. Way to Go!

Copy a screenshot from clipboard to TJPEGImage 1

Status
Not open for further replies.

BobbaFet

Programmer
Feb 25, 2001
903
NL
Hi all,

What I am trying to do (and failing miserably at) is to develop a utility that will automatically take screenshot at semi-random intervals (aka between 15 and 45 seconds).

I found this thread > thread102-392160 but it doesnt seem to work for me. Here is my code:

Code:
procedure TForm1.TakeScreenShot;
var myJPEG: TJPEGImage;
begin
Clipboard.Clear;
myJPEG := TJPEGImage.Create;
myJPEG.CompressionQuality := 75;
keybd_event(VK_SNAPSHOT,0,0,0);
myJPEG.LoadFromClipboardFormat(CF_BITMAP, ClipBoard.GetAsHandle(CF_BITMAP), 0);
myJPEG.Compress;
myJPEG.SaveToFile(myPath + '\playername.jpg');
myJPEG.Free;
end;

Can you guys spot what I am doing wrong, because I sure can't. I've tried both methods suggested in the other thread but it seems as if nothing is ever placed on the clipboard.

[bobafett] BobbaFet [bobafett]
Code:
if not Programming = 'Severe Migraine' then
                       ShowMessage('Eureka!');
 
If you are trying to get a desktop snapshot, save yourself of all the clipboard hassle.

Code:
  var
    DSK   : HDC;
    BMP   : TBitmap;
    JPG   : TJPEGImage;
  begin
    DSK := GetDC(GetDesktopWindow);

    BMP := TBitmap.Create;
    BMP.Width := Screen.Width;
    BMP.Height := Screen.Height;
    BitBlt(BMP.Canvas.Handle, 0, 0, BMP.Width, BMP.Height,
           DSK, 0, 0, 
           SrcCopy);

    JPG := TJPEGImage.Create;
    JPG.Assign(BMP);
    JPG.CompressionQuality := 75;
    JPG.Compress;
    JPG.SaveToFile('playername.jpg');

    JPG.Free;
    BMP.Free;
  end;

buho (A).
 
No, this is going to be part of a ftp client server program that takes screenshots of the active window (but as the computer games we play at our league are modern ones it doesnt really matter wether it's going to use the active window or the full screen, amounts to the same thing 999999 out of a million times and if not) at random intervals of the active screen. Those screenshots are to be saved to file so I can then proceed to upload it to a server where admins can check them out (as part of an anti-cheat to find DirectX hacks, those cause for example being able to see through walls and we think this will be an effective measure against it). We can enforce it's use through rules.

[bobafett] BobbaFet [bobafett]
Code:
if not Programming = 'Severe Migraine' then
                       ShowMessage('Eureka!');
 
Forget the clipboard, then, and use the code above.

If you decide that snapping a particular window is better, use FindWindow to get the window handle, get the window HDC (with GetDC, GetDCEx or GetWindowDC) and it size (with GetClientRect or GetWindowsRect) and use the same code (using the window height and width instead of screen height and width).

buho (A).
 
Can you show an example of what you are talking about?
(BTW: I still feel that the code I posted above should work, can anyone explain why it doesn't?)

[bobafett] BobbaFet [bobafett]
Code:
if not Programming = 'Severe Migraine' then
                       ShowMessage('Eureka!');
 
Weirdness, I've gotten the Clipboard thing to work by not directly loading the clipboard bitmap into the jpegimage. I feel it should've worked just fine the way I had it earlier... but anyway, here is the code I've used:


NOTE: myPath is a global variable used in multiple procedures that are not related directly.

Code:
procedure TForm1.TakeScreenShot;
var myJPEG: TJPEGImage;
var BMP: TBitmap;
begin
if FileExists(myPath + '\' + Edit1.Text + '.jpg') then
        DeleteFile(myPath + '\' + Edit1.Text + '.jpg');

myJPEG := TJPEGImage.Create;
BMP := TBitmap.Create;
myJPEG.CompressionQuality := 75;

// press printscreen button
keybd_event(VK_SNAPSHOT,1,0,0);
// release printscreen button
keybd_event(VK_SNAPSHOT,1,KEYEVENTF_KEYUP,0);

while not Clipboard.HasFormat(CF_BITMAP) do
        begin
        Sleep(100);
        Application.ProcessMessages;
        end;

if Clipboard.HasFormat(CF_BITMAP) then
        begin
        BMP.LoadFromClipboardFormat(CF_BITMAP, ClipBoard.GetAsHandle(CF_BITMAP), 0);
        myJPEG.Assign(BMP);
        if not myJPEG.Empty then
                begin
                myJPEG.Compress;
                myJPEG.SaveToFile(myPath + '\' + Edit1.Text + '.jpg');
                end;
        end;

BMP.Free;
myJPEG.Free;
Clipboard.Clear;
end;

So this works now, but I am going to look into buho's suggestion now to see if I can get that to work and see which code I like better.

[bobafett] BobbaFet [bobafett]
Code:
if not Programming = 'Severe Migraine' then
                       ShowMessage('Eureka!');
 
Example of what I'm talking about:

1) Find the handle of the window you want with FindWindow.
2) Get the window DC with GetWindowDC.
3) Get the window size with GetWindowSize.
4) Create the bitmap and size it using the window size.
5) BitBlt the window DC in the bitmap canvas.
6) Save the BMP (eventually using a JPG image).

Now you have a window snapshot instead of a desktop snapshot.

Take a look at the SDK to see how FindWindow, GetWindowDC, GetWindowSize and BitBlt works. They are straightforward.

buho (A).
 
Yeah, I have, the only problem is that this is going to be used for multiple games and so I would have to update the list of games to look for everytime a new game is added to the league, so I think I am going to stick to what I've got now as it is doing exactly what I want.

Got some other strangeness though, I've tried to use the idFTP [Client] to upload the snapshots taken to a server, but I keep getting this exception:

10049 (Cannot assign requested address)

Which is weird as I am using the same code (except of course for hostname, port, username and password) as in an example I found on the internet.

My code [Again I feel confidently that it should work]:
Code:
procedure TForm1.UploadScreenShot(myFile: string);
var FTPCLient: TidFTP;
begin
FTPCLient := TIdFTP.Create(Self);
FTPClient.Host := myHost;
FTPClient.Port := myPort;

FTPClient.Username := '########';
FTPClient.Password := '########';

FTPClient.Passive  := True;
FTPClient.Connect();
FTPCLient.ChangeDir('httpdocs');

if FTPClient.Connected then
        begin
        FTPClient.Put(myFile, ExtractFileName(myFile), False);
        FTPClient.Disconnect;
        end;
FTPCLient.Free;
end;

Can someone tell me what I am doing wrong here?

[bobafett] BobbaFet [bobafett]
Code:
if not Programming = 'Severe Migraine' then
                       ShowMessage('Eureka!');
 
Why your code doesn't work.

The clipboard is not "part of your code" but an independent process (running on another thread, comtrolled by the OS). Furthermore, you are not acting directly over it but sending it a keybord command.

The statements:

keybd_event(VK_SNAPSHOT,0,0,0);
myJPEG.LoadFromClipboardFormat(CF_BITMAP, ClipBoard.GetAsHandle(CF_BITMAP), 0);

are being executed quickly one after another; when myJPG trys to read the clipboard, the clipboard process has not yet processed the command.

You need to give the OS some time to see the keyboard command, process it, awake the clipboard and, after that, for the clipboard to do the work.

That is way your second code works: you are waiting for the clipboard to kick in (with the while not Clipboard.Has format loop).

buho (A).



 
Ok buho, thanks for clearing that up, I figured as much but this way everyone reading the thread will know. Can you tell me what is wrong with my FTPClient code? (I'm really feeling quite stupid now, I am convinced it should work and for some reason completely beyond me it doesn't)

[bobafett] BobbaFet [bobafett]
Code:
if not Programming = 'Severe Migraine' then
                       ShowMessage('Eureka!');
 
The code is ok. I connected to my FTP server using it without a glitch.

Check your address/port carefully. May be a firewall/proxy messing things?

buho (A).
 
After taking a long and hard look at my code I finally found the problem! YAY!
...
...
...
...
Too bad the problem was me :S I never assigned the proper values to myHost and myPort... Excuse me for being stupid... If you'll excuse me... I've got some paste to eat..

[bobafett] BobbaFet [bobafett]
Code:
if not Programming = 'Severe Migraine' then
                       ShowMessage('Eureka!');
 
The clipboard is a user resource.

Personally, I never use the clipboard without the user directly asking for it. If the user have something important saved in the clipboard and some program scratch it, his work will be compromissed.

Way worse if the process interfering with the clipboard is kicking cyclically.

Now, you are trying to snapshot games; check carefully any code you decide to use, you can have problems with the way some games handle DirectX/OpenGL. I have the feeling that some old games can't be snapshoted via keyboard (and probably neither via GetDesktopWindow).

May be it is a problem with old versions of DX/OGL, may be it is a problem with the games itself, may be I'm wrong here; anyway, do some extensive tests before releasing the program.

buho (A).
 
Status
Not open for further replies.

Part and Inventory Search

Sponsor

Back
Top