Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

PowerShell script fails first time but works second time

I've found a PowerShell script that can change the desktop wallpaper of my Windows 7 PC of an image file whose path is supplied as a parameter. The end result of what I want is to have this script called by a batch file on startup.

[CmdletBinding()]
Param(
   [Parameter(Position=0, Mandatory=$true, ValueFromPipelineByPropertyName=$true)]
   [Alias("FullName")]
   [string]
   $Path
,
   [Parameter(Position=1, Mandatory=$false)]
   $Style = "NoChange"
)

BEGIN {
try {
   $WP = [Wallpaper.Setter]
} catch {
   $WP = add-type @"
using System;
using System.Runtime.InteropServices;
using Microsoft.Win32;
namespace Wallpaper
{
public enum Style : int
{
     Tile, Center, Stretch, NoChange
}

public class Setter {
  public const int SetDesktopWallpaper = 20;
  public const int UpdateIniFile = 0x01;
  public const int SendWinIniChange = 0x02;

  [DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Auto)]
  private static extern int SystemParametersInfo (int uAction, int uParam, string lpvParam, int fuWinIni);

  public static void SetWallpaper ( string path, Wallpaper.Style style ) {
     SystemParametersInfo( SetDesktopWallpaper, 0, path, UpdateIniFile | SendWinIniChange );

     RegistryKey key = Registry.CurrentUser.OpenSubKey("Control Panel\\Desktop", true);
     switch( style )
     {
        case Style.Stretch :
           key.SetValue(@"WallpaperStyle", "2") ; 
           key.SetValue(@"TileWallpaper", "0") ;
           break;
        case Style.Center :
           key.SetValue(@"WallpaperStyle", "1") ; 
           key.SetValue(@"TileWallpaper", "0") ; 
           break;
        case Style.Tile :
           key.SetValue(@"WallpaperStyle", "1") ; 
           key.SetValue(@"TileWallpaper", "1") ;
           break;
        case Style.NoChange :
           break;
     }
     key.Close();
    }
   }
}
"@ -Passthru
}
}
PROCESS {
   Write-Verbose "Setting Wallpaper ($Style) to $(Convert-Path $Path)"
   $WP::SetWallpaper( (Convert-Path $Path), $Style )
}

I'm calling this script using the command:

C:\scripts\Set-Wallpaper.ps1 C:\Users\myProfile\Pictures\MyWallpaper.jpg

I'm totally new to the world of PowerShell scripts, and the issue I'm having is that when I execute the script from within PowerShell it always fails the first time with the following error:

C:\scripts\Set-Wallpaper.ps1 : Unable to cast object of type 'System.Object[]' to type 'System.Type'.

At line:1 char:29

  • C:\scripts\Set-Wallpaper.ps1 <<<< C:\Users\mbaleato\Pictures\MyWallpaper.jpg
    • CategoryInfo : NotSpecified: (:) [Set-Wallpaper.ps1], InvalidCastException
    • FullyQualifiedErrorId : System.InvalidCastException,Set-Wallpaper.ps1

But when I call the script with the exact same command and parameter the second time it works.

It is this failing the first time that is causing my batch file to fail.

Anyone who is more experienced have some suggestions as to why it fails the first time, but works the second time? Any suggestions as to how I can get it to work the first time?

like image 668
Span Avatar asked May 27 '11 04:05

Span


2 Answers

Look at line that begins with $WP = add-type @". That's the problem. You create two types:

$wp

IsPublic IsSerial Name                                     BaseType
-------- -------- ----                                     --------
True     True     Style                                    System.Enum
True     False    Setter                                   System.Object

Try to call Add-Type without -Passthru and after that assign to $wp

Add-Type -typedef @"
...
"@
$WP = [Wallpaper.Setter]
like image 168
stej Avatar answered Oct 05 '22 14:10

stej


I believe it's because-passthru is making $WP into an array - you could try this try this instead:

try {
   $WP = [Wallpaper.Setter]
} catch {
   add-type @"
....
"@
    $WP = [Wallpaper.Setter]
}

You can see by running it line by line and checking the tyoe:

PS D:\bin\OpenSSL-Win32\bin> $WP

IsPublic IsSerial Name                                     BaseType
-------- -------- ----                                     --------
True     True     Style                                    System.Enum
True     False    Setter                                   System.Object

PS D:\bin\OpenSSL-Win32\bin> $WP.GetType()

IsPublic IsSerial Name                                     BaseType
-------- -------- ----                                     --------
True     True     Object[]                                 System.Array


PS D:\bin\OpenSSL-Win32\bin> $WP = [Wallpaper.Setter]
PS D:\bin\OpenSSL-Win32\bin> $WP.GetType()

IsPublic IsSerial Name                                     BaseType
-------- -------- ----                                     --------
False    True     RuntimeType                              System.Type

The second time around the type is already there so %WP is loaded correctly.

like image 28
Matt Avatar answered Oct 05 '22 15:10

Matt