Escaping characters in a WMI Query I've knocked up this code to allow me to query for specific service names using PowerShell.

I'm after the executable (PathName) for the service, so the Get-Service cmdlet is not appropriate here. I don't actually have any services with special characters in their names; but I like to build robust code where I can foresee future gotchas.

I'm posting here as want to know if my ConvertTo-WmiEscapedQuery is the right way to escape a WMI Query.

function ConvertTo-WmiEscapedQuery {
    [CmdletBinding()]
    param (
        [Parameter(Mandatory = $true)]
        [string]$Query
        ,
        [Parameter()]
        [string[]]$Parameters = @()
    )
    begin {
        [string]$EscapeCharatersRegex = '([\\''"])'
    }
    process {
        [string[]]$EscapedParameters = $Parameters | %{$_ -replace $EscapeCharatersRegex, '\$1'}
        $Query -f $EscapedParameters
    }
}

function Get-WmiService {
    [CmdletBinding()]
    param (
        [Parameter(Mandatory = $true)]
        [string]$ServiceName
    )
    begin {
        [string]$Query = 'select * from win32_service where name = "{0}"'
    }
    process {
        Get-WmiObject -Query (ConvertTo-WmiEscapedQuery -Query $Query -Parameters $ServiceName)
    }
}

Get-WmiService 'John''s Example Service'


I realise some people don't like the | %{...} shorthand for a foreach statement.  I've deliberately opted to use such shorthand options only for foreach/% and where/? statements, since these are pretty universally known conventions.

(I've also posted here as this question falls into the grey area between question and code-review.)
Per @Dangph's comment on the question, it seems that whether to escape ' or " depends on the context; i.e. if within quotes of the same type these characters need escaping; if not however, they must not be escaped.

The below version only escapes apostrophes (' / single quotes) and backslashes (\); not quotation marks (" / double quotes).  

It assumes that the query will only use single quotes for surrounding parameter names.

It has a bit of extra logic to capture the more common cases where double quotes are used (i.e. where it's simply quotes around the placeholder, or quotes and wildcards (for "starts with" / "ends with" / "contains") matching around the placeholders.  I don't really like this as it's inconsistent / may lead to unpredictable behaviour, so will likely not keep that line / rather will state that only single quotes should be used... but included here to illustrate one possible option.

function ConvertTo-WmiEscapedQuery {
    [CmdletBinding()]
    param (
        [Parameter(Mandatory = $true)]
        [string]$Query
        ,
        [Parameter()]
        [string[]]$Parameters = @()
    )
    begin {
        [string]$EscapeCharatersRegex = '([\\''])' #Doesn't escape " as assumes strings are enclosed using apostrophes; i.e. "... where field = '{0}'"
    }
    process {
        [string[]]$EscapedParameters = $Parameters | %{$_ -replace $EscapeCharatersRegex, '\$1'}
        [string]$escapedQuery = $Query -replace '(")(%?{\d+}%?)\1', '''$2''' #enforce the above assumption by replacing double quotes around parameterised input with apostrophes.  Not guaranteed to work, but better than nothing.  Remove this line &amp; replace the below with `[string]$escapedQuery = $Query -f $EscapedParameters` if you don't like this behaviour.
        $escapedQuery = $escapedQuery -f $EscapedParameters
        Write-Verbose "Escaped Query: $escapedQuery"
        $escapedQuery
    }
}

function Get-WmiService {
    [CmdletBinding()]
    param (
        [Parameter(Mandatory = $true)]
        [string]$ServiceName
    )
    begin {
        [string]$Query = 'select * from win32_service where name = ''{0}'''
    }
    process {
        Get-WmiObject -Query (ConvertTo-WmiEscapedQuery -Query $Query -Parameters $ServiceName)
    }
}

Get-WmiService '"John''s Example Service\String"' -Verbose
Get-WmiObject -Query (ConvertTo-WmiEscapedQuery -Query 'select * from win32_service where name = ''{0}'' or displayname like "%{1}%"' -Parameters 'bits', 'transfer' -Verbose) | select name, displayName #just to prove it works
Get-WmiObject -Query (ConvertTo-WmiEscapedQuery -Query 'select * from win32_service where name = ''{0}'' or displayname like "%{1}%"' -Parameters '''bits"', '"transfer''' -Verbose) | select name, displayName #to test escaping logic