Jump to content

Sky Slate Blueberry Blackcurrant Watermelon Strawberry Orange Banana Apple Emerald Chocolate
Photo

[tutorial] Creating windows without GUI commands


  • Please log in to reply
3 replies to this topic
majkinetor
  • Moderators
  • 4512 posts
  • Last active: Jul 29 2016 12:40 AM
  • Joined: 24 May 2006
This is just quick sample of advanced things that can be done with RegisterCallback.

Presentation describes creating very simple message box, with customizable font (this goes to Laszlo).
No GUI commands are used, and very little effort is needed to extended it to support for instance hyperlink (SySLink instead Static) more buttons, images etc... i.e. to recreate classic MsgBox just more customizable.

U can use this as basis for some other work, even custom controls can be created, with the very same look and feel like those contained in various dll's around (they will be slower, ofcourse, but no dll's will be required)

Script registers its own window class - MM_AHKMsgBox - and that you can see with Window Spy.

Finaly, possibility of Msg interacting with AHK Guis is 0, its true black box. U can create 1000 GUIs without having to worry about 99 Guis limitation of AHK.

Notes
[*:3aygez43] U may notice icon becomes "falling sand" on the start. This can be solved by explicitely setting cursor in RegisterClass but I was to lazy to do that

[*:3aygez43] Some cleaning should be done, but that was not the point of this tut.

[*:3aygez43] Script is kinda big for this simple task, but in true API world, even Hello World appears to be very complex.

#Persistent
#SingleInstance, force

	str =
		(LTrim
		 	 Volume in drive C has no label.
		 Volume Serial Number is B857-7430

		 Directory of C:\Documents and Settings\mmilic

		07.09.2007  16:18    <DIR>          .
		07.09.2007  16:18    <DIR>          ..
		07.09.2007  10:12    <DIR>          Desktop
		19.01.2007  16:01    <DIR>          Favorites
		28.08.2007  15:28    <DIR>          My Documents
		07.09.2007  16:18               532 out
		14.02.2007  16:57    <DIR>          Start Menu
					   1 File(s)            532 bytes
					   6 Dir(s)  180.445.904.896 bytes free
		)

	Msg(str, "s10, Courier New")
return


Msg(str, font) {
	global
	static init=0

	if (!init++)
		Msg_RegisterClass()
	
	msg_str := str,	msg_font := font
	hwnd := Msg_CreateWindow()
	SysGet, d, 31
	WinMove,  ahk_id %hwnd%,, 100,100,w+40,h+d+80
}

Msg_WndProc(hwnd, uMsg, wParam, lParam) {
	global
	static WS_VISIBLE=0x10000000, WS_CHILD=0x40000000, WS_CLIPCHILDREN=0x2000000

	if (uMsg = 1) {

		GetTextSize(msg_str, msg_font, w, h)
		hCtrl := API_CreateWindowEx(0,"Static", msg_str, WS_CHILD | WS_VISIBLE  ,0,0,w,h    ,hwnd, 0, 0, 0)
		hFont := CreateFont(msg_font) 
		SendMessage, 0x30, %hFont%, 1,, ahk_id %hCtrl%  ;WM_SETFONT = 0x30

		hCtrl := API_CreateWindowEx(0,"Button", "OK", WS_CHILD | WS_VISIBLE  ,10,h+30,50,25    ,hwnd, 101, 0, 0)
	}

	if (uMsg=0x111) {
		;the only command is now bn_clicked so ....
		DllCall("DestroyWindow", "uint", hwnd)
	}
					

	return, DllCall("DefWindowProc", "uint", hwnd, "uint", umsg, "uint", wParam, "uint", lParam)
}

Msg_CreateWindow(){
	static WS_VISIBLE=0x10000000, WS_CLIPCHILDREN=0x2000000, WS_SYSMENU=0x80000,WS_MINIMIZEBOX=0x20000,WS_MAXIMIZEBOX=0x10000


	return API_CreateWindowEx(1, "MM_AHKMsgBox", A_ScriptName,  WS_VISIBLE |  WS_SYSMENU 
							 ,0,0,0,0,   0,0,0,0)
}

Msg_RegisterClass(){
	cName := "MM_AHKMsgBox"
	WndProc := RegisterCallback("Msg_WndProc", "F")

	VarSetCapacity(WC, 40, 0)
	NumPut(3, WC, 0)
	NumPut(WndProc, WC, 4)
	NumPut(0, WC, 8)   ;reserve
	NumPut(0, WC, 12)  ;reserve
	NumPut(0, WC, 16)  ;instance
	NumPut(0, WC, 20)  ;icon
	NumPut(0, WC, 24)  ;curcors					;put some cursor here 
	NumPut(16, WC, 28) ;background
	NumPut(0, WC, 32)  ;menu
	NumPut(&cName, WC, 36) ;class

	return DllCall("RegisterClass", "uint", &WC)
}

;-------------------------------------------------------------------------- 
; Function:  CreateFont 
;          Creates font in memory which can be used with any API function accepting font handles. 
; 
; Parameters: 
;         pFont   - AHK font description, "style, face" 
; 
; Returns: 
;         Font handle 
; 
; Example: 
;>         hFont := CreateFont("s12 italic, Courier New") 
;>         SendMessage, 0x30, %hFont%, 1,, ahk_id %hGuiControl%  ;WM_SETFONT = 0x30 

CreateFont(pFont="") { 

   ;parse font 
   italic      := InStr(pFont, "italic")    ?  1    :  0 
   underline   := InStr(pFont, "underline") ?  1    :  0 
   strikeout   := InStr(pFont, "strikeout") ?  1    :  0 
   weight      := InStr(pFont, "bold")      ? 700   : 400 

   ;height 
   RegExMatch(pFont, "(?<=[S|s])(\d{1,2})(?=[ ,])", height) 
   if (height = "") 
     height := 10 
   RegRead, LogPixels, HKEY_LOCAL_MACHINE, SOFTWARE\Microsoft\Windows NT\CurrentVersion\FontDPI, LogPixels 
   height := -DllCall("MulDiv", "int", Height, "int", LogPixels, "int", 72) 

   ;face 
   RegExMatch(pFont, "(?<=,).+", fontFace) 
   if (fontFace != "") 
      fontFace := RegExReplace( fontFace, "(^\s*)|(\s*$)")      ;trim 
   else fontFace := "MS Sans Serif" 

   ;create font 
   hFont   := DllCall("CreateFont", "int",  height, "int",  0, "int",  0, "int", 0 
                 ,"int",  weight,   "Uint", italic,   "Uint", underline 
                 ,"uint", strikeOut, "Uint", nCharSet, "Uint", 0, "Uint", 0, "Uint", 0, "Uint", 0, "str", fontFace) 

   return hFont 
}

;----------------------------------------------------------------------------------------------------------------------- 
; Function: GetTextSize 
; Calculate widht and/or height of text. 
; Font face, style and number of lines is taken into account 
;: 
; <By.majkinetor> 
; 
; Parameters: 
;      pStr   - Text to be measured 
;      pFont   - Font description in AHK syntax, default size is 10, default font is MS Sans Serif 
;      pHeight   - Set to true to return height also. False is default. 
;      pAdd   - Number to add on width and height. 
; 
; Returns: 
;      Text width if pHeight=false. Otherwise, dimension is returned as "width,height" 
; 
; Dependencies: 
;      <ExtractInteger> 
;       
; Examples: 
;      width := GetTextSize("string to be measured", "bold s22, Courier New" ) 
;    
GetTextSize(pStr, pFont, ByRef w, ByRef h) { 
   local height, weight, italic, underline, strikeout , nCharSet 
   local hdc := DllCall("GetDC", "Uint", 0) 
   local hFont, hOldFont 
   local resW, resH, SIZE 

   hFont := CreateFont( pFont )
   hOldFont := DllCall("SelectObject", "Uint", hDC, "Uint", hFont)                                

   VarSetCapacity(SIZE, 16) 
   curW=0 
   Loop,  parse, pStr, `n 
   { 
      DllCall("DrawTextA", "Uint", hDC, "str", A_LoopField, "int", StrLen(pStr), "uint", &SIZE, "uint", 0x400) 
      resW := NumGet(SIZE, 8) 
      curW := resW > curW ? resW : curW 
   } 
   DllCall("DrawTextA", "Uint", hDC, "str", pStr, "int", StrLen(pStr), "uint", &SIZE, "uint", 0x400) 
 ;clean    
    
   DllCall("SelectObject", "Uint", hDC, "Uint", hOldFont) 
   DllCall("DeleteObject", "Uint", hFont) 
   DllCall("ReleaseDC", "Uint", 0, "Uint", hDC) 

   w := NumGet(SIZE, 8), h := NumGet(SIZE, 12)
   return 1
}


API_CreateWindowEx(dwExStyle,lpClassName,lpWindowName,dwStyle,x,y,nWidth,nHeight,hWndParent,hMenu,hInstance,lpParam){
	return DllCall("CreateWindowEx"
		, "Uint",	dwExStyle
		, "str",	lpClassName
		, "str",	lpWindowName
		, "Uint",	dwStyle
		, "int",	x 
		, "int",	y 
		, "int",	nWidth 
		, "int",	nHeight 
		, "Uint",	hWndParent 
		, "Uint",	hMenu 
		, "Uint",	hInstance
		, "Uint",	lpParam)
}

Posted Image
Posted Image

aingles
  • Guests
  • Last active:
  • Joined: --
Interesting example, but I have a question. This code worked fine on my XP system, but when I gave it to my son to play with on his win98 box, the font size selection does not work.

After looking at the code I think the problem is that win98 does not have the registry entry to support this line (#116) of code in function CreateFont.

RegRead, LogPixels, HKEY_LOCAL_MACHINE, SOFTWARE\Microsoft\Windows NT\CurrentVersion\FontDPI, LogPixels


Replacing the line with:
LogPixels = 96
enabled font size selection to work correctly on his box.

My question is, is there some way to automatically determine the current FontDPI on a win98 system? Perhaps some other registry entry(s) can be used to calculate the value?

Nice tutorial.

majkinetor
  • Moderators
  • 4512 posts
  • Last active: Jul 29 2016 12:40 AM
  • Joined: 24 May 2006
Hello aingles.

I really can't tell anything about Win98, I used it very long time ago, but perhaps you can search the registry for FontDPI key occurance or google around. BTW, 96 is default value on XP.

Your question is related to CreateFont function anyway. It is made on XP and I don't support Win9x systems as they are dead.
Posted Image

  • Guests
  • Last active:
  • Joined: --
In case somebody needs it, this is the compatible version for AutoHotkey_L 32-bit Unicode and 64-bit. This requires the DllStructlibrary by just me.
#Persistent
#SingleInstance, force
#include <Class_DllStruct>
	global Ptr := (A_PtrSize = 8) ? "ptr" : "uint"

	str =
	(LTrim
		Volume in drive C has no label.
		Volume Serial Number is B857-7430

		Directory of C:\Documents and Settings\mmilic

		07.09.2007  16:18    <DIR>          .
		07.09.2007  16:18    <DIR>          ..
		07.09.2007  10:12    <DIR>          Desktop
		19.01.2007  16:01    <DIR>          Favorites
		28.08.2007  15:28    <DIR>          My Documents
		07.09.2007  16:18               532 out
		14.02.2007  16:57    <DIR>          Start Menu
				  1 File(s)            532 bytes
				  6 Dir(s)  180.445.904.896 bytes free
	)

	Msg(str, "s10, Courier New") 
return


Msg(str, font) {
	global
	static init=0

	if (!init++)
		Msg_RegisterClass()

	msg_str := str,   msg_font := font
	hwnd := Msg_CreateWindow()
	SysGet, d, 31
	WinMove,  ahk_id %hwnd%,, 200,100,w+40,h+d+80
}

Msg_WndProc(hwnd, uMsg, wParam, lParam) {
	global
	static WS_VISIBLE=0x10000000, WS_CHILD=0x40000000, WS_CLIPCHILDREN=0x2000000

	if (uMsg = 1) {
		GetTextSize(msg_str, msg_font, w, h)
		hCtrl := API_CreateWindowEx(0,"Static", msg_str, WS_CHILD | WS_VISIBLE  ,0,0,w,h    ,hwnd, 0, 0, 0)
		hFont := CreateFont(msg_font)
		SendMessage, 0x30, %hFont%, 1,, ahk_id %hCtrl%  ;WM_SETFONT = 0x30
		hCtrl := API_CreateWindowEx(0,"Button", "OK", WS_CHILD | WS_VISIBLE  ,10,h+30,50,25    ,hwnd, 101, 0, 0)
	}

	if (uMsg=0x111) {
		;the only command is now bn_clicked so ....
		DllCall("DestroyWindow", ptr, hwnd)
	}
		  
	return, DllCall("DefWindowProc", ptr, hwnd, "uint", umsg, ptr, wParam, ptr, lParam)
}

Msg_CreateWindow(){
	static WS_VISIBLE=0x10000000, WS_CLIPCHILDREN=0x2000000, WS_SYSMENU=0x80000,WS_MINIMIZEBOX=0x20000,WS_MAXIMIZEBOX=0x10000
	return API_CreateWindowEx(1, "MM_AHKMsgBox", A_ScriptName,  WS_VISIBLE |  WS_SYSMENU
					  ,0,0,0,0,   0,0,0,0)
}

Msg_RegisterClass(){
	cName := "MM_AHKMsgBox"
	WndProc := RegisterCallback("Msg_WndProc", "F")

	def_tagWNDCLASS := "
	(LTrim Join
	   UINT      style;
	   HANDLE   lpfnWndProc;
	   int       cbClsExtra;
	   int       cbWndExtra;
	   HINSTANCE hInstance;
	   HICON     hIcon;
	   HCURSOR   hCursor;
	   HBRUSH    hbrBackground;
	   LPCTSTR   lpszMenuName;
	   LPCTSTR   lpszClassName;
	)"
	tagWNDCLASS := new DllStruct(def_tagWNDCLASS)
	tagWNDCLASS.style := 3            
	tagWNDCLASS.lpfnWndProc := WndProc   
	tagWNDCLASS.cbClsExtra := 0         
	tagWNDCLASS.cbWndExtra := 0         
	tagWNDCLASS.hInstance := 0         
	tagWNDCLASS.hIcon := 0            
	tagWNDCLASS.hCursor := 0         
	tagWNDCLASS.hbrBackground := 16      
	tagWNDCLASS.lpszMenuName := 0      
	tagWNDCLASS.lpszClassname := &cName   
	Return DllCall("RegisterClass", ptr, tagWNDCLASS.GetPtr())
}

;--------------------------------------------------------------------------
; Function:  CreateFont
;          Creates font in memory which can be used with any API function accepting font handles.
;
; Parameters:
;         pFont   - AHK font description, "style, face"
;
; Returns:
;         Font handle
;
; Example:
;>         hFont := CreateFont("s12 italic, Courier New")
;>         SendMessage, 0x30, %hFont%, 1,, ahk_id %hGuiControl%  ;WM_SETFONT = 0x30

CreateFont(pFont="") {

	;parse font
	italic      := InStr(pFont, "italic")    ?  1    :  0
	underline   := InStr(pFont, "underline") ?  1    :  0
	strikeout   := InStr(pFont, "strikeout") ?  1    :  0
	weight      := InStr(pFont, "bold")      ? 700   : 400

	;height
	RegExMatch(pFont, "(?<=[S|s])(\d{1,2})(?=[ ,])", height)
	if (height = "")
	 height := 10
	RegRead, LogPixels, HKEY_LOCAL_MACHINE, SOFTWARE\Microsoft\Windows NT\CurrentVersion\FontDPI, LogPixels
	height := -DllCall("MulDiv", "int", Height, "int", LogPixels, "int", 72)

	;face
	RegExMatch(pFont, "(?<=,).+", fontFace)
	if (fontFace != "")
		fontFace := RegExReplace( fontFace, "(^\s*)|(\s*$)")      ;trim
	else fontFace := "MS Sans Serif"

	;create font
	hFont   := DllCall("CreateFont", "int",  height, "int",  0, "int",  0, "int", 0
				 ,"int",  weight,   "Uint", italic,   "Uint", underline
				 ,"uint", strikeOut, "Uint", nCharSet, "Uint", 0, "Uint", 0, "Uint", 0, "Uint", 0, "str", fontFace)

	return hFont
}

;-----------------------------------------------------------------------------------------------------------------------
; Function: GetTextSize
; Calculate widht and/or height of text.
; Font face, style and number of lines is taken into account
;:
; <By.majkinetor>
;
; Parameters:
;      pStr   - Text to be measured
;      pFont   - Font description in AHK syntax, default size is 10, default font is MS Sans Serif
;      pHeight   - Set to true to return height also. False is default.
;      pAdd   - Number to add on width and height.
;
; Returns:
;      Text width if pHeight=false. Otherwise, dimension is returned as "width,height"
;
; Dependencies:
;      <ExtractInteger>
;       
; Examples:
;      width := GetTextSize("string to be measured", "bold s22, Courier New" )
;   
GetTextSize(pStr, pFont, ByRef w, ByRef h) {
	global
	local height, weight, italic, underline, strikeout , nCharSet
	local hdc := DllCall("GetDC", "Uint", 0)
	local hFont, hOldFont
	local resW, resH, SIZE

	hFont := CreateFont( pFont )
	hOldFont := DllCall("SelectObject", ptr, hDC, ptr, hFont)                               

	VarSetCapacity(SIZE, 16)
	curW=0
	def_Rect := "
	(LTrim Join
		LONG left;
		LONG top;
		LONG right;
		LONG bottom;
	)"	
	_Rect := new DllStruct(def_Rect)	
	_Rect.left := _Rect.top := _Rect.right := _Rect.bottom := 0
	Loop,  parse, pStr, `n
	{
		DllCall("DrawText" (A_IsUnicode ? "W" : "A"), ptr, hDC, "str", A_LoopField, "int", StrLen(pStr), ptr, _Rect.GetPtr(), "uint", 0x400)
		resW := _Rect.right		
		curW := resW > curW ? resW : curW
	}
	DllCall("DrawText" (A_IsUnicode ? "W" : "A"), ptr, hDC, ptr, &pStr, "int", StrLen(pStr), ptr, _Rect.GetPtr(), "uint", 0x400)
	;clean   
   
	DllCall("SelectObject", ptr, hDC, ptr, hOldFont)
	DllCall("DeleteObject", ptr, hFont)
	DllCall("ReleaseDC", "Uint", 0, ptr, hDC)

	w := _Rect.right 
	h := _Rect.bottom 
	return 1
}

API_CreateWindowEx(dwExStyle,lpClassName,lpWindowName,dwStyle,x,y,nWidth,nHeight,hWndParent,hMenu,hInstance,lpParam){
   return DllCall("CreateWindowEx"
      , "Uint",   dwExStyle
      , ptr,   &lpClassName
      , ptr,   &lpWindowName
      , "Uint",   dwStyle
      , "int",   x
      , "int",   y
      , "int",   nWidth
      , "int",   nHeight
      , ptr,   hWndParent
      , ptr,   hMenu
      , ptr,   hInstance
      , ptr,   lpParam)
}