This project is read-only.

Refresh-Desktop: Updated function to check for custom Add-Type before adding to avoid errors

Topics: Archive - Toolkit Extensions
Jul 10, 2014 at 12:02 AM
Edited Jul 10, 2014 at 12:03 AM
If you are like me, you have one functions library and use it for all of your scripts. If you are calling one script from another or executing multiple scripts in the same PowerShell console, you will get Add-Type errors if you try to use a function that adds a custom type. The error states that the custom type already exists and cannot be added again.

I have updated the Refresh-Desktop function to avoid this problem. I am also using a custom logging function I wrote which is posted in another thread in this forum. Write-ErrorStack function is also posted in another thread in this forum.
Function Refresh-Desktop
{
<#
.SYNOPSIS
    Forces the Windows Exporer Shell to refresh, which causes environment variables and desktop icons to be reloaded
.DESCRIPTION
    Forces the Windows Exporer Shell to refresh, which causes environment variables and desktop icons to be reloaded.
    Informs the Explorer Shell to refresh its settings after you change registry values or other settings to avoid a reboot.
.EXAMPLE
    Refresh-Desktop
.PARAMETER ContinueOnError
    Continue if an error is encountered
.NOTES
#>
    [CmdletBinding()]
    Param
    (
        [boolean] $ContinueOnError = $true
    )
    
    Begin
    {
        ${CmdletName} = $PSCmdlet.MyInvocation.MyCommand.Name
        $PSParameters = New-Object -TypeName PSObject -Property $PSBoundParameters
        
        Write-Log -Message "Function Start" -Component ${CmdletName} -Severity 1
        If (-not [string]::IsNullOrEmpty($PSParameters))
        {
            Write-Log -Message "Function invoked with bound parameters [$PSParameters]" -Component ${CmdletName} -Severity 1
        }
        Else
        {
            Write-Log -Message "Function invoked without any bound parameters" -Component ${CmdletName} -Severity 1
        }
        
        $refreshDesktopCode = @'
        private static readonly IntPtr HWND_BROADCAST = new IntPtr(0xffff);
        private const int WM_SETTINGCHANGE = 0x1a;
        private const int SMTO_ABORTIFHUNG = 0x0002;
        
        [System.Runtime.InteropServices.DllImport("user32.dll", SetLastError=true, CharSet=CharSet.Auto)] static extern bool SendNotifyMessage(IntPtr hWnd, uint Msg, UIntPtr wParam, IntPtr lParam);
        [System.Runtime.InteropServices.DllImport("user32.dll", SetLastError = true)] private static extern IntPtr SendMessageTimeout ( IntPtr hWnd, int Msg, IntPtr wParam, string lParam, uint fuFlags, uint uTimeout, IntPtr lpdwResult );
        [System.Runtime.InteropServices.DllImport("Shell32.dll")] private static extern int SHChangeNotify(int eventId, int flags, IntPtr item1, IntPtr item2);
        
        public static void Refresh()
        {
            SHChangeNotify(0x8000000, 0x1000, IntPtr.Zero, IntPtr.Zero);
            SendMessageTimeout(HWND_BROADCAST, WM_SETTINGCHANGE, IntPtr.Zero, null, SMTO_ABORTIFHUNG, 100, IntPtr.Zero);
        }
'@
        If (-not ([System.Management.Automation.PSTypeName]'MyWinAPI.Explorer').Type)
        {
            Add-Type -MemberDefinition $refreshDesktopCode -Namespace MyWinAPI -Name Explorer
        }
    }
    Process
    {
        Write-Log -Message "Refreshing desktop and environment variables for explorer" -Component ${CmdletName} -Severity 1
        
        Try
        {
            [MyWinAPI.Explorer]::Refresh()
        }
        Catch
        {
            Write-Log -Message "Failed to refresh desktop and environment variables for explorer `n$(Write-ErrorStack)" -Component ${CmdletName} -Severity 3
            
            If ($ContinueOnError -eq $true)
            {
                Continue
            }
            Else
            {
                Exit
            }
        }
    }
    End
    {
        Write-Log -Message "Function End" -Component ${CmdletName} -Severity 1
    }
}