Trouble Passing Banks to user32 api functions

Blitz3D Forums/Blitz3D Userlibs/Trouble Passing Banks to user32 api functions

Hi all,

I'm trying to work out how to call api functions that want a struct as a parameter.

The example Im using is
.lib user32
MessageBoxIndirect% (lpMsgBoxParams*) : "MessageBoxIndirectA"

_IF_ you were using Visual basic you would create a Type as follows:

Const SHGFI_ICONLOCATION = &H1000
Const MB_ICONASTERISK = &H40&
Const MB_ICONEXCLAMATION = &H30&
Const MAX_PATH = 260
Private Type MSGBOXPARAMS
cbSize As Long
hwndOwner As Long
hInstance As Long
lpszText As String
lpszCaption As String
dwStyle As Long
lpszIcon As String
dwContextHelpId As Long
lpfnMsgBoxCallback As Long
dwLanguageId As Long
End Type

But I'm not, Im using Blitz Basic 3D. However the first thing I did was create a type in B3d and try passing that to the function directly. It sort of worked. The message box has the correct Icon but only single characters for the caption and message.

heres the code I have been experimenting with:

Type MSGBOXPARAMS
   Field cbSize
   Field hwndOwner 
   Field hInstance
   Field lpszText$
   Field lpszCaption$ 
   Field dwStyle
   Field lpszIcon$
   Field dwContextHelpId
   Field lpfnMsgBoxCallback
   Field dwLanguageId
End Type

 
Graphics3D 800,600,32,2

While Not KeyHit(1)
val = MessageBox (getActiveWindow(),"All Your Base Are Belong To Us","Warning",$10 + $3)

 mbx.msgboxparams=New msgboxparams
 mbx\hwndOwner = getActiveWindow()
 mbx\lpszText$="What foul creature wakes me from mine slumber?" +Chr(0)
 mbx\lpszCaption="Warning" + Chr(0)
 mbx\dwStyle=$30
 mbx\cbSize=83


 DoMessageBoxIndirect(mbx)

Print val

WaitKey


Wend

Function doMessageBoxIndirect(mbx.msgboxparams)
	Stop
	;mbx\cbSize=31+Len(mbx\lpszcaption)+Len(mbx\lpsztext)+Len(mbx\lpszIcon)
	
	bnk=CreateBank(mbx\cbSize)
	PokeInt (bnk,0,mbx\cbSize)
	PokeInt (bnk,4,mbx\hwndOwner)
	PokeInt (bnk,8,mbx\hInstance)
	For f=12 To Len(mbx\lpsztext)
		char$=Mid((mbx\lpsztext),f-11,1)
		
		PokeByte bnk,f,Asc(char)
	Next
	f=f+1
	PokeByte bnk,f,0
	f=f+1
	For n=f To Len(mbx\lpszCaption)
		PokeByte bnk,n,Asc(Mid(mbx\lpszCaption,n-(f-1),1))
	Next
	n=n+1
	PokeByte bnk,n,0
	n=n+1
	PokeInt(bnk,n,mbx\dwStyle)
	f=f+1
		For f=n To Len(mbx\lpszCaption)
		PokeByte bnk,f,Asc(Mid(mbx\lpszCaption,f-(n-1),1))
	Next
	f=f+1
	PokeByte bnk,f,0
	f=f+1
	PokeInt (bnk,f,mbx\dwContextHelpID)
	f=f+4
	PokeInt (bnk,f,mbx\lpfnMsgBoxCallback)
	f=f+4
	PokeInt (bnk,f,mbx\dwLanguageId)

	messageboxindirect(bnk)
	


End Function



as I said. If I call messageBoxIndirect (mbx) I get a scrambled output but at least it works a bit. If I use my function to turn the type into a bank and pass that then I get a MAV at runtime.

The method using banks is the same principle as used by Peter Sheutz in the openGLdirect code on Blitz coder:

http://www.blitzcoder.com/cgi-bin/showcase/showcase_showentry.pl?id=peter__scheutz02202003193552&comments=no

So I am pretty sure I am on the right track. I just need someone to point out what I am doing wrong here.

The VB example I found sets cbSize = Len(mbx)
however that wont work in blitz so I guessed it was the size of the whole struct including the text which is 7 ints plus 3 arbitrary null terminated strings Hence the odd formula at the front of my doMessageboxIndirect function.

Would be greatfull if an enlightened one could put me back on the path as at the moment I am face down examining the dirt :)

thanks in advance

This is in fact a bit of a pain. What you need to do is poke the address of the null terminated strings for the caption and text into the parameter block (not the strings themselves). Now you can not just poke a bank address because banks are themselves structures, and although blitz will just pass the bank data when you pass a bank directly to a userlib function, it will not know to do this if you store it in a parameter block. So you will need to poke in the address stored at the bank address + 4 which is where the data is stored. One way of getting that is to use one of those external memory functions. So you could have something like...

Type MSGBOXPARAMS
   Field cbSize
   Field hwndOwner 
   Field hInstance
   Field lpszText$
   Field lpszCaption$ 
   Field dwStyle
   Field lpszIcon$
   Field dwContextHelpId
   Field lpfnMsgBoxCallback
   Field dwLanguageId
End Type

Const IDOK = 1
Const IDCANCEL = 2
Const IDABORT = 3
Const IDRETRY = 4
Const IDCONTINUE = 5
Const IDIGNORE = 5
Const IDYES = 6
Const IDNO = 7
Const MB_APPLMODAL = $0
Const MB_COMPOSITE = $2
Const MB_DEFAULT_DESKTOP_ONLY = $20000
Const MB_DEFBUTTON1 = $0
Const MB_DEFBUTTON2 = $100
Const MB_DEFBUTTON3 = $200
Const MB_DEFMASK = $F00
Const MB_ICONASTERISK = $40
Const MB_ICONEXCLAMATION = $30
Const MB_ICONHAND = $10
Const MB_ICONINFORMATION = MB_ICONASTERISK
Const MB_ICONMASK = $F0
Const MB_ICONQUESTION = $20
Const MB_ICONSTOP = MB_ICONHAND
Const MB_MISCMASK = $C000
Const MB_MODEMASK = $3000
Const MB_NOFOCUS = $8000
Const MB_OK = $0
Const MB_OKCANCEL = $1
Const MB_PRECOMPOSED = $1
Const MB_RETRYCANCEL = $5
Const MB_SETFOREGROUND = $10000
Const MB_SYSTEMMODAL = $1000
Const MB_TASKMODAL = $2000
Const MB_TYPEMASK = $F
Const MB_USEGLYPHCHARS = $4
Const MB_YESNO = $4
Const MB_YESNOCANCEL = $3

 
Graphics3D 800,600,32,2

While Not KeyHit(1)
;val = MessageBox(GetActiveWindow(),"All Your Base Are Belong To Us","Warning",MB_ICONEXCLAMATION+MB_YESNOCANCEL)

 mbx.msgboxparams=New msgboxparams
 mbx\hwndOwner = GetActiveWindow()
 mbx\lpszText$="What foul creature wakes me from mine slumber?"
 mbx\lpszCaption$="Warning"
 mbx\dwStyle=MB_ICONEXCLAMATION+MB_YESNOCANCEL

 val=DoMessageBoxIndirect(mbx)

Print val

WaitKey


Wend

Function doMessageBoxIndirect(mbx.msgboxparams)

	mbx\cbsize=40
	bnk=CreateBank(40)
	PokeInt (bnk,0,mbx\cbSize)
	PokeInt (bnk,4,mbx\hwndOwner)
	PokeInt (bnk,8,mbx\hInstance)
	strbank1=stringtobank(mbx\lpsztext)
	PokeInt (bnk,12,BLZPeekInt(strbank1+4))
	strbank2=stringtobank(mbx\lpszCaption)
	PokeInt (bnk,16,BLZPeekInt(strbank2+4))
	PokeInt(bnk,20,mbx\dwStyle)
	PokeInt (bnk,24,0)
	PokeInt (bnk,28,mbx\dwContextHelpID)
	PokeInt (bnk,32,mbx\lpfnMsgBoxCallback)
	PokeInt (bnk,36,mbx\dwLanguageId)

	val=MessageBoxIndirect(bnk)

	FreeBank bnk
	FreeBank strbank1
	FreeBank strbank2
	
	Return Val
	
End Function

Function Stringtobank(s$,bnk=0)
	size=Len(s$)+1
	If bnk=0 Then
		 bnk=CreateBank(size)
	Else
		If BankSize(bnk)<size Then ResizeBank bnk,size
	EndIf
	For f=0 To size-2
		PokeByte bnk,f,Asc(Mid(s$,f+1,1))
	Next
	PokeByte bnk,f,0
	Return bnk
End Function


In this case I am using the BLZ memory DLL and decls which was posted around here some time since, probably as part of something else. I could email it to you if you do not have it or something similar.

I also came across some constants which you might find useful. I think you may find explanations for them by looking up the MessageboxEx function.

I thought that might be the problem. When I passed the function an empty bank I got an empty message box. Working through, the code only failed when I reached the string. This still left me stuck as I had no idea how to pass the address of the variable storing the string.

Thanks for the help. I may well email you for that dll if I cant find it myself or another way around in the api.. just occured to me I may be able to write the dll myself quite easily by passing the string to a function in C and then returning the address as an int.

I found the BLZmem dll and got MessageBoxIndirect working. One last question.. why +4 on the blzpeekint()?

That is the offset address of the bank data address. Not sure what is at bank+0, could just be a bank identifier sequence or maybe something to do with memory management, the int at bank+4 is the address of the actual bank data and the int at bank+8 is the size of the bank.

Thanks :)