Extended MessageBox function - Updated ! - change button captions, main icon, disable buttons

Updated version - Now allowing changing icons and disabling buttons

lnOption = MsgboxEx( ;
   "You have entered a wrong password for 5 times.
" + chr(13) + ;
   "For security reasons this login was suspended.", ; && main message
   0, ; && default icon info
   "Password incorrect", ; && title bar caption
   "\&Retry,&Exit,Get &new pwd", ; && new button captions
   "41.ico") && icon file

This is a simple function that allows us to customize the captions of buttons of the messagebox() dialog window, like in the image shown above.

Thanks to Mike gagnon and Anatolyi Mogylevetz, here's an updated version of the version that allows to change the icon and also to disable the buttons.

Basically I followed Anatolyi's instructions to allow disabling the buttons. Changing the dialog icon was also very simple, with a short research in his great site - Using Win32 functions in Visual Foxpro .

I just adapted a sample provided by Craig Boyd in his blog article - Bindevent on steroids using the VFPEX.FLL that he created and kindly provided in that article. Please refer to the original article for more details.

I called it MSGBOXEX.PRG, and it can be used very simply, to obtain the result below - notice the 4th parameter, that contains the captions of the buttons to be used, delimited by a comma:

lnOption = MsgboxEx(;
   "An unexpected error has occurred and the system needs to be restarted." + ;
   chr(13) + chr(13) + "What do you want to do ?", ;
   "X", ;
   "MsgboxEx sample", ;
   "Restart &Now,Restart &later,&Never restart")

lnOption = MsgboxEx( ;
   "Could not find the file 'Import.csv' in the selected folder.", ;
   0, ;
   "File not found", ;
   "&Abort,\&Retry,Change folder", ;

function: MsgboxEx.prg

description: Modifies the captions of messagebox buttons


  • tcCaption - the text that appears in the dialog box.

  • tnIcon - the icon sign - can be numeric, using the default messagebox() icon values or character:
       - stop sign: 16 or "x"
       - question mark: 32 or "?"
       - exclamation point: 48 or "!"
       - information: 64 or "i"

  • tcTitle - the text that appears in the title bar of the dialog box

  • tcButtons - the captions to be used in the buttons using the comma "," delimiter
    use the "&" character to determine the hotkeys to be used - eg: "option&1,option&2,option&3" - the "\" backslash can be used to show the current button disabled.

  • tcIconFile - (optional) the icon image file

returns: The index number according to the option selected - eg. returns the value 3 if the 3rd button was selected.

  • use the same dialog interface from the messagebox() function, allowing us to easilly customize existing dialog boxes.
  • almost the same structure of parameters of the original function
  • behaves correctly in all operating systems.
  • allows hotkeys (thanks to thiago takehana for reminding about this possibility)

  • up to 3 buttons are allowed
  • size of buttons is limited to the original messagebox() buttons size, not allowing big sized buttons.

lparameters tccaption, tnicon, tctitle, tcbuttons, tciconfile
* msgboxex.prg
* description: modifies the captions of messagebox buttons
* prerequisites: needs vfpex.fll by craigboyd
* http://www.sweetpotatosoftware.com/spsblog/ct.ashx?id=f7644db8-b155-4d43-8216-4cfde233edb7&url=http%3a%2f%2fwww.sweetpotatosoftware.com%2ffiles%2fvfpex.zip
* more info about vfpex.fll
* http://www.sweetpotatosoftware.com/spsblog/2005/08/07/bindeventonsteroids.aspx
* parameters:
* tccaption - the text that appears in the dialog box.
* tnicon - the icon sign
* tctitle - the text that appears in the title bar of the dialog box
* tcbuttons - the captions to be used in the buttons using the comma "," delimiter
* use the "&" character to determine the hotkeys to be used - eg: "option&1,option&2,option&3"
* use a "\" to disable the button
* tciconfile - the icon file to replace the default from messagebox()
* returns: the index number according to the option selected - eg. returns the value 3 if the 3rd button was selected.
* sample:
* =msgboxex("this is a common text", "!", "window title", "option1,option2,option3")
if vartype(tntimeout) = "C" and (pcount() = 4)
   tcbuttons = tntimeout
   tntimeout = 0

private pnbuttoncnt, pcbuttons, pnbutttype, pciconfile, phicon
pciconfile = iif(empty(tciconfile),"", tciconfile)
pnbuttoncnt = getwordcount(tcbuttons, ",")
pcbuttons = tcbuttons
*!* stop 16
*!* question 32
*!* exclamation 48
*!* info 64
if vartype(tnicon) = "c"
   tnicon = upper(tnicon)
   do case
   case tnicon = "X"
      tnicon = 16
   case tnicon = "?"
      tnicon = 32
   case tnicon = "!"
      tnicon = 48
   case tnicon = "I"
      tnicon = 64
      tnicon = 0

* check if an icon will be shown
* if an icon file was passed, we need to ensure that messagebox() will
* show an icon, that will be changed further.

#define image_bitmap 0
#define image_icon 1
#define lr_loadfromfile 0x0010
#define lr_defaultsize 0x0040
phicon = 0
if not empty(pciconfile) and ;
   (not (bittest(tnicon, 4) or bittest(tnicon, 5) or bittest(tnicon, 6)))
   tnicon = tnicon + 16
   phicon = xmbloadimage(0, fullpath(pciconfile), image_icon,;
0,0, lr_loadfromfile + lr_defaultsize)

* windows hook constants
#define wh_cbt 5

* set library so bindeventex and unbindeventex can be used in vfp
local lcoldsetlib
lcoldsetlib = set("library")
set library to (locfile("vfpex.fll"))
bindeventex('wineventhandler()', wh_cbt) && setwindowshookex

* this messagebox will be modified before it is shown
local lnoption, lnindex
do case
case pnbuttoncnt = 1
   pnbutttype = 0 && ok
case pnbuttoncnt = 2
   pnbutttype = 4 && yes / no
case pnbuttoncnt = 3
   pnbutttype = 2 && abort / retry / ignore

lnoption = messagebox(tccaption, tnicon + pnbutttype, tctitle)

local lnoffset
lnoffset = icase(pnbuttoncnt = 3, 2, pnbuttoncnt = 2, 5 , 0)
lnindex = lnoption - lnoffset

if phicon <> 0
   =xmbdeleteobject(phicon) && clear icon handle

if not empty(lcoldsetlib)
   set library to (lcoldsetlib)
return lnindex

procedure wineventhandler
#define dlg_ctrlid_icon 0x0014
#define stm_seticon 0x0170
#define stm_setimage 0x0172
if ncode == 5
   if not empty(phicon)
      * changing the dialog icon
      local lhiconwindow
      lhiconwindow = xmbgetdlgitem(wparam, dlg_ctrlid_icon)
      if lhiconwindow <> 0
         if phicon <> 0
            =xmbsendmessage(lhiconwindow, stm_seticon, phicon, 0)

   * change button attributes
   local n, lnoffset, lccaption
   lnoffset = icase(pnbuttoncnt = 3, 2, pnbuttoncnt = 2, 5 , 0)
   for n = 1 to pnbuttoncnt
      lccaption = getwordnum(pcbuttons, n, ",")
      * disable current button
      if left(lccaption, 1) = "\"
         lccaption = substr(lccaption, 2) && get the rest of the string
         local lnbtnhwnd
         lnbtnhwnd = xmbgetdlgitem(wparam, lnoffset + n)
         =xmbenablewindow(lnbtnhwnd, 0)

      * change the caption
      =xmbsetdlgitemtext(wparam, lnoffset + n, lccaption)

   =xmbcallnexthookex(hhook, ncode, wparam, lparam) && all 4 variables exist
   =xmbcallnexthookex(hhook, ncode, wparam, lparam) && all 4 variables created by fll

release ncode, wparam, lparam, hhook


function xmbsetdlgitemtext(hdlg, niddlgitem, lpstring)
declare integer SetDlgItemText in user32 as xmbsetdlgitemtext ;
   long hdlg,;
   long niddlgitem,;
   string lpstring
return xmbsetdlgitemtext(hdlg, niddlgitem, lpstring)

function xmbcallnexthookex(hhook, ncode, wparam, lparam)
declare long CallNextHookEx in user32 as xmbcallnexthookex ;
   long hhook, long ncode, long wparam, long lparam
return xmbcallnexthookex(hhook, ncode, wparam, lparam)

function xmbgetdlgitem(hdlg, niddlgitem)
* hdlg [in] handle to the dialog box that contains the control.
* niddlgitem [in] specifies the identifier of the control to be retrieved.
* http://msdn.microsoft.com/en-us/library/ms645481(vs.85).aspx
declare integer GetDlgItem in user32 as xmbgetdlgitem ;
   long hdlg,;
   long niddlgitem
return xmbgetdlgitem(hdlg, niddlgitem)

function xmbenablewindow(hwnd, fenable)
declare integer EnableWindow in user32 as xmbenablewindow integer hwnd, integer fenable
return xmbenablewindow(hwnd, fenable)

function xmbsendmessage(hwindow, msg, wparam, lparam)
* http://msdn.microsoft.com/en-us/library/bb760780(vs.85).aspx
* http://www.news2news.com/vfp/?group=-1&function=312
declare integer SendMessage in user32 as xmbsendmessage;
   integer hwindow, integer msg,;
   integer wparam, integer lparam
return xmbsendmessage(hwindow, msg, wparam, lparam)

function xmbloadimage(hinst, lpszname, utype, cxdesired, cydesired, fuload)
declare integer LoadImage in user32 as xmbloadimage;
   integer hinst,;
   string lpszname,;
   integer utype,;
   integer cxdesired,;
   integer cydesired,;
   integer fuload
return xmbloadimage(hinst, lpszname, utype, cxdesired, cydesired, fuload)

function xmbdeleteobject(hobject)
declare integer DeleteObject in gdi32 as xmbdeleteobject integer hobject
return xmbdeleteobject(hobject)

download the vfpex.fll here, created by craig boyd, directly from the sps weblog.
download the MessageboxEx function  


2009-10-19 original version (change captions)
2009-10-23 updated, allowing icon change and disabling buttons



A GdiPlusX analog clock

After I posted a Gauge sample with gdiplusx some months ago, Rafael Lippert from Brazil adapted my original code and created this cool analog watch, using GdiplusX and the 'ImageCanvas' class:

Cool isn't it ? it permits many customizations, and you may use it as a sample to learn other GdiplusX techniques.

You may get it directly from Rafael' blog, at: http://rafaellippert.wordpress.com/2011/06/21/a-gdiplusx-analog-clock/

It's worth to spend some time in Rafael's blog and website - www.lipsil.com.br . I'm sure you'll find some really very neat utilities.