# Tahir Hassan's Blog

My Technical Notes

## Saturday, 21 January 2017

### PowerShell: Propogation of -Verbose option and ShouldProcess non-handling

The following is an answer I posted on StackOverflow that was about propagation of -Verbose and how it ain't respected by ShouldProcess.

I was looking to write exactly the same question, and I am writing this almost 7 years later. I am surprised that Microsoft's PowerShell team have not fixed this yet. I have reproduced the issue with PowerShell Version 6 Preview (latest version).

I have come up with a simple workaround, that is, inside the Inner function, we create an run a scriptblock, setting the -Verbose flag by checking $VerbosePreference which is correctly set to Continue, even though it is not respected by ShouldProcess:  Function Outer { [CmdletBinding(SupportsShouldProcess=$true, ConfirmImpact="Medium")]
param([string]$Name) Process { Write-Host "Outer called"; Inner$Name
}
}

Function Inner {
[CmdletBinding(SupportsShouldProcess=$true, ConfirmImpact="Medium")] param([string]$Name)

Process {
$sb = { [CmdletBinding(SupportsShouldProcess=$true, ConfirmImpact="Medium")]

param([string]$Name) if ($PSCmdlet.ShouldProcess($Name, "Inner")) { Write-Host "Inner called"; } } &$sb $Name -Verbose:($VerbosePreference -eq 'Continue');
}
}

Export-ModuleMember *


### PowerShell: Overriding ConfirmPreference

In PowerShell, if you define a function which SupportsShouldProcess and you call it with -Confirm switch, it will run the function with $ConfirmPreference set to Low. Therefore those commands which even have a Medium impact (ConfirmImpact), such as Move-Item will ask for confirmation. To prevent this, we can call such functions with -Confirm set to $false:


Move-Item -Path .\Foo.txt -Destination .\Bar.txt -Confirm:$false  Another method of doing this, is to set the $ConfirmPreference to High (which is the default), and isolate its effects from the rest of the function by using the call operator (&) on a script block:


& {
$ConfirmPreference = 'High'; Move-Item -Path .\Foo.txt -Destination .\Bar.txt }  The second approach is well-suited to situations in which we have many function calls for which we want to disable confirmation. ### PowerShell Get-ConfirmImpact Function In PowerShell, there is no easy way to find out the ComfirmImpact of a command. For that, I have implemented the following function Get-ConfirmImpact:  Function Get-ConfirmImpact { [CmdletBinding()] param( [Parameter(Mandatory=$true,ValueFromPipeline=$true)]$Command
)

Begin {
$ErrorActionPreference = 'Stop'; } Process {$commandObj = (Get-Command $Command); if ($commandObj.CommandType -eq 'Alias') {
return Get-ConfirmImpact $commandObj.Definition; } else {$attrs = & {
if ($commandObj.ImplementingType) {$commandObj.ImplementingType.GetCustomAttributes($true); } elseif ($commandObj.ScriptBlock) {
$commandObj.ScriptBlock.Attributes } };$attrs |
? { $_ -is [System.Management.Automation.CmdletCommonMetadataAttribute] } | % {$_.ConfirmImpact };
}
}
}


To use it, you pass a command name to the function:


Get-ConfirmImpact Move-Item


which will return Medium;

## Sunday, 15 January 2017

### Compiling Bootstrap 4 on Windows Issue

When compiling Bootstrap 4 on Windows using the following command:


grunt dist


You can potentially get the following error message:


>> fs.js:951
>>                  ^
>>
>> Error: ENOENT: no such file or directory, scandir 'C:\_code\bootstrap-4.0.0-alpha.6\node_modules\node-sass\vendor'
>>     at Error (native)
>>     at Object.getInstalledBinaries (C:\_code\bootstrap-4.0.0-alpha.6\node_modules\node-sass\lib\extensions.js:122:13)
>>     at foundBinariesList (C:\_code\bootstrap-4.0.0-alpha.6\node_modules\node-sass\lib\errors.js:20:15)
>>     at foundBinaries (C:\_code\bootstrap-4.0.0-alpha.6\node_modules\node-sass\lib\errors.js:15:5)
>>     at Object.module.exports.missingBinary (C:\_code\bootstrap-4.0.0-alpha.6\node_modules\node-sass\lib\errors.js:45:5)
>>     at module.exports (C:\_code\bootstrap-4.0.0-alpha.6\node_modules\node-sass\lib\binding.js:15:30)
>>     at Object.<anonymous> (C:\_code\bootstrap-4.0.0-alpha.6\node_modules\node-sass\lib\index.js:14:35)
>>     at Module._compile (module.js:570:32)
>>     at Object.Module._extensions..js (module.js:579:10)


You can fix the issue using:


npm rebuild node-sass


### Basic Dialog/Window In AutoHotkey

This example displays a simple GUI using AutoHotkey. It asks for a forename and surname and displays the entered data in a message box.


; Create a new GUI called "PersonGui"
Gui, PersonGui:New
; 1. +AlwaysOnTop - make it always on top so that users can see it
; 2. -SysMenu gets rid of the top-left icon, and the minimize, maximize and close buttons
; 3. +Owner removes the dialog's taskbar icon
; Add the forename and surname labels
; Add forename and surname textboxes. The ym option starts a new column of controls.
; Add an "OK" button, which, when when pressed, will run the PersonGuiOK label.
; "Default" makes it the default action when Enter key is pressed
Gui, Add, Button, gPersonGuiOK Default, OK
; We want the "Cancel" button to be next to the "OK" button with a margin of 5 pixels
GuiControlGet, OK, Pos
NewXPos := OKX + OKW + 5
; Add the "Cancel" button, running label "PersonGuiCancel",
; setting its location to be next to the "OK" button, by setting its xy coordinates.
Gui, Add, Button, gPersonGuiCancel x%NewXPos% y%OKY%, Cancel
; Show the dialog, with the title of "Person Details"
Gui, Show,, Person Details
; return ends the auto-execute section. This script is idle until the user does something with the GUI.
return

; PersonGuiOK label is associated with the "OK" button
PersonGuiOK:
; Gui, Submit saves the input from the user to each control's associated variable.
Gui, Submit
; Show the dialog with the entered Forename and Surname values
MsgBox You entered "%Forename% %Surname%".
; ExitApp terminates the script
ExitApp

; GuiEscape will be run when the user presses the Escape key while the GUI is active
PersonGuiGuiEscape:
; GuiClose will be run when the dialog is closed.
PersonGuiGuiClose:
; PersonGuiCancel is the label associated with the "Cancel" button
PersonGuiCancel:
; Gui, Destroy: destroy the GUI
Gui, Destroy
ExitApp


If you had more than one GUI in your app, and you wanted to set this this particular GUI PersonGui as the default that commands such as Add operate on, we use the Default sub-command:


Gui, PersonGui:Default


If on the other-hand, you wanted to add a control to a GUI without setting it as the Default, you can specify the GUI's name:




## Sunday, 8 January 2017

### Finding max-width and max-device-width programmatically

The following Javascript code finds out the max-width and max-device-width of a page/device:


(function () {
function getWidth(type) {
var startWidth = 100;
var endWidth = 1000;

for (var i = startWidth; i <= endWidth; ++i) {
if (!(window.matchMedia("(min-" + type + ": " + i + "px)").matches)) {
return i - 1;
}
}

return ">= 1000";
}

alert('max-width is ' + getWidth('width') + 'px and max-device-width is ' + getWidth('device-width') + 'px');
})();


## Tuesday, 3 January 2017

### Querying Results from a stored procedure

To query the result of a stored procedure we first need to execute the stored procedure:


DECLARE @RC int
EXECUTE @RC = [MyDatabase].[dbo].[myStoredProc]


To store the result of this query into a temporary table, we first need to create the temporary table first and thereafter dump the result of the stored procedure into the new temporary table:


-- creat the temporary table:
CREATE TABLE #tmp(
[id] int,
name varchar(64)
-- other cols here
)

-- now call the
INSERT INTO #tmp
exec [MyDatabase].[dbo].[myStoredProc]


A problem with the above method is that we need to write the CREATE TABLE and specify all the columns which might be a laborious task.

An alternative to creating a temp table and writing all the column names is to use the OPENROWSET to run the query:


SELECT tmp.*
FROM OPENROWSET(
'SQLOLEDB',
'Server_name';  -- the server name e.g .\SQLEXPRESS
'EXEC [database_name].[dbo].[stored_procedure_name]' -- statement to execute
) AS tmp


For the server name we can use:


SELECT @@SERVERNAME

Unfortunately, the server needs to be configured for this access and you may not have permissions to change the configuration. The following error message was shown for me:


Msg 15281, Level 16, State 1, Line 1
SQL Server blocked access to STATEMENT 'OpenRowset/OpenDatasource' of component 'Ad Hoc Distributed Queries' because this component is turned off as part of the security configuration for this server. A system administrator can enable the use of 'Ad Hoc Distributed Queries' by using sp_configure. For more information about enabling 'Ad Hoc Distributed Queries', see "Surface Area Configuration" in SQL Server Books Online.


If we had access to the server we can always use OPENQUERY which is easier and more elegant to use than OPENROWSET but at the same time we need to have admin privileges. To do this we first add a linked server called loopback (link):


@server = 'loopback',
@srvproduct = '',
@provider = 'SQLNCLI',
@datasrc = @@SERVERNAME;

EXEC master..sp_serveroption
@server = 'loopback',
@optname = 'DATA ACCESS',
@optvalue = 'TRUE';


We can then use OPENQUERY to use loopback to query the same server we are on:


-- do a select only
SELECT * FROM OPENQUERY(loopback, 'EXEC [MyDatabase].[dbo].[myStoredProc];');

-- store the results of the stored procedure into a temporary table
SELECT * INTO #tmp FROM OPENQUERY(loopback, 'EXEC [MyDatabase].[dbo].[myStoredProc];');


Lastly we can use C# to run the stored procedure and then we can determine the column names and column types by interrogating the DataTable and then outputting a CREATE TABLE statement (link):