Localizing Task Dialog's from a resource.dll
Posted: Mon Apr 09, 2012 7:32 pm
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.)
The flags are assigned to the TASKDIALOGCONFIG structure dwFlags member along with any of the other flags, for example;
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
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)
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.
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.
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
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
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)
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
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