Localizing Task Dialog's from a resource.dll

Share your advanced PureBasic knowledge/code with the community.
SFSxOI
Addict
Addict
Posts: 2970
Joined: Sat Dec 31, 2005 5:24 pm
Location: Where ya would never look.....

Localizing Task Dialog's from a resource.dll

Post by SFSxOI »

Most of you by now have discovered how to use Task Dialogs. What I am going to do is show you what you need, and how easy it is, to localize your task dialogs and use icons from another .dll (or .exe) or your own custom icons from your own reseource.dll. I've been using Task Dialogs for a while and it never occured to me until recently, due to a few questions from someone about a very early post I made for Task Dialogs when i first started using them, that some people may not know how to localize a Task Dialog with PureBasic.

Localization begins with two flags and defining our resources - and placement of a few flags TDF_USE_HICON_FOOTER and TDF_USE_HICON_MAIN found in the enumeration TASKDIALOG_FLAGS shown below:

(Note: the flags are the correct way to do it if TASKDIALOGCONFIG hInstance member isn't null, but you can still use the string resources, and icon resources at the hMainIcon, hFooterIcon, pszMainIcon, pszFooterIcon, even if hInstance is null even though technically its not supposed to be null if your using resources and is supposed to be a handle to your resouce.dll file. I have it null below, but in actual use i would not leave it null if using recources because it might cause some unwanted operation if it is null and your using resources.)

Code: Select all

Enumeration ;_TASKDIALOG_FLAGS
  #TDF_ENABLE_HYPERLINKS               = $0001
  #TDF_USE_HICON_MAIN                  = $0002
  #TDF_USE_HICON_FOOTER                = $0004
  #TDF_ALLOW_DIALOG_CANCELLATION       = $0008
  #TDF_USE_COMMAND_LINKS               = $0010
  #TDF_USE_COMMAND_LINKS_NO_ICON       = $0020
  #TDF_EXPAND_FOOTER_AREA              = $0040
  #TDF_EXPANDED_BY_DEFAULT             = $0080
  #TDF_VERIFICATION_FLAG_CHECKED       = $0100
  #TDF_SHOW_PROGRESS_BAR               = $0200
  #TDF_SHOW_MARQUEE_PROGRESS_BAR       = $0400
  #TDF_CALLBACK_TIMER                  = $0800
  #TDF_POSITION_RELATIVE_TO_WINDOW     = $1000
  #TDF_RTL_LAYOUT                      = $2000
  #TDF_NO_DEFAULT_RADIO_BUTTON         = $4000
  #TDF_CAN_BE_MINIMIZED                = $8000
  ;#TDIF_SIZE_TO_CONTENT                = $10000000 ; This flag is deprecated. Use TDF_SIZE_TO_CONTENT instead.
  #TDF_SIZE_TO_CONTENT                 = $1000000
EndEnumeration
The flags are assigned to the TASKDIALOGCONFIG structure dwFlags member along with any of the other flags, for example;

Code: Select all

TDconfig.TASKDIALOGCONFIG
TDconfig\dwFlags = #TDF_USE_COMMAND_LINKS|#TDF_ALLOW_DIALOG_CANCELLATION|#TDF_POSITION_RELATIVE_TO_WINDOW|#TDF_USE_HICON_FOOTER|#TDF_USE_HICON_MAIN
Now, how do you localize your Task Dialog? Say for example if you wanted english, french and german wording for a distribution of your application that uses Task Dialogs, or what if you had some custom icons to include in your distribution resources.dll file (you can get strings and icons from .exe also) you wanted to use with your Task Dialogs? Well i'm using english and already available .dll on a Windows 7 system here but the principal is the same for your own custom rescources.dll file. The first thing we need is the source of our resources. I'm using "comdlg32.dll" and "imageres.dll" in this post for examples, in reality for your distribution you would include everything you wanted for strings and icons in your own resource.dll file and that would be your resource source. Our example icon resource is "imageres.dll" and the example string resource is "comdlg32.dll".

for the strings you just include them all in a common rescources.dll (you can call this .dll anything you want, i just use resources.dll here for an example) and just change the location of the string in the .dll in the "LoadString_(hInst_str, lang_dll_location, @lpBuffer, 256)" (see below) depending on user preference or primary language used on computer instead of bloating up your .exe with all the different strings for the task dialog and dealing with numerous pointers and stuff.

So...

Step 1: Set up the loading for our resource file

Code: Select all

str_resource.s = "comdlg32.dll"
img_resource.s = "imageres.dll"
res_module_img.i = LoadLibrary_(img_resource)
hInst_img.i = GetModuleHandle_(img_resource)
res_module_str.i = LoadLibrary_(str_resource)
hInst_str.i = GetModuleHandle_(str_resource)
hIconFooter.i = LoadIcon_(hInst_img,  MAKEINTRESOURCE(5100))
hIconMain.i = LoadIcon_(hInst_img,  MAKEINTRESOURCE(24))
lpBuffer.s = Space(256)
; make language decision.....
; if language = english etc.....
lang_dll_location.i = 1557 ; or what ever the location ordinal is in your resources.dll
hStrA.i = LoadString_(hInst_str, lang_dll_location, @lpBuffer, 256) ; string lang_dll_location in your chosen language for the decision
; etc....
LocalizedString$ = PeekS(@lpBuffer)
Step 2: Set up our flags and our structure (only show the needed parts here, its up to you to set up the whole thing correctly)

Code: Select all

TDconfig\dwFlags = #TDF_USE_HICON_FOOTER|#TDF_USE_HICON_MAIN ; plus .... other flags as needed for example #TDF_USE_COMMAND_LINKS|#TDF_ALLOW_DIALOG_CANCELLATION|#TDF_POSITION_RELATIVE_TO_WINDOW
TDconfig\hInstance = #Null ; hInst ; according to the MSDN this is supposed to be the hInstance to the resource .dll but can be null and still get the resources
TDconfig\hMainIcon = hIconMain
TDconfig\hFooterIcon = hIconFooter

....
TDconfig\pszWindowTitle = @WindowTitle ; can be the localized string or your own string with or without the localized
TDconfig\pszContent = @Content ; can be the localized string or your own string with or without the localized
TDconfig\pszFooter = @Footer ; can be the localized string or your own string with or without the localized

Same applies for other buttons or check boxes or strings like the pszVerification Text, pszExpandedInformation, pszExpandedControlText, pszCollapsedControlText. members of the TASKDIALOGCONFIG structure
Step 3: Plug in the other things needed for a Task Dialog

To show how it all fits together here is some quick mockup code for a basics "do some work" purpose (that can quickly be converted into a working task dialog by adding the needed struture and contant definitions) to show how it all goes together, what it does is up to you and the below is just an example. Note though that I commented out the actual ExitWindowsEx and replaced that area with some debugs to simulate the ExitWindowsEx function for testing purposes but I assure you that it does indeed work (not that this is intended to be a finished product, its for example purposes only) Please note in the below that I did not include a call back, thats up to you, I just presented simply here with the basics in case someone had never used Task Dialogs before.

Code: Select all

Procedure.b System_Reboot()
  Protected FuncRet.i, EWXREBOOT.i, img_resource.s, str_resource.s
  Protected pnButton.i, nButtonClick.i, res_module_img.i, res_module_str.i, hIconMain.i
  Protected ProcRet.b, SHTDN_REASON.q, hInst_img.i, hInst_str.i, hIconFooter.i, hStr.i
  nButtonClick = 0 : ProcRet = #False
  
  ; this section is the resource we use for icons for this example
  str_resource = "comdlg32.dll"
  img_resource = "imageres.dll"
  res_module_img = LoadLibrary_( img_resource)
  hInst_img = GetModuleHandle_(img_resource)
  res_module_str = LoadLibrary_(str_resource)
  hInst_str = GetModuleHandle_(str_resource)
  hIconFooter = LoadIcon_(hInst_img,  MAKEINTRESOURCE(5100))
  hIconMain = LoadIcon_(hInst_img,  MAKEINTRESOURCE(24))
  lpBuffer.s = Space(256)
  hStr = LoadString_(hInst_str, 1557, @lpBuffer, 256) ; string 1557
  ;Debug PeekS(@lpBuffer)
  
 ;**************************************************************
  Dim buttons.TASKDIALOG_BUTTON(4)
  sBtn0.s = "Reboot Computer" + #LF$ + "Log off And reboot computer"
  sBtn1.s = "Shutdown Computer" + #LF$ + "Log off and power down computer"
  sBtn2.s = "Log Off Computer" + #LF$ + "Log off without computer reboot or power down"
  sBtn3.s = "Cancel" + #LF$ + "Close this dialog and take no action"
  buttons(0)\nButtonID = #IDRESTART : buttons(0)\pszButtonText = @sBtn0
  buttons(1)\nButtonID = #IDSHUTDOWN : buttons(1)\pszButtonText = @sBtn1
  buttons(2)\nButtonID = #IDLOGOFF : buttons(2)\pszButtonText = @sBtn2
  buttons(3)\nButtonID = #IDCANCEL : buttons(3)\pszButtonText = @sBtn3
  
  WindowTitle.s = "Computer Reboot, Shutdown Or Log Out" 
  MainInstruction.s = "Select Desired Action Below via " + PeekS(@lpBuffer) ; lpBuffer is the string resource from the .dll for this example
  ContentA.s = "This application will reboot, power down, or log the user off, the computer." + #LF$
  ContentB.s = "Please save any work and close applications before continuing with 'Reboot', 'Shutdown', or 'Log Off'." + #LF$
  ContentC.s = "If you do not wish to take action at this time please select 'Cancel', close the dialog, press escape key (Esc), or use ALT-F4 key combo on the keyboard."
  Content.s = ContentA + #LF$ + ContentB + #LF$ + ContentC
  Footer.s = "This application will reboot, power down, or log the user off, the computer" + #LF$ + "depending on which action is selected above."
  
  TDconfig.TASKDIALOGCONFIG
  TDconfig\cbSize = SizeOf(TDconfig)
  TDconfig\hwndParent = #Null ; a window handle goes here or can be null - when a valid window handle apprears here the icon in the task dialog title bar adopts the icon of the window if there is one
  TDconfig\hInstance = #Null ; hInst ; according to the API this is supposed to be the hInstance to the resource .dll but can be null and still get the resources
  TDconfig\dwFlags = #TDF_USE_COMMAND_LINKS|#TDF_ALLOW_DIALOG_CANCELLATION|#TDF_POSITION_RELATIVE_TO_WINDOW|#TDF_USE_HICON_FOOTER|#TDF_USE_HICON_MAIN 
  TDconfig\pszWindowTitle = @WindowTitle
  TDconfig\hMainIcon = hIconMain
  ;TDconfig\pszMainIcon = hIconMain;#TD_SECURITY_SHIELD_ICON_GREEN
  TDconfig\pszMainInstruction = @MainInstruction
  TDconfig\pszContent = @Content
  TDconfig\cButtons = ArraySize(buttons())
  TDconfig\pButtons = @buttons(0)\nButtonID
  TDconfig\nDefaultButton = #IDCANCEL
  TDconfig\hFooterIcon = hIconFooter
  ;TDconfig\pszFooterIcon = hIconFooter; #TD_INFORMATION_ICON
  TDconfig\pszFooter = @Footer
  TDconfig\cxWidth = 0
    
  If TaskDialogIndirect(@TDconfig, @nButtonClick, #Null, #Null) = #S_OK
    Select nButtonClick
      Case #IDRESTART, #IDSHUTDOWN
        If nButtonClick = #IDRESTART
          EWXREBOOT = #EWX_REBOOT|#EWX_FORCEIFHUNG : ProcRet = #True
        EndIf
        If nButtonClick = #IDSHUTDOWN
          EWXREBOOT = #EWX_POWEROFF|#EWX_FORCEIFHUNG : ProcRet = #True
        EndIf
        If ProcRet = #True And Enable_Privilege(#SE_SHUTDOWN_NAME$) = #True
          If IsPrivilegeEnabled(#SE_SHUTDOWN_NAME$) = #True
            ProcRet = #True
          Else
            ProcRet = #False
            MessageBeep_(#MB_ICONERROR)
          EndIf
        EndIf
      Case #IDCANCEL, #IDLOGOFF ; = 18
        ProcRet = #True
        If nButtonClick = #IDCANCEL : MessageBeep_(#MB_OK) : EndIf
      Default ; should never happen but just in case and for form
        ProcRet = #False : MessageBeep_(#MB_ICONERROR)
    EndSelect
      
    If ProcRet = #True And nButtonClick <> #IDCANCEL
      If nButtonClick = #IDRESTART Or nButtonClick = #IDSHUTDOWN
        SHTDN_REASON = #SHTDN_REASON_REBOOT_SHUTDOWN
      Else
        EWXREBOOT = #EWX_LOGOFF : SHTDN_REASON = #EWX_LOGOFF
      EndIf
      MessageBeep(#MB_OK)
      Debug "nButtonClick > " + Str(nButtonClick)
      Debug "IsPrivilegeEnabled > " + Str(IsPrivilegeEnabled(#SE_SHUTDOWN_NAME$))
      Debug "EWXREBOOT > " + Str(EWXREBOOT)
      Debug "SHTDN_REASON > " + Str(SHTDN_REASON)
      Debug "ExitWindowsEx"
      FuncRet = 1
      ;FuncRet = ExitWindowsEx_(EWXREBOOT, SHTDN_REASON)
      If FuncRet <> #False
        ProcRet = #True
      Else
        ProcRet = #False
        MessageBeep_(#MB_ICONERROR)
      EndIf
    EndIf
  Else
    ProcRet = #False
    MessageBeep(#MB_ICONERROR)
  EndIf
  
  FreeLibrary_(res_module_img)
  FreeLibrary_(res_module_str)
  ProcedureReturn ProcRet
  
EndProcedure

In the next post after this one i'll post the structures and needed constant definitions so you will have them. TaskDialogIndirect needs to be called from the Comctl32.dll. No manifest file needed in case your wondering as these are part of PB now. Only tested on Windows 7 x 86 but should be fine in Windows Vista and above only x86 or 64 bit - TaskDialogIndirect is unicode only so if your going to go ansi your strings will need ansi-to-unicode conversion, but i'd recommnd you do this in unicode only. Anyway, thats how you localize TaskDialog and use other icons from a resource file.
Last edited by SFSxOI on Tue Apr 10, 2012 12:16 pm, edited 26 times in total.
The advantage of a 64 bit operating system over a 32 bit operating system comes down to only being twice the headache.
SFSxOI
Addict
Addict
Posts: 2970
Joined: Sat Dec 31, 2005 5:24 pm
Location: Where ya would never look.....

Re: Localizing Task Dialog's

Post by SFSxOI »

structures and constant definitions for the above code, a few things are defined by me for the example, these defined by me are > #IDRESTART = 16, #IDSHUTDOWN = 17, #IDLOGOFF = 18, #SHTDN_REASON_REBOOT_SHUTDOWN. MAKEINTRESOURCE is forum code, the Enable_Privilege and IsPrivilegeEnabled procedures are mine but there are similar in the forum if you perfer one of the others, the rest is fom API and .h files from the MS SDK for Windows 7 converted to PureBasic. I use API for this so i load them from the .dll's, I added the underscore to the API function name for those that prefer doing it that way, i can't for my work projects because the PB methods can't be certified doing it that way with the "APIFunction_()" method so i have to load them from .dll and call them normally but i put them in for you.

Code: Select all

Macro MAKEINTRESOURCE(INT)
(INT & $FFFF)
EndMacro

Enumeration ; TASKDIALOG_COMMON_BUTTON_FLAGS
  #TDCBF_OK_BUTTON            = 1 ;selected control Return value IDOK
  #TDCBF_YES_BUTTON           = 2 ;selected control Return value IDYES
  #TDCBF_NO_BUTTON            = 4 ;selected control Return value IDNO
  #TDCBF_CANCEL_BUTTON        = 8 ;selected control Return value IDCANCEL
  #TDCBF_RETRY_BUTTON         = 16 ;selected control Return value IDRETRY
  #TDCBF_CLOSE_BUTTON         = 32 ;selected control Return value IDCLOSE
EndEnumeration

Enumeration ;_TASKDIALOG_FLAGS
  #TDF_ENABLE_HYPERLINKS               = $0001
  #TDF_USE_HICON_MAIN                  = $0002
  #TDF_USE_HICON_FOOTER                = $0004
  #TDF_ALLOW_DIALOG_CANCELLATION       = $0008
  #TDF_USE_COMMAND_LINKS               = $0010
  #TDF_USE_COMMAND_LINKS_NO_ICON       = $0020
  #TDF_EXPAND_FOOTER_AREA              = $0040
  #TDF_EXPANDED_BY_DEFAULT             = $0080
  #TDF_VERIFICATION_FLAG_CHECKED       = $0100
  #TDF_SHOW_PROGRESS_BAR               = $0200
  #TDF_SHOW_MARQUEE_PROGRESS_BAR       = $0400
  #TDF_CALLBACK_TIMER                  = $0800
  #TDF_POSITION_RELATIVE_TO_WINDOW     = $1000
  #TDF_RTL_LAYOUT                      = $2000
  #TDF_NO_DEFAULT_RADIO_BUTTON         = $4000
  #TDF_CAN_BE_MINIMIZED                = $8000
  ;#TDIF_SIZE_TO_CONTENT                = $10000000 ; This flag is deprecated. Use TDF_SIZE_TO_CONTENT instead.
  #TDF_SIZE_TO_CONTENT                 = $1000000
EndEnumeration

Enumeration ;_TASKDIALOG_ELEMENTS
  #TDE_CONTENT
  #TDE_EXPANDED_INFORMATION
  #TDE_FOOTER
  #TDE_MAIN_INSTRUCTION
EndEnumeration

Enumeration ;_TASKDIALOG_ICON_ELEMENTS
  #TDIE_ICON_MAIN
  #TDIE_ICON_FOOTER
EndEnumeration

#IDOK = 1
#IDCANCEL = 2
#IDABORT = 3
#IDRETRY = 4
#IDIGNORE = 5
#IDYES = 6
#IDNO = 7
#IDCLOSE = 8
; user defined
#IDRESTART = 16
#IDSHUTDOWN = 17
#IDLOGOFF = 18

; the standard icons are on the MSDN if your not doing customization

#EWX_LOGOFF = 0
#EWX_SHUTDOWN = $00000001
#EWX_REBOOT = $00000002

#EWX_FORCEIFHUNG = $00000010 ; sends the WM_QUERYENDSESSION message - dont use #EWX_FORCE except as last resort because it can cause data loss
; EWX_FORCE inhibits sending the WM_QUERYENDSESSION message, #EWX_FORCEIFHUNG doesn't inhibit

#SE_SHUTDOWN_NAME$ = "SeShutdownPrivilege" ; privilege needed for reboot and shutdown, not needed for log off

#SHTDN_REASON_MAJOR_OPERATINGSYSTEM  = $00020000
#SHTDN_REASON_MINOR_RECONFIG  = $00000004
#SHTDN_REASON_FLAG_PLANNED  = $80000000
; user defined to shorten the line
#SHTDN_REASON_REBOOT_SHUTDOWN = #SHTDN_REASON_MAJOR_OPERATINGSYSTEM|#SHTDN_REASON_MINOR_RECONFIG|#SHTDN_REASON_FLAG_PLANNED

The TaskDialogIndirect function needs to be loaded from the .dll, i'll leave that up to you to use whats better for you.

The Enable_Privilege and IsPrivilegeEnabled procedures in case anyone wants them.

Code: Select all

Procedure.b Enable_Privilege(priv.s)
  Protected TokenPriv.TOKEN_PRIVILEGES, token.i
  Protected hToken.i, ProcRet.b, FuncRet.i
  ProcRet = #False
  
  FuncRet = OpenProcessToken_(GetCurrentProcess(), #TOKEN_ADJUST_PRIVILEGES, @hToken)
  If FuncRet <> #False
    TokenPriv\PrivilegeCount = 1
    FuncRet = LookupPrivilegeValue_(#Null, @priv, @TokenPriv\Privileges[0]\Luid)
    TokenPriv\Privileges[0]\Attributes = #SE_PRIVILEGE_ENABLED
    If FuncRet <> #False
      FuncRet = AdjustTokenPrivileges_(hToken, #False, @TokenPriv, SizeOf(TokenPriv), #Null, #Null)
      token = CloseHandle_(hToken)
      If FuncRet <> #False
        ProcRet = #True
      Else
        ProcRet = #False
      EndIf
    Else
      ProcRet = #False
    EndIf
  Else
    ProcRet = #False
  EndIf
  
  If token <> 0
    CloseHandle_(hToken)
  EndIf
  
  ProcedureReturn ProcRet 
EndProcedure

; both thread or process
Procedure.b IsPrivilegeEnabled(Privilege.s)
  Protected hToken.i, PrvEnb.b, TokenPriv.PRIVILEGE_SET
  Protected FuncRet.i, PChkRes.i
  hToken = 0
  PChkRes = #False
  
  FuncRet = OpenThreadToken_(GetCurrentThread(), #TOKEN_QUERY, #False, @Token)
  If FuncRet = 0 And GetLastError_() = #ERROR_NO_TOKEN
    FuncRet = OpenProcessToken_(GetCurrentProcess_(), #TOKEN_QUERY, @hToken)
    If FuncRet <> 0
      TokenPriv\PrivilegeCount = 1
      TokenPriv\Control = 0
      FuncRet = LookupPrivilegeValue_(#Null, @Privilege, @TokenPriv\Privilege[0]\Luid)
      If FuncRet <> 0
        PChkRes = PrivilegeCheck_(hToken, @TokenPriv, @PrvEnb) And PrvEnb
      Else
        PrvEnb = #False
      EndIf
    Else
      PrvEnb = #False
    EndIf
  EndIf
  
  CloseHandle_(hToken)
  ProcedureReturn PrvEnb
  
EndProcedure
The advantage of a 64 bit operating system over a 32 bit operating system comes down to only being twice the headache.
Post Reply