Get primalforms dialog box results in powershell - powershell

From a PS script, I am calling a primalforms dialog box err.ps1, but I don't know how to return to the main script which button the users clicked on err.ps1 (Yes/No).
if (.{.\errDestination.ps1})
Doesn't seem to return the value being true as yes or false as no...
Any idea where should I set the return result?

You have to edit the generated code a bit to return the DialogResult. I put this at the bottom:
#endregion Generated Form Code
# ....
#Show the Form
$form1.ShowDialog()| Out-Null
return $form1.DialogResult
} #End Function
#Call the Function
return GenerateForm
#endregion
To evaluate it form the calling script:
$result = & .\errDestination.ps1
if ($result -eq "Yes") {
# Yes
} else {
# No
}

Related

Intune powershell script

I cant for the life of me figure out how to save the current PowerShell script to a specific location on a client so I can create a scheduled task to execute it on log on. The script is being run from intune. So after it runs I need to save it to Program data and be able to call it.
something along the lines of copy "`$PSCommandPath:" to c:\programdata\script
if ($PSCommandPath -eq $null) { function GetPSCommandPath() { return $MyInvocation.PSCommandPath; } $PSCommandPath = GetPSCommandPath; }
function PSCommandPath() { return $PSCommandPath; }
function ScriptName() { return $MyInvocation.ScriptName; }
function MyCommandName() { return $MyInvocation.MyCommand.Name; }
function MyCommandDefinition() {
# Begin of MyCommandDefinition()
# Note: ouput of this script shows the contents of this function, not the
execution result
return $MyInvocation.MyCommand.Definition;
# End of MyCommandDefinition()
}
function MyInvocationPSCommandPath() { return
$MyInvocation.PSCommandPath; }
Write-Host "`$PSCommandPath:";
Write-Host " * Direct: $PSCommandPath";
Write-Host " * Function: $(PSCommandPath)";
Thanks in advance.

[Class]::New() causing blank line on the console

I am working on implementing a singleton class to store some regularly accessed status information for my script, including hacking around the issue of $myInvocation only being populated in the main script. All working as planned with this.
class pxStatus {
static [pxStatus] $singleton = $null
[string]$Context = 'machine'
[string]$Path = $null
[datetime]$StartTime = (Get-Date)
pxStatus ([string]$path) {
if ([pxStatus]::singleton -eq $null) {
$this.Path = $path
[pxStatus]::singleton = $this
} else {
Throw "Singleton already initialized"
}
}
static [pxStatus] Get() {
if ([pxStatus]::singleton -eq $null) {
Throw "Singleton not yet initialized"
} else {
return [pxStatus]::singleton
}
}
}
CLS
[void]([pxStatus]::New((Split-Path ($myInvocation.myCommand.path) -parent)))
([pxStatus]::Get()).StartTime
([pxStatus]::Get()).Context
([pxStatus]::Get()).Path
With one exception. Even with that [void] on the [pxStatus]::New() line, I am getting a blank line in the console. Even $null = ([pxStatus]::New((Split-Path ($myInvocation.myCommand.path) -parent))) is echoing a blank line to the console. And for the life of me I can't see what is causing it.
It's not new that causes a blank line but ([pxStatus]::Get()).StartTime.
To fix the issue, you may output it as string, i.e. not formatted, e.g. ([pxStatus]::Get()).StartTime.ToString()
You problem has already been diagnosed, but I wanted to take a second to show how to actually implement a singleton-like type in PowerShell (see inline comments):
class pxStatus {
# hide backing field from user
hidden static [pxStatus] $singleton = $null
[string]$Context = 'machine'
[string]$Path = $null
[datetime]$StartTime = (Get-Date)
# hide instance constructor, no one should call this directly
hidden pxStatus ([string]$path) {
# Only allow to run if singleton instance doesn't exist already
if ($null -eq [pxStatus]::singleton) {
$this.Path = $path
} else {
Throw "Singleton already initialized - use [pxStatus]::Get()"
}
}
# Use a static constructor to initialize singleton
# guaranteed to only run once, before [pxStatus]::Get() or [pxStatus]::singleton
static pxStatus () {
# grab the path from context, don't rely on user input
if(-not $PSScriptRoot){
throw "[pxStatus] can only be used in scripts!"
}
# this will only succeed once anyway
[pxStatus]::singleton = [pxStatus]::new($PSScriptRoot)
}
static [pxStatus] Get() {
# No need to (double-)check ::singleton, static ctor will have run already
return [pxStatus]::singleton
}
}
[pxStatus]::Get().StartTime

Checking what closed your Windows Form in Powershell

Was wondering if there is a way to check what event closed the window, pretty much either clicking the red x in the top corner or if $form.Close() was called?
Each will automatically initiate the $form.Add_Closing({}) if I have it in my script, but I wanted to know which way of closing the window did this.
The FormClosing event argument object's .CloseReason property doesn't allow you to distinguish between the .Close() method having been called on the form and the user closing the form via the title bar / window system menu / pressing Alt+F4 - all these cases equally result in the .CloseReason property reflecting enumeration value UserClosing.
However, you can adapt the technique from Reza Aghaei's helpful C# answer on the subject, by inspecting the call stack for a call to a .Close() method:
using assembly System.Windows.Forms
using namespace System.Windows.Forms
using namespace System.Drawing
# Create a sample form.
$form = [Form] #{
ClientSize = [Point]::new(400,100)
Text = 'Closing Demo'
}
# Create a button and add it to the form.
$form.Controls.AddRange(#(
($btnClose = [Button] #{
Text = 'Close'
Location = [Point]::new(160, 60)
})
))
# Make the button call $form.Close() when clicked.
$btnClose.add_Click({
$form.Close()
})
# The event handler called when the form is closing.
$form.add_Closing({
# Look for a call to a `.Close()` method on the call stack.
if ([System.Diagnostics.StackTrace]::new().GetFrames().GetMethod().Name -ccontains 'Close') {
Write-Host 'Closed with .Close() method.'
} else {
Write-Host 'Closed via title bar / Alt+F4.'
}
})
$null = $form.ShowDialog() # Show the form modally.
$form.Dispose() # Dispose of the form.
If you run this code and try various methods of closing the form, a message indicating the method used should print (.Close() call vs. title bar / Alt+F4).
Note:
Closing the form via buttons assigned to the form's .CancelButton and .SubmitButton properties that don't have explicit $form.Close() calls still causes .Close() to be called behind the scenes.
The code requires PowerShell v5+, but it can be adapted to earlier versions.
Checking call stack works fine and you can rely on it.
Just for the sake of completeness, specially for cases that you find a C# example and you want to use it in PowerShell in an easy way, I'll share an example showing how you can handle WM_SYSCOMMAND as shown in my linked post, in PowerShell.
using assembly System.Windows.Forms
using namespace System.Windows.Forms
using namespace System.Drawing
# Create the C# derived Form
$assemblies = "System.Windows.Forms", "System.Drawing"
$code = #'
using System;
using System.Windows.Forms;
public class MyForm:Form
{
public bool ClosedByXButtonOrAltF4 { get; private set;}
public const int SC_CLOSE = 0xF060;
public const int WM_SYSCOMMAND = 0x0112;
protected override void WndProc(ref Message msg)
{
if (msg.Msg == WM_SYSCOMMAND && msg.WParam.ToInt32() == SC_CLOSE)
ClosedByXButtonOrAltF4 = true;
base.WndProc(ref msg);
}
protected override void OnShown(EventArgs e)
{
ClosedByXButtonOrAltF4 = false;
}
}
'#
Add-Type -ReferencedAssemblies $assemblies -TypeDefinition $code -Language CSharp
# Create an instance of MyForm.
$form = [MyForm] #{
ClientSize = [Point]::new(400,100)
Text = "Closing Demo"
}
# Create a button and add it to the form.
$form.Controls.AddRange(#(
($btnClose = [Button] #{
Text = "Close"
Location = New-Object System.Drawing.Point 160, 60
})
))
# Make the button call $form.Close() when clicked.
$btnClose.add_Click({
$form.Close()
})
# The event handler called when the form is closing.
$form.add_Closing({
if ($form.ClosedByXButtonOrAltF4) {
Write-Host 'Closed via title bar / Alt+F4.'
} else {
Write-Host 'Closed with .Close() method.'
}
})
$null = $form.ShowDialog()
$form.Dispose()

Class based streamReader & xmlReader locks files, function doesn't

I am refactoring some function based XML reader code to class methods, and seeing some issues. With the function, I can run a test and verify the XML loaded right, then change the XML and test for error conditions. But this class based approach fails due to "the file is open in another program", forcing me to close the console before I can revise the XML.
Initially I was using the path directly in the xmlReader. So I moved to a StreamReader input to the xmlReader. And I even played with creating an all new xmlDocument and importing the root node of the loaded XML into that new xmlDocument. None works.
I suspect the reason the function based version works is because the xmlReader variable is local scope, so it goes out of scope when the function completes. But I'm grasping at straws there. I also read that Garbage Collection could be an issue, so I added [system.gc]::Collect() right after the Dispose and still no change.
class ImportXML {
# Properties
[int]$status = 0
[xml.xmlDocument]$xml = ([xml.xmlDocument]::New())
[collections.arrayList]$message = ([collections.arrayList]::New())
# Methods
[xml.xmlDocument] ImportFile([string]$path) {
$importError = $false
$importFile = ([xml.xmlDocument]::New())
$xmlReaderSettings = [xml.xmlReaderSettings]::New()
$xmlReaderSettings.ignoreComments = $true
$xmlReaderSettings.closeInput = $true
$xmlReaderSettings.prohibitDtd = $false
try {
$streamReader = [io.streamReader]::New($path)
$xmlreader = [xml.xmlreader]::Create($streamReader, $xmlReaderSettings)
[void]$importFile.Load($xmlreader)
$xmlreader.Dispose
$streamReader.Dispose
} catch {
$exceptionName = $_.exception.GetType().name
$exceptionMessage = $_.exception.message
switch ($exceptionName) {
Default {
[void]$this.message.Add("E_$($exceptionName): $exceptionMessage")
$importError = $true
}
}
}
if ($importError) {
$importFile = $null
}
return $importFile
}
}
class SettingsXML : ImportXML {
# Constructor
SettingsXML([string]$path){
if ($this.xml = $this.ImportFile($path)) {
Write-Host "$path!"
} else {
Write-Host "$($this.message)"
}
}
}
$settingsPath = '\\Mac\iCloud Drive\Px Tools\Dev 4.0\Settings.xml'
$settings = [SettingsXML]::New($settingsPath)
EDIT:
I also tried a FileStream rather than a StreamReader, with FileShare of ReadWrite, like so
$fileMode = [System.IO.FileMode]::Open
$fileAccess = [System.IO.FileAccess]::Read
$fileShare = [System.IO.FileShare]::ReadWrite
$fileStream = New-Object -TypeName System.IO.FileStream $path, $fileMode, $fileAccess, $fileShare
Still no luck.
I think you're on the right lines with Dispose, but you're not actually invoking the method - you're just getting a reference to it and then not doing anything with it...
Compare:
PS> $streamReader = [io.streamReader]::New(".\test.xml");
PS> $streamReader.Dispose
OverloadDefinitions
-------------------
void Dispose()
void IDisposable.Dispose()
PS> _
with
PS> $streamReader = [io.streamReader]::New(".\test.xml");
PS> $streamReader.Dispose()
PS> _
You need to add some () after the method name so your code becomes:
$xmlreader.Dispose()
$streamReader.Dispose()
And then it should release the file lock ok.

Powershell Error Validation

Function Button_Click()
{
Param([Parameter(Mandatory=$True)]
$telephone,
$calledtoSee,
$wantstoSeeyou,
$pleaseCall,
$willcallAgain,
$returningYourCall,
$ToSelection,
$from,
$telephoneNumber,
$message)
[boolean]$okayContinue=$true
[string]$radioSelectedText
[string]$comboBoxSectionText
[string]$persontoEmail
$callType
$messageCount
$comboBoxSectionText = $ToSelection.GetItemText($ToSelection.SelectedItem)
if($okayContinue){
if($comboBoxSectionText -eq "Please Select Contact"){
[System.Windows.Forms.MessageBox]::Show("Please Select Recipient")
$okayContinue=$false
}
}
if($okayContinue){
if([string]::IsNullOrWhiteSpace($from.Text)){
[System.Windows.Forms.MessageBox]::Show("Please Enter Who The Message is From")
$from.focus()
$okayContinue=$false
}
}
if($okayContinue){
if([string]::IsNullOrWhiteSpace($telephoneNumber.Text)){
[System.Windows.Forms.MessageBox]::Show("Please Enter Telephone Number")
$telephoneNumber.focus()
$okayContinue=$false
}
}
#######################################################################################################################################################
if($okayContinue){
if($telephone.Checked){
$callType = $telephone.Text
}
elseif($calledtoSee.Checked){
$callType = $calledtoSee.Text
}
elseif($wantstoSeeyou.Checked){
$callType = $wantstoSeeyou.Text
}
elseif($pleaseCall.Checked){
$callType= $pleaseCall.Text
}
elseif($willcallAgain.Checked){
$callType = $willcallAgain.Text
}
elseif($returningYourCall.Checked){
$callType = $returningYourCall.Text
}
else{
[System.Windows.Forms.MessageBox]::Show("Please Select Call Type")
$okayContinue=$false
}
}
if($okayContinue){
if([string]::IsNullOrWhiteSpace($message.Text)){
[System.Windows.Forms.MessageBox]::Show("Please Enter Message")
$okayContinue=$false
}
}
if($okayContinue){
$buildPerson=$comboBoxSectionText.Split(',')
$personObject = [pscustomobject]#{
FirstName = $buildPerson[0]
LastName = $buildPerson[1]
Email = $buildPerson[2]
}
$messageObject = [pscustomobject]#{
Message = $message.Text
MessageFor = $personObject
From = $from.Text
CallType = $callType
Telephone = $telephoneNumber.Text
}
}
I've got a form with 6 radio buttons, and 2 text boxes, and a combobox. Now, in terms of error validation, I decided to use a boolean value and check to see that the textboxes are properly filled and that a recipient has been selected. After everything has been filled in, then an object is created.
Am I on the right track when it comes to error validation? Could I be handling it better?
If you want full programmatic control over validation, or need to perform complex validation checks, you should use the validation events built into most Windows Forms controls. Each control that accepts free-form user input has a Validating event that will occur whenever the control requires data validation. In the Validating event-handling method, you can validate user input in several ways. Have a look to Event-Driven Validation. And More explanations here : Extending Windows Forms with a Custom Validation.