Putting a screenshot from the clipboard onto a GUI

Get help with using AutoHotkey (v1.1 and older) and its commands and hotkeys
CosmicThing2
Posts: 19
Joined: 07 Oct 2016, 06:37
Location: UK

Putting a screenshot from the clipboard onto a GUI

09 Mar 2018, 07:36

Hello all,

This script is my attempt at re-creating a screen freeze functionality. It does a screenshot of the current monitor and copies it to the other screen, putting it over the top of everything else (with F8). It does run and does work although currently it has to be run using ANSI - AutoHotkeyA32.exe in your installation folder, (you'll also have to change 'displayswlocation' to an existing location if you want to run it). F9 then gets rid of the 'frozen screen' and asks the user if they want to use Duplicate mode (don't ask, I just need this).

Note that the bottom half of this program is not my code, it's Sean's Screen Capture script (here https://autohotkey.com/board/topic/8323 ... ntry530486).

Firstly, the code!

Code: Select all

#SingleInstance,Force
DetectHiddenWindows,On
open = 0
SetBatchLines -1

;Location of where the DisplaySwitch.exe is located on the server - (Note that this *should* be in System32 by default on a windows installation, but sometimes it isn't... because Microsoft) (Hidden this bit from AHK forums because rather not show the file structure, it does work
displayswloc = [location]\Freeze

F8::
;This section detects whether the monitors are in duplicate or extend. If in duplicate, it forces extend
SysGet, monitorcount, MonitorCount 
if (monitorcount < 2) {
	IfExist, C:\Windows\System32\DisplaySwitch.exe 
	{
		Run, C:\Windows\System32\DisplaySwitch.exe /extend
	}
	else 
	{
		FileCopy, %displayswloc%\DisplaySwitch.exe, C:\Windows\System32
		Run, C:\Windows\System32\DisplaySwitch.exe /extend
	}
	Sleep 2500
}

;If we've already got a frozen screen window open, kill the existing one before opening another
if (open = 1) { 
	Gui, Destroy
	open = 0
	goto next
}

;Press printscreen and retrieve the data from it. Also make sure the program knows the window is now open (UPDATED TO TRY TO USE SEANS SCREEN CAPTURE SCRIPT)
next:
if (open = 0) {
	open = 1
	Gui,+hwndhwnd
	CaptureScreen(3, false, 0)
	;send,^{PrintScreen}
	loop,c:\windows\*.bmp,1,1
	if file:=A_LoopFileFullPath
	break

	;Set the window up to have no borders and not appear in the taskbar. (Comment these two lines out for debugging)
	Gui, Margin, 0, 0
	Gui, -Border -SysMenu -Caption -DPIScale +ToolWindow

	;Determine which is the primary monitor. Then get data about the other one
	SysGet, primary, MonitorPrimary 
	if (primary = 1)
		SysGet, smart, Monitor, 2
	else
		SysGet, smart, Monitor, 1

	;Format the picture to fit on the NON primary monitor (The other screen)
	if (smartLeft < 0)  
		Gui,Add,Picture,x-1920 y0 w%A_ScreenWidth% h%A_ScreenHeight%,%file%
	else
		Gui,Add,Picture,x0 y0 w%A_ScreenWidth% h%A_ScreenHeight%,%file%

	;Not my code! Unsure about this, something to do with the clipboard! This is messy, WIP
	
	f:=DllCall("OpenClipboard", "Uint", hwnd)
	;DllCall("SetClipboardData", "Uint", 8, "Uint", hDIB)
	;DllCall("CloseClipboard")
	;SendMessage,0x172,0,hDIB,Static1,ahk_id%hwnd%
	;f:=DllCall("User32\OpenClipboard",int,hwnd)
	;info:=DllCall("User32\GetClipboardData",Uint,8)
	handle:=DllCall("User32\GetClipboardData",Uint,2)
	f:=DllCall("User32\CloseClipboard",int,hwnd)
	SendMessage,0x172,0,handle,Static1,ahk_id%hwnd%
	

	;Need to display the picture differently depending on whether the other screen is to the left or right of the primary
	;If the other screen is to the left 
	if (smartLeft < 0) 
		Gui,Show, x-3840 y0 w3840 h1080
	;If the other screen is to the right
	else	           
		Gui, Show, w3840 h1080 x1920 y0

	;Finally, maximize the window and then wait for the user to press another button
	Gui, maximize
	return
}

F9::
;Ask the question. '292' is the type of message box, this one has a yes and a no in it as well as a few other options
MsgBox, 292, Force Duplicate Mode?, Would you like to switch to duplicate mode?
IfMsgBox Yes 
{
	IfExist, C:\Windows\System32\DisplaySwitch.exe
	{
		Run C:\Windows\System32\DisplaySwitch.exe /clone
	}
	else 
	{
		FileCopy, %displayswloc%\DisplaySwitch.exe, C:\Windows\System32
		Run C:\Windows\System32\DisplaySwitch.exe /clone
	}
}

;Whether the user pressed yes or no, kill the window and then wait for further input
Gui, Destroy
reload

;In an emergency or if required, kill the whole script with ctrl-shift-F12
^+F12::
ExitApp

/* CaptureScreen(aRect, bCursor, sFileTo, nQuality)
1) If the optional parameter bCursor is True, captures the cursor too.
2) If the optional parameter sFileTo is 0, set the image to Clipboard.
   If it is omitted or "", saves to screen.bmp in the script folder,
   otherwise to sFileTo which can be BMP/JPG/PNG/GIF/TIF.
3) The optional parameter nQuality is applicable only when sFileTo is JPG. Set it to the desired quality level of the resulting JPG, an integer between 0 - 100.
4) If aRect is 0/1/2/3, captures the entire desktop/active window/active client area/active monitor.
5) aRect can be comma delimited sequence of coordinates, e.g., "Left, Top, Right, Bottom" or "Left, Top, Right, Bottom, Width_Zoomed, Height_Zoomed".
   In this case, only that portion of the rectangle will be captured. Additionally, in the latter case, zoomed to the new width/height, Width_Zoomed/Height_Zoomed.

Example:
CaptureScreen(0)
CaptureScreen(1)
CaptureScreen(2)
CaptureScreen(3)
CaptureScreen("100, 100, 200, 200")
CaptureScreen("100, 100, 200, 200, 400, 400")   ; Zoomed
*/

/* Convert(sFileFr, sFileTo, nQuality)
Convert("C:\image.bmp", "C:\image.jpg")
Convert("C:\image.bmp", "C:\image.jpg", 95)
Convert(0, "C:\clip.png")   ; Save the bitmap in the clipboard to sFileTo if sFileFr is "" or 0.
*/

;CaptureScreen()
;Return

CaptureScreen(aRect = 0, bCursor = False, sFile = "", nQuality = "")
{
	If	!aRect
	{
		SysGet, nL, 76  ; virtual screen left & top
		SysGet, nT, 77
		SysGet, nW, 78	; virtual screen width and height
		SysGet, nH, 79
	}
	Else If	aRect = 1
		WinGetPos, nL, nT, nW, nH, A
	Else If	aRect = 2
	{
		WinGet, hWnd, ID, A
		VarSetCapacity(rt, 16, 0)
		DllCall("GetClientRect" , "Uint", hWnd, "Uint", &rt)
		DllCall("ClientToScreen", "Uint", hWnd, "Uint", &rt)
		nL := NumGet(rt, 0, "int")
		nT := NumGet(rt, 4, "int")
		nW := NumGet(rt, 8)
		nH := NumGet(rt,12)
	}
	Else If	aRect = 3
	{
		VarSetCapacity(mi, 40, 0)
		DllCall("GetCursorPos", "int64P", pt)
		DllCall("GetMonitorInfo", "Uint", DllCall("MonitorFromPoint", "int64", pt, "Uint", 2), "Uint", NumPut(40,mi)-4)
		nL := NumGet(mi, 4, "int")
		nT := NumGet(mi, 8, "int")
		nW := NumGet(mi,12, "int") - nL
		nH := NumGet(mi,16, "int") - nT
	}
	Else
	{
		StringSplit, rt, aRect, `,, %A_Space%%A_Tab%
		nL := rt1	; convert the Left,top, right, bottom into left, top, width, height
		nT := rt2
		nW := rt3 - rt1
		nH := rt4 - rt2
		znW := rt5
		znH := rt6
	}

	mDC := DllCall("CreateCompatibleDC", "Uint", 0)
	hBM := CreateDIBSection(mDC, nW, nH)
	oBM := DllCall("SelectObject", "Uint", mDC, "Uint", hBM)
	hDC := DllCall("GetDC", "Uint", 0)
	DllCall("BitBlt", "Uint", mDC, "int", 0, "int", 0, "int", nW, "int", nH, "Uint", hDC, "int", nL, "int", nT, "Uint", 0x40000000 | 0x00CC0020)
	DllCall("ReleaseDC", "Uint", 0, "Uint", hDC)
	If	bCursor
		CaptureCursor(mDC, nL, nT)
	DllCall("SelectObject", "Uint", mDC, "Uint", oBM)
	DllCall("DeleteDC", "Uint", mDC)
	If	znW && znH
		hBM := Zoomer(hBM, nW, nH, znW, znH)
	If	sFile = 0
		SetClipboardData(hBM)
	Else	Convert(hBM, sFile, nQuality), DllCall("DeleteObject", "Uint", hBM)
}

CaptureCursor(hDC, nL, nT)
{
	VarSetCapacity(mi, 20, 0), mi := Chr(20)
	DllCall("GetCursorInfo", "Uint", &mi)
	bShow   := NumGet(mi, 4)
	hCursor := NumGet(mi, 8)
	xCursor := NumGet(mi,12)
	yCursor := NumGet(mi,16)

	If	bShow && hCursor:=DllCall("CopyIcon", "Uint", hCursor)
	{
	VarSetCapacity(ni, 20, 0)
	DllCall("GetIconInfo", "Uint", hCursor, "Uint", &ni)
	bIcon    := NumGet(ni, 0)
	xHotspot := NumGet(ni, 4)
	yHotspot := NumGet(ni, 8)
	hBMMask  := NumGet(ni,12)
	hBMColor := NumGet(ni,16)

	DllCall("DrawIcon", "Uint", hDC, "int", xCursor - xHotspot - nL, "int", yCursor - yHotspot - nT, "Uint", hCursor)
	DllCall("DestroyIcon", "Uint", hCursor)
	If	hBMMask
	DllCall("DeleteObject", "Uint", hBMMask)
	If	hBMColor
	DllCall("DeleteObject", "Uint", hBMColor)
	}
}

Zoomer(hBM, nW, nH, znW, znH)
{
	mDC1 := DllCall("CreateCompatibleDC", "Uint", 0)
	mDC2 := DllCall("CreateCompatibleDC", "Uint", 0)
	zhBM := CreateDIBSection(mDC2, znW, znH)
	oBM1 := DllCall("SelectObject", "Uint", mDC1, "Uint",  hBM)
	oBM2 := DllCall("SelectObject", "Uint", mDC2, "Uint", zhBM)
	DllCall("SetStretchBltMode", "Uint", mDC2, "int", 4)
	DllCall("StretchBlt", "Uint", mDC2, "int", 0, "int", 0, "int", znW, "int", znH, "Uint", mDC1, "int", 0, "int", 0, "int", nW, "int", nH, "Uint", 0x00CC0020)
	DllCall("SelectObject", "Uint", mDC1, "Uint", oBM1)
	DllCall("SelectObject", "Uint", mDC2, "Uint", oBM2)
	DllCall("DeleteDC", "Uint", mDC1)
	DllCall("DeleteDC", "Uint", mDC2)
	DllCall("DeleteObject", "Uint", hBM)
	Return	zhBM
}

Convert(sFileFr = "", sFileTo = "", nQuality = "")
{
	If	sFileTo  =
		sFileTo := A_ScriptDir . "\screen.bmp"
		;sFileTo := "C:\temp\screen.bmp"
	SplitPath, sFileTo, , sDirTo, sExtTo, sNameTo

	If Not	hGdiPlus := DllCall("LoadLibrary", "str", "gdiplus.dll")
		Return	sFileFr+0 ? SaveHBITMAPToFile(sFileFr, sDirTo . "\" . sNameTo . ".bmp") : ""
	VarSetCapacity(si, 16, 0), si := Chr(1)
	DllCall("gdiplus\GdiplusStartup", "UintP", pToken, "Uint", &si, "Uint", 0)

	If	!sFileFr
	{
		DllCall("OpenClipboard", "Uint", 0)
		If	 DllCall("IsClipboardFormatAvailable", "Uint", 2) && (hBM:=DllCall("GetClipboardData", "Uint", 2))
		DllCall("gdiplus\GdipCreateBitmapFromHBITMAP", "Uint", hBM, "Uint", 0, "UintP", pImage)
		DllCall("CloseClipboard")
	}
	Else If	sFileFr Is Integer
		DllCall("gdiplus\GdipCreateBitmapFromHBITMAP", "Uint", sFileFr, "Uint", 0, "UintP", pImage)
	Else	DllCall("gdiplus\GdipLoadImageFromFile", "Uint", Unicode4Ansi(wFileFr,sFileFr), "UintP", pImage)

	DllCall("gdiplus\GdipGetImageEncodersSize", "UintP", nCount, "UintP", nSize)
	VarSetCapacity(ci,nSize,0)
	DllCall("gdiplus\GdipGetImageEncoders", "Uint", nCount, "Uint", nSize, "Uint", &ci)
	Loop, %	nCount
		If	InStr(Ansi4Unicode(NumGet(ci,76*(A_Index-1)+44)), "." . sExtTo)
		{
			pCodec := &ci+76*(A_Index-1)
			Break
		}
	If	InStr(".JPG.JPEG.JPE.JFIF", "." . sExtTo) && nQuality<>"" && pImage && pCodec
	{
	DllCall("gdiplus\GdipGetEncoderParameterListSize", "Uint", pImage, "Uint", pCodec, "UintP", nSize)
	VarSetCapacity(pi,nSize,0)
	DllCall("gdiplus\GdipGetEncoderParameterList", "Uint", pImage, "Uint", pCodec, "Uint", nSize, "Uint", &pi)
	Loop, %	NumGet(pi)
		If	NumGet(pi,28*(A_Index-1)+20)=1 && NumGet(pi,28*(A_Index-1)+24)=6
		{
			pParam := &pi+28*(A_Index-1)
			NumPut(nQuality,NumGet(NumPut(4,NumPut(1,pParam+0)+20)))
			Break
		}
	}

	If	pImage
		pCodec	? DllCall("gdiplus\GdipSaveImageToFile", "Uint", pImage, "Uint", Unicode4Ansi(wFileTo,sFileTo), "Uint", pCodec, "Uint", pParam) : DllCall("gdiplus\GdipCreateHBITMAPFromBitmap", "Uint", pImage, "UintP", hBitmap, "Uint", 0) . SetClipboardData(hBitmap), DllCall("gdiplus\GdipDisposeImage", "Uint", pImage)

	DllCall("gdiplus\GdiplusShutdown" , "Uint", pToken)
	DllCall("FreeLibrary", "Uint", hGdiPlus)
}

CreateDIBSection(hDC, nW, nH, bpp = 32, ByRef pBits = "")
{
	NumPut(VarSetCapacity(bi, 40, 0), bi)
	NumPut(nW, bi, 4)
	NumPut(nH, bi, 8)
	NumPut(bpp, NumPut(1, bi, 12, "UShort"), 0, "Ushort")
	NumPut(0,  bi,16)
	Return	DllCall("gdi32\CreateDIBSection", "Uint", hDC, "Uint", &bi, "Uint", 0, "UintP", pBits, "Uint", 0, "Uint", 0)
}

SaveHBITMAPToFile(hBitmap, sFile)
{
	DllCall("GetObject", "Uint", hBitmap, "int", VarSetCapacity(oi,84,0), "Uint", &oi)
	hFile:=	DllCall("CreateFile", "Uint", &sFile, "Uint", 0x40000000, "Uint", 0, "Uint", 0, "Uint", 2, "Uint", 0, "Uint", 0)
	DllCall("WriteFile", "Uint", hFile, "int64P", 0x4D42|14+40+NumGet(oi,44)<<16, "Uint", 6, "UintP", 0, "Uint", 0)
	DllCall("WriteFile", "Uint", hFile, "int64P", 54<<32, "Uint", 8, "UintP", 0, "Uint", 0)
	DllCall("WriteFile", "Uint", hFile, "Uint", &oi+24, "Uint", 40, "UintP", 0, "Uint", 0)
	DllCall("WriteFile", "Uint", hFile, "Uint", NumGet(oi,20), "Uint", NumGet(oi,44), "UintP", 0, "Uint", 0)
	DllCall("CloseHandle", "Uint", hFile)
}

SetClipboardData(hBitmap)
{
	DllCall("GetObject", "Uint", hBitmap, "int", VarSetCapacity(oi,84,0), "Uint", &oi)
	hDIB :=	DllCall("GlobalAlloc", "Uint", 2, "Uint", 40+NumGet(oi,44))
	pDIB :=	DllCall("GlobalLock", "Uint", hDIB)
	DllCall("RtlMoveMemory", "Uint", pDIB, "Uint", &oi+24, "Uint", 40)
	DllCall("RtlMoveMemory", "Uint", pDIB+40, "Uint", NumGet(oi,20), "Uint", NumGet(oi,44))
	DllCall("GlobalUnlock", "Uint", hDIB)
	DllCall("DeleteObject", "Uint", hBitmap)
	DllCall("OpenClipboard", "Uint", 0)
	DllCall("EmptyClipboard")
	DllCall("SetClipboardData", "Uint", 8, "Uint", hDIB)
	DllCall("CloseClipboard")
}

Unicode4Ansi(ByRef wString, sString)
{
	nSize := DllCall("MultiByteToWideChar", "Uint", 0, "Uint", 0, "Uint", &sString, "int", -1, "Uint", 0, "int", 0)
	VarSetCapacity(wString, nSize * 2)
	DllCall("MultiByteToWideChar", "Uint", 0, "Uint", 0, "Uint", &sString, "int", -1, "Uint", &wString, "int", nSize)
	Return	&wString
}

Ansi4Unicode(pString)
{
	nSize := DllCall("WideCharToMultiByte", "Uint", 0, "Uint", 0, "Uint", pString, "int", -1, "Uint", 0, "int",  0, "Uint", 0, "Uint", 0)
	VarSetCapacity(sString, nSize)
	DllCall("WideCharToMultiByte", "Uint", 0, "Uint", 0, "Uint", pString, "int", -1, "str", sString, "int", nSize, "Uint", 0, "Uint", 0)
	Return	sString
}
So I've been having issues for a long time with transparent windows. All I want to do is to put the clipboard data (which has a perfectly formed screenshot!) onto a GUI. I swapped to using Sean's Screen Capture script as I thought it would work to eliminate the issue however the issue seems to lie with these three lines:

Code: Select all

handle:=DllCall("User32\GetClipboardData",Uint,2)
f:=DllCall("User32\CloseClipboard",int,hwnd)
SendMessage,0x172,0,handle,Static1,ahk_id%hwnd%
Honestly I barely understand how they work. Somehow, in the process of retrieving the data from the clipboard, I loose a chunk of it, specifically related to certain applications with transparent windows. Notepad++ for example. I'm not quite sure how best to do this. Is there something else I can use other than 'GetClipboardData' and 'SendMessage'? I think these actually add it to the GUI? Or is there a better cleaner way of doing this?

I'd really appreciate any help at all, I never anticipated taking a screenshot and putting it on a GUI to be so difficult. If anyone has the time or patience to take a look, that would be amazing! :)
CosmicThing2
Posts: 19
Joined: 07 Oct 2016, 06:37
Location: UK

Re: Putting a screenshot from the clipboard onto a GUI

09 Mar 2018, 10:36

BoBo wrote:Smells like a prank ... :wtf:
What!?

Not a prank at all, it's for a number of users who want a 'freeze screen' function in a multi-monitor setup. It has been really useful so far but this transparent windows thing has been really getting to me.

This script took me a solid week to get going originally and now I'm spending more time fixing bugs with it, why would I bother if it was just a prank... Please give me a hand?

I mean I can write a solid paragraph or two explaining exactly how and why it's used if you would like but I assumed it would kinda put people off if I put an essay in here
User avatar
jeeswg
Posts: 6902
Joined: 19 Dec 2016, 01:58
Location: UK

Re: Putting a screenshot from the clipboard onto a GUI

09 Mar 2018, 18:31

I would welcome the essay. I can't easily see how freezing the screen would be useful, but I don't like when people assume that something can't be useful, so I'm interested.
homepage | tutorials | wish list | fun threads | donate
WARNING: copy your posts/messages before hitting Submit as you may lose them due to CAPTCHA
CosmicThing2
Posts: 19
Joined: 07 Oct 2016, 06:37
Location: UK

Re: Putting a screenshot from the clipboard onto a GUI

12 Mar 2018, 05:08

jeeswg wrote:I would welcome the essay. I can't easily see how freezing the screen would be useful, but I don't like when people assume that something can't be useful, so I'm interested.
Alright, I'm sure I didn't have to explain it last time I posted asking for some help.

I work as an IT Tech in a secondary school, all the teacher PCs have two screens, one being their own monitor and one being the Smartboard on the wall for the students to see. Some teachers set their boards up in Extend mode - so they can drag windows back and forth between the two screens, whereas some prefer to have them in duplicate so both screens will always be identical to make it simpler. Extend is fine and probably what all teachers should be using, however some still don't like this and find it a 'faff' to fiddle with dragging windows around while trying to teach. The issue with duplicate, is that because the screens are identical, confidential information can be put up on the screens accidentally. Maybe the teachers emails or other students personal information e.g.

This script is for the duplicate users. Previously the smartboards would have a 'freeze' button which, with one press, would simply freeze whatever was currently on it. This was useful for teachers maybe doing an explanation (in duplicate) but still want to be able to browse their own emails or whatever on their own screens. Our new smartboards do not have this functionality. Therefore this script mimics that, it copies whatever is on the main monitor to the other screen and 'freezes' it. Although effectively it is just putting a borderless GUI over the top, to the end user it appears as if it's frozen. They are then easily able to unfreeze it with another button whenever needed.

This has worked fine(ish) for a while however it has always had an issue with certain applications. Maybe AHK struggles with retrieving screenshot data from the clipboard? I genuinely don't know. For some programs it's fine, but Notepad++ for example, I can't get a screenshot of it onto a GUI without it missing half the information
BoBo
Posts: 6564
Joined: 13 May 2014, 17:15

Re: Putting a screenshot from the clipboard onto a GUI

12 Mar 2018, 06:30

Thx 4 taking the time to explain what the whole story is about, much appreciated :thumbup:
CosmicThing2
Posts: 19
Joined: 07 Oct 2016, 06:37
Location: UK

Re: Putting a screenshot from the clipboard onto a GUI

12 Mar 2018, 07:50

I might actually, using someone else's idea that posted on a previous question of mine... have sorted it! The script still isn't perfect but it works.

This is it now, the only things I've left out are the 'displayswloc' still (hidden because it shows our server folder structure) and I've also hidden all 3000 lines of the Gdip_All library (found here https://github.com/Masonjar13/AHK-Libra ... ip_All.ahk) which is just underneath this code. It feels so much snappier and works much nicer than before. I've ditched all the 'Dllcall's and replaced with Gdip functions (which are just a collection of Dllcalls anyway... so I don't really get why it seems quite a bit faster, but hey!).

I actually jumped with joy.

Code: Select all

#SingleInstance,Force
#persistent
#include *i <Lib_1>
OnExit, ExitProg

DetectHiddenWindows,On
open = 0
SetBatchLines -1

;Location of where the DisplaySwitch.exe is located - (Note that this *should* be in System32 by default... but Windows is useful)
displayswloc = [location]\Freeze

if(!(p:=Gdip_Startup())){
    msgbox,,Error,Couldn't start GDI+
    ExitApp
}
Gui,+hwndghwnd -caption +alwaysontop +toolWindow
Gui,add,picture,vmp 0xE
GuiControlGet,chwnd,hwnd,mp
return

F8::
;This section detects whether the monitors are in duplicate or extend. If in duplicate, it forces extend
SysGet, monitorcount, MonitorCount 
if (monitorcount < 2) {
	IfExist, C:\Windows\System32\DisplaySwitch.exe 
	{
		Run, C:\Windows\System32\DisplaySwitch.exe /extend
	}
	else 
	{
		FileCopy, %displayswloc%\DisplaySwitch.exe, C:\Windows\System32
		Run, C:\Windows\System32\DisplaySwitch.exe /extend
	}
	Sleep 2500
}

;Hide the existing GUI if it already exists
Gui, cancel

;Determine which is the primary monitor. Then get data about the other one (the smartboard)
SysGet, primary, MonitorPrimary 
if (primary = 1) {
	bfs:=Gdip_BitmapFromScreen(1) ; number indicates which screen
	SysGet, smart, Monitor, 2
}
else {
	bfs:=Gdip_BitmapFromScreen(2) ; number indicates which screen
	SysGet, smart, Monitor, 1
}
	
GuiControl,move,mp,% "x0 y0 w" Gdip_GetImageWidth(bfs) . " h" . Gdip_GetImageHeight(bfs)

hfb:=Gdip_CreateHBITMAPFromBitmap(bfs)
SetImage(chwnd,hfb)

;Need to display the picture differently depending on whether the smartboard is to the left or right of the primary
;If the smartboard is to the left 
if (smartLeft < 0) 
	Gui,Show,% "x-1920 y0 w" . Gdip_GetImageWidth(bfs) . " h" . Gdip_GetImageHeight(bfs)
;If the smartboard is to the right
else	           
	Gui,Show,% "x1920 y0 w" . Gdip_GetImageWidth(bfs) . " h" . Gdip_GetImageHeight(bfs)
	
Gdip_DisposeImage(bfs)
Gdip_DisposeImage(hfb)
return

F9::
;Whether the user pressed yes or no, kill the window and then wait for further input
Gui, cancel
return

F11::
Run, %comspec% /K taskkill /IM SMARTInk.exe /F,, Hide
;Have to make sure it works for both 32 and 64 bit
try {
	Run, C:\Program Files\SMART Technologies\SMART Product Drivers\SmartInk.exe
	MsgBox, 0, Smart Ink, SmartInk process killed and restarted
} catch e {
	try {
		Run, C:\Program Files (x86)\SMART Technologies\SMART Product Drivers\SmartInk.exe
		MsgBox, 0, Smart Ink, SmartInk process killed and restarted
	} catch e {
		MsgBox, 16, Error, Cannot find SmartInk`, are you sure you have SMART Installed?
	}
}
return

;In an emergency or if required, kill the whole script with ctrl-shift-F12
^+F12::
ExitApp

ExitProg:
ExitApp
If anyone would like the whole program, with all 3000 lines of Gdip, it's here: https://pastebin.com/nB73sxYn
The only thing you'll need to do, is to change 'displayswloc' to a valid location. Assuming windows works fine, this won't do anything anyway. This is only because the script requires the PC to be in extend mode to work and if the PC is currently in duplicate, it needs to run 'DisplaySwitch.exe' to switch it. However sometimes windows deletes or hides this exe, it *should* be in System32, but I also copied it to a secondary location in case the script gets stuck.
User avatar
jeeswg
Posts: 6902
Joined: 19 Dec 2016, 01:58
Location: UK

Re: Putting a screenshot from the clipboard onto a GUI

12 Mar 2018, 09:18

- Thanks very much for the explanation. I don't usually ask for one, but this did smell like a prank as BoBo said. I will investigate GUIs, screen capture and transparency.
- One thing I don't understand though. If the screens are on duplicate mode, if you freeze one screen, you freeze both screens, so therefore how can freezing the screen be helpful. Or are you also changing the mode to extend or something else, at that point that you do the freezing.
- Also, if you're going down the AHK route, you might like to consider writing scripts that facilitate everyone using 'extend' mode, by maintaining the cursor within a screen, and easily moving windows to the second monitor. Without sufficient convenience in the day-to-day usage of the software, I'm sympathetic to teachers who don't want to use 'extend' mode.
- [EDIT:] Potentially shortcut keys to switch between monitor modes could be part of the solution. As well an obvious red/green icon to indicate the mode. Even changes/warnings in how some programs are used depending on what the current monitor mode is.
- Also, even if you could get the freeze screen idea to work, it seems inherently more risky, and I would imagine that confidential data would still be revealed from time to time.
- [EDIT:] One user on Stack Overflow, with a high rating, actually questioned why I would even want to automate Explorer windows. I was looking for a cleaner solution to invert the file selection, than the one I currently had, in the end I managed to do it via a PostMessage to the right control.
Last edited by jeeswg on 12 Mar 2018, 09:38, edited 1 time in total.
homepage | tutorials | wish list | fun threads | donate
WARNING: copy your posts/messages before hitting Submit as you may lose them due to CAPTCHA
BoBo
Posts: 6564
Joined: 13 May 2014, 17:15

Re: Putting a screenshot from the clipboard onto a GUI

12 Mar 2018, 09:25

I'd go with a "blurry screen"-layer option, as it won't bear the risk to provide a screenshot of confidential data by mistake.
CosmicThing2
Posts: 19
Joined: 07 Oct 2016, 06:37
Location: UK

Re: Putting a screenshot from the clipboard onto a GUI

12 Mar 2018, 10:44

jeeswg wrote:- Thanks very much for the explanation. I don't usually ask for one, but this did smell like a prank as BoBo said. I will investigate GUIs, screen capture and transparency.
- One thing I don't understand though. If the screens are on duplicate mode, if you freeze one screen, you freeze both screens, so therefore how can freezing the screen be helpful. Or are you also changing the mode to extend or something else, at that point that you do the freezing.
- Also, if you're going down the AHK route, you might like to consider writing scripts that facilitate everyone using 'extend' mode, by maintaining the cursor within a screen, and easily moving windows to the second monitor. Without sufficient convenience in the day-to-day usage of the software, I'm sympathetic to teachers who don't want to use 'extend' mode.
- [EDIT:] Potentially shortcut keys to switch between monitor modes could be part of the solution. As well an obvious red/green icon to indicate the mode. Even changes/warnings in how some programs are used depending on what the current monitor mode is.
- Also, even if you could get the freeze screen idea to work, it seems inherently more risky, and I would imagine that confidential data would still be revealed from time to time.
- [EDIT:] One user on Stack Overflow, with a high rating, actually questioned why I would even want to automate Explorer windows. I was looking for a cleaner solution to invert the file selection, than the one I currently had, in the end I managed to do it via a PostMessage to the right control.
It's fine, no problem.

For the first question - the script swaps the PC to extend just before the second screen (the smartboard) is then frozen. This is why I need to run 'DisplaySwitch.exe', which is Microsoft's program for swapping between Duplicate and Extend (run whenever you press Windows-P on any Windows 10 machine). Unfortunately, sometimes Windows decides to remove or hide this exe, I'm unsure why. So I had to make another copy of it available to all PCs on a network share which the script can access (hence 'displayswloc').

We are trying to move all teachers to using extend, and in fact, most teachers are now using it. You could then ask "Why did you even bother making this script if most teachers are already using extend". Well, some of these users, even using extend want the ability to put a copy of document up on the board for students to read or copy... and still be able to edit it at the same time. This script effectively allows you to 'snapshot' a document, so the students can work on that while the teacher edits it and prepares it for the next topic. They can then simply press 'F8' again and bam, the screen will re-freeze with all the new content. They are able to prepare a document for the next topic, without having to disrupt the learning of the current topic.

Also, some teachers are just stubborn! They refuse to move to extend and say it over-complicates the teaching.

As for shortcut keys, Windows does provide it's own Windows-P. This works fine but teachers always forget it which leads to a panicked call to IT! I could make an icon or something as well as warnings, hadn't thought about that yet.
Last edited by CosmicThing2 on 12 Mar 2018, 10:47, edited 1 time in total.
CosmicThing2
Posts: 19
Joined: 07 Oct 2016, 06:37
Location: UK

Re: Putting a screenshot from the clipboard onto a GUI

12 Mar 2018, 10:46

BoBo wrote:I'd go with a "blurry screen"-layer option, as it won't bear the risk to provide a screenshot of confidential data by mistake.
Blurry screen layer? You mean as a GUI option? I can't find anything about this on the GUI page? (https://autohotkey.com/docs/commands/Gui.htm)
BoBo
Posts: 6564
Joined: 13 May 2014, 17:15

Re: Putting a screenshot from the clipboard onto a GUI

12 Mar 2018, 11:01

WinSet, TransColor, Color [N], WinTitle
Makes all pixels of the chosen color invisible inside the target window, which allows the contents of the window behind it to show through. If the user clicks on an invisible pixel, the click will "fall through" to the window behind it. Specify for Color a color name or RGB value (see the color chart for guidance, or use PixelGetColor in its RGB mode). To additionally make the visible part of the window partially transparent, append a space (not a comma) followed by the transparency level (0-255). For example: WinSet, TransColor, EEAA99 150, WinTitle.

TransColor is often used to create on-screen displays and other visual effects. There is an example of an on-screen display at the bottom of the Gui page.

The word OFF may be specified to completely turn off transparency for a window. Both of the following are identical in function: ...
Thinking of creating a fullscreen monochrome gui(AKA layer) with its color been dimmed using the above method (?)
User avatar
FanaticGuru
Posts: 1906
Joined: 30 Sep 2013, 22:25

Re: Putting a screenshot from the clipboard onto a GUI

15 Mar 2018, 13:08

CosmicThing2 wrote:If anyone would like the whole program, with all 3000 lines of Gdip, it's here: https://pastebin.com/nB73sxYn
Not that it matters greatly but below is a script that freezes the screen and has in the script only the functions required from Gdip to make it work.

Code: Select all

onExit("quit")

if (!(p := Gdip_Startup())){
    msgbox,,Error,Couldn't start GDI+
    exitApp
}
gui,+hwndghwnd -caption +alwaysontop +toolWindow
gui,add,picture,vmp 0xE
guiControlGet,chwnd,hwnd,mp
return

F5::
gui,cancel
bfs := Gdip_BitmapFromScreen(0) ; number indicates which screen
guiControl,move,mp,% "x0 y0 w" Gdip_GetImageWidth(bfs) . " h" . Gdip_GetImageHeight(bfs)
hfb := Gdip_CreateHBITMAPFromBitmap(bfs)
SetImage(chwnd,hfb)
gui,show,% "x0 y0 w" . Gdip_GetImageWidth(bfs) . " h" . Gdip_GetImageHeight(bfs)

Gdip_DisposeImage(bfs)
Gdip_DisposeImage(hfb)
return

F6::gui,cancel

quit() {
    Gdip_Shutdown(p)
    exitApp
}

; FUNCTIONS - LIBRARY
;{-----------------------------------------------
;

BitBlt(ddc, dx, dy, dw, dh, sdc, sx, sy, Raster="")
{
	Ptr := A_PtrSize ? "UPtr" : "UInt"
	
	return DllCall("gdi32\BitBlt"
					, Ptr, dDC
					, "int", dx
					, "int", dy
					, "int", dw
					, "int", dh
					, Ptr, sDC
					, "int", sx
					, "int", sy
					, "uint", Raster ? Raster : 0x00CC0020)
}

CreateCompatibleDC(hdc=0)
{
   return DllCall("CreateCompatibleDC", A_PtrSize ? "UPtr" : "UInt", hdc)
}

CreateDIBSection(w, h, hdc="", bpp=32, ByRef ppvBits=0)
{
	Ptr := A_PtrSize ? "UPtr" : "UInt"
	
	hdc2 := hdc ? hdc : GetDC()
	VarSetCapacity(bi, 40, 0)
	
	NumPut(w, bi, 4, "uint")
	, NumPut(h, bi, 8, "uint")
	, NumPut(40, bi, 0, "uint")
	, NumPut(1, bi, 12, "ushort")
	, NumPut(0, bi, 16, "uInt")
	, NumPut(bpp, bi, 14, "ushort")
	
	hbm := DllCall("CreateDIBSection"
					, Ptr, hdc2
					, Ptr, &bi
					, "uint", 0
					, A_PtrSize ? "UPtr*" : "uint*", ppvBits
					, Ptr, 0
					, "uint", 0, Ptr)

	if !hdc
		ReleaseDC(hdc2)
	return hbm
}

DeleteDC(hdc)
{
   return DllCall("DeleteDC", A_PtrSize ? "UPtr" : "UInt", hdc)
}

DeleteObject(hObject)
{
   return DllCall("DeleteObject", A_PtrSize ? "UPtr" : "UInt", hObject)
}

Gdip_BitmapFromScreen(Screen=0, Raster="")
{
	if (Screen = 0)
	{
		Sysget, x, 76
		Sysget, y, 77	
		Sysget, w, 78
		Sysget, h, 79
	}
	else if (SubStr(Screen, 1, 5) = "hwnd:")
	{
		Screen := SubStr(Screen, 6)
		if !WinExist( "ahk_id " Screen)
			return -2
		WinGetPos,,, w, h, ahk_id %Screen%
		x := y := 0
		hhdc := GetDCEx(Screen, 3)
	}
	else if (Screen&1 != "")
	{
		Sysget, M, Monitor, %Screen%
		x := MLeft, y := MTop, w := MRight-MLeft, h := MBottom-MTop
	}
	else
	{
		StringSplit, S, Screen, |
		x := S1, y := S2, w := S3, h := S4
	}

	if (x = "") || (y = "") || (w = "") || (h = "")
		return -1

	chdc := CreateCompatibleDC(), hbm := CreateDIBSection(w, h, chdc), obm := SelectObject(chdc, hbm), hhdc := hhdc ? hhdc : GetDC()
	BitBlt(chdc, 0, 0, w, h, hhdc, x, y, Raster)
	ReleaseDC(hhdc)
	
	pBitmap := Gdip_CreateBitmapFromHBITMAP(hbm)
	SelectObject(chdc, obm), DeleteObject(hbm), DeleteDC(hhdc), DeleteDC(chdc)
	return pBitmap
}

Gdip_CreateBitmapFromHBITMAP(hBitmap, Palette=0)
{
	Ptr := A_PtrSize ? "UPtr" : "UInt"
	
	DllCall("gdiplus\GdipCreateBitmapFromHBITMAP", Ptr, hBitmap, Ptr, Palette, A_PtrSize ? "UPtr*" : "uint*", pBitmap)
	return pBitmap
}

Gdip_CreateHBITMAPFromBitmap(pBitmap, Background=0xffffffff)
{
	DllCall("gdiplus\GdipCreateHBITMAPFromBitmap", A_PtrSize ? "UPtr" : "UInt", pBitmap, A_PtrSize ? "UPtr*" : "uint*", hbm, "int", Background)
	return hbm
}

Gdip_DisposeImage(pBitmap)
{
   return DllCall("gdiplus\GdipDisposeImage", A_PtrSize ? "UPtr" : "UInt", pBitmap)
}

Gdip_GetImageHeight(pBitmap)
{
   DllCall("gdiplus\GdipGetImageHeight", A_PtrSize ? "UPtr" : "UInt", pBitmap, "uint*", Height)
   return Height
}

Gdip_GetImageWidth(pBitmap)
{
   DllCall("gdiplus\GdipGetImageWidth", A_PtrSize ? "UPtr" : "UInt", pBitmap, "uint*", Width)
   return Width
}

Gdip_Shutdown(pToken)
{
	Ptr := A_PtrSize ? "UPtr" : "UInt"
	
	DllCall("gdiplus\GdiplusShutdown", Ptr, pToken)
	if hModule := DllCall("GetModuleHandle", "str", "gdiplus", Ptr)
		DllCall("FreeLibrary", Ptr, hModule)
	return 0
}

Gdip_Startup()
{
	Ptr := A_PtrSize ? "UPtr" : "UInt"
	
	if !DllCall("GetModuleHandle", "str", "gdiplus", Ptr)
		DllCall("LoadLibrary", "str", "gdiplus")
	VarSetCapacity(si, A_PtrSize = 8 ? 24 : 16, 0), si := Chr(1)
	DllCall("gdiplus\GdiplusStartup", A_PtrSize ? "UPtr*" : "uint*", pToken, Ptr, &si, Ptr, 0)
	return pToken
}

GetDC(hwnd=0)
{
	return DllCall("GetDC", A_PtrSize ? "UPtr" : "UInt", hwnd)
}

GetDCEx(hwnd, flags=0, hrgnClip=0)
{
	Ptr := A_PtrSize ? "UPtr" : "UInt"
	
    return DllCall("GetDCEx", Ptr, hwnd, Ptr, hrgnClip, "int", flags)
}

ReleaseDC(hdc, hwnd=0)
{
	Ptr := A_PtrSize ? "UPtr" : "UInt"
	
	return DllCall("ReleaseDC", Ptr, hwnd, Ptr, hdc)
}

SelectObject(hdc, hgdiobj)
{
	Ptr := A_PtrSize ? "UPtr" : "UInt"
	
	return DllCall("SelectObject", Ptr, hdc, Ptr, hgdiobj)
}

SetImage(hwnd, hBitmap)
{
	SendMessage, 0x172, 0x0, hBitmap,, ahk_id %hwnd%
	E := ErrorLevel
	DeleteObject(E)
	return E
}

;}
I have a script that does this for me to make a script independent from my library.

Gets it down from 3000 to a couple of hundred lines.

FG
Hotkey Help - Help Dialog for Currently Running AHK Scripts
AHK Startup - Consolidate Multiply AHK Scripts with one Tray Icon
Hotstring Manager - Create and Manage Hotstrings
[Class] WinHook - Create Window Shell Hooks and Window Event Hooks
User avatar
jeeswg
Posts: 6902
Joined: 19 Dec 2016, 01:58
Location: UK

Re: Putting a screenshot from the clipboard onto a GUI

15 Mar 2018, 21:18

- I've written a script which toggles between PC only/PC+projector, which uses a familiar image as the image for the second screen.
- I will do some more investigations regarding printscreens, and I might start a new thread on it in the next day or so.
- The script uses displayswitch.exe, and is tested on Windows 7. I don't know which OSes have displayswitch.exe available. Cheers.

Code: Select all

~^Esc:: ;reload script
Reload
return

#p:: ;block Projection options window
return

;tested on Windows 7
q:: ;monitors - toggle PC only / PC + projector
SysGet, vCount, MonitorCount
;MsgBox, % vCount

;if more than one monitor, show one monitor only, and end
if !(vCount = 1)
{
	;remove 'DUAL' notification and remove image
	Progress, Off
	SplashImage, Off
	;show PC only
	Run, displayswitch /internal ;Disconnect Projector
	return
}

;if we reach here, show both monitors

;safety check (only show second screen if Notepad is the active window)
if !WinActive("ahk_class Notepad")
{
	MsgBox, % "error: Notepad must be active"
	return
}

;get image data
hBitmap := LoadPicture(A_AhkPath, "W1280 H720")

;show both monitors
RunWait, displayswitch /extend ;Extend
vPosX := A_ScreenWidth-200

;show 'DUAL' notification
;Progress, % "ZH0 B1 C0 FS18 X" vPosX "Y0 W80 CWFF0000", DUAL
Progress, % "ZH0 B1 C0 FS18 X" vPosX "Y0 W80", DUAL

;get coordinates for monitor 2
SysGet, vMon2, Monitor, 2
vMon2X := vMon2Left, vMon2Y := vMon2Top
vMon2R := vMon2Right, vMon2B := vMon2Bottom
vMon2W := vMon2R-vMon2X, vMon2H := vMon2B-vMon2Y
;vMon2Pos := Format("X{} Y{} R{} B{}", vMon2X, vMon2Y, vMon2R, vMon2B)
;MsgBox, % vMon2Pos ;e.g. X1280 Y0 R2560 B720
vMon2Pos := Format("X{} Y{} W{} H{}", vMon2X, vMon2Y, vMon2W, vMon2H)
;MsgBox, % vMon2Pos ;e.g. X1280 Y0 W1280 H720

;show image
SplashImage, % "HBITMAP:" hBitmap, % "B " vMon2Pos ;B: borderless
return

;RunWait, displayswitch /internal ;Computer only/Disconnect Projector
;RunWait, displayswitch /clone ;Duplicate
;RunWait, displayswitch /extend ;Extend
;RunWait, displayswitch /external ;Projector only
homepage | tutorials | wish list | fun threads | donate
WARNING: copy your posts/messages before hitting Submit as you may lose them due to CAPTCHA
CosmicThing2
Posts: 19
Joined: 07 Oct 2016, 06:37
Location: UK

Re: Putting a screenshot from the clipboard onto a GUI

16 Mar 2018, 09:50

jeeswg wrote:- I've written a script which toggles between PC only/PC+projector, which uses a familiar image as the image for the second screen.
- I will do some more investigations regarding printscreens, and I might start a new thread on it in the next day or so.
- The script uses displayswitch.exe, and is tested on Windows 7. I don't know which OSes have displayswitch.exe available. Cheers.

Code: Select all

~^Esc:: ;reload script
Reload
return

#p:: ;block Projection options window
return

;tested on Windows 7
q:: ;monitors - toggle PC only / PC + projector
SysGet, vCount, MonitorCount
;MsgBox, % vCount

;if more than one monitor, show one monitor only, and end
if !(vCount = 1)
{
	;remove 'DUAL' notification and remove image
	Progress, Off
	SplashImage, Off
	;show PC only
	Run, displayswitch /internal ;Disconnect Projector
	return
}

;if we reach here, show both monitors

;safety check (only show second screen if Notepad is the active window)
if !WinActive("ahk_class Notepad")
{
	MsgBox, % "error: Notepad must be active"
	return
}

;get image data
hBitmap := LoadPicture(A_AhkPath, "W1280 H720")

;show both monitors
RunWait, displayswitch /extend ;Extend
vPosX := A_ScreenWidth-200

;show 'DUAL' notification
;Progress, % "ZH0 B1 C0 FS18 X" vPosX "Y0 W80 CWFF0000", DUAL
Progress, % "ZH0 B1 C0 FS18 X" vPosX "Y0 W80", DUAL

;get coordinates for monitor 2
SysGet, vMon2, Monitor, 2
vMon2X := vMon2Left, vMon2Y := vMon2Top
vMon2R := vMon2Right, vMon2B := vMon2Bottom
vMon2W := vMon2R-vMon2X, vMon2H := vMon2B-vMon2Y
;vMon2Pos := Format("X{} Y{} R{} B{}", vMon2X, vMon2Y, vMon2R, vMon2B)
;MsgBox, % vMon2Pos ;e.g. X1280 Y0 R2560 B720
vMon2Pos := Format("X{} Y{} W{} H{}", vMon2X, vMon2Y, vMon2W, vMon2H)
;MsgBox, % vMon2Pos ;e.g. X1280 Y0 W1280 H720

;show image
SplashImage, % "HBITMAP:" hBitmap, % "B " vMon2Pos ;B: borderless
return

;RunWait, displayswitch /internal ;Computer only/Disconnect Projector
;RunWait, displayswitch /clone ;Duplicate
;RunWait, displayswitch /extend ;Extend
;RunWait, displayswitch /external ;Projector only
That's great, very nice! I can definitely tell you that Windows 10 has DisplaySwitch.exe. Although I have found it to be a bit unreliable at times.

I found extracting data from a printscreen to be the difficult bit. I think I assumed that AHK would be able to just press PrtScn on the keyboard and then add the result from the clipboard to a GUI easily. But it proved to be a lot more finicky than I thought. Also, I'm not a genius at AHK unlike some of you folks!
Last edited by CosmicThing2 on 16 Mar 2018, 09:55, edited 1 time in total.
CosmicThing2
Posts: 19
Joined: 07 Oct 2016, 06:37
Location: UK

Re: Putting a screenshot from the clipboard onto a GUI

16 Mar 2018, 09:51

FanaticGuru wrote:Not that it matters greatly but below is a script that freezes the screen and has in the script only the functions required from Gdip to make it work.

Code: Select all

onExit("quit")

if (!(p := Gdip_Startup())){
    msgbox,,Error,Couldn't start GDI+
    exitApp
}
gui,+hwndghwnd -caption +alwaysontop +toolWindow
gui,add,picture,vmp 0xE
guiControlGet,chwnd,hwnd,mp
return

F5::
gui,cancel
bfs := Gdip_BitmapFromScreen(0) ; number indicates which screen
guiControl,move,mp,% "x0 y0 w" Gdip_GetImageWidth(bfs) . " h" . Gdip_GetImageHeight(bfs)
hfb := Gdip_CreateHBITMAPFromBitmap(bfs)
SetImage(chwnd,hfb)
gui,show,% "x0 y0 w" . Gdip_GetImageWidth(bfs) . " h" . Gdip_GetImageHeight(bfs)

Gdip_DisposeImage(bfs)
Gdip_DisposeImage(hfb)
return

F6::gui,cancel

quit() {
    Gdip_Shutdown(p)
    exitApp
}

; FUNCTIONS - LIBRARY
;{-----------------------------------------------
;

BitBlt(ddc, dx, dy, dw, dh, sdc, sx, sy, Raster="")
{
	Ptr := A_PtrSize ? "UPtr" : "UInt"
	
	return DllCall("gdi32\BitBlt"
					, Ptr, dDC
					, "int", dx
					, "int", dy
					, "int", dw
					, "int", dh
					, Ptr, sDC
					, "int", sx
					, "int", sy
					, "uint", Raster ? Raster : 0x00CC0020)
}

CreateCompatibleDC(hdc=0)
{
   return DllCall("CreateCompatibleDC", A_PtrSize ? "UPtr" : "UInt", hdc)
}

CreateDIBSection(w, h, hdc="", bpp=32, ByRef ppvBits=0)
{
	Ptr := A_PtrSize ? "UPtr" : "UInt"
	
	hdc2 := hdc ? hdc : GetDC()
	VarSetCapacity(bi, 40, 0)
	
	NumPut(w, bi, 4, "uint")
	, NumPut(h, bi, 8, "uint")
	, NumPut(40, bi, 0, "uint")
	, NumPut(1, bi, 12, "ushort")
	, NumPut(0, bi, 16, "uInt")
	, NumPut(bpp, bi, 14, "ushort")
	
	hbm := DllCall("CreateDIBSection"
					, Ptr, hdc2
					, Ptr, &bi
					, "uint", 0
					, A_PtrSize ? "UPtr*" : "uint*", ppvBits
					, Ptr, 0
					, "uint", 0, Ptr)

	if !hdc
		ReleaseDC(hdc2)
	return hbm
}

DeleteDC(hdc)
{
   return DllCall("DeleteDC", A_PtrSize ? "UPtr" : "UInt", hdc)
}

DeleteObject(hObject)
{
   return DllCall("DeleteObject", A_PtrSize ? "UPtr" : "UInt", hObject)
}

Gdip_BitmapFromScreen(Screen=0, Raster="")
{
	if (Screen = 0)
	{
		Sysget, x, 76
		Sysget, y, 77	
		Sysget, w, 78
		Sysget, h, 79
	}
	else if (SubStr(Screen, 1, 5) = "hwnd:")
	{
		Screen := SubStr(Screen, 6)
		if !WinExist( "ahk_id " Screen)
			return -2
		WinGetPos,,, w, h, ahk_id %Screen%
		x := y := 0
		hhdc := GetDCEx(Screen, 3)
	}
	else if (Screen&1 != "")
	{
		Sysget, M, Monitor, %Screen%
		x := MLeft, y := MTop, w := MRight-MLeft, h := MBottom-MTop
	}
	else
	{
		StringSplit, S, Screen, |
		x := S1, y := S2, w := S3, h := S4
	}

	if (x = "") || (y = "") || (w = "") || (h = "")
		return -1

	chdc := CreateCompatibleDC(), hbm := CreateDIBSection(w, h, chdc), obm := SelectObject(chdc, hbm), hhdc := hhdc ? hhdc : GetDC()
	BitBlt(chdc, 0, 0, w, h, hhdc, x, y, Raster)
	ReleaseDC(hhdc)
	
	pBitmap := Gdip_CreateBitmapFromHBITMAP(hbm)
	SelectObject(chdc, obm), DeleteObject(hbm), DeleteDC(hhdc), DeleteDC(chdc)
	return pBitmap
}

Gdip_CreateBitmapFromHBITMAP(hBitmap, Palette=0)
{
	Ptr := A_PtrSize ? "UPtr" : "UInt"
	
	DllCall("gdiplus\GdipCreateBitmapFromHBITMAP", Ptr, hBitmap, Ptr, Palette, A_PtrSize ? "UPtr*" : "uint*", pBitmap)
	return pBitmap
}

Gdip_CreateHBITMAPFromBitmap(pBitmap, Background=0xffffffff)
{
	DllCall("gdiplus\GdipCreateHBITMAPFromBitmap", A_PtrSize ? "UPtr" : "UInt", pBitmap, A_PtrSize ? "UPtr*" : "uint*", hbm, "int", Background)
	return hbm
}

Gdip_DisposeImage(pBitmap)
{
   return DllCall("gdiplus\GdipDisposeImage", A_PtrSize ? "UPtr" : "UInt", pBitmap)
}

Gdip_GetImageHeight(pBitmap)
{
   DllCall("gdiplus\GdipGetImageHeight", A_PtrSize ? "UPtr" : "UInt", pBitmap, "uint*", Height)
   return Height
}

Gdip_GetImageWidth(pBitmap)
{
   DllCall("gdiplus\GdipGetImageWidth", A_PtrSize ? "UPtr" : "UInt", pBitmap, "uint*", Width)
   return Width
}

Gdip_Shutdown(pToken)
{
	Ptr := A_PtrSize ? "UPtr" : "UInt"
	
	DllCall("gdiplus\GdiplusShutdown", Ptr, pToken)
	if hModule := DllCall("GetModuleHandle", "str", "gdiplus", Ptr)
		DllCall("FreeLibrary", Ptr, hModule)
	return 0
}

Gdip_Startup()
{
	Ptr := A_PtrSize ? "UPtr" : "UInt"
	
	if !DllCall("GetModuleHandle", "str", "gdiplus", Ptr)
		DllCall("LoadLibrary", "str", "gdiplus")
	VarSetCapacity(si, A_PtrSize = 8 ? 24 : 16, 0), si := Chr(1)
	DllCall("gdiplus\GdiplusStartup", A_PtrSize ? "UPtr*" : "uint*", pToken, Ptr, &si, Ptr, 0)
	return pToken
}

GetDC(hwnd=0)
{
	return DllCall("GetDC", A_PtrSize ? "UPtr" : "UInt", hwnd)
}

GetDCEx(hwnd, flags=0, hrgnClip=0)
{
	Ptr := A_PtrSize ? "UPtr" : "UInt"
	
    return DllCall("GetDCEx", Ptr, hwnd, Ptr, hrgnClip, "int", flags)
}

ReleaseDC(hdc, hwnd=0)
{
	Ptr := A_PtrSize ? "UPtr" : "UInt"
	
	return DllCall("ReleaseDC", Ptr, hwnd, Ptr, hdc)
}

SelectObject(hdc, hgdiobj)
{
	Ptr := A_PtrSize ? "UPtr" : "UInt"
	
	return DllCall("SelectObject", Ptr, hdc, Ptr, hgdiobj)
}

SetImage(hwnd, hBitmap)
{
	SendMessage, 0x172, 0x0, hBitmap,, ahk_id %hwnd%
	E := ErrorLevel
	DeleteObject(E)
	return E
}

;}
I have a script that does this for me to make a script independent from my library.

Gets it down from 3000 to a couple of hundred lines.

FG
That's great, yeah I may cut it down like that. It doesn't matter too much but it's nice to make the code simpler and easier to read!

Cheers
User avatar
FanaticGuru
Posts: 1906
Joined: 30 Sep 2013, 22:25

Re: Putting a screenshot from the clipboard onto a GUI

16 Mar 2018, 13:56

CosmicThing2 wrote:That's great, yeah I may cut it down like that. It doesn't matter too much but it's nice to make the code simpler and easier to read!
You can probably just replace all the Gdip library in your script with everything below:

Code: Select all

; FUNCTIONS - LIBRARY
;{-----------------------------------------------
;
in my script.

Your script probably does not use any functions from Gdip that mine does not use. (I use 'mine' loosely, as I did not really write any of this)

FG
Hotkey Help - Help Dialog for Currently Running AHK Scripts
AHK Startup - Consolidate Multiply AHK Scripts with one Tray Icon
Hotstring Manager - Create and Manage Hotstrings
[Class] WinHook - Create Window Shell Hooks and Window Event Hooks
User avatar
jeeswg
Posts: 6902
Joined: 19 Dec 2016, 01:58
Location: UK

Re: Putting a screenshot from the clipboard onto a GUI

16 Mar 2018, 22:05

- Here is a simple script to send the Printscreen button and then retrieve the image from the clipboard, and display the image. Ideally the image would be created without using the clipboard.

Code: Select all

q:: ;printscreen to clipboard + SplashImage
Clipboard := ""
SendInput, {PrintScreen}
ClipWait, 3, 1
if ErrorLevel
	return
if DllCall("user32\IsClipboardFormatAvailable", UInt,0x2) ;CF_BITMAP := 0x2
&& DllCall("user32\OpenClipboard", Ptr,0)
{
	hBitmap := DllCall("user32\GetClipboardData", UInt,0x2, Ptr) ;CF_BITMAP := 0x2
	DllCall("user32\CloseClipboard")
}
if !hBitmap
	return
SplashImage, % "HBITMAP:" hBitmap, B ;B: borderless
Sleep, 2000
SplashImage, Off
DllCall("gdi32\DeleteObject", Ptr,hBitmap)
return
- You could create a modified version of the CaptureScreen function to return the hBitmap (hBM) directly, return hBM, hBM := CaptureScreenMod(...), the script doesn't need to put the image data on the clipboard.
- Btw does anyone know why the Gdip_CreateBitmapFromClipboard function checks for CF_DIB (0x8), but then retrieves CF_BITMAP (0x2).
- Btw where possible, I think that using the Gdip library is a good route to take, even if most of the functions aren't used. The way I see it, Gdip and Acc, are like semi-official parts of AutoHotkey. The Gdip library also has the Gdip_SaveBitmapToFile function, it can be useful to store the images for future use.
- Btw does anyone have ideas re. the transparency issue?
homepage | tutorials | wish list | fun threads | donate
WARNING: copy your posts/messages before hitting Submit as you may lose them due to CAPTCHA
User avatar
jeeswg
Posts: 6902
Joined: 19 Dec 2016, 01:58
Location: UK

Re: Putting a screenshot from the clipboard onto a GUI

17 Mar 2018, 17:42

Do any of the scripts here solve your printscreen issues, including transparency issues?
printscreen: common issues (e.g. include ToolTips and cursor) - AutoHotkey Community
https://autohotkey.com/boards/viewtopic.php?f=5&t=45778
homepage | tutorials | wish list | fun threads | donate
WARNING: copy your posts/messages before hitting Submit as you may lose them due to CAPTCHA
burque505
Posts: 1736
Joined: 22 Jan 2017, 19:37

Re: Putting a screenshot from the clipboard onto a GUI

17 Mar 2018, 18:20

Hi jeeswg, re that question
- Btw does anyone know why the Gdip_CreateBitmapFromClipboard function checks for CF_DIB (0x8), but then retrieves CF_BITMAP (0x2).
I think the answer is here.
The system implicitly converts data between certain clipboard formats: if a window requests data in a format that is not on the clipboard, the system converts an available format to the requested format. The system can convert data as indicated in the following table.
clipboard formats.PNG
clipboard formats.PNG (8.88 KiB) Viewed 3482 times
Thanks for that script, by the way.
Regards,
burque505

Return to “Ask for Help (v1)”

Who is online

Users browsing this forum: roysubs and 286 guests