Tuesday, October 7, 2014

Creating your own personalised HBCDMenu - Part 2

In Part 1 of this tutorial, we created our menu's GUI, now in the second part we will add all of our functionality to the menu. Mainly, populate of "Programs" drop-down menu.
We finished last time with the Main GUI of our program, the main part needed being our "Programs" drop-down menu. This will contain all of the programs we have added to our csv file (or the one supplied by Hirens Boot CD.).

This is where it get a little complicated so if you have no interest in learning or don't want to do any work simply go to the end of the article to download the completed product (including the AutoIt script for modification.)

The main features of this script are the two functions we will use to get our menu items. These being:
  1. MenuGet() - This function will get our 'Top-Level' menu items. (The top row of our csv file.)
  2. ParseMenu() - This function will get the rest of our menu items. (Everything under the top row.)
But before we start looking at our functions, lets start by looking at our global variables. These will be defined at the beginning of our script and used throughout our various functions.
#include <File.au3>
Global $aMenuItems[1][2] = [[0]]
Global $aClickItems[1] = [0]
Global $FilePath = ".\Programs\HBCDMenu.csv"
 
;Check if our menu file actually exists
If Not FileExists($FilePath) Then
    MsgBox(0, "Error!", "Unable to locate Menu File!")
    Exit
EndIf
 
;This variable is used to store our file content
Global $content
_FileReadToArray($FilePath, $content)
To explain this, we have 4 variables defined here:
  1. $aMenuItems - This is an array that will store our menu item switches (not the names, just the programmatic switches.)
  2. $aClickItems - This is an array that will store our click item names (this time it is the names, as we need it in our Run() function later.)
  3. $FilePath -This is the path to our menu file beginning with '.\' meaning its relative from our script location.
  4. $content - This is the variable that will hold the contents of our 'Menu' file for processing later.
While we are looking at this section of code, I will verify the last couple of snippets:
  1. #include <File.au3> - We need to include this for our _FileReadToArray function needed later.
  2. If Not FileExists($FilePath) Then - This line and the following three (3) lines are simply checking if our 'Menu' file is present, if it isn't it will alert us.
  3. _FileReadToArray($FilePath, $content) - This line of code will get the content of our 'Menu' file and load them into our content variable.
Next lets look at our MenuGet() function:
Func MenuGet()
 $menuTitles = StringSplit($content[1], ",") ; These are our Top-Level menus
 
 ; Create an array to hold the Menu Names
 Global $MenuTitle[$menuTitles[0] + 1] = [$menuTitles[0]]
 
 ; Create the Top-Level menus
 For $i = 1 To $menuTitles[0]
  ;Don't forget to make our Main Menu the parent
  $MenuTitle[$i] = GUICtrlCreateMenu($menuTitles[$i], $MenuItem1)
 Next
EndFunc   ;==>MenuGet
Now let me explain the various parts in detail:
$menuTitles = StringSplit($content[1], ",") ; These are our Top-Level menus
This is a temporary array that will store our menu item names. We get this array by splitting the first line ($content[1]) by the commas (Because its a comma separated value or csv file.)
; Create an array to hold the Menu Names
Global $MenuTitle[$menuTitles[0] + 1] = [$menuTitles[0]]
This is an array that will store the top-level menu items, these are all going to be dropdown menus. We create this to be the size of our $menuTitles array and adding the array count to $MenuTitle[0].
; Create the Top-Level menus
For $i = 1 To $menuTitles[0]
 ;Don't forget to make our Main Menu the parent
 $MenuTitle[$i] = GUICtrlCreateMenu($menuTitles[$i], $MenuItem1)
Next 
Now we need to loop through each of our array items (menu titles) and create their very own GUI item. We can't forget to make our 'Main Programs Menu' (Labeled 'Programs' in this Tutorial) our parent item.

Next, lets look at the ParseMenu() function, this is where all the magic happens. The function that will parse all of the options from our HBCDMenu.csv file.
Here it is:
Func ParseMenu()
 ; Get rid of the first line as we already have our Top-Level Menus
 _ArrayDelete($content, 1)
 ;Load the first line into an array (for our Count Later)
 $columnsCounter = StringSplit($content[1], ",")
 ;Here we use the row count (from $content) and column count (from $columnsCounter)
 ;We define an array that is the perfect size for our Menu Items
 Dim $MenuItems[$content[0] + 1][$columnsCounter[0] + 1]
 
 ; Now we loop through each row and column
 For $x = 1 To ($content[0]) - 1
  ;Create an array with the row we are looking at
  $oneRow = StringSplit($content[$x], ",")
  ;now loop through each column
  For $y = 1 To ($columnsCounter[0])
   ;Grab the item we want and add it to our menu items array
   $MenuItems[$x][$y] = $oneRow[$y]
  Next
 Next
 
 ;Count our rows
 $rowCount = UBound($MenuItems, 2)
 ;Count our columns
 $columnCount = UBound($MenuItems, 1)
 
 ;Set our initial value and create a 'While' loop, for our rows
 $i = 1
 While $i < $rowCount
  ;create a second initial value and another 'While' loop, for our columns
  $j = 1
  While $j < $columnCount
   ;We need to make sure we aren't working on an empty string
   If Not $MenuItems[$j][$i] = "" Then
    ;Create our menu items
    ;Increase the count of menuitems
    $aMenuItems[0][0] += 1
    ; Increase the array size by adding a new element
    ReDim $aMenuItems[$aMenuItems[0][0] + 1][2]
    ; Add the ControlID and text to the newly created array element
    $aMenuItems[$aMenuItems[0][0]][0] = GUICtrlCreateMenuItem($MenuItems[$j][$i], $MenuTitle[$i])
    $aMenuItems[$aMenuItems[0][0]][1] = $MenuItems[$j][$i]
    ;Increase the count on our global click items array
    $aClickItems[0] += 1
    ;Increase the array size
    ReDim $aClickItems[$aClickItems[0] + 1]
    ;Add our 'Click' command (notice the +1, this is so we get the batch file path)
    $aClickItems[$aClickItems[0]] = $MenuItems[$j + 1][$i]
   EndIf
   ;We increment by 2 so we skip over our 'Click' commands
   $j += 2
  WEnd
  $i += 1
 WEnd
 
EndFunc   ;==>ParseMenu
Now, lets look at what is going on here. Its pretty well commented in the script, so I will explain only the main parts.
The first part:
 ; Now we loop through each row and column
 For $x = 1 To ($content[0]) - 1
  ;Create an array with the row we are looking at
  $oneRow = StringSplit($content[$x], ",")
  ;now loop through each column
  For $y = 1 To ($columnsCounter[0])
   ;Grab the item we want and add it to our menu items array
   $MenuItems[$x][$y] = $oneRow[$y]
  Next
 Next
Gets all of the values from our content array (defined at the beginning of the script) and throws them into an array for us to work with, practically splitting each item into its own cozy little box in an array. This will make it much easier to work on in our next section:
 ;Set our initial value and create a 'While' loop, for our rows
 $i = 1
 While $i < $rowCount
  ;create a second initial value and another 'While' loop, for our columns
  $j = 1
  While $j < $columnCount
   ;We need to make sure we aren't working on an empty string
   If Not $MenuItems[$j][$i] = "" Then
    ;Create our menu items
    ;Increase the count of menuitems
    $aMenuItems[0][0] += 1
    ; Increase the array size by adding a new element
    ReDim $aMenuItems[$aMenuItems[0][0] + 1][2]
    ; Add the ControlID and text to the newly created array element
    $aMenuItems[$aMenuItems[0][0]][0] = GUICtrlCreateMenuItem($MenuItems[$j][$i], $MenuTitle[$i])
    $aMenuItems[$aMenuItems[0][0]][1] = $MenuItems[$j][$i]
    ;Increase the count on our global click items array
    $aClickItems[0] += 1
    ;Increase the array size
    ReDim $aClickItems[$aClickItems[0] + 1]
    ;Add our 'Click' command (notice the +1, this is so we get the batch file path)
    $aClickItems[$aClickItems[0]] = $MenuItems[$j + 1][$i]
   EndIf
   ;We increment by 2 so we skip over our 'Click' commands
   $j += 2
  WEnd
  $i += 1
 WEnd
With this section we use all of the items we have already defined an finally create our "Programs" menu (and it all works too).

Now that is the entire program all looked at. Please keep in mind a few things:

  1. This is the very first draft of the program and may have a couple of bugs
  2. It does not split the programs by the backslash "\" like the official HBCDMenu does. So only 2 level "Programs" menu
  3. This does use a scripting language (not a programming language like .NET or Java) so it might be limited in functionality
  4. I have not tested this in any preinstalled environments (MiniXP or Mini7)
  5. Fell free to take this code and improve on it in any way you want. Let me know of any improvements and I may include them into the next version.
Link to full zip: Download Here

Monday, April 28, 2014

Creating your own personalised HBCDMenu - Part 1

This article will guide you through the process you can take to program your very own HBCDMenu program. The HBCDMenu is the menu you run from within a Windows environment i.e. to perform diagnostics from within the Windows OS.
We are going to use the AutoIt scripting language, its free, easy and requires no additional runtimes to run the programs.
So first, head over to the AutoIt website and download the AutoIt package as well as the ScITE script editor.
Once we have these installed we can start writing our script.
Note: You will need to have a bit of programming knowledge or at least be willing to learn, otherwise this will be a long and boring article. You can simply skip to the end of the article to download a zip of all the associated files for downloading and modifying.

Create our Script file

To start off we need to create the file we will use to write our script. This is extremely easy, and can be done a couple of ways. Either right click in your desired location and go to New > AutoIt v3 Script.

Or create a .txt file and rename the file extension to .au3 (more advanced users).
After that the file should automatically open in ScITE script editor.

Designing the Main Menu screen

Ok, so first we will design our 'Main Menu' screen. This is the GUI we will be seeing when the script is booted. To make this process a little more streamlined (also for beginners) we can use the Koda (FormDesigner) tool included in the full version of ScITE (we download and installed earlier). Note: if you are using the Lite version of ScITE (installed with the AutoIt library) this will not work.

So, we locate Koda in the Tools menu of ScITE (shown Below).



I'm not going to get into the details of creating a form using Koda. You can go nuts and create it however you would like it to look. For this tutorial, I have simply gone for the classic HBCD menu look (with a couple of small changes).
The main element you will need for this tutorial to work is a Main Menu object with an item named 'Programs' (or whatever you name it.)

Once you have had fun designing your marvelous Main Menu form, select Tools > Generate Form Code... This will give you your forms AutoIt code, so copy it and paste it into your AutoIt script. This is the beginning of our new HBCDMenu.
So once you have finished you will have something like this...
#include <GUIConstantsEx.au3>
#include <GUIMenu.au3>
 
MainMenu()
 
Func MainMenu()
 ;Create and show our main GUI
 Global $Form1
 $Form1 = GUICreate("anarcist's USB Menu", 300, 200, 195, 123)
 ;This is the menu that will hold all of our User-Defined menus
 Global $MenuItem1 = GUICtrlCreateMenu("Programs")
 
 ;Create our About Menu
 $AboutMenu = GUICtrlCreateMenu("About")
 $Blog = GUICtrlCreateMenuItem("anarcist's Blog", $AboutMenu)
 $AboutItem = GUICtrlCreateMenuItem("About", $AboutMenu)
 ;Now create our 'Browse' button
 $Folder = GUICtrlCreateButton("Browse USB...", 80, 65, 140, 50)
 GUISetState(@SW_SHOW)
 
 While 1
  $nMsg = GUIGetMsg()
  Switch $nMsg
   Case $GUI_EVENT_CLOSE
    Exit
 
   Case $Folder
    Run("C:\WINDOWS\explorer.exe /n /e, .\Programs")
 
   Case $Blog
    ShellExecute("http://anarcist69.blogspot.com/")
 
   Case $AboutItem ;This come later...
    ;GUISetState(@SW_HIDE)
    ;AboutMenu()
 
  EndSwitch
 WEnd
EndFunc   ;==>MainMenu 
To explain this code a bit better there a a few things to explain:
  1. I have wrapped the menu form in a function name MainMenu(), this allows us to easy hide it and call it again later if we need to. This also means that we don't need to have this as our boot screen (Notice the call to the MainMenu function at the beginning of the script.)
  2. I have renamed a few items (i.e. $AboutMenu and $Blog), this makes them a bit easier to ID later in the script.
  3. There is a 'While' loop at the end of the function, this handles all of our 'Click' events. We will modify this later for our 'Programs' menu once we populate it with our .csv file.
  4. At the very top of our script we have a couple of include files, this contain our functions for creating the GUI and knowing what to do with it. We will be adding more later.
This concludes the first part of the tutorial, stay tuned for part 2 in the next week or so. If you have any questions, ask in the comments, I am always happy to help.