Inspired by the tools PBWinSpy by Börje Hagsten and WinSpy, I have done some preliminary work for FBWinSpy.
It extracts information about a GUI and its child controls. This information could be used to attempt to reconstruct the GUI generating code for FreeBasic. As an example, if the GUI has a menu, it generates code to reconstruct it.
thank you :)
:-)
There is an undocumented function to retrieve the DPI awareness of an application, but requires windows 8.1+
' ========================================================================================
' Get process DPI awareness (undocumented)
' Untested. Requires Windows 8.1+
' ========================================================================================
FUNCTION GetProcessDpiAwarenessInternal (BYVAL hProcess AS HANDLE, BYVAL pValue AS ULONG PTR) AS BOOLEAN
DIM pLib AS ANY PTR = DyLibLoad("user32.dll")
IF pLib = NULL THEN EXIT FUNCTION
DIM pGetProcessDpiAwarenessInternal AS FUNCTION (BYVAL hProcess AS HANDLE, BYVAL pValue AS ULONG PTR) AS LONG
pGetProcessDpiAwarenessInternal = DyLibSymbol(pLib, "GetProcessDpiAwarenessInternal")
IF pGetProcessDpiAwarenessInternal THEN FUNCTION = pGetProcessDpiAwarenessInternal(hProcess, pValue)
DyLibFree(pLib)
END FUNCTION
' ========================================================================================
I have added more information. Unfortunately, although I can retrieve the number of parts of a status bar of another application, I can't retrieve the size of the parts. Also limitations with other controls such Toolbars and Rebars.
' ========================================================================================
' Retrieve child windows
' ========================================================================================
FUNCTION EnumChildProc (BYVAL hwnd AS HWND, BYVAL lParam AS LPARAM) AS LONG
DIM wszClassName AS WSTRING * 260 = AfxGetWindowClassName(hwnd)
DIM cwsText AS CWSTR = wszClassName
cwsText += "; cID = " & WSTR(GetDlgCtrlID(hwnd))
cwsText += "; Width = " & WSTR(AfxGetWindowWidth(hwnd))
cwsText += "; Height = " & WSTR(AfxGetWindowHeight(hwnd))
cwsText += "; Style = &h" & HEX(AfxGetWindowStyle(hwnd))
cwsText += "; StyleEx = &h" & HEX(AfxGetWindowExStyle(hwnd))
cwsText += CHR(13, 10)
SELECT CASE UCASE(wszClassName)
CASE "STATIC"
cwsText += " - Styles: " & GetStaticStyles(AfxGetWindowStyle(hwnd)) & CHR(13, 10)
cwsText += " - Window styles: " & GetWindowStyles(hwnd, AfxGetWindowStyle(hwnd)) & CHR(13, 10)
DIM rc AS RECT = AfxGetWindowRect(hwnd)
cwsText += " - Window Rect: Left = " & WSTR(rc.Left) & "; Right = " & WSTR(rc.Right) & "; Top = " & WSTR(rc.Top) & "; Bottom = " & WSTR(rc.Bottom) & CHR(13, 10)
rc = AfxGetWindowClientRect(hwnd)
cwsText += " - Client Rect: Left = " & WSTR(rc.Left) & "; Right = " & WSTR(rc.Right) & "; Top = " & WSTR(rc.Top) & "; Bottom = " & WSTR(rc.Bottom) & CHR(13, 10)
CASE "BUTTON"
cwsText += " - Styles: " & GetButtonStyles(AfxGetWindowStyle(hwnd)) & CHR(13, 10)
cwsText += " - Window styles: " & GetWindowStyles(hwnd, AfxGetWindowStyle(hwnd)) & CHR(13, 10)
DIM rc AS RECT = AfxGetWindowRect(hwnd)
cwsText += " - Window Rect: Left = " & WSTR(rc.Left) & "; Right = " & WSTR(rc.Right) & "; Top = " & WSTR(rc.Top) & "; Bottom = " & WSTR(rc.Bottom) & CHR(13, 10)
rc = AfxGetWindowClientRect(hwnd)
cwsText += " - Client Rect: Left = " & WSTR(rc.Left) & "; Right = " & WSTR(rc.Right) & "; Top = " & WSTR(rc.Top) & "; Bottom = " & WSTR(rc.Bottom) & CHR(13, 10)
CASE "TOOLBARWINDOW32"
cwsText += " - Styles: " & GetToolbarStyles(AfxGetWindowStyle(hwnd)) & CHR(13, 10)
cwsText += " - ExStyles: " & GetToolbarExStyles(Toolbar_GetExtendedStyle(hwnd)) & CHR(13, 10)
cwsText += " - Window styles: " & GetWindowStyles(hwnd, AfxGetWindowStyle(hwnd)) & CHR(13, 10)
cwsText += " - Buttons: " & WSTR(Toolbar_ButtonCount(hwnd))
cwsText += "; Width: " & WSTR(Toolbar_GetButtonWidth(hwnd))
cwsText += "; Height: " & WSTR(Toolbar_GetButtonHeight(hwnd)) & CHR(13, 10)
DIM rc AS RECT = AfxGetWindowRect(hwnd)
cwsText += " - Window Rect: Left = " & WSTR(rc.Left) & "; Right = " & WSTR(rc.Right) & "; Top = " & WSTR(rc.Top) & "; Bottom = " & WSTR(rc.Bottom) & CHR(13, 10)
rc = AfxGetWindowClientRect(hwnd)
cwsText += " - Client Rect: Left = " & WSTR(rc.Left) & "; Right = " & WSTR(rc.Right) & "; Top = " & WSTR(rc.Top) & "; Bottom = " & WSTR(rc.Bottom) & CHR(13, 10)
CASE "COMBOBOX"
cwsText += " - Styles: " & GetComboBoxStyles(AfxGetWindowStyle(hwnd)) & CHR(13, 10)
cwsText += " - Window styles: " & GetWindowStyles(hwnd, AfxGetWindowStyle(hwnd)) & CHR(13, 10)
DIM rc AS RECT = AfxGetWindowRect(hwnd)
cwsText += " - Window Rect: Left = " & WSTR(rc.Left) & "; Right = " & WSTR(rc.Right) & "; Top = " & WSTR(rc.Top) & "; Bottom = " & WSTR(rc.Bottom) & CHR(13, 10)
rc = AfxGetWindowClientRect(hwnd)
cwsText += " - Client Rect: Left = " & WSTR(rc.Left) & "; Right = " & WSTR(rc.Right) & "; Top = " & WSTR(rc.Top) & "; Bottom = " & WSTR(rc.Bottom) & CHR(13, 10)
CASE "LISTBOX"
cwsText += " - Styles: " & GetListBoxStyles(AfxGetWindowStyle(hwnd)) & CHR(13, 10)
cwsText += " - Window styles: " & GetWindowStyles(hwnd, AfxGetWindowStyle(hwnd)) & CHR(13, 10)
DIM rc AS RECT = AfxGetWindowRect(hwnd)
cwsText += " - Window Rect: Left = " & WSTR(rc.Left) & "; Right = " & WSTR(rc.Right) & "; Top = " & WSTR(rc.Top) & "; Bottom = " & WSTR(rc.Bottom) & CHR(13, 10)
rc = AfxGetWindowClientRect(hwnd)
cwsText += " - Client Rect: Left = " & WSTR(rc.Left) & "; Right = " & WSTR(rc.Right) & "; Top = " & WSTR(rc.Top) & "; Bottom = " & WSTR(rc.Bottom) & CHR(13, 10)
CASE "REBARWINDOW32"
cwsText += " - Styles: " & GetRebarStyles(AfxGetWindowStyle(hwnd)) & CHR(13, 10)
cwsText += " - Window styles: " & GetWindowStyles(hwnd, AfxGetWindowStyle(hwnd)) & CHR(13, 10)
DIM rc AS RECT = AfxGetWindowRect(hwnd)
cwsText += " - Window Rect: Left = " & WSTR(rc.Left) & "; Right = " & WSTR(rc.Right) & "; Top = " & WSTR(rc.Top) & "; Bottom = " & WSTR(rc.Bottom) & CHR(13, 10)
rc = AfxGetWindowClientRect(hwnd)
cwsText += " - Client Rect: Left = " & WSTR(rc.Left) & "; Right = " & WSTR(rc.Right) & "; Top = " & WSTR(rc.Top) & "; Bottom = " & WSTR(rc.Bottom) & CHR(13, 10)
CASE "EDIT"
cwsText += " - Styles: " & GetEditStyles(AfxGetWindowStyle(hwnd)) & CHR(13, 10)
cwsText += " - Window styles: " & GetWindowStyles(hwnd, AfxGetWindowStyle(hwnd)) & CHR(13, 10)
DIM rc AS RECT = AfxGetWindowRect(hwnd)
cwsText += " - Window Rect: Left = " & WSTR(rc.Left) & "; Right = " & WSTR(rc.Right) & "; Top = " & WSTR(rc.Top) & "; Bottom = " & WSTR(rc.Bottom) & CHR(13, 10)
rc = AfxGetWindowClientRect(hwnd)
cwsText += " - Client Rect: Left = " & WSTR(rc.Left) & "; Right = " & WSTR(rc.Right) & "; Top = " & WSTR(rc.Top) & "; Bottom = " & WSTR(rc.Bottom) & CHR(13, 10)
CASE "MSCTLS_PROGRESS32"
cwsText += " - Styles: " & GetProgressBarStyles(AfxGetWindowStyle(hwnd)) & CHR(13, 10)
cwsText += " - Window styles: " & GetWindowStyles(hwnd, AfxGetWindowStyle(hwnd)) & CHR(13, 10)
DIM rc AS RECT = AfxGetWindowRect(hwnd)
cwsText += " - Window Rect: Left = " & WSTR(rc.Left) & "; Right = " & WSTR(rc.Right) & "; Top = " & WSTR(rc.Top) & "; Bottom = " & WSTR(rc.Bottom) & CHR(13, 10)
rc = AfxGetWindowClientRect(hwnd)
cwsText += " - Client Rect: Left = " & WSTR(rc.Left) & "; Right = " & WSTR(rc.Right) & "; Top = " & WSTR(rc.Top) & "; Bottom = " & WSTR(rc.Bottom) & CHR(13, 10)
CASE "MSCTLS_STATUSBAR32"
cwsText += " - Styles: " & GetStatusBarStyles(AfxGetWindowStyle(hwnd)) & CHR(13, 10)
cwsText += " - Window styles: " & GetWindowStyles(hwnd, AfxGetWindowStyle(hwnd)) & CHR(13, 10)
DIM rc AS RECT = AfxGetWindowRect(hwnd)
cwsText += " - Window Rect: Left = " & WSTR(rc.Left) & "; Right = " & WSTR(rc.Right) & "; Top = " & WSTR(rc.Top) & "; Bottom = " & WSTR(rc.Bottom) & CHR(13, 10)
rc = AfxGetWindowClientRect(hwnd)
cwsText += " - Client Rect: Left = " & WSTR(rc.Left) & "; Right = " & WSTR(rc.Right) & "; Top = " & WSTR(rc.Top) & "; Bottom = " & WSTR(rc.Bottom) & CHR(13, 10)
DIM nParts AS LONG = StatusBar_GetPartsCount(hwnd)
cwsText += " - Parts: " & WSTR(nParts) & "; Text part 1: " & **AfxGetWindowText(hwnd) & CHR(13, 10)
CASE "SYSTABCONTROL32"
cwsText += " - Window styles: " & GetWindowStyles(hwnd, AfxGetWindowStyle(hwnd)) & CHR(13, 10)
cwsText += " - ExStyles: " & GetSysTabExStyles(TabCtrl_GetExtendedStyle(hwnd)) & CHR(13, 10)
DIM rc AS RECT = AfxGetWindowRect(hwnd)
cwsText += " - Window Rect: Left = " & WSTR(rc.Left) & "; Right = " & WSTR(rc.Right) & "; Top = " & WSTR(rc.Top) & "; Bottom = " & WSTR(rc.Bottom) & CHR(13, 10)
rc = AfxGetWindowClientRect(hwnd)
cwsText += " - Client Rect: Left = " & WSTR(rc.Left) & "; Right = " & WSTR(rc.Right) & "; Top = " & WSTR(rc.Top) & "; Bottom = " & WSTR(rc.Bottom) & CHR(13, 10)
CASE "SYSHEADER32"
cwsText += " - Window styles: " & GetWindowStyles(hwnd, AfxGetWindowStyle(hwnd)) & CHR(13, 10)
DIM rc AS RECT = AfxGetWindowRect(hwnd)
cwsText += " - Window Rect: Left = " & WSTR(rc.Left) & "; Right = " & WSTR(rc.Right) & "; Top = " & WSTR(rc.Top) & "; Bottom = " & WSTR(rc.Bottom) & CHR(13, 10)
rc = AfxGetWindowClientRect(hwnd)
cwsText += " - Client Rect: Left = " & WSTR(rc.Left) & "; Right = " & WSTR(rc.Right) & "; Top = " & WSTR(rc.Top) & "; Bottom = " & WSTR(rc.Bottom) & CHR(13, 10)
CASE "SYSTREEVIEW32"
cwsText += " - Window styles: " & GetWindowStyles(hwnd, AfxGetWindowStyle(hwnd)) & CHR(13, 10)
DIM rc AS RECT = AfxGetWindowRect(hwnd)
cwsText += " - Window Rect: Left = " & WSTR(rc.Left) & "; Right = " & WSTR(rc.Right) & "; Top = " & WSTR(rc.Top) & "; Bottom = " & WSTR(rc.Bottom) & CHR(13, 10)
rc = AfxGetWindowClientRect(hwnd)
cwsText += " - Client Rect: Left = " & WSTR(rc.Left) & "; Right = " & WSTR(rc.Right) & "; Top = " & WSTR(rc.Top) & "; Bottom = " & WSTR(rc.Bottom) & CHR(13, 10)
CASE "SYSLISTVIEW32"
cwsText += " - Window styles: " & GetWindowStyles(hwnd, AfxGetWindowStyle(hwnd)) & CHR(13, 10)
cwsText += " - Styles: " & GetListViewStyles(AfxGetWindowStyle(hwnd)) & CHR(13, 10)
cwsText += " - ExStyles: " & GetSysTabExStyles(ListView_GetExtendedListViewStyle(hwnd)) & CHR(13, 10)
DIM rc AS RECT = AfxGetWindowRect(hwnd)
cwsText += " - Window Rect: Left = " & WSTR(rc.Left) & "; Right = " & WSTR(rc.Right) & "; Top = " & WSTR(rc.Top) & "; Bottom = " & WSTR(rc.Bottom) & CHR(13, 10)
rc = AfxGetWindowClientRect(hwnd)
cwsText += " - Client Rect: Left = " & WSTR(rc.Left) & "; Right = " & WSTR(rc.Right) & "; Top = " & WSTR(rc.Top) & "; Bottom = " & WSTR(rc.Bottom) & CHR(13, 10)
CASE "RICHED20", "RICHEDIT20A", "RICHEDIT20W", "RICHEDIT", "RICHEDIT50A", "RICHEDIT50W"
cwsText += " - Styles: " & GetRichEditStyles(AfxGetWindowStyle(hwnd)) & CHR(13, 10)
cwsText += " - Window styles: " & GetWindowStyles(hwnd, AfxGetWindowStyle(hwnd)) & CHR(13, 10)
DIM rc AS RECT = AfxGetWindowRect(hwnd)
cwsText += " - Window Rect: Left = " & WSTR(rc.Left) & "; Right = " & WSTR(rc.Right) & "; Top = " & WSTR(rc.Top) & "; Bottom = " & WSTR(rc.Bottom) & CHR(13, 10)
rc = AfxGetWindowClientRect(hwnd)
cwsText += " - Client Rect: Left = " & WSTR(rc.Left) & "; Right = " & WSTR(rc.Right) & "; Top = " & WSTR(rc.Top) & "; Bottom = " & WSTR(rc.Bottom) & CHR(13, 10)
CASE ELSE
cwsText += " - Window styles: " & GetWindowStyles(hwnd, AfxGetWindowStyle(hwnd)) & CHR(13, 10)
DIM rc AS RECT = AfxGetWindowRect(hwnd)
cwsText += " - Window Rect: Left = " & WSTR(rc.Left) & "; Right = " & WSTR(rc.Right) & "; Top = " & WSTR(rc.Top) & "; Bottom = " & WSTR(rc.Bottom) & CHR(13, 10)
rc = AfxGetWindowClientRect(hwnd)
cwsText += " - Client Rect: Left = " & WSTR(rc.Left) & "; Right = " & WSTR(rc.Right) & "; Top = " & WSTR(rc.Top) & "; Bottom = " & WSTR(rc.Bottom) & CHR(13, 10)
END SELECT
DIM pcws AS CWSTR PTR = cast(CWSTR PTR, lParam)
IF pcws THEN pcws->Add(cwsText)
FUNCTION = CTRUE
END FUNCTION
' ========================================================================================
Hola Jose,
About the StatusBar, what if you try with VirtualAllocEx(), ReadProcessMemory(), and friends...
Pierre
Dim ByteCount AS SIZE_T_ Ptr
Dim pStatusBarRect AS LPCVOID
Dim StatusBarRect AS RECT
Dim hStatusBar AS HANDLE
Dim hProcess AS HANDLE
Dim hThread AS DWORD
Dim pid AS DWORD
Dim StatusBarPartId AS LONG
Dim StatusBarPartCount AS LONG
hStatusBar = The status bar handle
StatusBarPartCount = SendMessage(hStatusBar, SB_GETPARTS, 0, 0)
StatusBarPartId = StatusBarPartCount - 1 'Zero based
hThread = GetWindowThreadProcessId(hStatusBar, @pid)
hProcess = OpenProcess(PROCESS_VM_OPERATION OR PROCESS_VM_READ OR PROCESS_VM_WRITE OR _
PROCESS_QUERY_INFORMATION, FALSE, pid)
pStatusBarRect = VirtualAllocEx(BYVAL hProcess, BYVAL 0, SIZEOF(StatusBarRect), _
BYVAL MEM_COMMIT, BYVAL PAGE_READWRITE)
SendMessage(hStatusBar, SB_GETRECT, StatusBarPartId, Cast(lParam, pStatusBarRect))
ReadProcessMemory(hProcess, pStatusBarRect, BYVAL VARPTR(StatusBarRect), BYVAL SIZEOF(StatusBarRect), ByteCount)
VirtualFreeEx(hProcess, @pStatusBarRect, 0, MEM_RELEASE)
CloseHandle(hProcess)
MessageBox(HWND_DESKTOP, _
!"StatusBarPartCount \t" & STR(StatusBarPartCount) & _
!"\nStatusBarPartId \t" & STR(StatusBarPartId) & _
!"\nStatusBarRect.Left \t" & STR(StatusBarRect.Left) & _
!"\nStatusBarRect.Right \t" & STR(StatusBarRect.Right), _
!"StatusBarRect", MB_OK OR MB_TOPMOST)
Thanks very much, Pierre. I was looking at OpenProcess, but I was missing the VirtualAllocEx, ReadProcessMemory friends. I remember now to have read code that used them many years ago, but could not remember it. To fix knowledge about rarely used techniques is one of the reasons of what I write so many wrappers. Like this one, that I never have needed to use until now:
' ========================================================================================
' Retrieves the path of the executable file that created the specified window.
' ========================================================================================
PRIVATE FUNCTION AfxGetPathFromWindowHandle (BYVAL hwnd AS HWND) AS CWSTR
DIM idProc AS DWORD, hProcess AS HANDLE, wszPath AS WSTRING * MAX_PATH
GetWindowThreadProcessId(hwnd, @idProc)
IF idProc THEN
hProcess = OpenProcess(PROCESS_QUERY_INFORMATION OR PROCESS_VM_READ, FALSE, idProc)
IF hProcess THEN
GetModuleFileNameExW(hProcess, NULL, wszPath, SIZEOF(wszPath))
CloseHandle(hProcess)
RETURN wszPath
END IF
END IF
END FUNCTION
' ========================================================================================
I'm using it in FBWinSpy to detect if the application is 32 or 64 bit:
' // ------ Get the binary type ------------
wszExePath = AfxGetPathFromWindowHandle(_hwnd)
cwsText += wszExePath & CHR(13, 10)
DIM nType AS DWORD
GetBinaryTypeW(@wszExePath, @nType)
DIM wszType AS WSTRING * 260
IF nType = 0 THEN
wszType = " (32 bit)"
ELSEIF nType = 6 THEN
wszType = " (64 bit)"
END IF
cwsText += "Binary type: " & WSTR(nType) & wszType & CHR(13, 10)
Coming back to the status bar, this function does the job:
' ========================================================================================
' Retrieves the bounding rectangle of a part in a status window.
' Parameters:
' - hStatusBar = Handle of the status bar control. It can belong to another application.
' - nPart = Zero-based index of the part whose bounding rectangle is to be retrieved.
' Return value: A RECT structure with the bounding rectangle.
' ========================================================================================
PRIVATE FUNCTION AfxGetStatusBarRect (BYVAL hStatusBar AS HWND, BYVAL nPart AS LONG) AS RECT
DIM idProc AS DWORD, hProcess AS HANDLE, pSbRect AS ANY PTR, rc AS RECT, cbBytesRead AS SIZE_T
GetWindowThreadProcessId(hStatusBar, @idProc)
IF idProc = 0 THEN EXIT FUNCTION
hProcess = OpenProcess(PROCESS_VM_OPERATION OR PROCESS_VM_READ OR PROCESS_VM_WRITE OR _
PROCESS_QUERY_INFORMATION, FALSE, idProc)
IF hProcess = NULL THEN EXIT FUNCTION
pSbRect = VirtualAllocEx(hProcess, NULL, SIZEOF(RECT), MEM_COMMIT, PAGE_READWRITE)
IF pSbRect THEN
SendMessageW(hStatusBar, SB_GETRECT, nPart, cast(LPARAM, pSbRect))
ReadProcessMemory(hProcess, pSbRect, @rc, SIZEOF(rc), @cbBytesRead)
VirtualFreeEx(hProcess, pSbRect, 0, MEM_RELEASE)
END IF
CloseHandle(hProcess)
FUNCTION = rc
END FUNCTION
' ========================================================================================
Being used as:
CASE "MSCTLS_STATUSBAR32"
cwsText += " - Styles: " & GetStatusBarStyles(AfxGetWindowStyle(hwnd)) & CHR(13, 10)
cwsText += " - Window styles: " & GetWindowStyles(hwnd, AfxGetWindowStyle(hwnd)) & CHR(13, 10)
DIM rc AS RECT = AfxGetWindowRect(hwnd)
cwsText += " - Window Rect: Left = " & WSTR(rc.Left) & "; Right = " & WSTR(rc.Right) & "; Top = " & WSTR(rc.Top) & "; Bottom = " & WSTR(rc.Bottom) & CHR(13, 10)
rc = AfxGetWindowClientRect(hwnd)
cwsText += " - Client Rect: Left = " & WSTR(rc.Left) & "; Right = " & WSTR(rc.Right) & "; Top = " & WSTR(rc.Top) & "; Bottom = " & WSTR(rc.Bottom) & CHR(13, 10)
DIM nParts AS LONG = StatusBar_GetPartsCount(hwnd)
cwsText += " - Parts: " & WSTR(nParts) & "; Text part 1: " & **AfxGetWindowText(hwnd) & CHR(13, 10)
FOR i AS LONG = 0 to nParts - 1
rc = AfxGetStatusBarRect(hwnd, i)
cwsText += " - Part " & WSTR(i) &": Left = " & WSTR(rc.Left) & ", Right = " & WSTR(rc.Right) & ", Top = " & WSTR(rc.Top) & ", Bottom = " & WSTR(rc.Bottom) & CHR(13, 10)
NEXT
Looks like fbWinSpy is coming great. :-)
I don't think it is usefull in the current scope, but if somebody ever need an extended GetBinaryType() that do more than exe,
then "Checking for 32/64 bit exe, dll, cpl, scr... files" (https://forum.powerbasic.com/forum/user-to-user-discussions/powerbasic-for-windows/55621-files-or-dlls-are-they-16bit-32bit-64bit) might help,
or easier yet for non critial code,
the unDocumented Ole32.dll's CoGetModuleType(wFileName, dwModuleType) (http://undoc.airesoft.co.uk/ole32.dll/CoGetModuleType.php) who also does check the FileHeader.
Pierre
In fact, using the C aliases for data types, we don't even need to know if the application is 32 or 64 bit, because the goal is to generate code that compiles to 32 or 64 bit without changes.
For example, in the CWindows class, instead of assigning 4 or 8 bytes to the cbWndExtra member, I'm using SIZEOF(HANDLE). If it is 32 bit, SIZEOF(HANDLE) will return 4, and if it is 64 bit, it will return 8.
' // Fill the WNDCLASSEXW structure
WITH wcexw
.cbSize = SIZEOF(wcexw)
.style = CS_DBLCLKS OR CS_HREDRAW OR CS_VREDRAW
.lpfnWndProc = lpfnWndProc
.cbClsExtra = 0
.cbWndExtra = SIZEOF(HANDLE)
.hInstance = hInstance
.hCursor = ..LoadCursorW(NULL, CAST(LPCWSTR, IDC_ARROW))
.hbrBackground = CAST(HBRUSH, COLOR_3DFACE + 1)
.lpszMenuName = NULL
.lpszClassName = @m_wszClassName
.hIcon = 0
.hIconSm = 0
END WITH
I have noticed your use of BYVAL when passing some parameters in calls to Windows API functions. The FB headers always use BYVAL (there is not a single BYREF in any of the declares), so you won't ever need to use the BYVAL override when calling them (a different matter are my wrappers, in which I often use BYREF). What you will need a lot is to use VARPTR or @, and also CAST.
BTW here is the WINE code for GetBinary Type:
https://github.com/wine-mirror/wine/blob/master/dlls/kernel32/module.c
I often check the WINE source code to see what the Windows API functions do under the hood.
Files reuploaded in the first post.
Added code to retrieve information about the toolbar buttons.
Added a check to ascertain if the form handle is the same that the main window before getting the menu information. The form handle could be a popup dialog or a tab page.
I'm thinking that the best approach could be to use a TreeView to classify all the information retrieved, as I do in my TypeLib Browser. Then, this information can be parsed to generate code.
Yep, about the ByVal you are right, code was a ported from PB
and I'm often lazy and sloppy so I posted without good cleaning.
I never did pay much attention to Wine, a second look made it more interesting, thank.
Now time to look at the new fbWinSpy, ...it will be much easier to port code.
Pierre
> I never did pay much attention to Wine, a second look made it more interesting, thank.
I don't use Wine, but sometimes I look at the source code to know what and how the Windows API functions do.
If you don't have already a function to retrieve controls handle that are inside a frame then, J. Brown wrote WindowFromPointEx.c (http://read.pudn.com/downloads14/sourcecode/windows/system/55084/winspy+SCR/scr/WindowFromPointEx.c__.htm).
Even if it's not really needed in the present case, I find it useful in many occasions. It is working pretty well.
Here is a fb adaptation, and some pb code with a couple of frames and controls to show what I mean...
For the fun of it, I did replace WindowFromPoint(pt) with WindowFromPointEx(pt) in fbWinSpy.
Pierre
' ========================================================================================
' WindowFromPointEx procedure
' ========================================================================================
Function WindowFromPointEx(CursorPosDesktop AS POINT) AS HANDLE
'Thank to J. Brown @ http://read.pudn.com/downloads14/sourcecode/windows/system/55084/winspy+SCR/scr/WindowFromPointEx.c__.htm
'J. Brown says...
'The problem:
' WindowFromPoint API is not very good. It cannot cope
' with odd window arrangements, i.e. a group-box in a dialog
' may contain a few check-boxes. These check-boxes are not
' children of the groupbox, but are at the same "level" in the
' window-hierachy. WindowFromPoint will just return the
' first available window it finds which encompasses the mouse
' (i.e. the group-box), but will NOT be able to detect the contents.
'The solution:
' We use WindowFromPoint to start us off, and Then step back one
' level (i.e. from the parent of what WindowFromPoint returned).
' Once we have this window, we enumerate ALL children of this window
' ourselves, and find the one that best fits under the mouse -
' the smallest window that fits, in fact.
' I've tested this on alot of dIfferent apps, and it seems
' to work flawlessly - in fact, I havn't found a situation yet
' that this method doesn't work on.....we'll see!
'J. Brown -
Dim hFromPoint AS HANDLE
Dim hParent AS HANDLE
Dim hControlTry AS HANDLE
Dim CtrlRect AS RECT
Dim Area AS LONG
Dim AreaPrev AS LONG
hFromPoint = WindowFromPoint(CursorPosDesktop)
Function = hFromPoint
hParent = GetParent(hFromPoint)
If hParent Then
hControlTry = GetWindow(hParent, GW_Child) 'Get first child
Do While hControlTry 'Enumerate all child including ourselve
GetWindowRect(hControlTry, @CtrlRect) 'Get child's rect
If PtInRect(@CtrlRect, CursorPosDesktop) Then 'Is mouse on control
Area = (CtrlRect.Right - CtrlRect.Left) * (CtrlRect.Bottom - CtrlRect.Top) 'Calculate area
If AreaPrev Then 'This is another control under same mouse position, like FRAME or BUTTON
If Area < AreaPrev Then 'If it's smaller Then we want it
If isWindowVisible(hControlTry) Then
Function = hControlTry
End If
End If
End If
AreaPrev = Area 'Memorise for next loop, If any
End If
hControlTry = GetWindow(hControlTry, GW_HWNDNEXT)
Loop
End If
END Function
' ========================================================================================
Dialog with frames and other controls
#COMPILE EXE '#Win 9.07#
#DIM ALL
#INCLUDE "Win32Api.inc"
'#INCLUDE "Commctrl.inc"
%FrameA = 101
%FrameB = 102
%Static = 201
%Edit = 202
%Button = 203
'_____________________________________________________________________________
CALLBACK FUNCTION MainProc()
SELECT CASE CBMSG
CASE %WM_COMMAND
SELECT CASE CBCTL
CASE %Button
IF CBCTLMSG = %BN_CLICKED OR CBCTLMSG = 1 THEN
WinBeep(1500, 100)
END IF
END SELECT
END SELECT
END FUNCTION
'_____________________________________________________________________________
FUNCTION PBMAIN()
LOCAL RetVal AS LONG
LOCAL hDlg AS DWORD
DIALOG NEW %HWND_DESKTOP, "FrameInFrame", , , 200, 200, _
%WS_CAPTION OR %WS_MINIMIZEBOX OR %WS_SYSMENU, 0 TO hDlg
CONTROL ADD FRAME, hDlg, %FrameA, "Frame 101", 5, 5, 190, 190
CONTROL ADD LABEL, hDlg, %Static, "Label 201", 25, 25, 150, 15, %SS_CENTER
CONTROL ADD TEXTBOX, hDlg, %Edit, "TextBox 202", 35, 60, 130, 15
CONTROL ADD FRAME, hDlg, %FrameB, "Frame 102", 25, 100, 150, 80
CONTROL ADD BUTTON, hDlg, %Button, "Button 203", 70, 135, 60, 15
DIALOG SHOW MODAL hDlg, CALL MainProc TO RetVal
END FUNCTION
'_____________________________________________________________________________
'
Thanks very much, Pierre, it has been providential. I was pulling my hair because in my CSED editor I have popup windows with group boxes in which the controls are not children of the group box, but of the popup window, and it was not returning the selected control.
I have made a GUI. You can select the main window, a popup window with controls or an individual contros (with or without child controls) and it displays the retrieved information in a Treeview. Next step will be to retrieve and display information about the child controls.
New files reuploaded in the first post.
Jose,
I have investigated this type of app previously and found not knowing the font name, size of the Window/Dialog/Control places them quite low on my must have utilities list.
Have you found (or created) an api that will give you this info?
James
As the title of the thread says, it is a preliminary work. The code to retrieve and display information about the controls has not been yet implemented. I have posted it because I knew that Pierre would be interested. The others will have to wait.
Quote from: James Fuller on June 10, 2017, 10:05:59 AM
Jose,
I have investigated this type of app previously and found not knowing the font name, size of the Window/Dialog/Control places them quite low on my must have utilities list.
Have you found (or created) an api that will give you this info?
James
To get the size of a window is a piece of cake.
Regarding the font, there is not guarantee that this info can be obtainable, except for Windows standard controls. I will display it if the info is retrievable.
This function retrieves the font. See Remaks.
' ========================================================================================
' Retrieves the font used by the specified window.
' Parameters:
' - hwnd = Handle to the window.
' - lplfw = Pointer to a LOGFONTW stucture.
' Return value:
' If the function succeeds, and lplfw is a valid pointer, the return value is the number
' of bytes stored into the buffer.
' If the function succeeds, and lplfw is NULL, the return value is the number of bytes
' required to hold the information the function would store into the buffer.
' If the function fails, the return value is zero.
' Remarks:
' WM_GETFONT works fine cross-process, provided that the window you're sending it to
' responds to this message. This is only guaranteed for standard Windows controls. If the
' standard Windows control is using the system font, it returns NULL.
' The WM_GETFONT message does not return a font handle if the message is sent to a dialog
' box created by the DialogBoxParam, DialogBoxIndirectParam, CreateDialogParam, or
' CreateDialogIndirectParam function.
' A window can use an arbitrary font for rendering, or even multiple fonts.
' ========================================================================================
FUNCTION FBWS_GetFont (BYVAL hwnd AS HWND, BYVAL lplfw AS LOGFONTW PTR) AS LONG
IF IsWindow(hwnd) = FALSE THEN EXIT FUNCTION
DIM hFont AS HFONT = CAST(HFONT, SendMessageW(hwnd, WM_GETFONT, 0, 0))
IF hFont = 0 THEN EXIT FUNCTION
FUNCTION = GetObjectW(hFont, SIZEOF(LOGFONTW), lplfw)
END FUNCTION
' ========================================================================================
Added information of the controls, including font name and point size if available.
Code reuploaded in the first post.
Jose,
Thank you for the confirmation.
Now to be any use for DDT -> SDK (CWindow) you will need to add functionality to query the user for the font name and pont size for the Dialog and controls.
I had visions of doing this but lost interest.
James
It already retrieves the font of the controls.
To retrieve the font used by the dialog, it's simply a matter of adding
' // Get the font, if available
DIM lfw AS LOGFONTW
IF FBWS_GetFont(hwndMain, @lfw) THEN
TreeView_AddItem(hTreeView, __hMainWindowNode, NULL, "Font mame: " & lfw.lfFaceName)
TreeView_AddItem(hTreeView, __hMainWindowNode, NULL, "Font point size: " & AfxGetFontPointSize(lfw.lfHeight))
END IF
I have checked it and DDT dialogs respond to the WM_GETFONT message.
BTW I don't have any interest in translating DDT code to SDK. I'm allergic to dialogs in general and to DDT in particular.
At most, we could translate the GUI, but what is going to do with this the typical DDTer, without much knowledge of SDK code (Pierre is an exception), with the rest of DDT code?
Added menu information and other changes.
Code reuploaded in the first post.
Added information about rebars and other changes.
Code reuploaded in the first post.
Now it searches recursively if a child window or control (e.g. a tab control) has also children and displays full information.
Code reuploaded in the first post.
Hey Jose,
On my machine, using Control-A in the "Code TAB" edit control will beep.
Also Control-E is assigned to "è" on my keyboard making the character to be inserted.
All seem's fine if I use the modified code, except that, of course, the code for "è" is not a valid solution
Is Control-E is standard for selection in Spanish?
Pierre
' ========================================================================================
' Code text box window procedure
' ========================================================================================
FUNCTION FBWS_Edit_CodeView_WndProc (BYVAL hwnd AS HWND, BYVAL uMsg AS UINT, BYVAL wParam AS WPARAM, BYVAL lParam AS LPARAM) AS LRESULT
SELECT CASE uMsg
#If 0 'Genuine Jose code
CASE WM_KEYDOWN
' // If Ctrl+A or Ctrl+E pressed, select all the text
IF wParam = VK_A OR wParam = VK_E THEN
IF GetAsyncKeyState(VK_CONTROL) THEN
PostMessage hwnd, EM_SETSEL, 0, -1
END IF
END IF
' // Eat the Escape key to avoid the page being destroyed
IF wParam = VK_ESCAPE THEN EXIT FUNCTION
#Else 'Modified code
CASE WM_CHAR
SELECT CASE wParam
CASE 1, 232 '1 = Control-A, 232 = è
'Using WM_CHAR/1 instead of WM_KEYDOWN/VK_A will not beep
'On my keyboard Control-e give "è". So it add "è" to the text before selection
SendMessage(hWnd, EM_SETSEL, 0, - 1) 'Select everything
EXIT FUNCTION
END SELECT
CASE WM_KEYDOWN
IF wParam = VK_ESCAPE THEN EXIT FUNCTION
#EndIf
CASE WM_DESTROY
' // REQUIRED: Remove control subclassing
SetWindowLongPtrW hwnd, GWLP_WNDPROC, CAST(LONG_PTR, RemovePropW(hwnd, "OLDWNDPROC"))
END SELECT
' // Default processing of Windows messages
FUNCTION = CallWindowProcW(GetPropW(hwnd, "OLDWNDPROC"), hwnd, uMsg, wParam, lParam)
END FUNCTION
' ========================================================================================
As I normally have my speaker off because of these annoying web pages with sound, I didn't have noticed the beep :)
Maybe better to use only
CASE WM_CHAR
SELECT CASE wParam
CASE 1 ' // Ctrl+A
SendMessage(hWnd, EM_SETSEL, 0, - 1) 'Select everything
EXIT FUNCTION
END SELECT
Otherwise, we can't use è.
> Is Control-E is standard for selection in Spanish?
Explorer and Notepad use it.
Hey,
The "è" is certainly no big deal for me, I might be the only one having it cause I got my own layout using MS-Keyboard-Layout-Creator.
It's not a french keyboard standard asignment, still other may have the letter "e" inserted with a beep.
This is interesting stuff, one more thing to take care about... :-) Comparison of shortcuts in English and Spanish (http://stelio.net/stiki/IT:Comparison_of_shortcuts_in_English_and_Spanish)
Pierre
These differences came because they try to choose a shortcut that is easier to remember according the language of the user.
For example:
Ctrl+F (for English "Find"), Ctrl+B (for Spanish "Buscar")
Ctrl+O (for English "Open"), Ctrl+A (for Spanish "Abrir")
Ctrl+E (for English "All"), Ctrl+E (could have been Ctrl+T, for Spanish "Todo", but Ctrl+T is already used for "Tamaño", i.e. "Size")
Ctrl+U (for English "Underline"), Ctrl+S (for Spanish "Subrayado")
etc.
In Notepad, using the Spanish Windows version, to do a "Find" you have to use Ctrl+B (not Ctrl+F) and to select all Ctrl+E (not Ctrl-A).