Within Powershell, the CopyHere method for the Shell-Application Namespace is asynchronous. My main goal with this is to convert a KML file to a KMZ file. The process of doing this is to create a ZIP file with the same name, copy the KML into the KMZ (compresses the file) and then rename the ZIP to KMZ. Unfortunately, being asynchronous means the rename function is being called before the CopyHere method is completed. I have found many examples of solving this. The cleanest one I found is below:
$kmlPath = $global:directoryPath + "Test.kml"
$zip = $global:directoryPath + "Test.zip"
New-Item $zip -ItemType file
$shellApplication = new-object -com shell.application
$zipPackage = $shellApplication.NameSpace($zip)
$zipPackage.CopyHere($kmlPath, 16)
while($zipPackage.Items().Item($zip.Name) -Eq $null)
{
start-sleep -seconds 1
write-host "." -nonewline
}
write-host "."
Rename-Item -Path $zip -NewName $([System.IO.Path]::ChangeExtension($zip, ".kmz"))
This responds with the following error:
Exception calling "Item" with "1" argument(s): "Not implemented
(Exception from HRESULT: 0x80004001 (E_NOTIMPL))"
+ while($zipPackage.Items().Item($zip.Name) -Eq $null)
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : NotSpecified: (:) [], MethodInvocationException
+ FullyQualifiedErrorId : ComMethodTargetInvocation
Am I misusing the Item method for this particular package? I am confused why something that "appears" to be neatly done is not working. I have also tried the snippet of code provided Here. It also complains about the .Item method in this particular situation.
The issue i ran into was trying to find away to check on zip status.
So instead i did a trigger for a while that would fire ...If the Zipfile was openable and the File name was inside.
function kml_to_kmz([string]$kmlPath){
[Reflection.Assembly]::LoadWithPartialName('System.IO.Compression.FileSystem')
$kmlInfo = Get-ChildItem -Path $kmlPath
$zipLocation = ($kmlInfo.Directory.FullName + '\' + $kmlInfo.Name.Remove($kmlInfo.Name.LastIndexOf('.')) + '.zip')
New-item $zipLocation
((new-object -com shell.application).NameSpace($zipLocation)).CopyHere($kmlPath, 16)
$trigger = $false
while ($trigger -eq $false){
try{
$zip = [IO.Compression.ZipFile]::OpenRead($zipLocation)
If(($zip.Entries | %{$_.Name}) -contains $kmlInfo.Name){
$zip.Dispose();
$trigger = $true
break;
}
}catch{}
start-sleep -seconds 1
write-host "." -nonewline
}
[IO.Compression.ZipFile]::OpenRead($zipLocation).Dispose()
Rename-Item -Path $zipLocation -NewName $([System.IO.Path]::ChangeExtension($zipLocation, '.kmz'))
}
kml_to_kmz -kmlPath "C:\Users\Default\Desktop\Test.kml"
Related
I am trying to create a shortcut on desktop using a Powershell Script. However, I got an error code when trying to run the code below.
$new_object = New-Object -ComObject WScript.Shell
$destination = $new_object.SpecialFolders.Item("AllUsersDesktop")
$source_path = Join-Path -Path $destination -ChildPath "\\Test Intranet.url"
$source = $new_object.CreateShortcut($source_path)
$source.TargetPath = "https://sharepoint.com/"
$source.IconLocation="C:\Users\Public\Pictures\ShortcutIcon.ico"
$source.Save()
Any help will be appreciated.
Thanks.
You didn't show this, but the error message you received is probably this one:
Exception setting "IconLocation": "The property 'IconLocation' cannot be found on this object. Verify that the property exists and can be set."
At line:8 char:1
+ $source.IconLocation="C:\Users\Public\Pictures\ShortcutIcon.ico"
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : NotSpecified: (:) [], SetValueInvocationException
+ FullyQualifiedErrorId : ExceptionWhenSetting
That is because an Internet shortcut has different properties than a 'normal' (.lnk) shortcut to a file of folder.
Another thing is that you have prefixed the shortcut filename with a double backslash and by doing so, you will get a wrong path: C:\Users\Public\Desktop\\Test Intranet.url
In below code, I have changed some of the variable names to be more self-descripting (at least, I like to think so..)
$shell = New-Object -ComObject WScript.Shell
$destination = $shell.SpecialFolders.Item("AllUsersDesktop")
$shortcutPath = Join-Path -Path $destination -ChildPath 'Test Intranet.url'
# create the shortcut
$shortcut = $shell.CreateShortcut($shortcutPath)
# for a .url shortcut only set the TargetPath
$shortcut.TargetPath = 'https://sharepoint.com/'
$shortcut.Save()
# next update the shortcut with a path to the icon file and the index of that icon
# you can do that because a .url file is just a text file in INI format
Add-Content -Path $shortcutPath -Value "IconFile=C:\Users\Public\Pictures\ShortcutIcon.ico"
Add-Content -Path $shortcutPath -Value "IconIndex=0"
# clean up the COM objects
[System.Runtime.Interopservices.Marshal]::ReleaseComObject($shortcut) | Out-Null
[System.Runtime.Interopservices.Marshal]::ReleaseComObject($shell) | Out-Null
[System.GC]::Collect()
[System.GC]::WaitForPendingFinalizers()
When opened in notepad, your shortcut file looks like this:
[{000214A0-0000-0000-C000-000000000046}]
Prop3=19,11
[InternetShortcut]
IDList=
URL=https://sharepoint.com/
IconFile=C:\Users\Public\Pictures\ShortcutIcon.ico
IconIndex=0
Long story short, I'm trying to dynamically use a parameter -Directory or -File in PowerShell's Get-ChildItem. Guess what? I'm unable to.
Here's the deal (note: pseudo-code):
Param(
[string]$filter = $(throw "Error: name"),
[string]$type = $(throw "error: file or directory")
)
if( $type -eq "file" ) {
$mode = '-File'
}
elseif( $type -eq "directory" ) {
$mode = '-Directory'
}
Function Find_Plugin_folder {
Write-Host "look for: '$($filter)'"
Invoke-Command -ComputerName (Get-Content servers.txt ) -ScriptBlock {
(Get-ChildItem -Path "z:\www" -Depth 5 $Using:mode -Filter $Using:filter -Recurse ) | %{$_.FullName}
} -ThrottleLimit 80
}
Find_Plugin_folder
$Using:mode is where it throws an error, either:
PS C:\Users\janreilink> v:\test.ps1 vevida-optimizer file
look for: 'vevida-optimizer'
A positional parameter cannot be found that accepts argument '-File'.
+ CategoryInfo : InvalidArgument: (:) [Get-ChildItem], ParameterBindingException
+ FullyQualifiedErrorId : PositionalParameterNotFound,Microsoft.PowerShell.Commands.GetChildItemCommand
+ PSComputerName : webserver-01.example.net
Or
PS C:\Users\janreilink> v:\test.ps1 vevida-optimizer directory
look for: 'vevida-optimizer'
A positional parameter cannot be found that accepts argument '-Directory'.
+ CategoryInfo : InvalidArgument: (:) [Get-ChildItem], ParameterBindingException
+ FullyQualifiedErrorId : PositionalParameterNotFound,Microsoft.PowerShell.Commands.GetChildItemCommand
+ PSComputerName : webserver-01.example.net
I've been reading about Dynamic Parameter sets all afternoon, but can't wrap my head around it yet. Any points are much (much, much) appreciated.
You'll want to use splatting for this instead. Start by creating a hashtable with some or all of the parameters you want to pass:
$dynamicArgs = #{}
if( $type -eq "file" ) {
$dynamicArgs['File'] = $true
}
elseif( $type -eq "directory" ) {
$dynamicArgs['Directory'] = $true
}
Then, inside Invoke-Command, prefix the variable name with # to indicate that you want to "splat" the arguments:
Get-ChildItem -Path "z:\www" -Depth 5 #Using:dynamicArgs -Filter $Using:filter -Recurse
If the splatting table contains the key File with a value of $true, it's the equivalent of adding -File:$true on the command line, and vice versa for the Directory argument
Here is my for loop
function Generate_bin($input_1, $bin_file_name, $Codes){
$counter=1
foreach ($Code in $Codes)
{
Write-Host "Code = $Code"
$input_11 = "$input_1" + "$counter"
$pattern = "0x"
$New = ""
$Code = [regex]::replace($Code, $pattern, "$New")
$bin_file_name2 = "$bin_file_name"
$bin_file_name2 += "$Code" + ".bin"
#This utility generates "out.bin"
Invoke-Command -ScriptBlock {.\xyz.exe -i -t "$input_1"}
Rename-Item out.bin -NewName $bin_file_name2
$counter++
}
}
I am getting following error
Rename-Item : Cannot create a file when that file already exists.
At C:\script\myParser_unique.ps1:18 char:7
+ Rename-Item out.bin -NewName $bin_file_name2
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : WriteError: (C:\Users\v-ashi...Unified\out.bin:String) [Rename-Item], IOException
+ FullyQualifiedErrorId : RenameItemIOError,Microsoft.PowerShell.Commands.RenameItemCommand
You should add -Force parameter to the Rename-Item to make it overwrite existing files.
If your code happens to generate multiple equal file names you could add a counter to the filename.
I have a powershell script to download items from a TFS directory.
The code is as follows:
$TfsWorkspace = "$/Region/Application/Master/Payments"
$TfsUri = "http://tfs.company.net:8080/tfs/global"
$LocalDir = "C:\Deployment\"
Add-PSSnapin Microsoft.TeamFoundation.PowerShell
# Connect to TFS
$TFSServer = Get-TfsServer -Name $TfsUri
# Get all directories and files in the $TfsWorkspace directory
$items = Get-TfsChildItem $TfsWorkspace -Recurse -Server $TFSServer
$tfsCollection = New-Object -TypeName Microsoft.TeamFoundation.Client.TfsTeamProjectCollection -ArgumentList $TfsUri
$tfsVersionControl = $tfsCollection.GetService([Microsoft.TeamFoundation.VersionControl.Client.VersionControlServer])
# Download each item to a specific destination
foreach ($item in $items)
{
#If it finds a folder create the folder first
if ($item.ItemType -eq "Folder")
{
$newFolder = ($LocalDir + $([IO.Path]::GetFullPath($item.ServerItem.Remove(0,1)).Remove(0,3)))
New-Item -ItemType directory -Path $($newFolder) -Force
}
else
{
$newFile = $($LocalDir + $([IO.Path]::GetFullPath($item.ServerItem.Remove(0,1)).Remove(0,3)))
$tfsVersionControl.DownloadFile($item.ServerItem, $newFile)
}
}
When I run this script it creates the folders on the drive but when it hits the part to create a new file I get this error message:
Exception calling "DownloadFile" with "2" argument(s): "Invalid URI: Invalid port specified."
At C:\Users\UKAutoUser\Documents\TFS_Update2.ps1:40 char:9
+ $tfsVersionControl.DownloadFile($item.ServerItem, $newFile)
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : NotSpecified: (:) [], MethodInvocationException
+ FullyQualifiedErrorId : UriFormatException
I've tried digging through the documentation for that error but it doesnt seem to make sense. Since its connected fine as it finds the items.
Here are an example of the string values being passed into the DownloadFile method:
C:\Deployment\Region\Application\Master\Payments\Screens.cs
$/Region/Application/Master/Payments/Screens.cs
Any help would be appreciated. I seem to be almost there as I can get a list of the files which im iterating through. I just need to be able to download them to the Deployment folder.
This is so that I can later build the code using Jenkins.
I found script on internet, which install WindowsAzurePowerShell, but it doesn't work:
[reflection.assembly]::LoadWithPartialName("Microsoft.Web.PlatformInstaller") | Out-Null
$ProductManager = New-Object Microsoft.Web.PlatformInstaller.ProductManager
$ProductManager.Load()
$product = $ProductManager.Products | Where { $_.ProductId -eq "WindowsAzurePowerShell" }
$InstallManager = New-Object Microsoft.Web.PlatformInstaller.InstallManager
$Language = $ProductManager.GetLanguage("en")
$installertouse = $product.GetInstaller($Language)
$installer = New-Object 'System.Collections.Generic.List[Microsoft.Web.PlatformInstaller.Installer]'
$installer.Add($installertouse)
$InstallManager.Load($installer)
$failureReason=$null
foreach ($installerContext in $InstallManager.InstallerContexts) {
$InstallManager.DownloadInstallerFile($installerContext, [ref]$failureReason)
}
$InstallManager.StartInstallation()
I see exception:
Exception calling "DownloadInstallerFile" with "2" argument(s): "The InstallerContext passed to this method requires a non-Null InstallerFile."
At C:\Users\test.ps1:18 char:5
+ $InstallManager.DownloadInstallerFile($installerContext, [ref]$failureReason ...
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : NotSpecified: (:) [], MethodInvocationException
+ FullyQualifiedErrorId : InvalidOperationException
So. How it's fixed?
This example I have put together below doesn't go about installing that package the way you where attempting but instead downloads the WebPi CLI, extracts the zip and runs it in an elevated prompt then returns an exit code. I think this will get the job done and can be reproduced on all boxes that may not have the WebPI installed as you can place the file on a share and run this across many computers.
$SourcePath = "http://www.iis.net/community/files/webpi/webpicmd_x86.zip"
$DestinationPath = "c:\Temp\webpicmd_x86.zip"
$ExtractionPath = "c:\Temp\WebPICmd"
$CWebPiCmdLineTool = "$ExtractionPath\WebpiCmdLine.exe"
Import-Module BitsTransfer
Start-BitsTransfer -Source $SourcePath -Destination $DestinationPath
New-Item -Path C:\Temp -Name WebPICmd -ItemType directory | Out-Null
$shell = new-object -com shell.application
$zip = $shell.NameSpace($DestinationPath)
foreach($item in $zip.items())
{
$shell.Namespace($ExtractionPath).copyhere($item)
}
$InstallWebPiPackages = Start-Process -FilePath $CWebPiCmdLineTool -ArgumentList "/Products:WindowsAzurePowerShell" -Verb "RunAs" -Wait -PassThru
$InstallWebPiPackages.ExitCode