Capture Cursor to File / Compare to File Topic is solved

Get help with using AutoHotkey (v2 or newer) and its commands and hotkeys
User avatar
WarlordAkamu67
Posts: 219
Joined: 21 Mar 2023, 06:52

Capture Cursor to File / Compare to File

Post by WarlordAkamu67 » 07 Jun 2023, 13:56

I have been using v1 with: https://www.autohotkey.com/board/topic/73686-compare-current-cursor-to-saved-image/

I tried converting into v2 with: https://github.com/mmikeww/AHK-v2-script-converter

This error is revcieved.
ccerror.png
ccerror.png (17.81 KiB) Viewed 902 times
I know very little about DllCalls, and have attempted a few very small scripts, but alas to no avail. What I think I know...
https://learn.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-writefile says that the 2nd argument is the buffer.
We are giving it a 64 integer.
syntax.png
syntax.png (10.84 KiB) Viewed 902 times
arg2.PNG
arg2.PNG (10.73 KiB) Viewed 902 times
Help with updating, finding another source, ect. are all appreciated.

Off Topic:
I would like some assistance with figuring out DllCalls if possible, so that I may work with them on my own in the future. I currently do not know enough to work on them. Maybe some small examples? (Maybe along the lines of the current topic).

My version of the v2 script

Code: Select all

SendMode("Input")  ; Recommended for new scripts due to its superior speed and reliability.
SetWorkingDir(A_ScriptDir)  ; Ensures a consistent starting directory.

#SingleInstance Force
^\::ExitApp()

;capturing current mouse cursor and save to "saved_Cursor.bmp"
F11:: {
	CaptureCursor("saved_Cursor.bmp")
return
}

;compare to testCursor.bmp and current mouse cursor
F12:: {
	Loop
	{
		ToolTip(IsMatchCursor("saved_Cursor.bmp")? "Match" : "Unmatch")
		Sleep(100)
	}
return
}


;---------------------------------------------------------------
;	CaptureCursor and IsMatchCursor
;---------------------------------------------------------------
;captureTo: "clipboard"=save to clipboard , "bitmap_handle"=return current cursor bitmap handle.
;return: 0=fail , 1=success
CaptureCursor(captureTo:="A_Clipboard") {
	CURSORINFO := Buffer(20, 0), CURSORINFO := Chr(20) ; V1toV2: if 'CURSORINFO' is a UTF-16 string, use 'VarSetStrCapacity(&CURSORINFO, 20)'
	ICONINFO := Buffer(A_PtrSize=8? 32:20, 0) ; V1toV2: if 'ICONINFO' is a UTF-16 string, use 'VarSetStrCapacity(&ICONINFO, A_PtrSize=8? 32:20)'
	NumPut("UInt", A_PtrSize=8? 24:20, CURSORINFO, 0)
	DllCall("GetCursorInfo", "Uint", CURSORINFO)
	hCursor := NumGet(CURSORINFO, 8, "Uint")
	flags := NumGet(CURSORINFO, 4, "UInt")
	if (!hCursor || !flags) {
		return 0
}
	hCursor := DllCall("CopyIcon", "Uint", hCursor)
	DllCall("GetIconInfo", "UPTR", hCursor, "UPTR", ICONINFO)

	mDC := DllCall("CreateCompatibleDC", "Uint", 0)
	hBM := CreateDIBSection(mDC, 32, 32)
	oBM := DllCall("SelectObject", "Uint", mDC, "Uint", hBM)

	DllCall("DrawIcon", "Uint", mDC, "int", 0, "int", 0, "Uint", hCursor)
	DllCall("DestroyIcon", "Uint", hCursor)
	DllCall("SelectObject", "Uint", mDC, "Uint", oBM)
	DllCall("DeleteDC", "Uint", mDC)

	If (hbmMask := NumGet(ICONINFO, A_PtrSize=8? 16:12, "UPtr")) {
		DllCall("DeleteObject", "UPTR", hbmMask)
}
	If (hbmColor := NumGet(ICONINFO, A_PtrSize=8? 24:16, "UPtr")) {
		DllCall("DeleteObject", "UPTR", hbmColor)
}

	if (captureTo=bitmap_handle) {
		return hBM
}
	If (captureTo=A_Clipboard) {
		SetClipboardData(hBM)
} else {
		SaveHBITMAPToFile(hBM, captureTo)
	DllCall("DeleteObject", "Uint", hBM)
	return 1
}
}

;compare cursor bmp file to current mouse cursor.
; 1 : cursor image match
; 0 : cursor image unmatch
; ""; hide mouse cursor or can't get cursor handle.
IsMatchCursor(bmpCursorFile) {
	if (!hCursorBmp := CaptureCursor("bitmap_handle")) {
		return ""
}
	hSourceBmp := LoadBMP(bmpCursorFile)
	return !CompareBitmap(hSourceBmp, hCursorBmp, 32)
}


;---------------------------------------------------------------
;	Sub function
;---------------------------------------------------------------
;this function takes two bitmaps and compares the first 32x32 pixel square on them
;hBM1 and hBM2: bitmap handle
;return: 0=match, 1=unmatch
CompareBitmap(hBM1, hBM2, size:=32) {
	x := "0"
	mDC1 := DllCall("CreateCompatibleDC", "Uint", 0)			;create DC compatible with screen
	mDC2 := DllCall("CreateCompatibleDC", "Uint", 0)
	oBM1 := DllCall("SelectObject", "UPTR", mDC1, "UPTR", hBM1)	;put the object in the device context
	oBM2 := DllCall("SelectObject", "UPTR", mDC2, "UPTR", hBM2)
	while (x < size) {
		y := "0"
		while (y < size) {
			color1 := DllCall("GetPixel", "UPTR", mDC1, "int", x, "int", y)	;get the RGB of pixel (x, y)
			color2 := DllCall("GetPixel", "UPTR", mDC2, "int", x, "int", y)
			if (color1 != color2)	;if colors are different, didn't match
				return 1
			y+=1
		}
		x+=1
	}
	DllCall("SelectObject", "UPTR", mDC1, "UPTR", oBM1)	;put the original contents back in DC
	DllCall("SelectObject", "UPTR", mDC2, "UPTR", oBM2)
	DllCall("DeleteDC", "UPTR", mDC1)					;delete DC (prevent memory leak)
	DllCall("DeleteDC", "UPTR", mDC2)
	DllCall("DeleteObject", "UPTR", hBM1)				;delete the images in memory
	DllCall("DeleteObject", "UPTR", hBM2)
	return 0	;0 return if match
}

CreateDIBSection(hDC, nW, nH, bpp := 32, &pBits := "") {
	BITMAPINFO := Buffer(44, 0) ; V1toV2: if 'BITMAPINFO' is a UTF-16 string, use 'VarSetStrCapacity(&BITMAPINFO, 44)'
	NumPut("UInt", 44, BITMAPINFO, 0)
	NumPut("Int", nW, BITMAPINFO, 4)
	NumPut("Int", nH, BITMAPINFO, 8)
	NumPut("UShort", 1, BITMAPINFO, 12)
	NumPut("UShort", bpp, BITMAPINFO, 14)
	Return DllCall("gdi32\CreateDIBSection", "Uint", hDC, "Uint", BITMAPINFO, "Uint", 0, "Uint", pBits, "Uint", 0, "Uint", 0)
}

SetClipboardData(hBitmap) {
	DIBSECTION := Buffer(A_PtrSize=8? 104:84, 0) ; V1toV2: if 'DIBSECTION' is a UTF-16 string, use 'VarSetStrCapacity(&DIBSECTION, A_PtrSize=8? 104:84)'
	NumPut("UInt", 40, DIBSECTION, A_PtrSize=8? 32:24)	;dsBmih.biSize
	DllCall("GetObject", "UPTR", hBitmap, "int", A_PtrSize=8? 104:84, "UPTR", DIBSECTION)
	biSizeImage := NumGet(DIBSECTION, A_PtrSize=8? 52:44, "UInt")
	hDIB :=	DllCall("GlobalAlloc", "Uint", 2, "Uint", 40+biSizeImage)
	pDIB :=	DllCall("GlobalLock", "UPTR", hDIB)
	DllCall("RtlMoveMemory", "UPTR", pDIB, "UPTR", DIBSECTION + (A_PtrSize=8? 32:24), "Uint", 40)
	DllCall("RtlMoveMemory", "UPtr", pDIB+40, "Uint", NumGet(DIBSECTION, A_PtrSize=8? 24:20, "UPtr"), "Uint", biSizeImage)
	DllCall("GlobalUnlock", "UPTR", hDIB)
	DllCall("DeleteObject", "UPTR", hBitmap)
	DllCall("OpenClipboard", "Uint", 0)
	DllCall("EmptyClipboard")
	DllCall("SetClipboardData", "Uint", 8, "UPTR", hDIB)
	DllCall("CloseClipboard")
}

LoadBMP(bmpFile) {
	bmpFile := GetValidFilePath(bmpFile)
	hBmp := DllCall("LoadImage", "Uint", 0, "str", bmpFile, "Uint", 0, "int", 32, "int", 32, "Uint", 0x00000010) ;load the image from file
	return hBmp
}

SaveHBITMAPToFile(hBitmap, sFile) {
	sFile := GetValidFilePath(sFile)
	DIBSECTION := Buffer(A_PtrSize=8? 104:84, 0) ; V1toV2: if 'DIBSECTION' is a UTF-16 string, use 'VarSetStrCapacity(&DIBSECTION, A_PtrSize=8? 104:84)'
	NumPut("UInt", 40, DIBSECTION, A_PtrSize=8? 32:24)	;dsBmih.biSize
	DllCall("GetObject", "Uint", hBitmap, "int", A_PtrSize=8? 104:84, "Uint", DIBSECTION)
	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+(biSizeImage:=NumGet(DIBSECTION, A_PtrSize=8? 52:44, "UInt"))<<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", DIBSECTION + (A_PtrSize=8? 32:24), "Uint", 40, "UintP", &0, "Uint", 0)
	DllCall("WriteFile", "Uint", hFile, "Uint", NumGet(DIBSECTION, A_PtrSize=8? 24:20, "UPtr"), "Uint", biSizeImage, "UintP", &0, "Uint", 0)
	DllCall("CloseHandle", "Uint", hFile)
}

GetValidFilePath(filename) {
	SplitPath(filename, , &sDir, &sExt, &sName)
	if !InStr(sDir, ":")
		sDir := A_ScriptDir . "\" . sDir
	filename := sDir . "\" . sName . "." . sExt
	; StrReplace() is not case sensitive
	; check for StringCaseSense in v1 source script
	; and change the CaseSense param in StrReplace() if necessary
	filename := StrReplace(filename, "\\", "\")
	return filename
}

;---------------------------------------------------------------
;	Struct List
;---------------------------------------------------------------
/*

typedef struct {
  DWORD   cbSize;
  DWORD   flags;
  HCURSOR hCursor;
  POINT   ptScreenPos;
} CURSORINFO, *PCURSORINFO, *LPCURSORINFO;

typedef struct _ICONINFO {
  BOOL    fIcon;
  DWORD   xHotspot;
  DWORD   yHotspot;
  HBITMAP hbmMask;
  HBITMAP hbmColor;
} ICONINFO, *PICONINFO;

typedef struct tagDIBSECTION {
  BITMAP           dsBm;
  BITMAPINFOHEADER dsBmih;
  DWORD            dsBitfields[3];
  HANDLE           dshSection;
  DWORD            dsOffset;
} DIBSECTION, *PDIBSECTION;

typedef struct tagBITMAPINFOHEADER {
  DWORD biSize;
  LONG  biWidth;
  LONG  biHeight;
  WORD  biPlanes;
  WORD  biBitCount;
  DWORD biCompression;
  DWORD biSizeImage;
  LONG  biXPelsPerMeter;
  LONG  biYPelsPerMeter;
  DWORD biClrUsed;
  DWORD biClrImportant;
} BITMAPINFOHEADER;

typedef struct tagBITMAP {
  LONG   bmType;
  LONG   bmWidth;
  LONG   bmHeight;
  LONG   bmWidthBytes;
  WORD   bmPlanes;
  WORD   bmBitsPixel;
  LPVOID bmBits;
} BITMAP, *PBITMAP;

typedef struct tagBITMAPINFO {
  BITMAPINFOHEADER bmiHeader;
  RGBQUAD          bmiColors[1];
} BITMAPINFO, *PBITMAPINFO;

typedef struct tagRGBQUAD {
  BYTE rgbBlue;
  BYTE rgbGreen;
  BYTE rgbRed;
  BYTE rgbReserved;
} RGBQUAD;

*/

User avatar
WarlordAkamu67
Posts: 219
Joined: 21 Mar 2023, 06:52

Re: Capture Cursor to File / Compare to File

Post by WarlordAkamu67 » 16 Jun 2023, 11:12

I've trying to work with DllCall and have been browsing https://learn.microsoft.com/en-us/windows/win32/api/. There is a lot for me to learn still :crazy: . I started fresh and copied over some things, but I am starting from the beginning.

I saw on https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-getcursor that this function returns the handle to the cursor.

https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-getcursorinfo simply returns success, but takes a variable. It takes "A pointer to a CURSORINFO structure"? So we placing the cursor information in a variable, or object, or object property? In this case it appears to work with a Buffer Object named "c_Info".
I changed

Code: Select all

CURSORINFO := Buffer(20, 0), CURSORINFO := Chr(20)
to

Code: Select all

c_Info := Buffer(20)

Code: Select all

DllCall("GetCursorInfo", "Uint", CURSORINFO)
became

Code: Select all

DllCall("GetCursorInfo", "Ptr", c_Info)
I am also still checking what I thought was the handle, NumGet(8). It does not return the same value as DllCall("GetCursor").
I copied over part of the code that creates the file, and modified it so I am atleast not recieving errors, and a file is created, but I am unsure of how to get the information (probably the CursorStructure?) into the file creation. I removed the DllCall("GetObject...") that is (probably) responsible for doing so because I have been unable to work it out just yet. I am getting error for hBM not being set and such. Stepping through it creates an empty file.

Are there any suggested guides for intro to Dll stuff? Obviously tinkering around with it is what helps the most, trying things out and debugging/trouble shooting. Many resources onlines talk about creating DLLs. Some day I may craft my own little Library :D

Code: Select all

#Requires AutoHotkey v2.0

toggle := 0
return

; ------------------------- ; ------------------------- ; ------------------------- ; ------------------------- ; ------------------------- ; ------------------------- ; ------------------------- ; ------------------------- ; ------------------------- ; ------------------------- ;

F2:: {
  Reload
}

; ------------------------- ; ------------------------- ; ------------------------- ; ------------------------- ; ------------------------- ; ------------------------- ; ------------------------- ; ------------------------- ; ------------------------- ; ------------------------- ;

F1:: {
  global
  toggle := !toggle
  if (toggle) {
    c_Info := Buffer(20)
    SetTimer(update_TT, 100)
} else {
    SetTimer(update_TT, 0)
    ToolTip()

/*
    mDC := DllCall("CreateCompatibleDC", "Uint", 0)
  	hBM := CreateDIBSection(mDC, 32, 32)

    DIBSECTION := Buffer(A_PtrSize=8? 104:84, 0)
  	NumPut("UInt", 40, DIBSECTION, A_PtrSize=8? 32:24)
  	DllCall("GetObject", "Uint", hBM, "int", A_PtrSize=8? 104:84, "Uint", DIBSECTION)

    sFile := A_ScriptDir "\tester.bmp"
    hFile:=	DllCall("CreateFile", "Str", sFile, "Uint", 0x40000000, "Uint", 0, "Uint", 0, "Uint", 2, "Uint", 0, "Uint", 0)
    DllCall("WriteFile", "Uint", hFile, "int64P", 52, "Uint", 6, "UintP", 0, "Uint", 0)
    DllCall("WriteFile", "Uint", hFile, "int64P", 54, "Uint", 8, "UintP", 0, "Uint", 0)
    DllCall("WriteFile", "Uint", hFile, "Uint", 32, "Uint", 40, "UintP", 0, "Uint", 0)
    DllCall("WriteFile", "Uint", hFile, "Uint", 24, "Uint", 52, "UintP", 0, "Uint", 0)
    DllCall("CloseHandle", "Uint", hFile)
*/

}

return
}

; ------------------------- ; ------------------------- ; ------------------------- ; ------------------------- ; ------------------------- ; ------------------------- ; ------------------------- ; ------------------------- ; ------------------------- ; ------------------------- ;

update_TT() {
  global

  cursor_H := DllCall("GetCursor")

  DllCall("GetCursorInfo", "Ptr", c_Info)
  hCursor := NumGet(c_Info, 8, "Uint")

  ToolTip(cursor_H " vs " hCursor)

  return
}

; ------------------------- ; ------------------------- ; ------------------------- ; ------------------------- ; ------------------------- ; ------------------------- ; ------------------------- ; ------------------------- ; ------------------------- ; ------------------------- ;

neogna2
Posts: 586
Joined: 15 Sep 2016, 15:44

Re: Capture Cursor to File / Compare to File

Post by neogna2 » 17 Jun 2023, 13:45

Some General DllCall tips:

Besides the v2 DllCall documentation check out jeeswg's DllCall and structs tutorial which is for v1 but many explanations are still useful.

Use jNizM's script to translate types from Microsoft's WinAPI pages to v2 DllCall types.

Use https://www.magnumdb.com/ to look up values for constants in case Microsoft's documentation doesn't include the value.

User avatar
WarlordAkamu67
Posts: 219
Joined: 21 Mar 2023, 06:52

Re: Capture Cursor to File / Compare to File

Post by WarlordAkamu67 » 27 Jun 2023, 12:41

This code runs without giving me any errors. F12, the matching function, all ways reports unmatched. F11, the save function saves a blank bitmap in the script folder.

I continued to run the script, with each error I changed some of the "Int" to "Ptr" or whatever the respective data type should be. I looked at both https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-geticoninfo (and other functions) directly from Microsoft, as well as WinAPI DataTypes https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-geticoninfo.

Code: Select all

SendMode("Input")  ; Recommended for new scripts due to its superior speed and reliability.
SetWorkingDir(A_ScriptDir)  ; Ensures a consistent starting directory.

#SingleInstance Force
^\::ExitApp()

;capturing current mouse cursor and save to "saved_Cursor.bmp"
F11:: {
	CaptureCursor("saved_Cursor.bmp")
return
}

;compare to testCursor.bmp and current mouse cursor
F12:: {
	Loop
	{
		ToolTip(IsMatchCursor("saved_Cursor.bmp")? "Match" : "Unmatch")
		Sleep(100)
	}
return
}

;---------------------------------------------------------------
;	CaptureCursor and IsMatchCursor
;---------------------------------------------------------------
;captureTo: "clipboard"=save to clipboard , "bitmap_handle"=return current cursor bitmap handle.
;return: 0=fail , 1=success
CaptureCursor(captureTo:="A_Clipboard") {
	global
	CURSORINFO := Buffer(20) ; CURSORINFO := Chr(20)
	ICONINFO := Buffer(A_PtrSize=8? 32:20, 0)
	NumPut("UInt", (A_PtrSize = 8 ? 24:20), CURSORINFO, 0)
	DllCall("GetCursorInfo", "Ptr", CURSORINFO)
	hCursor := NumGet(CURSORINFO, 8, "Uint")
	flags := NumGet(CURSORINFO, 4, "UInt")
	if (!hCursor || !flags) {
		return 0
}
	hCursor := DllCall("CopyIcon", "Uint", hCursor)
	DllCall("GetIconInfo", "Ptr", hCursor, "Ptr", ICONINFO)

	mDC := DllCall("CreateCompatibleDC", "Uint", 0)
	hBM := CreateDIBSection(mDC, 32, 32)
	oBM := DllCall("SelectObject", "Uint", mDC, "Uint", hBM)

	DllCall("DrawIcon", "Uint", mDC, "int", 0, "int", 0, "Uint", hCursor)
	DllCall("DestroyIcon", "Uint", hCursor)
	DllCall("SelectObject", "Uint", mDC, "Uint", oBM)
	DllCall("DeleteDC", "Uint", mDC)

	If (hbmMask := NumGet(ICONINFO, A_PtrSize=8? 16:12, "UPtr")) {
		DllCall("DeleteObject", "UPTR", hbmMask)
}
	If (hbmColor := NumGet(ICONINFO, A_PtrSize=8? 24:16, "UPtr")) {
		DllCall("DeleteObject", "UPTR", hbmColor)
}

	if (captureTo="bitmap_handle") {
		return hBM
}
	If (captureTo=A_Clipboard) {
		SetClipboardData(hBM)
} else {
		SaveHBITMAPToFile(hBM, captureTo)
	DllCall("DeleteObject", "Uint", hBM)
	return 1
}
}

;compare cursor bmp file to current mouse cursor.
; 1 : cursor image match
; 0 : cursor image unmatch
; ""; hide mouse cursor or can't get cursor handle.
IsMatchCursor(bmpCursorFile) {
	if (!hCursorBmp := CaptureCursor("bitmap_handle")) {
		return ""
}
	hSourceBmp := LoadBMP(bmpCursorFile)
	return !CompareBitmap(hSourceBmp, hCursorBmp, 32)
}


;---------------------------------------------------------------
;	Sub function
;---------------------------------------------------------------
;this function takes two bitmaps and compares the first 32x32 pixel square on them
;hBM1 and hBM2: bitmap handle
;return: 0=match, 1=unmatch
CompareBitmap(hBM1, hBM2, size:=32) {
	x := "0"
	mDC1 := DllCall("CreateCompatibleDC", "Uint", 0)			;create DC compatible with screen
	mDC2 := DllCall("CreateCompatibleDC", "Uint", 0)
	oBM1 := DllCall("SelectObject", "UPTR", mDC1, "UPTR", hBM1)	;put the object in the device context
	oBM2 := DllCall("SelectObject", "UPTR", mDC2, "UPTR", hBM2)
	while (x < size) {
		y := "0"
		while (y < size) {
			color1 := DllCall("GetPixel", "UPTR", mDC1, "int", x, "int", y)	;get the RGB of pixel (x, y)
			color2 := DllCall("GetPixel", "UPTR", mDC2, "int", x, "int", y)
			if (color1 != color2)	;if colors are different, didn't match
				return 1
			y+=1
		}
		x+=1
	}
	DllCall("SelectObject", "UPTR", mDC1, "UPTR", oBM1)	;put the original contents back in DC
	DllCall("SelectObject", "UPTR", mDC2, "UPTR", oBM2)
	DllCall("DeleteDC", "UPTR", mDC1)					;delete DC (prevent memory leak)
	DllCall("DeleteDC", "UPTR", mDC2)
	DllCall("DeleteObject", "UPTR", hBM1)				;delete the images in memory
	DllCall("DeleteObject", "UPTR", hBM2)
	return 0	;0 return if match
}

CreateDIBSection(hDC, nW, nH, bpp := 32, &pBits := "None") {
	BITMAPINFO := Buffer(44, 0) ; V1toV2: if 'BITMAPINFO' is a UTF-16 string, use 'VarSetStrCapacity(&BITMAPINFO, 44)'
	NumPut("UInt", 44, BITMAPINFO, 0)
	NumPut("Int", nW, BITMAPINFO, 4)
	NumPut("Int", nH, BITMAPINFO, 8)
	NumPut("UShort", 1, BITMAPINFO, 12)
	NumPut("UShort", bpp, BITMAPINFO, 14)
	Return DllCall("gdi32\CreateDIBSection", "Ptr", hDC, "Ptr", BITMAPINFO, "Uint", 0, "Str", pBits, "Uint", 0, "Uint", 0)
}

SetClipboardData(hBitmap) {
	DIBSECTION := Buffer(A_PtrSize=8? 104:84, 0) ; V1toV2: if 'DIBSECTION' is a UTF-16 string, use 'VarSetStrCapacity(&DIBSECTION, A_PtrSize=8? 104:84)'
	NumPut("UInt", 40, DIBSECTION, A_PtrSize=8? 32:24)	;dsBmih.biSize
	DllCall("GetObject", "UPTR", hBitmap, "int", A_PtrSize=8? 104:84, "UPTR", DIBSECTION)
	global biSizeImage := NumGet(DIBSECTION, A_PtrSize=8? 52:44, "UInt")
	hDIB :=	DllCall("GlobalAlloc", "Uint", 2, "Uint", 40+biSizeImage)
	pDIB :=	DllCall("GlobalLock", "UPTR", hDIB)
	DllCall("RtlMoveMemory", "UPTR", pDIB, "UPTR", DIBSECTION + (A_PtrSize=8? 32:24), "Uint", 40)
	DllCall("RtlMoveMemory", "UPtr", pDIB+40, "Uint", NumGet(DIBSECTION, A_PtrSize=8? 24:20, "UPtr"), "Uint", biSizeImage)
	DllCall("GlobalUnlock", "UPTR", hDIB)
	DllCall("DeleteObject", "UPTR", hBitmap)
	DllCall("OpenClipboard", "Uint", 0)
	DllCall("EmptyClipboard")
	DllCall("SetClipboardData", "Uint", 8, "UPTR", hDIB)
	DllCall("CloseClipboard")
}

LoadBMP(bmpFile) {
	bmpFile := GetValidFilePath(bmpFile)
	hBmp := DllCall("LoadImage", "Uint", 0, "str", bmpFile, "Uint", 0, "int", 32, "int", 32, "Uint", 0x00000010) ;load the image from file
	return hBmp
}

SaveHBITMAPToFile(hBitmap, sFile) {
	sFile := GetValidFilePath(sFile)
	DIBSECTION := Buffer(A_PtrSize=8? 104:84, 0)
	NumPut("UInt", 40, DIBSECTION, A_PtrSize=8? 32:24)
	DllCall("GetObject", "Ptr", hBitmap, "int", A_PtrSize=8? 104:84, "Ptr", DIBSECTION)
	biSizeImage := NumGet(DIBSECTION, A_PtrSize=8? 52:44, "UInt")
	hFile:=	DllCall("CreateFile", "Str", sFile, "Uint", 0x40000000, "Uint", 0, "Uint", 0, "Uint", 2, "Uint", 0, "Uint", 0)
	DllCall("WriteFile", "Uint", hFile, "int64P", 52, "Uint", 6, "UintP", 0, "Uint", 0)
	DllCall("WriteFile", "Uint", hFile, "int64P", 54, "Uint", 8, "UintP", 0, "Uint", 0)
	DllCall("WriteFile", "Uint", hFile, "Uint", 32, "Uint", 40, "UintP", 0, "Uint", 0)
	DllCall("WriteFile", "Uint", hFile, "Uint", 24, "Uint", biSizeImage, "UintP", 0, "Uint", 0)
	DllCall("CloseHandle", "Uint", hFile)
}

GetValidFilePath(filename) {
	SplitPath(filename, , &sDir, &sExt, &sName)
	if !InStr(sDir, ":")
		sDir := A_ScriptDir . "\" . sDir
	filename := sDir . "\" . sName . "." . sExt
	; StrReplace() is not case sensitive
	; check for StringCaseSense in v1 source script
	; and change the CaseSense param in StrReplace() if necessary
	filename := StrReplace(filename, "\\", "\")
	return filename
}

;---------------------------------------------------------------
;	Struct List
;---------------------------------------------------------------
/*

typedef struct {
  DWORD   cbSize;
  DWORD   flags;
  HCURSOR hCursor;
  POINT   ptScreenPos;
} CURSORINFO, *PCURSORINFO, *LPCURSORINFO;

typedef struct _ICONINFO {
  BOOL    fIcon;
  DWORD   xHotspot;
  DWORD   yHotspot;
  HBITMAP hbmMask;
  HBITMAP hbmColor;
} ICONINFO, *PICONINFO;

typedef struct tagDIBSECTION {
  BITMAP           dsBm;
  BITMAPINFOHEADER dsBmih;
  DWORD            dsBitfields[3];
  HANDLE           dshSection;
  DWORD            dsOffset;
} DIBSECTION, *PDIBSECTION;

typedef struct tagBITMAPINFOHEADER {
  DWORD biSize;
  LONG  biWidth;
  LONG  biHeight;
  WORD  biPlanes;
  WORD  biBitCount;
  DWORD biCompression;
  DWORD biSizeImage;
  LONG  biXPelsPerMeter;
  LONG  biYPelsPerMeter;
  DWORD biClrUsed;
  DWORD biClrImportant;
} BITMAPINFOHEADER;

typedef struct tagBITMAP {
  LONG   bmType;
  LONG   bmWidth;
  LONG   bmHeight;
  LONG   bmWidthBytes;
  WORD   bmPlanes;
  WORD   bmBitsPixel;
  LPVOID bmBits;
} BITMAP, *PBITMAP;

typedef struct tagBITMAPINFO {
  BITMAPINFOHEADER bmiHeader;
  RGBQUAD          bmiColors[1];
} BITMAPINFO, *PBITMAPINFO;

typedef struct tagRGBQUAD {
  BYTE rgbBlue;
  BYTE rgbGreen;
  BYTE rgbRed;
  BYTE rgbReserved;
} RGBQUAD;

*/

iseahound
Posts: 1434
Joined: 13 Aug 2016, 21:04
Contact:

Re: Capture Cursor to File / Compare to File

Post by iseahound » 02 Jul 2023, 22:21

Before you dig any deeper, I have to inform you that your approach will never work for any Windows 10/11 users who set their cursors to be a different color such as green or blue or pink, or increase the size of the pointer. You will always get a 64x64 image using these legacy functions. To capture the true cursor you must use DirectX.

If for some reason you are okay with these limitations, you can always use my library https://github.com/iseahound/ImagePut and do:

Code: Select all

ImagePutFile(A_Cursor)
For a faster approach using raw bitmaps:

Code: Select all

ImagePutBuffer(A_Cursor).Save()
But you don't really even need a file:

Code: Select all

ImagePutBuffer(A_Cursor)
And you can do this for comparisons:

Code: Select all

cursor1 := ImagePutBuffer(A_Cursor)
Sleep 3000
cursor2 := ImagePutBuffer(A_Cursor)
MsgBox ImageEqual(cursor1, cursor2)
Comparing the current cursor to a file is fine too:

Code: Select all

MsgBox ImageEqual("mycursor.bmp", A_Cursor)

User avatar
WarlordAkamu67
Posts: 219
Joined: 21 Mar 2023, 06:52

Re: Capture Cursor to File / Compare to File

Post by WarlordAkamu67 » 03 Jul 2023, 07:11

Thank you very much @iseahound ^.^

I downloaded the library and tried some stuff out. Saving the current cursor to a file was very easy with your function.

I was able to get the "ImageEqual" function to compare the cursor against itself after some time; however, comparing it to a file does not seem to be working, as the return value is all ways 0. I tried a "myFile := ImagePutBuffer('mycursor.bmp')" comparison as well. I will play more with it given the time.

F12 = Reload
F11 = Save Cursor to "mycursor.bmp"

F1 = Compare to File (Toggle)
F2 = Compare after 1 Second.

Code: Select all

#Requires AutoHotkey v2.0
#SingleInstance Force
#Include ImagePut.ahk

toggle := 0
myFile := ImagePutBuffer("mycursor.bmp")

; ------------------------- ; ------------------------- ; ------------------------- ; ------------------------- ; ------------------------- ; ------------------------- ; ------------------------- ; ------------------------- ; ------------------------- ; ------------------------- ;

F12::Reload

; ------------------------- ; ------------------------- ; ------------------------- ; ------------------------- ; ------------------------- ; ------------------------- ; ------------------------- ; ------------------------- ; ------------------------- ; ------------------------- ;

F11:: {
  global
  ImagePutBuffer(A_Cursor).Save("mycursor.bmp")
  Sleep(100)
  myFile := ImagePutBuffer("mycursor.bmp")
  Return
}

; ------------------------- ; ------------------------- ; ------------------------- ; ------------------------- ; ------------------------- ; ------------------------- ; ------------------------- ; ------------------------- ; ------------------------- ; ------------------------- ;

F1:: {
  global
  toggle := !toggle
  if (toggle) {
    SetTimer(fileMatch, 100)
} else {
    SetTimer(fileMatch, 0)
    ToolTip()
}
  Return
}

; ------------------------- ; ------------------------- ; ------------------------- ; ------------------------- ; ------------------------- ; ------------------------- ; ------------------------- ; ------------------------- ; ------------------------- ; ------------------------- ;

fileMatch() {
  global
  Tooltip(ImageEqual("mycursor.bmp", A_Cursor))
  Sleep(100)
  Return
}

; ------------------------- ; ------------------------- ; ------------------------- ; ------------------------- ; ------------------------- ; ------------------------- ; ------------------------- ; ------------------------- ; ------------------------- ; ------------------------- ;

F2:: {
  global
  Sleep(50)
  cursor1 := ImagePutBuffer(A_Cursor)
  Sleep(1000)
  cursor2 := ImagePutBuffer(A_Cursor)
  MsgBox ImageEqual(cursor1, cursor2)
  Return
}

iseahound
Posts: 1434
Joined: 13 Aug 2016, 21:04
Contact:

Re: Capture Cursor to File / Compare to File  Topic is solved

Post by iseahound » 03 Jul 2023, 08:00

My mistake! The cursor has transparency, but the bitmap (bmp) does not support it. If you change bmp to png everything will work.

Code: Select all

#Requires AutoHotkey v2.0
#SingleInstance Force
#Include ImagePut.ahk

; Get initial cursor
initial := ImagePutFile(A_Cursor, "mycursor.png")
ImagePutWindow(initial)

toggle := 0
myFile := ImagePutBuffer("mycursor.png")

; ------------------------- ; ------------------------- ; ------------------------- ; ------------------------- ; ------------------------- ; ------------------------- ; ------------------------- ; ------------------------- ; ------------------------- ; ------------------------- ;

F12::Reload

; ------------------------- ; ------------------------- ; ------------------------- ; ------------------------- ; ------------------------- ; ------------------------- ; ------------------------- ; ------------------------- ; ------------------------- ; ------------------------- ;

F11:: {
  global
  ImagePutBuffer(A_Cursor).Save("mycursor.png")
  Sleep(100)
  myFile := ImagePutBuffer("mycursor.png")
  myFile.show(1)
  Return
}

; ------------------------- ; ------------------------- ; ------------------------- ; ------------------------- ; ------------------------- ; ------------------------- ; ------------------------- ; ------------------------- ; ------------------------- ; ------------------------- ;

F1:: {
  global
  toggle := !toggle
  if (toggle) {
    SetTimer(fileMatch, 100)
} else {
    SetTimer(fileMatch, 0)
    ToolTip()
}
  Return
}

; ------------------------- ; ------------------------- ; ------------------------- ; ------------------------- ; ------------------------- ; ------------------------- ; ------------------------- ; ------------------------- ; ------------------------- ; ------------------------- ;

fileMatch() {
  global
  Tooltip(ImageEqual("mycursor.png", A_Cursor))
  Sleep(100)
  Return
}

; ------------------------- ; ------------------------- ; ------------------------- ; ------------------------- ; ------------------------- ; ------------------------- ; ------------------------- ; ------------------------- ; ------------------------- ; ------------------------- ;

F2:: {
  global
  Sleep(50)
  cursor1 := ImagePutBuffer(A_Cursor)
  Sleep(1000)
  cursor2 := ImagePutBuffer(A_Cursor)
  MsgBox ImageEqual(cursor1, cursor2)
  Return
}

iseahound
Posts: 1434
Joined: 13 Aug 2016, 21:04
Contact:

Re: Capture Cursor to File / Compare to File

Post by iseahound » 03 Jul 2023, 08:03

Speaking of cursors there is also ImagePutCursor("cats.png") which will change your cursor to any image. Be careful however...

User avatar
WarlordAkamu67
Posts: 219
Joined: 21 Mar 2023, 06:52

Re: Capture Cursor to File / Compare to File

Post by WarlordAkamu67 » 03 Jul 2023, 10:44

Amazing and simple, thank you so much! :D
iseahound wrote:
03 Jul 2023, 08:03
Speaking of cursors there is also ImagePutCursor("cats.png") which will change your cursor to any image. Be careful however...
I saw some scaling things in there, and you mentioned it would all ways be 64x64. So I wouldn't think to worry about size- itl be fun to find out. Also, the documentation in the library code is most helpful. :cheers:

User avatar
WarlordAkamu67
Posts: 219
Joined: 21 Mar 2023, 06:52

Re: Capture Cursor to File / Compare to File

Post by WarlordAkamu67 » 03 Jul 2023, 13:37

Size matters. I also have a follow up question. How do I "release" or reset the cursor to normal functionality after setting it?

iseahound
Posts: 1434
Joined: 13 Aug 2016, 21:04
Contact:

Re: Capture Cursor to File / Compare to File

Post by iseahound » 03 Jul 2023, 14:42

Use:

Code: Select all

RestoreCursor() => DllCall("SystemParametersInfo", "uint", 20, "uint", 0, "ptr", 0, "uint", 2)
RestoreCursor()
or

Code: Select all

ImageDestroy(A_Cursor)
https://github.com/iseahound/ImagePut/wiki/Input-Types-&-Output-Functions#imageputcursorimage-xhotspot-yhotspot

User avatar
WarlordAkamu67
Posts: 219
Joined: 21 Mar 2023, 06:52

Re: Capture Cursor to File / Compare to File

Post by WarlordAkamu67 » 05 Jul 2023, 06:13

Thank you again for pointing me in the right direction!
destroyer.png
destroyer.png (7.4 KiB) Viewed 547 times

Post Reply

Return to “Ask for Help (v2)”