Well, it looks like I need to revisit my syntax color copy for PS3. I'll put it on the list.
But for now, Eric (http://ericsowell.com/) did a presentation at NorthDallas.NET last night about the Canvas via Java. Not .NET really, but I didn't even think to harass him about it. So while he was talking I was trying to get it accessible from powershell. I had the wrong version of IE accessible, but this morning in IE10. I got it working.. Nothing rocket science, but I did manage to make an example as nothing showed up when I searched google for "HTML5 Canvas PowerShell" This really is a bit of an edge case, I'll buy that.
$oie = new-object -com internetexplorer.application
$oIE.navigate2("About:blank")
while ($oIE.busy) {
sleep -milliseconds 50
}
$oIE.visible=$true
$doc = $oie.Document
$oIE.document.IHTMLDOcument2_write([string]'<body><canvas id="mycanvas" width="350" height="350">Test</canvas></body>')
while ($oIE.busy) {
sleep -milliseconds 50
}
$canvas = $doc.getElementsByTagName("canvas")
$canvas = $doc.getElementByID("mycanvas")
$context = $canvas.getContext("2d")
$context.clearRect(0, 0, $canvas.width, $canvas.height);
for ($i = 0; $i -lt 3; $i++) {
$context.fillStyle = "#95B524";
$context.beginPath();
$context.strokeStyle = "#fff";
$context.lineWidth = 3;
$context.arc(100,100,100,0,[math]::PI*2,$true);
$context.closePath();
$context.stroke();
$context.fill();
}
Thursday, November 8, 2012
Thursday, May 31, 2012
Alternate to Jobs via a Batch and a Wait-Job to manage them.
We have a series of retrieve process that run each morning. Most of these can run Async, but I have had two problems with using Jobs to accomplish this.
- If we pass simple variables to the job we have to make a DB call to get the full record and Dot Includes to the functions we have don't seem to want to work.
- If we gather the details and populate the class to pass along we get serialization issues.
Once the process is over the batch needs to log-out to release the session. Simply waiting for the process to close doesn't seem to always cut it for us on 2003 Server. But if Start has each one running on its own, we need to know when those are done. So before we can call the ShutDown.exe logout command, we need to test that every powerShell session is closed. And we need to make sure that only the sessions in this RDC are being checked. It is helpful to mention that the server has 5 or 6 interactive users RDC in at times.
So:
001
002 003 004 005 006 007 008 009 010 |
#http://blogs.msdn.com/b/powershell/archive/2009/02/12/stopping-every-instance-of-powershell-exe-except-the-one-i-m-in.aspx
#$SessionID = Get-Process | Where-Object { $_.ID -eq $pid } | Select -expandproperty SessionID # 24 MilliSeconds $SessionID = [System.Diagnostics.Process]::GetCurrentProcess() | Select -expandproperty SessionID; # 15 MilliSeconds Do { Sleep -seconds 30 $ActivePSEXE = Get-process PowerShell | Where {$_.SessionID -eq $SessionID -and $_.ID -ne $pid} } While ($ActivePSEXE) |
------
Will asked why not use a threading approach, but these scripts load some generated code that has overlapping namespaces and they would fail to load if called from the same appdomain. (9:17a CT)
Monday, May 7, 2012
Table of an array
I had a a PSObject with to fields [bool]"Done" and [string]"Name". To signify the status of some production items.
I need these results to be human readable and not print pages of data.
I don't propose that this is the best solution, but this is what I came up with as a quick solution.
This forces each name to a 20 character column and puts for columns on the page at once.
Try number 2 breaks out the old modulus function. I asked Will if he had an answer and did a quick "Is this what you mean?" using a 1..11 array. Seeing the array as numbers modulus was the clear grouping method.
I need these results to be human readable and not print pages of data.
I don't propose that this is the best solution, but this is what I came up with as a quick solution.
This forces each name to a 20 character column and puts for columns on the page at once.
$data2 | Group {$_.Done} | %{
$_.Name $ColumnCount = 4 $Count = $_.Group.Count For ($index = 0; $index -le $Count;$index+=$ColumnCount) { For ($i=0;$i -lt $columnCount;$i++) { if ($index+$i -lt $Count) { Write-host $_.Group[$index+$i].Name.Padleft(20) -NoNewline } } Write-host } } |
Try number 2 breaks out the old modulus function. I asked Will if he had an answer and did a quick "Is this what you mean?" using a 1..11 array. Seeing the array as numbers modulus was the clear grouping method.
$data2 | Group {$_.Done} | %{
Write-host $_.Name $ColumnCount = 6 For ($index = 0; $index -lt $_.Group.Count;$index++) { Write-host $_.Group[$index].Name.Padleft(20) -NoNewline if ($index % $ColumnCount -eq 0) { Write-host } } Write-host } |
I also considered, and I may yet revise my solution, creating a PSObject with 4 members and assign them in a similar fashion. This would allow me to ConvertTo-CSV, and use the normal Format-Table.
Friday, April 20, 2012
Accessing old Assemblies
By "Accessing old Assemblies" I don't just mean that I am looking at a .NET 1.1 assembly. I mean, this is the project that I wrote when we bought a VS2003 license and tried to leave VB6 behind us "OLD".
Of specific issue, I have some assemblies that are built from source by our process and loaded in VB.NET as a script. They really do what I use PowerShell for now for other areas as they define specific logic and call into a central assembly to do anything complicated. An instance is created and the "script" is passed a class of configuration properties and told to run. The parent application then waits for completion and relays results back to an MSMQ where a VB6 application relays the results to a user.
This is an old and patchwork approach. So I want to be able to remove the VB6 app and the MSMQ and call these compiled "scripts" from Powershell. The problem is that I can't just New-Object without a namespace.... and I really have no idea what the namespace is.
Fortunately Add-Type has a PassThru argument. What comes out of this is an array of RuntimeType. Unfortunately it is an array and not a hash table. Fortunately PowerShell is built for this.
Now I don't have to find the namespace, just the name that you see when you write-output.
Of specific issue, I have some assemblies that are built from source by our process and loaded in VB.NET as a script. They really do what I use PowerShell for now for other areas as they define specific logic and call into a central assembly to do anything complicated. An instance is created and the "script" is passed a class of configuration properties and told to run. The parent application then waits for completion and relays results back to an MSMQ where a VB6 application relays the results to a user.
This is an old and patchwork approach. So I want to be able to remove the VB6 app and the MSMQ and call these compiled "scripts" from Powershell. The problem is that I can't just New-Object without a namespace.... and I really have no idea what the namespace is.
Fortunately Add-Type has a PassThru argument. What comes out of this is an array of RuntimeType. Unfortunately it is an array and not a hash table. Fortunately PowerShell is built for this.
001
002 003 |
$ScriptAssembly = add-type -Path "D:\source\MyScript.dll" -pass
$MyScript = new-object $($ScriptAssembly | Where {$_.Name -eq "MyScriptBaseClass"}) $MyScript.Run() |
Now I don't have to find the namespace, just the name that you see when you write-output.
Write-output $ScriptAssembly
|
Sunday, April 1, 2012
Excel Part 2 - Import-Excel
Continuing from http://import-powershell.blogspot.com/2012/03/excel-part-1.html
Import-Excel - I have this broken into 3 basic parts. Open/Close Excel, Populate Headers/ Member Names, and saving row data into the collection. My original implementation pulled out an array of hash tables, but to be more effective I have started using a more true "Import" convention and output an array of PSObjects. To maintain compatibility an argument will toggle between the two.
I am adding in another few helper functions.
Get-ExcelWorkSheet helps directly retrieve the Sheet Object:
Import-Row will read a Row and output a hash table.
Yields:
TAIL ARMS LEGS ANIMAL
---- ---- ---- ------
TRUE 0 4 Dog
FALSE 2 2 Human
TRUE 0 0 Snake
Import-Excel - I have this broken into 3 basic parts. Open/Close Excel, Populate Headers/ Member Names, and saving row data into the collection. My original implementation pulled out an array of hash tables, but to be more effective I have started using a more true "Import" convention and output an array of PSObjects. To maintain compatibility an argument will toggle between the two.
I am adding in another few helper functions.
Get-ExcelWorkSheet helps directly retrieve the Sheet Object:
001
002 003 004 005 006 007 008 009 010 011 012 013 014 015 016 |
function Get-ExcelWorkSheet {
Param([parameter(Mandatory=$true,ValueFromPipeline=$true)] $inputObject ,$SheetName ,[switch] $Visible ,[switch] $readonly) if ($inputObject -is [Microsoft.Office.Interop.Excel.Workbook]) { $WorkBook = $inputObject } else { $WorkBook = Get-ExcelWorkBook $inputObject -Visible:$Visible -readonly:$readonly } if (($SheetName -eq $null) -or $SheetName -eq 0) { $WorkBook.ActiveSheet } else { $WorkBook.WorkSheets.item($SheetName) } } |
Import-Row will read a Row and output a hash table.
001
002 003 004 005 006 007 008 009 010 011 012 013 014 015 |
Function Import-Row {
Param($Row,[hashtable] $Headers =@{},$ColumnStart = 1,$ColumnCount = $Row.Value2.Count) $output = @{} for ($index=$ColumnStart;$index -le $ColumnCount;$index ++) { If ($Headers.Count -eq 0) { $Key = $Index } Else { $Key = $Headers[$index] } $output.Add($Key,$row.Cells.Item(1,$index).Text) } return $output } |
I have a sample XLS like the following:
ANIMAL
|
ARMS
|
LEGS
|
TAIL
|
Dog
|
0
|
4
|
TRUE
|
Human
|
2
|
2
|
FALSE
|
Snake
|
0
|
0
|
TRUE
|
001
002 003 004 005 006 007 008 009 010 011 012 013 014 015 016 017 018 019 020 021 022 023 024 025 026 027 028 029 030 031 032 033 034 035 036 037 038 039 040 |
Function Import-Excel {
Param( [parameter(Mandatory=$true,ValueFromPipeline=$true)] $inputObject ,[Object] $SheetName ,[switch] $Visible ,[switch] $readonly ,[int] $startOnLineNumber =1 ,[switch] $closeExcel ,[switch] $asHashTable ,[hashtable] $FieldNames =@{}) #Check what the input is. if ($inputObject -is [Microsoft.Office.Interop.Excel.range]) { $range = $inputObject } elseif ($inputObject -isnot [Microsoft.Office.Interop.Excel.Worksheet]) { $WorkSheet = Get-ExcelWorkSheet $inputObject -SheetName $SheetName -Visible:$Visible -readonly:$readonly $range = $WorkSheet.UsedRange } else { $WorkSheet = $inputObject $range = $WorkSheet.UsedRange } # populate the Header if ($FieldNames.Count -eq 0) { $FieldNames = Import-Row $range.Rows.Item($startOnLineNumber++) } for ($RowIndex=$startOnLineNumber;$RowIndex -le $range.Rows.Count;$RowIndex++) { $output = Import-Row $range.Rows.Item($RowIndex) -Headers $FieldNames if ($asHashtAble) { Write-Output $output } else { New-Object PSObject -property $output } } # If we opened Excel, we should close Excel. if ($closeExcel) { $WorkSheet.Activate() | Out-Null Close-ExcelApplication $WorkSheet } } Import-Excel "$PWD\Animal.xlsx" -Visible -closeExcel |
Yields:
TAIL ARMS LEGS ANIMAL
---- ---- ---- ------
TRUE 0 4 Dog
FALSE 2 2 Human
TRUE 0 0 Snake
Saturday, March 31, 2012
Excel Part 1
The core MS Office apps have their application and inner objects exposed via COM. These COM interfaces have distributable .NET Interop assemblies available to download.
There are two basic ways to interact with Excel via the COM objects and via the interop assembly. Functionally I think the COM will allow you to accomplish the same tasks, but it will not be as easy. To load the interop you will need:
I tend to distribute the inerop DLL in my network share so I don't have to make sure that all servers and workstations have it installed. The above should take care of either loading a local assembly or looking in the GAC.
To access Excel data, you have to be aware of the hierarchy of things. At the top is the application class that contains one or more workbooks that contain one or more worksheets. Within the worksheet are ranges. Each layer can access down to some of the other layers.
Yields:
Name MemberType Definition
---- ---------- ----------
ActiveCell Property Microsoft.Office.Interop.Excel.Range ActiveCell {get;}
ActiveChart Property Microsoft.Office.Interop.Excel.Chart ActiveChart {get;}
ActiveDialog Property Microsoft.Office.Interop.Excel.DialogSheet ActiveDialog ...
ActiveEncryptionSession Property int ActiveEncryptionSession {get;}
ActiveMenuBar Property Microsoft.Office.Interop.Excel.MenuBar ActiveMenuBar {get;}
ActivePrinter Property string ActivePrinter {get;set;}
ActiveProtectedViewWindow Property Microsoft.Office.Interop.Excel.ProtectedViewWindow Activ...
ActiveSheet Property System.Object ActiveSheet {get;}
ActiveWindow Property Microsoft.Office.Interop.Excel.Window ActiveWindow {get;}
ActiveWorkbook Property Microsoft.Office.Interop.Excel.Workbook ActiveWorkbook {...
All of the classes also have a .Application property that points back to the top.
Yields:
TypeName: System.__ComObject#{000208da-0000-0000-c000-000000000046}
Name MemberType Definition
---- ---------- ----------
ActiveChart Property Chart ActiveChart () {get}
ActiveSheet Property IDispatch ActiveSheet () {get}
ActiveSlicer Property Slicer ActiveSlicer () {get}
The Interop allows you easy access to the classes and enumerations. The largest caveat is what you may expect vs what you get when you look at the COM collections. These collections are built implementing default properties that do not come across in powershell. A recorded macro may reference WorkSheets("Sheet1") but in PS you will need to say $WorkSheets.item("Sheet1"). So, what looks like it may be an array may need a call to the item property to do what you expect.
When you look at Excel you see cells, when you automate it you have ranges.
Yields:
TypeName: System.__ComObject#{000208d8-0000-0000-c000-000000000046}
Name MemberType Definition
---- ---------- ----------
Range ParameterizedProperty Range Range (Variant, Variant) {get}
Cells Property Range Cells () {get}
CircularReference Property Range CircularReference () {get}
Columns Property Range Columns () {get}
Rows Property Range Rows () {get}
UsedRange Property Range UsedRange () {get}
All of the following are the same:
If you were going to use a formula in a cell, this same convention is used for the .Range ParameterizedProperty. Cells you have to use the .Item property but you can more easily use in loops as the cell is a coordinate. UsedRange is limited to the cells that have or have had data in them as a block. So furthest X and furthest Y make up the range. If you want to know what these bounds are:
To close it all down you can do something like:
This will work fine if you only have one workbook open with changes. If you have more than one open you will need to make sure that you close or save each sheet. If you try to close and have more than the active workbook not saved, excel will prompt you to save. This is not something that you want if you expect your script to run unattended.
Another big point to consider is the garbage collection. I know it was a big concern with Office 2003 and may be unneeded in 2007 or 2010, but an extra step to clean up your variables should be used.
If you don't do this, Excel may (may) fail to close.
More later...
There are two basic ways to interact with Excel via the COM objects and via the interop assembly. Functionally I think the COM will allow you to accomplish the same tasks, but it will not be as easy. To load the interop you will need:
001
002 003 004 005 006 007 |
#Load the Excel Assembly, Locally or from GAC
try { Add-Type -ASSEMBLY "Microsoft.Office.Interop.Excel" | out-null }catch { #If the assembly can't be found this will load the most recent version in the GAC [Reflection.Assembly]::LoadWithPartialname("Microsoft.Office.Interop.Excel") | out-null } |
I tend to distribute the inerop DLL in my network share so I don't have to make sure that all servers and workstations have it installed. The above should take care of either loading a local assembly or looking in the GAC.
To access Excel data, you have to be aware of the hierarchy of things. At the top is the application class that contains one or more workbooks that contain one or more worksheets. Within the worksheet are ranges. Each layer can access down to some of the other layers.
001
002 003 004 005 006 007 008 009 |
Function Open-ExcelApplication {
Param([switch] $Visible,[switch] $HideAlerts) $app = New-Object Microsoft.Office.Interop.Excel.ApplicationClass $app.Visible = $Visible $app.DisplayAlerts = -not $HideAlerts return $app } $app = open-excelApplication -Visible $app | gm active* |
Yields:
Name MemberType Definition
---- ---------- ----------
ActiveCell Property Microsoft.Office.Interop.Excel.Range ActiveCell {get;}
ActiveChart Property Microsoft.Office.Interop.Excel.Chart ActiveChart {get;}
ActiveDialog Property Microsoft.Office.Interop.Excel.DialogSheet ActiveDialog ...
ActiveEncryptionSession Property int ActiveEncryptionSession {get;}
ActiveMenuBar Property Microsoft.Office.Interop.Excel.MenuBar ActiveMenuBar {get;}
ActivePrinter Property string ActivePrinter {get;set;}
ActiveProtectedViewWindow Property Microsoft.Office.Interop.Excel.ProtectedViewWindow Activ...
ActiveSheet Property System.Object ActiveSheet {get;}
ActiveWindow Property Microsoft.Office.Interop.Excel.Window ActiveWindow {get;}
ActiveWorkbook Property Microsoft.Office.Interop.Excel.Workbook ActiveWorkbook {...
All of the classes also have a .Application property that points back to the top.
001
002 003 004 005 006 007 008 009 010 011 012 013 |
function New-ExcelWorkBook {
Param([parameter(ValueFromPipeline=$true)] $ExcelApplication ,[switch] $Visible) process { if ($ExcelApplication -eq $null ) { $ExcelApplication = Open-ExcelApplication -Visible:$Visible } $WorkBook = $ExcelApplication.WorkBooks.Add() return $WorkBook } } $book = $app | New-ExcelWorkBook $book | gm active* |
Yields:
TypeName: System.__ComObject#{000208da-0000-0000-c000-000000000046}
Name MemberType Definition
---- ---------- ----------
ActiveChart Property Chart ActiveChart () {get}
ActiveSheet Property IDispatch ActiveSheet () {get}
ActiveSlicer Property Slicer ActiveSlicer () {get}
Alternately, you can open an existing workbook:
001
002 003 004 005 006 007 008 009 010 011 012 013 014 015 016 017 018 019 020 021 |
function Get-ExcelWorkBook {
Param([parameter(Mandatory=$true,ValueFromPipeline=$true)] $inputObject ,[switch] $Visible ,[switch] $readonly) [Microsoft.Office.Interop.Excel.ApplicationClass] $app = $null if ($inputObject -is [Microsoft.Office.Interop.Excel.ApplicationClass]) { $app = $inputObject $WorkBook = $app.ActiveWorkbook } else { $app = Open-ExcelApplication -Visible:$Visible try { if ($inputObject.Contains("\\") -or $inputObject.Contains("//")) { $WorkBook = $app.Workbooks.Open($inputObject,$true,[System.Boolean]$readonly) } else { $WorkBook = $app.Workbooks.Open((Resolve-path $inputObject),$true,[System.Boolean]$readonly) }} catch {$WorkBook = $app.Workbooks.Open((Resolve-path $inputObject),$true,[System.Boolean]$readonly)} } #todo: Add Switch to toggle Full Rebuild (this does an update data) $app.CalculateFullRebuild() return $WorkBook } |
The Interop allows you easy access to the classes and enumerations. The largest caveat is what you may expect vs what you get when you look at the COM collections. These collections are built implementing default properties that do not come across in powershell. A recorded macro may reference WorkSheets("Sheet1") but in PS you will need to say $WorkSheets.item("Sheet1"). So, what looks like it may be an array may need a call to the item property to do what you expect.
When you look at Excel you see cells, when you automate it you have ranges.
001
002 |
$Sheet = $Book.Worksheets.item("Sheet1")
$sheet | gm -MemberType *Property | where { $_.Definition -match "Range" } |
Yields:
TypeName: System.__ComObject#{000208d8-0000-0000-c000-000000000046}
Name MemberType Definition
---- ---------- ----------
Range ParameterizedProperty Range Range (Variant, Variant) {get}
Cells Property Range Cells () {get}
CircularReference Property Range CircularReference () {get}
Columns Property Range Columns () {get}
Rows Property Range Rows () {get}
UsedRange Property Range UsedRange () {get}
All of the following are the same:
001
002 003 004 005 006 007 |
$sheet.Range("A1").Text
$sheet.Range("A1:A1").Text $sheet.Range("A1","A1").Text $sheet.cells.Item(1,1).text $sheet.Columns.Item(1).Rows.Item(1).Text $sheet.Rows.Item(1).Columns.Item(1).Text $sheet.UsedRange.Range("a1").Text |
If you were going to use a formula in a cell, this same convention is used for the .Range ParameterizedProperty. Cells you have to use the .Item property but you can more easily use in loops as the cell is a coordinate. UsedRange is limited to the cells that have or have had data in them as a block. So furthest X and furthest Y make up the range. If you want to know what these bounds are:
001
002 |
$sheet.UsedRange.Columns.Count
$sheet.UsedRange.Rows.Count |
To close it all down you can do something like:
001
002 003 004 005 006 007 008 009 010 011 012 |
Function Close-ExcelApplication {
Param([parameter(Mandatory=$true,ValueFromPipeline=$true)] $inputObject) if ($inputObject -is [Microsoft.Office.Interop.Excel.ApplicationClass]) { $app = $inputObject } else { $app = $inputObject.Application Release-Ref $inputObject } $app.ActiveWorkBook.Close($false) | Out-Null $app.Quit() | Out-Null Release-Ref $app } |
This will work fine if you only have one workbook open with changes. If you have more than one open you will need to make sure that you close or save each sheet. If you try to close and have more than the active workbook not saved, excel will prompt you to save. This is not something that you want if you expect your script to run unattended.
Another big point to consider is the garbage collection. I know it was a big concern with Office 2003 and may be unneeded in 2007 or 2010, but an extra step to clean up your variables should be used.
001
002 003 004 005 |
function Release-Ref ($ref) {
([System.Runtime.InteropServices.Marshal]::ReleaseComObject([System.__ComObject]$ref) -gt 0) | Out-Null [System.GC]::Collect() | Out-Null [System.GC]::WaitForPendingFinalizers() | Out-Null } |
If you don't do this, Excel may (may) fail to close.
More later...
Wednesday, March 28, 2012
7z SFX without the temp Config File
I prefer to not use temp files unless I absolutely need to so I replaced
with:
I would like to have 7z build the zip to STDOUT but I don't think I can have it go to the 7z format. Will continue to look at it.
This was an update to http://import-powershell.blogspot.com/2012/03/powershell-7zip-sfx-installer.html
001
002 003 |
$SetupConfig | out-File "$path\$ConfigName" -enc UTF8
get-content $SFX,$ConfigName,$ZipName -Enc Byte -Read 512 | set-content $OutName -Enc Byte |
with:
001
002 003 004 005 |
$SFXBytes = [io.file]::ReadAllBytes("$PAth\$SFX")
$ConfigBytes = [text.encoding]::ascii.GetBytes($SetupConfig) $ZipBytes = [io.file]::ReadAllBytes("$PAth\$ZipName") ($SFXBytes,$ConfigBytes,$ZipBytes) | set-content $OutName -Enc Byte |
I would like to have 7z build the zip to STDOUT but I don't think I can have it go to the 7z format. Will continue to look at it.
This was an update to http://import-powershell.blogspot.com/2012/03/powershell-7zip-sfx-installer.html
PowerShell 7zip SFX Installer
- Save the below two scripts
- Download 7zsd_All_x64.sfx
- 7z the 3 files together as 7z.7z, or at a minimum zip Setup.ps1 as 7z.7z
- Run 7zInstaller.ps1
- It will build a New.EXE then run the EXE.
- This will extract the files to a temp folder, and call a powerShell prompt that will run the Setup.PS1 and leave the prompt open.
Modify the Setup.ps1, remove the -NoExit argument, and you can use the script to move the files from the temp location to anywhere you need it. When the powershell script finishes, the temp folder should be removed.
You can also specify an "InstallPath="path_to_extract"" in the SetupConfig block. If you do this you don't have to manually copy the files and 7z will not delete the files.
Take a look at http://7zsfx.info/en/parameters.html it has a good collection of what options can be used in the SetupConfig block. Of note, you can use environment variables, so you could extract directly to a user profile if you wanted to.
7zInstaller.ps1
001
002 003 004 005 006 007 008 009 010 011 012 013 014 015 016 017 018 019 020 021 022 023 024 025 026 027 028 029 030 031 032 033 034 035 036 037 038 039 040 041 |
# Script will not create the 7z file. $ZipName= "7z.7z" # Output SFX EXE $OutName= "new.exe" # This config data tells the SFX how you want to do things. # Other than the initial popup, we push all the other options into the PS1. # http://7zsfx.info/en/parameters.html $SetupConfig = @" ;!@Install@!UTF-8! Title="Title Spot" BeginPrompt="Do you want to install now?" RunProgram="powershell -NoExit -noprofile -executionpolicy unrestricted -file %%T\\Setup.PS1" ;!@InstallEnd@! "@ # get from http://7zsfx.info/en/ this was pulled from the "7zSD extra" option. # There are a selection of these that may display a console vs winforms UI, and # may also switch between 32bit and 64bit extraction. $SFX="7zsd_All_x64.sfx" #"7z.SFX" # this name can be whatever. It will overwrite each time. $ConfigName= "SetupConfig.txt" # Need to Move PWD to scripting folder (This simplifies the below, everthing should work with full paths) $path = $script:MyInvocation.MyCommand.Path cd $path $SetupConfig | out-File "$(Split-Path $path)\$ConfigName" -enc UTF8 # The first sample on Binary merges used ADD-Content. This is why I had to kill the file. # Set-Content though in the same pipeline will append all of the data so we are good. # The test-path and cleanup is just something nice to keep around. if (Test-Path $OutName) { del $OutName } get-content $SFX,$ConfigName,$ZipName -Enc Byte -Read 512 | set-content $OutName -Enc Byte #Test the installer Start-process $OutName |
Setup.ps1:
001
002 003 |
Write-Host "Installer Script"
$script:MyInvocation.MyCommand | fl ls env:7z*Folder* |
Posted an update @ http://import-powershell.blogspot.com/2012/03/7z-sfx-without-temp-config-file.html
Wednesday, March 21, 2012
Prize Drawing
We did a simple script with the attendance list piped to a Get-Random to pull winners for the SIG last week.
The problem was for multiple prizes the list didn't get smaller.
The problem was for multiple prizes the list didn't get smaller.
PS Script to HTML for Blog
I needed something other than Blogger's QUOTE for script.
Try 1:
If (-not (Test-Path "$home\Documents1\WindowsPowerShell\Highlight-Syntax 2.0.ps1"))
{
throw "You Need to get `'Highlight-Syntax 2.0.ps1`' from http://poshcode.org/1498`n`n"
}
$psISE.CurrentPowerShellTab.AddOnsMenu.SubMenus.Add("Out-Highlight",{
if ($psise.CurrentFile.Editor.SelectedText) {
$source = $psise.CurrentFile.Editor.SelectedText
} else {
$source =$psise.CurrentFile.Editor.Text
}
(& "$home\Documents\WindowsPowerShell\Highlight-Syntax 2.0.ps1" $source $true) |
Out-Clipboard
},$null) | out-null
{
throw "You Need to get `'Highlight-Syntax 2.0.ps1`' from http://poshcode.org/1498`n`n"
}
$psISE.CurrentPowerShellTab.AddOnsMenu.SubMenus.Add("Out-Highlight",{
if ($psise.CurrentFile.Editor.SelectedText) {
$source = $psise.CurrentFile.Editor.SelectedText
} else {
$source =$psise.CurrentFile.Editor.Text
}
(& "$home\Documents\WindowsPowerShell\Highlight-Syntax 2.0.ps1" $source $true) |
Out-Clipboard
},$null) | out-null
Now this worked, then I kept reading and found a Lee Holmes example that was much further along.
Try 2:
Get script from http://www.leeholmes.com/blog/2009/02/03/more-powershell-syntax-highlighting/ I had to fix some single quotes in the $tokenColours definition from copying off the site and the download wasn't clean either.
Around line 203 you find:
001
002 003 004 005 006 |
if (-not $psise.CurrentOpenedFile)
{ Write-Error 'No script is available for copying.� return } $text = $psise.CurrentOpenedFile.Editor.Text |
From the try one modify this to:
001
002 003 004 005 006 007 008 009 010 |
if (-not $psise.CurrentFile)
{ Write-Error 'No script is available for copying.� return } if ($psise.CurrentFile.Editor.SelectedText) { $text = $psise.CurrentFile.Editor.SelectedText } else { $text = $psise.CurrentFile.Editor.Text } |
...so I can pull a selection.
Now, I can copy with color to WordPad, but still not to MSWord, or the iframe that Blogger uses to compose in. In Word I get the plain text and in Blogger I get nothing.
With:
001
|
[System.Windows.Clipboard]::GetDataObject().GetFormats()
|
The Script produces:
HTML Format
UnicodeText
System.String
Rich Text Format
and copy from Blogger produces:
Text
UnicodeText
System.String
HTML Format
Locale
OEMText
001
002 |
$dataObject.SetText([string]$text, [Windows.TextDataFormat]::Text)
$dataObject.SetData([System.Windows.DataFormats]::OemText,[object]$text) |
Still no go!
Local is a byte[4]. I assume it is EN_US or something similar to show localization.
I did a copy from each method then ran:
001
|
[System.Windows.Clipboard]::GetText([System.Windows.TextDataFormat]::Html)
|
There is some variation in building the "CF_HTML" header. I think part of the problem may be formatting issues from making a copy of the script off the HTML, with converted quotes.
Reading this code and the associated blog, I understand how the HTML works much better now, but I also learned that PSCX Set-Clipboard has arguments to pupulate the HTML and RTF portions of the clipboard. So that really solves my problem. Unfortunately the solution is ignoring the problem. But I don't see a need to reinvent the wheel. The down side is that Set-Clipboard doesn't let you stack clipboard formats. So I can specify RTF, Text, or HTML. a bummer really.
Called Via an ISE Menu:
001
002 003 |
$psISE.CurrentPowerShellTab.AddOnsMenu.SubMenus.Add("Copy Color",{
(& "$home\Documents\WindowsPowerShell\Set-ClipboardScript.ps1") },$null) | out-null |
Subscribe to:
Posts (Atom)