Fix: Battery Icon and/or Volume Control Missing From Windows 10 Taskbar

tree
I came across a rather weird problem today where my taskbar was missing the icons for the volume control and battery meter:
Capture
My first attempt to fix this was to check the related system settings:

  1. Open the run dialog (Windows-key + R) and type ‘ms-settings:notifications’
  2. Click on ‘Turn system icons on or off’
  3. In my case the sliders for both Volume and Power were disabled at the off position

If you find yourself in the same situation the following might help you, too:
As an alternative you can click here to jump to the bottom of the post for a PowerShell solution that will modify the respective registry keys instead of applying the changes via group policy settings

  1. Open the run dialog (Windows-key + R) and type ‘gpedit.msc’ Note: The Local Group Policy Editor is not available in all windows versions here is a link to a post that claims to bring it to all versions of Windows 7, which you can try out at your own risk on Windows 10.
  2. Navigate to: User Configuration -> Administrative Templates -> Start Menu and Taskbar
  3. Configure the policies for ‘Remove the battery meter’ and ‘Remove the volume control icon’ to be Disabled
    1. Double-click each entry
    2. Select the radio-button next to Disabled
    3. Click Apply and OK


For the changes to take affect you can either restart your PC or just restart windows explorer. One way to achieve the latter is through PowerShell (at least to have one PowerShell command in this post 🙂 ). Just use the command

kill -n explorer

This will end the explorer.exe task and restart automatically. As an alternative if you don’t know your way around in PowerShell you could type the following into the run dialog:

powershell -exe bypass -nop -command "&{kill -n explorer}"

PowerShell only solution
The following commands need to be executed from an elevated PowerShell window (How to open an elevated PowerShell prompt in Windows 10)

Set-ItemProperty -Path HKCU:\Software\Microsoft\Windows\CurrentVersion\Policies\Explorer -Name HideSCAVolume -Value 0
Set-ItemProperty -Path HKCU:\Software\Microsoft\Windows\CurrentVersion\Policies\Explorer -Name HideSCAPower -Value 0
kill -n explorer

shareThoughts


Photo Credit: Indigo Skies Photography via Compfight cc

Advertisements

Using PowerShell to clean-up excel data with multiple entries per cell

8231960108_b07671cb72_m
How many times did you come across a situation where you were supposed to work with Data that looks like this?:
CleanUpData
Some of the cells have multiple entries that are either separated by comma or line-breaks (via Alt+Enter). Furthermore several of those entries contain extraneous spaces. Happy days! What would be actually needed in order to work with the data is one clean entry per cell. In order to do that the columns for those rows that contain multiple entries per cell also need to be cross-joined (or Cartesian product) so that all possible combinations for the entries are accounted for. The end result should look like this:
CleanUpData2
How could we do the same using PowerShell? Let’s first have a look on how to do the cross-join part. This can be done quite easily with nested loops. Taking the second row from the example, the following will lead to the desired result:

$name= @('Nigel')
$products = 'Product 1', 'Product 2'
$preferences = 'Fast Delivery', 'Product Quality'
foreach($n in $name){
    foreach($product in $products){
        foreach($preference in $preferences){
            "$n, $product, $preference"
        }
    }
}

One way to turn this into a more generic solution is using recursion (You need to understand recursion in order to understand recursion 😉 ).
Here is an implementation of the same:

Ok, having covered the difficult part we now only need to read the data from excel clean it up and apply the Cartesian product function to it. Here is the full code to automate the whole process:

The above contains a modified version of the CartesianProduct function in order handle objects (actually ordered hashtables since they preserve the column order). If time permits I would like to implement the same as an Excel macro and share it as part of another post.
Update: I’ve added another post outlining how to do the same (using another approach) via an Excel Macro

shareThoughts


Photo Credit: pni via Compfight cc

Add a default code template to the PowerShell ISE

tree
Some of the 3rd party PowerShell editors offer already built-in support for a default code template where the content of the code template replaces the default blank sheet for every new tab as a starting point for new scripts.
While the PowerShell ISE does not provide this functionality out-of-the-box, it can be quite easily added through the $psISE object model by registering a custom action for the $psise.CurrentPowerShellTab.Files ‘CollectionChanged’ event. This event is triggered whenever a tab is closed or opened:

After pasting and running the above code inside the ISE we first need to create the template. The code template is required to be located at “MyDocuments\WindowsPowerShell\ISETemplate.ps1” it can be created and/or edited using the Edit-ISETemplate function. Once the ISETemplate.ps1 contains some text. Every new tab should now be pre-filled with the content of the code template file.
In order to make this persistent the code should be added to your profile. You can find the path(s) to your profile by running ‘$profile | select *’. I personally favor the ‘CurrentUserAllHosts’ profile since I don’t want to maintain multiple profile files. Host specific code can be added by using conditions like:

if ($host.Name -eq 'Windows PowerShell ISE Host'){
#ISE specific code here
}
elseif ($host.Name -eq 'ConsoleHost'){
#console specific code here
}

I’ve also added this functionality to my ISE Add-On over on GitHub.

shareThoughts


Photo Credit: flavijus via Compfight cc

PowerShell tricks – replace and transform a value within a string

tree

Most PowerShell users know already of the String.Replace method and the PowerShell -replace operator. While the latter is quite powerful with its support for regular expressions … :

#using string's replace method
'this is good'.Replace('good','quite good')
#using PowerShell's replace operator to remove duplicate words
'this this is a a test' -replace '\b(\w+)(\s+\1){1,}\b', '$1'

… there is an even more powerful option available for cases where the characters that we want to replace need to be “transformed” in some way. Let’s look two examples:

  1. Increase a number within a string by one e.g. ‘XX33.txt’ should turn into ‘XX34.txt’
  2. Capitalize the second word within a sentence string e.g. ‘this is a test’ should turn into ‘this IS a test’

For the two examples there are of course multiple ways to accomplish this using PowerShell without involving regex replace, but there might be situations where it is your only option. For those case the .net Regex.Replace method could become your new best friend. The method provides an overload that takes the following three arguments:

Parameter Description
Input string Text that contains the character(s) to be replaced
Pattern string The regex pattern that identifies the character(s) to be replaced
MatchEvaluator A scriptBlock that is being evaluated against the captured matched characters

The 3rd argument (MatchEvaluator) is the key to the solution as the scriptBlock automatically receives the captured characters as an argument it can be used to transform the match(es) using any method available. Let’s use the same to solve the two example problems:

The automatic $args[0] variable can be used to access the match(es) within the scriptBlock. Note that for the second example the ToUpper method is called on the Value property rather than the $args[0] variable directly. Furthermore the $counter variable needs to be declared in script scope (see also PowerShell scope write-up) in order to be visible within the scriptBlock for PowerShell version 3 or later.

shareThoughts


Photo Credit: Indy Kethdy via Compfight cc

PowerShell Error – “Select-Object : Cannot convert System.Object[] to one of the following types…”

tree

I came across a bug (at least I would assume this a bug) in PowerShell while using the Select-Object (alias select) cmdlet in conjunction with a mix of literal and variable arguments for the Property parameter (attempting to use Get-ChangeLog with a multiple column identifier). Select-Object’s Property parameter accepts either an array of objects, or a hashtable of name/expression pairs as arguments. Let’s look at an example:

Get-Help select -Parameter Property
#usage with literal argument
Get-Process | select Name, ID, Handle, VM, PM
#usage with a variable argument
$props = "Name", "ID", "Handle"
Get-Process | select $props
#mixed usage of literal and a variable argument
Get-Process | select $props, VM, PM

The last command results into the following error message:
select_Error
Even if we change the literal values to strings and put the arguments in parentheses Select-Object still refuses to accept it:

$props = "Name", "ID", "Handle"
#check the type of the parameter value
($props, "VM", "PM").GetType().FullName
#should work, but still doesn't
Get-Process | select ($props, "VM", "PM")

I found two possible workarounds.

  1. Concatenation of the values
  2. Using Write-Output (alias echo) to produce an array of strings (I like that one more)

See also PowerShell tricks- Build an array of strings without quotation marks

#concatenate
$props = echo Name ID Handle
Get-Process | select ($props + "VM" + "PM")
#using echo
Get-Process | select (echo $props VM PM)

shareThoughts


Photo Credit: S.Raj via Compfight cc

Search file content by keyword using Everything + PowerShell + GUI

tree
Even with Windows 10 MS still didn’t manage to include a proper in-built file search functionality. If it is about searching for files I definitely prefer the excellent Everything search engine (see also my post on a PowerShell wrapper around Everything commandline) .But quite frequently I also need to search for keywords/pattern within files. PowerShell’s Get-ChildItem and Select-String can certainly do this together:

#search through all .ps(m)1 files for instances of the word 'mySearchString'
$path = 'c:\scripts\powershell'
Get-ChildItem $path -Include ("*.ps1","*.psm1")) -Recurse |
     Select-String 'mySearchString' | select Path, Line, LineNumber

While this does the job it doesn’t follow my preferred workflow and is also not very quick when running it against a large set of files. I would prefer to have the ability to search and drill down a list of files within a Graphical User Interface just like Everything and then search through the filtered list of files using keyword(s)/pattern(s) and get back the search results within a reasonable time-frame.
Say hello to “File Searcher” (I didn’t spend any time thinking about a catchy name):
FileSearcher
The three text boxes at the top of the UI can be used to:

  1. Search for files using Everything command-line (es.exe)
  2. Search within the list of files for content by keyword (using a replacement for Select-String more on that below)
  3. Filter the results by keywords (across all columns). This can be done against the list of files and against the list of results (Path, Line, LineNumber)

Let’s first look at two use cases.
1. Assuming we want to search for some PowerShell files starting with “Posh-” across the whole hard drive:

  • After importing the module (Import-Module $path\FileSearcher.psm1) files can be searched using the textbox at the top of the window
  • Using ‘posh-*.ps1’ and hitting Enter as the search term will get us what we want
  • On my machine this results into a quite long list. I can scroll through the list to see whether I really want to search through all those files or further drill it down either by refining the initial search or using the ‘filter results’ textbox.
  • For the example’s sake let’s assume I’d like to filter the result list to show only those entries that contain the word ‘string’ (within the full path)
  • Now I would like to search those files for instances of the word ‘select’. Entering the keyword into the 3rd text-box filters the results as I type.
  • The result is a list of ‘Path, Line, LineNumber’ results that can be further filtered by using the ‘filter results’ text-box again
  • Double-clicking one of the entries will open the file in notepad++ (of course only if this is installed) putting the cursor on the respective line. (This works only of notepad++ is not already open)

FilesearchExample1
2. A second use case are situations where I want to “pre-populate” the list of files via command-line instead of using the GUI. Here is how to do that:

  • Pipe a list of files into the FileSearcher function:
    Import-Module $path\FileSearcher.psm1
    $path = 'c:\scripts\powershell'
    Get-ChildItem $path -Include ("*.ps1","*.psm1")) -Recurse | FileSearcher
    
  • Use the UI to further refine and/or search the list of files for contents by keyword

The content search functionality is realized through a custom cmdlet (Search-FileContent) implemented in F# based on the solution (I have only changed the original solution to accept an array of strings for the full paths) provided in this blog post. This speeds up the performance significantly as compared to Select-String through the usage of parallel asynchronous tasks.
The UI also support some options:

  • For file search (through Everything) the “no Recurse” option is applied if the first search term is a path (using the parents:DEPTH option which requires an up-to-date Everything version) e.g. the search term ‘c:\scripts .ps1’ with the option enabled would only search for .ps1 files within the c:\scripts directory.
  • The content search offers options similar to the Select-String switches to treat the keyword not as a regular expression (SimpleMatch) or/and do a case sensitive search.

Dependencies:

  1. Everything command line version (requires the UI version to be installed,too) installed to ‘C:\Program Files*\es\es.exe’
  2. The Search-FileContent cmdlet is implemented via the SearchFileContent.dll which can be downloaded from my GitHub repository and needs to reside in the same folder as the FileSearcher.psm1 file.
  3. Because the Search-FileContent cmdlet is written in F# it requires the FSharp.Core assembly to be present which can be downloaded and installed via the following PowerShell code:
    $webclient = New-Object Net.WebClient
    $url = 'http://download.microsoft.com/download/E/A/3/EA38D9B8-E00F-433F-AAB5-9CDA28BA5E7D/FSharp_Bundle.exe'
    $webclient.DownloadFile($url, "$pwd\FSharp_Bundle.exe")
    .\FSharp_Bundle.exe /install /quiet
    
  4. The ability to open files from the file search content results via double-click with the cursor on the respective line requires Notepad++

The FileSearcher module itself can be also downloaded from my GitHub repository.
Please use the comment function if you have any feedback or suggestions on how to improve the tool.

shareThoughts


Photo Credit: Robb North via Compfight cc

Finding the index of an object within an array by property value using PowerShell

1500333523_685c69638c_m
Today I was looking for an option to find the index of an object within an array of objects. Using an array of one dimensional objects like integers this is as simple as using the static IndexOf method of the Array class in the System namespace:

$array=10..1
$index = $array.IndexOf(3)
$array[$index]

But in my case I wanted to get the index of an item within an array of multidimensional or rich objects. As an example let’s say after running Get-Process we would like to find the index of the process whose Name property equals “powershell” (the return type here is an array of System.Diagnostics.Process objects).
My first approach was to use the Select-String cmdlet since I knew it returns a LineNumber property. After some trial and error I came up with the following:

$processes = Get-Process
$index = (($processes | Out-String).Split("`n") | 
     Select-String "powershell").LineNumber
#index needs to be decremented by 4 since the data starts at line 3 and LineNumber is 1 based
$processes[$index-4]

While this returns the desired result it’s not a very robust solution. If there is, for example, powershell and powershell_ise running at the same time this would return two line numbers instead of one. Furthermore, the approach does not permit to look for items by property values (unless you throw in some crazy regex).
Ok, let’s give it another try. The problem with the $processes array is that it doesn’t have an index property, but fortunately, with PowerShell it’s not a problem at all to add one:

$processes = Get-Process | foreach {$i=0} {$_ | Add-Member Index ($i++) -PassThru}
$index = ($processes | where {$_.Name -eq "powershell"}).Index
$processes[$index]

This looks already better. Adding an Index property to the array makes it easy to replicate the IndexOf method’s functionality with an array of rich objects. It still involves quite some steps, though. Looking for yet another approach, I came across the FindIndex method that is part of the System.Collections.Generic namespace:

$processes = [Collections.Generic.List[Object]](Get-Process)
$index = $processes.FindIndex( {$args[0].Name -eq "powershell"} )
$processes[$index]

With that approach, we first need to cast the array to generic list. Generics are a concept of strongly typed languages like C# which make it possible to write a method that can be used against multiple types of objects (hence generic). For PowerShell, this doesn’t matter so much since its type system is rather dynamic and mainly implicit therefore we just use a generic list of objects (kind of a generic generic list) here. The FindIndex method expects a typed predicate as its only argument which in PowerShell conveniently translates to a ScriptBlock. The predicate is exactly the same as what is used as the FilterScript parameter for the Where-Object cmdlet. The only difference is that we need to use the built-in $args variable in order to access the current (“Pipeline”) element instead of “$_”.
How do those approaches compare in terms of execution speed?:

@'
(gps | foreach {$i=0} {$_ | Add-Member Index ($i++) -PassThru} | where Name -eq "powershell").Index
((gps | out-string).split("`n") | sls "powershell").LineNumber
([Collections.Generic.List[Object]](gps)).FindIndex({$args[0].Name -eq "powershell"})
'@ -split "`n" | foreach{
    (Measure-Command ([ScriptBlock]::Create($_))).TotalSeconds
}

On my machine I got 0.21, 1.67, 0.02 respectively. Looks like the last approach also outperformed the others by far.

shareThoughts


Photo Credit: Vince Alongi via Compfight cc