API Questions
#11
(08-19-2023, 03:48 PM)Dav Wrote: The wiki code is working for me as is.  Im In windows 7 32-bit using the current version of QB64-PE. 

- Dav
Well now, that's interesting. I'm in Windows 7 64-bit using the current version of QB64-PE. I'll fire up a VM of 32bit and see if it run there.

Update: The code works fine in a virtual machine running Windows 7 32bit and the latest 32bit version of QB64-PE. Seems to be a 64bit related issue then.
Software and cathedrals are much the same — first we build them, then we pray.
QB64 Tutorial
Reply
#12
It's not really an "issue". What it is, is the person who wrote the sample didn't take into account any architecture other than 32 bit. I try to make sure all my types are sized for both systems when I can.
Ask me about Windows API and maybe some Linux stuff
Reply
#13
(08-19-2023, 09:10 PM)SpriggsySpriggs Wrote: It's not really an "issue". What it is, is the person who wrote the sample didn't take into account any architecture other than 32 bit. I try to make sure all my types are sized for both systems when I can.
I was written by Galleon back in the 32bit .954 version of QB64.

What exactly would I need to do to bring the code to 64bit? I've studied the Microsoft docs pretty well and all but what I noted before seems in order. I've already tried changing the LONG values for handles to _OFFSETs and removing _OFFSETS as pointers to the UDT and instead passing the UDT directly but I get the same negative result.
Software and cathedrals are much the same — first we build them, then we pray.
QB64 Tutorial
Reply
#14
@ TerryRitchie 
there were a number of places in your code that seemed to needed to be _offset instead of a long, plus you missed hbmpItem in the Type MENUITEMINFO
here is your altered code, it successfully adds the menu item and silently crashes or simply vanishes
Code: (Select All)

DefLng A-Z

Const MIIM_STATE = &H1
Const MIIM_ID = &H2
Const MIIM_TYPE = &H10
Const MFT_SEPARATOR = &H800
Const MFT_STRING = &H0
Const MFS_ENABLED = &H0
Const MFS_CHECKED = &H8

Const HWND_TOPMOST = -1
Const HWND_NOTOPMOST = -2
Const SWP_NOMOVE = &H2
Const SWP_NOSIZE = &H1
'-----------------------------------------------------------------------------------

Type MENUITEMINFO
    cbSize As Long
    fMask As Long
    fType As Long
    fState As Long
    wID As Long
    hSubMenu As _Offset
    hbmpChecked As _Offset
    hbmpUnchecked As _Offset
    dwItemData As _Offset
    dwTypeData As _Offset
    cch As Long
    hbmpItem As _Offset
End Type

Declare Library
    Function FindWindow& (ByVal ClassName As _Offset, WindowName$) ' To get hWnd handle
End Declare

Declare Dynamic Library "user32"
    Function CreateMenu%& ()
    Function DrawMenuBar (ByVal hWnd As _Offset)
    Function SetMenu& (ByVal hWnd As _Offset, Byval hMenu As _Offset)
    Function InsertMenuItemA& (ByVal hMenu As _Offset, Byval uItem As _Unsigned Long, Byval fByPosition&, Byval lpmii As _Offset)
    Function GetMenuItemCount& (ByVal hMenu As _Offset)
    Function GetMenuItemInfoA& (ByVal hMenu As _Offset, Byval uItem As _Unsigned Long, Byval fByPosition&, Byval lpmii As _Offset)
End Declare

Dim hWnd As _Offset
Dim hMenu As _Offset
Dim MenuItem As MENUITEMINFO, BlankMenuItem As MENUITEMINFO
Dim TypeData As String * 1000

_Title "Menu bar API demo"
hWnd = _WindowHandle 'FindWindow(0, "Menu bar API demo" + CHR$(0))

hMenu = CreateMenu: BlankMenuItem.cbSize = Len(BlankMenuItem)

Color 7, 1: Cls

'Add a separator bar
count = GetMenuItemCount(hMenu): Print "MenuItemCount:"; count
MenuItem = BlankMenuItem
MenuItem.fMask = MIIM_ID Or MIIM_TYPE
MenuItem.fType = MFT_SEPARATOR
MenuItem.wID = count
If InsertMenuItemA(hMenu, count, 1, _Offset(MenuItem)) Then Print "Successfully added menu item!" Else Print "Failed to add menu item!": End

'Add a button
MenuItem = BlankMenuItem
count = GetMenuItemCount(hMenu): Print "MenuItemCount:"; count
MenuItem.fMask = MIIM_STATE Or MIIM_ID Or MIIM_TYPE
MenuItem.fType = MFT_STRING
MenuItem.fState = MFS_ENABLED
MenuItem.wID = count
TypeData = "&Fire Laser!" + Chr$(0)
MenuItem.dwTypeData = _Offset(TypeData)
MenuItem.cch = Len(MenuItem.dwTypeData)
MyButton = MenuItem.wID
If InsertMenuItemA(hMenu, count, 1, _Offset(MenuItem)) Then Print "Successfully added menu item!" Else Print "Failed to add menu item!": End

If SetMenu(hWnd, hMenu) Then Print "Successfully set menu!": Print "Menu handle is:"; hMenu Else Print "Failed to set menu!": End

Do: _Limit 70
    prev_state = new_state
    ok = GetMenuItemInfoA(hMenu, MyButton, 1, _Offset(MenuItem))
    new_state = MenuItem.fState And 128
    If prev_state = 0 And new_state <> 0 Then Print "Ouch! ";
Loop While InKey$ = ""
Reply
#15
(08-19-2023, 10:45 PM)Jack Wrote: @ TerryRitchie 
there were a number of places in your code that seemed to needed to be _offset instead of a long, plus you missed hbmpItem in the Type MENUITEMINFO
here is your altered code, it successfully adds the menu item and silently crashes or simply vanishes
Yeah, I pointed out that the original code was missing hbmpItem in an earlier post. The code is not mine, it's from the Wiki and was written by Galleon back around 2011.

As Dav pointed out, the code works correctly in 32bit versions of Windows and QB64.

Your code is closer than I have come to getting it to work. You got past the the first InsertMenuItemA, something I was unable to do. I'll study your changes to see if I can get it to work the rest of the way.

Thanks for changes.
Software and cathedrals are much the same — first we build them, then we pray.
QB64 Tutorial
Reply
#16
Here. This should work on 64-bit compilers.

Code: (Select All)

DEFLNG A-Z
OPTION _EXPLICIT

$IF 32BIT THEN
        $ERROR 32-bit compilers not supported!
$END IF

CONST NULL = 0

CONST MIIM_STATE = &H1
CONST MIIM_ID = &H2
CONST MIIM_TYPE = &H10
CONST MFT_SEPARATOR = &H800
CONST MFT_STRING = &H0
CONST MFS_ENABLED = &H0
CONST MFS_CHECKED = &H8

CONST HWND_TOPMOST = -1
CONST HWND_NOTOPMOST = -2
CONST SWP_NOMOVE = &H2
CONST SWP_NOSIZE = &H1

TYPE MENUITEMINFO
    AS _UNSIGNED LONG cbSize
    AS _UNSIGNED LONG fMask
    AS _UNSIGNED LONG fType
    AS _UNSIGNED LONG fState
    AS _UNSIGNED LONG wID
    AS STRING * 4 __padding1
    AS _UNSIGNED _OFFSET hSubMenu
    AS _UNSIGNED _OFFSET hbmpChecked
    AS _UNSIGNED _OFFSET hbmpUnchecked
    AS _UNSIGNED _OFFSET dwItemData
    AS _UNSIGNED _OFFSET dwTypeData ' str ptr
    AS _UNSIGNED LONG cch
    AS STRING * 4 __padding2
    AS _UNSIGNED _OFFSET hbmpItem
END TYPE

DECLARE DYNAMIC LIBRARY "user32"
    FUNCTION FindWindow~%& ALIAS FindWindowA (lpClassName AS STRING, lpWindowName AS STRING)
    FUNCTION CreateMenu~%&
    FUNCTION DrawMenuBar& (BYVAL hWnd AS _UNSIGNED _OFFSET)
    FUNCTION SetMenu& (BYVAL hWnd AS _UNSIGNED _OFFSET, BYVAL hMenu AS _UNSIGNED _OFFSET)
    FUNCTION InsertMenuItem& ALIAS InsertMenuItemA (BYVAL hMenu AS _UNSIGNED _OFFSET, BYVAL item AS _UNSIGNED LONG, BYVAL fByPosition AS LONG, BYVAL lpmi AS _UNSIGNED _OFFSET)
    FUNCTION GetMenuItemCount& (BYVAL hMenu AS _UNSIGNED _OFFSET)
    FUNCTION GetMenuItemInfo& ALIAS GetMenuItemInfoA (BYVAL hMenu AS _UNSIGNED _OFFSET, BYVAL item AS _UNSIGNED LONG, BYVAL fByPosition AS LONG, BYVAL lpmii AS _UNSIGNED _OFFSET)
END DECLARE

DIM MenuItem AS MENUITEMINFO, BlankMenuItem AS MENUITEMINFO
DIM TypeData AS STRING * 1000

_TITLE "Menu bar API demo"

DIM hWnd AS _UNSIGNED _OFFSET: hWnd = _WINDOWHANDLE 'FindWindow(0, "Menu bar API demo" + CHR$(0))

DIM hMenu AS _UNSIGNED _OFFSET: hMenu = CreateMenu: BlankMenuItem.cbSize = LEN(BlankMenuItem)

COLOR 7, 1

'Add a separator bar
DIM count AS LONG: count = GetMenuItemCount(hMenu): PRINT "MenuItemCount:"; count
MenuItem = BlankMenuItem
MenuItem.fMask = MIIM_ID OR MIIM_TYPE
MenuItem.fType = MFT_SEPARATOR
MenuItem.wID = count
IF InsertMenuItem(hMenu, count, 1, _OFFSET(MenuItem)) THEN PRINT "Successfully added menu item!" ELSE PRINT "Failed to add menu item!": END

'Add a button
MenuItem = BlankMenuItem
count = GetMenuItemCount(hMenu): PRINT "MenuItemCount:"; count
MenuItem.fMask = MIIM_STATE OR MIIM_ID OR MIIM_TYPE
MenuItem.fType = MFT_STRING
MenuItem.fState = MFS_ENABLED
MenuItem.wID = count
TypeData = "&Fire Laser!" + CHR$(NULL)
MenuItem.dwTypeData = _OFFSET(TypeData)
MenuItem.cch = LEN(MenuItem.dwTypeData)

DIM MyButton AS _UNSIGNED LONG: MyButton = MenuItem.wID
IF InsertMenuItem(hMenu, count, 1, _OFFSET(MenuItem)) THEN PRINT "Successfully added menu item!" ELSE PRINT "Failed to add menu item!": END

IF SetMenu(hWnd, hMenu) THEN PRINT "Successfully set menu!": PRINT "Menu handle is:"; hMenu ELSE PRINT "Failed to set menu!": END

DIM AS LONG prev_state, new_state, ok

DO
    _LIMIT 70
    prev_state = new_state
    ok = GetMenuItemInfo(hMenu, MyButton, 1, _OFFSET(MenuItem))
    new_state = MenuItem.fState AND 128
    IF prev_state = 0 AND new_state <> 0 THEN PRINT "Ouch! ";
LOOP WHILE INKEY$ = ""

END
Reply
#17
(08-20-2023, 12:01 AM)a740g Wrote: Here. This should work on 64-bit compilers.

Yes it does, thank you! This example should be added to the Windows Library examples in the Wiki.

Could you explain the changes you made and why they needed to be done so I can get a better understanding of your code and why 64-bit needs to be approached differently please?
Software and cathedrals are much the same — first we build them, then we pray.
QB64 Tutorial
Reply
#18
(08-20-2023, 05:47 PM)TerryRitchie Wrote:
(08-20-2023, 12:01 AM)a740g Wrote: Here. This should work on 64-bit compilers.

Yes it does, thank you! This example should be added to the Windows Library examples in the Wiki.

Could you explain the changes you made and why they needed to be done so I can get a better understanding of your code and why 64-bit needs to be approached differently please?

The issue lies in data alignment and type packing.  In 32 bit OSes, data is packed on a 4 byte alignment.  In 64-bit OSes, it's packed on 8-byte alignments.

For example:  
TYPE Foo
    x AS INTEGER
    y AS LONG
    z AS STRING *3
END TYPE

The above type (as a windows type) would align on 4-byte break points.  It'd actually use 12-bytes per record in memory as:

TRANSLATED TYPE Foo32
   x AS INTEGER
   buffer AS STRING *2 'complete that 4 byte block
   y AS LONG 'already 4 bytes; no buffer is needed
   z AS STRING * 3
   buffer2 AS STRING * 1 'complete the other 4 byte block
END TYPE

On a 64-bit type, the breakpoints are 8-bytes, for 16-bytes per record in memory.

TRANSLATED TYPE Foo64
   x AS INTEGER
   y AS LONG
   buffer AS STRING * 2 'complete the 8-byte block
   z AS STRING * 3
   buffer2 AS STRING * 5 'complete the second 8-byte block
END TYPE

For 32-bit windows API types, data is aligned in 4-byte segments.  For 64-bit window API types, it's in 8-byte segments.
Reply
#19
TYPE MENUITEMINFO
    AS _UNSIGNED LONG cbSize
    AS _UNSIGNED LONG fMask  << 8 bytes
    AS _UNSIGNED LONG fType
    AS _UNSIGNED LONG fState << 8 bytes
    AS _UNSIGNED LONG wID
    AS STRING * 4 __padding1  << 4 bytes + 4 bytes padding = 8 bytes
    AS _UNSIGNED _OFFSET hSubMenu  << *ALL* 64-bit OFFSET are 8 bytes
    AS _UNSIGNED _OFFSET hbmpChecked
    AS _UNSIGNED _OFFSET hbmpUnchecked
    AS _UNSIGNED _OFFSET dwItemData
    AS _UNSIGNED _OFFSET dwTypeData ' str ptr
    AS _UNSIGNED LONG cch
    AS STRING * 4 __padding2   << 4 bytes + 4 bytes padding = 8 bytes
    AS _UNSIGNED _OFFSET hbmpItem
END TYPE
Reply
#20
with all due respect to a740g, I am not comfortable with the padding in MENUITEMINFO
this C code prints 80 as the size of MENUITEMINFO
Code: (Select All)
#include <stdio.h>
#include <windows.h>
#include <winuser.h>

int main(void){
printf("%lld\n", sizeof(MENUITEMINFO));
return 0;
}
Reply




Users browsing this thread: 8 Guest(s)