Tahir Hassan's Blog

My Technical Notes

Wednesday, 17 May 2017

That vs Which

That

`That` is restrictive:


Our office that has a gym is in London.
This implies that we have more than one office, but the office that is has a gym (the others do not), is in London. The `that` in that sentence is essential, without it, the meaning of the sentence would be lost.

Which

`Which` is non-restrictive, and adds non-essential information, when removed, the sentence would still make sense. For instance:


Our office, which has a gym, is in London.
This means that we have only a single office, and the `which`-clause is merely adding extra information that is has a gym. It could be re-written as two sentences:

Our office is in London. It has a gym.

Wednesday, 10 May 2017

Generating a PDF from Graphviz

Create the `.ps` file first (PostScript) from the `.dot` file:


C:\_programs\graphviz\bin\dot.exe -Tps .\graph.dot -o outfile.ps; 

Turn the `.ps` file into a `.pdf` file:


ps2pdf .\outfile.ps; 

Finally use `start` to open it, which will open the `.pdf` using the default handler, in my case, Adobe Acrobat:


start outfile.pdf

To close instances of Acrobat if they are open:


$p = Get-Process Acrobat -ErrorAction SilentlyContinue; 
if ($p) { 
    Stop-Process $p 
}

To bring it all together into a simple function `View-DotPdf`:


Function View-DotPdf([string]$Path) {
    $psPath = ($Path -replace '\.dot$', '.ps');
    $pdfPath = ($Path -replace '\.dot$', '.pdf');

    C:\_programs\graphviz\bin\dot.exe -Tps $Path -o $psPath; 

    $p = Get-Process Acrobat -ErrorAction SilentlyContinue; 
    if ($p) { 
        Stop-Process $p 
    }
    ps2pdf $psPath; 
    start $pdfPath;
}

To view it in `.png` form, do:


Function View-DotPng([string]$Path) {
    $outPath = ($Path -replace '\.dot$', '.png');
    C:\_programs\graphviz\bin\dot.exe -Tpng $Path -o $outPath; 
    start $outPath;
}

Monday, 8 May 2017

PowerShell Confirm Step Without using `ShouldProcess`, `ShouldContinue` etc


if (Test-Path $_) {

    $title = "$_ already exists, delete it and return stored dir?";
    $prompt = ''
    $doOption = New-Object System.Management.Automation.Host.ChoiceDescription '&Yes','deletes and continues'
    $cancelOption = New-Object System.Management.Automation.Host.ChoiceDescription '&No','cancel this particular instance'
    $options = [System.Management.Automation.Host.ChoiceDescription[]] ($doOption,$cancelOption)

    $choice = $Host.UI.PromptForChoice($title,$prompt,$options,0)
    
    if ($choice -eq 1) {
        # user said no
        return;
    } else {
        $tempFolderPath = Join-Path $env:TEMP ([Guid]::NewGuid().ToString())
        mkdir $tempFolderPath | Out-Null;
        Move-Item $_ $tempFolderPath -ErrorVariable errorVariable
        
        if ($errorVariable) {
            Write-Host "Could not remove $_";
            return;
        } else {
            # no error - therefore we can now remove it
            Remove-Item $tempFolderPath -Force -Recurse
        }
    }
}

TODO: polish this up

Thursday, 4 May 2017

SpecFlow "Go to definition" / "Go to binding" / F12 not working

At least for me, the advice from Rob Savage worked:

  1. Close Visual Studio.
  2. In Windows Explorer, go to `%TEMP%` (your temp folder).
  3. Delete files whose name starts with `specflow-stepmap-YourProjectName`.
  4. Restart Visual Studio.

SpecFlow will regenerate the bindings, which will take some time. Until this is done, if you right-click on a step and select "Go To Step Definition", Visual Studio will say "Step bindings are still being analyzed. Please wait."

Monday, 1 May 2017

BAT File to load Program after setting up Environment Variables

You can use a BAT file to set environment variables before invoking a program. A shortcut can then be made to this BAT file.

One BAT file has the logic which sets the environment variables and invokes a program path passed in as an argument (`SetEnv_Invoke.bat`):


@echo off

REM set all the user environment variables
SET USERPROFILE=C:\TEMP\Users\Tahir
SET APPDATA=C:\TEMP\Users\tahir\AppData\Roaming
SET LOCALAPPDATA=C:\TEMP\Users\tahir\AppData\Local
SET TEMP=C:\TEMP\Users\tahir\AppData\Local\Temp
SET TMP=C:\TEMP\Users\tahir\AppData\Local\Temp

REM invoke the program
start "" %1
exit

For each application we want to set up, we create another BAT file which invokes `SetEnv_Invoke.bat` passing in the path of the program to be executed. For instance we could create a BAT file to load up Chrome (`InvokeChrome.bat`):


CALL "SetEnv_Invoke.bat" "C:\Program Files (x86)\Google\Chrome\Application\chrome.exe"

Note that both `SetEnv_Invoke.bat` and `InvokeChrome.bat` have to be in the same folder for this to work.

Friday, 7 April 2017

PowerShell: Performance of different approaches to `Add-Type` guard


function Get-Performance 
{
    [CmdletBinding()]
    param( [scriptblock]$sb )

    $name = $sb.ToString().Trim();
    
    Write-Verbose "Starting $name..."
    $s = [System.Diagnostics.Stopwatch]::StartNew(); 
    (1..250000) | % $sb; 
    $s.Stop(); 
    $elapsed = $s.Elapsed;
    
    Write-Verbose $elapsed;
    Write-Verbose "...Finished $name";
    
    [pscustomobject]@{
        Name = $name;
        Elapsed = $elapsed
    };
}

# the control function should have some code in there to ensure that we are measuring the different approaches.
function Lock-WorkStation_Control {
    $true | Out-Null
}

function Lock-WorkStation_TypeVariable {
    if ($Script:LockWorkStationTypeVariable -eq $null) {
        $namespace = 'Win32FunctionsTypeVariable'
        $name = 'Win32LockWorkStation'
        
        $signature = @'
[DllImport("user32.dll", SetLastError = true)]
public static extern bool LockWorkStation();
'@
        $Script:LockWorkStationTypeVariable = Add-Type -Namespace $namespace -Name $name -MemberDefinition $signature -PassThru
    }
    
    $Script:LockWorkStationTypeVariable::LockWorkStation() | Out-Null
}

function Lock-WorkStation_Exception {
    
    try {
        [Win32FunctionsException.Win32LockWorkStation] | Out-Null
    } catch {
        $namespace = 'Win32FunctionsException'
        $name = 'Win32LockWorkStation'
        
        $signature = @'
[DllImport("user32.dll", SetLastError = true)]
public static extern bool LockWorkStation();
'@
        Add-Type -Namespace $namespace -Name $name -MemberDefinition $signature
    }
    
    [Win32FunctionsException.Win32LockWorkStation]::LockWorkStation() | Out-Null
}

function Lock-WorkStation_Unconditional {
    $namespace = 'Win32FunctionsUnconditional'
    $name = 'Win32LockWorkStation'
    
    $signature = @'
[DllImport("user32.dll", SetLastError = true)]
public static extern bool LockWorkStation();
'@
        
    $LockWorkStationUnconditional = Add-Type -Namespace $namespace -Name $name -MemberDefinition $signature -PassThru      
    
    $LockWorkStationUnconditional::LockWorkStation() | Out-Null
}

function Lock-WorkStation_Mathias {
    $namespace = 'Win32FunctionsMathias'
    $name = 'Win32LockWorkStation'
        
    if(-not ($LockWorkStationMathias = "$namespace.$name" -as [type])){
        $signature = @'
[DllImport("user32.dll", SetLastError = true)]
public static extern bool LockWorkStation();
'@

        $LockWorkStationMathias = Add-Type -Namespace $namespace -Name $name -MemberDefinition $signature -PassThru
    }
    
    $LockWorkStationMathias::LockWorkStation() | Out-Null
}

function Lock-WorkStation_Boolean_VariableAccess {
    if (!$Script:LockWorkStationDefinedA) {
        $namespace = 'Win32FunctionsBooleanVAccess'
        $name = 'Win32LockWorkStation'
        
        $signature = @'
[DllImport("user32.dll", SetLastError = true)]
public static extern bool LockWorkStation();
'@
        $Script:LockWorkStationVar = Add-Type -Namespace $namespace -Name $name -MemberDefinition $signature -PassThru
        $Script:LockWorkStationDefinedA = $true;
    }
    
    $Script:LockWorkStationVar::LockWorkStation() | Out-Null
}

function Lock-WorkStation_Boolean_TypeAccess {
    if (!$Script:LockWorkStationDefinedB) {
        $namespace = 'Win32FunctionsTAccess'
        $name = 'Win32LockWorkStation'
        
        $signature = @'
[DllImport("user32.dll", SetLastError = true)]
public static extern bool LockWorkStation();
'@
        Add-Type -Namespace $namespace -Name $name -MemberDefinition $signature 
        $Script:LockWorkStationDefinedB = $true;
    }
    
    [Win32FunctionsTAccess.Win32LockWorkStation]::LockWorkStation() | Out-Null
}

$SBs = @(
    { Lock-WorkStation_Control },
    { Lock-WorkStation_Exception },
    # { Lock-WorkStation_Unconditional },
    { Lock-WorkStation_Mathias },
    { Lock-WorkStation_Boolean_VariableAccess },
    { Lock-WorkStation_Boolean_TypeAccess },
    { Lock-WorkStation_TypeVariable }
);

$performances = $SBs | % { try { Get-Performance $_ -Verbose } catch { } }

$performances | sort Elapsed -Descending | Format-Table
Running it once, I found:

Name                                    Elapsed
----                                    -------
Lock-WorkStation_Unconditional          00:01:34.9005335
Lock-WorkStation_Exception              00:00:47.0459598
Lock-WorkStation_Mathias                00:00:38.2524806
Lock-WorkStation_Boolean_TypeAccess     00:00:37.1698860
Lock-WorkStation_Boolean_VariableAccess 00:00:36.9747986
Lock-WorkStation_TypeVariable           00:00:36.8496781
Lock-WorkStation_Control                00:00:24.5562487

Monday, 3 April 2017

Reducing Bass on Windows 10 with Equalizer APO

If, like me, you are using headphones plugged directly into your PC/Laptop, you will have no hardware bass controls. Windows 10 also does not include sound equalizer software. Sometimes, sound drivers can include such software - a good example being Realtek - but my sound driver unfortunately did not. Instead, I am using Equalizer APO, a free, open-source equalizer. Here are some instructions on how to set it up to reduce bass levels.

Download/Install Equalizer APO   Download Equalizer APO from SourceForge and install it. When it asks for which devices you want to install APO for, I selected only the current playback device, but it shouldn't matter if you select all sound devices. After installation, you will have to restart your computer.

Download/Install Peace UI for Equalizer APO   Download Peace UI from SourceForge and install it. Accept all the default installation options. It should identify that Equalizer APO is already installed.

Opening Peace   You can open Peace from the Start menu. Select the Full interface, as it will allow you to save presets.

Creating a `Bass Reduce` preset   A bass reduction preset can be made by simply inverting the gain values of the `Bass Boost` option. First select the `Bass Boost` preset from the list in the bottom left of the screen. The app will re-open with the preset values applied. For each of the `Gain Values`, invert the value (multiply it by `-1`). For example, if the value was `10.5`, make it `-10.5`, and if it was `-3`, make it `3`. Click `Save` and enter the name `Bass Reduce`.

Reselecting `Bass Reduce` Option   Once the `Bass Reduce` preset is saved, it can be simply selected from the list in the bottom left of the screen. The app will re-open to apply the preset options.