Wednesday, June 15, 2011

PowerShell GUI Errors Referencing STA or Single Thread Apartment

So I've been working with Powershell GUIs for a bit now and run into some errors while running some of my test forms. Whenever I'd click a certain drop-down list, I get a large error message with the following text:

The following exception occurred in the DataGridView:
System.Threading.ThreadStateException: Current thread must be set to
single thread apartment (STA) mode before OLE calls can be made.
Ensure that your Main function has STAThreadAttribute marked on it.
at
System.Windows.Forms.ComboBox.set_AutoCompleteSource(AutoCo
mpleteSource value)
at
System.Windows.Forms.DataGridViewComboBoxCellJnitializeEditingCo
ntrol(1nt32 rowlndeƧ Object initialFormattedValue
DataGridViewCellStyle dataGridViewCellStyle)
at
System.Windows.Forms.DataGridViewJnitializeEditingControlValue(Dat
aGridViewCellStyle& dataGridViewCellStyle. DataGridViewCell
dataGridViewCell)

I found through a PowerShell forum that this is because PowerShell, by default, runs in multi-threaded apartment mode (MTA). For some GUI aspects, it is required to run in single-threaded aparetment mode (STA).

A quirk of working with different editors is knowing the difference and which mode loads by default. As an example, when working with PowerShell ISE, the integrated shell loads in STA mode. However, by default, PowerShell (itself) loads in MTA mode unless specified otherwise.

You can execute powershell in STA mode simply by adding a -STA switch when calling the powershell executable.

You can check which mode you're running in by running the following:

[threading.thread]::CurrentThread.GetApartmentState()

This will either return STA or MTA depending on what context powershell is being run in.

I haven't figured out how to programmatically set the apartment state inside of a script but I will repost that information if/when I find it.

A workaround for this problem is to add this snippet of code at the start of your script:

if ([threading.thread]::CurrentThread.GetApartmentState() -eq "MTA") {
   & $env:SystemRoot\system32\WindowsPowerShell\v1.0\powershell.exe -sta $MyInvocation.ScriptName
   Exit
}


Tuesday, June 14, 2011

Copy Text from Image

Optical Character Recognition (OCR) is how you can read text from image files. There are some tools out there that will allow you to do this (freeware/shareware/etc.) However, if you are in a Microsoft-heavy environment as I am and you have Microsoft Office OneNote available, you already have this function readily available. The steps are as follows:

- Take a screenshot of the image that has text that you want (error message, web image, etc.)

- Open Microsoft OneNote

- In a new tab, paste the image into the new Tab (Edit > Paste; Ctrl-v)

- Right-click on your newly pasted image and select, "Copy Text from Picture"

...that's it! Paste the text now on your clipboard wherever you want it!

Tuesday, June 7, 2011

PowerShell Create-ComplexPassword v1

NEW!:  http://thepip3r.blogspot.com/2012/02/powershell-create-complexpassword-v2.html


Description: Generates complex passwords based off of the criteria given. Able to create passwords of any length and any complexity given the following standard options:

  • length
  • lowercase
  • uppercase
  • numbers
  • special characters

Acknowledgements: Depending on the complexity given, with this being a true random password generation, a potential exists to generate a large number (thousands of iterations) of internal bad passwords before presenting one back that matches. As an example, if you specified a 30 character password where 29 had to be numbers, the possible iterations before you found a match could be in the millions or more.


Future: I'm currently working on a much more efficient version that will simply generate the random values requested and then shuffle the resulting string. The result will be much less random, but much more efficient.


Logic Overview:

  • Determine supplied options
  • Build Regular Expression based off of those options
  • Generate a random string of characters based off the $PasswordLength and compare it to the generated Regular Expression to see if it meets the remaining complexity requirements supplied.


Example Syntax:

Create-ComplexPassword -PasswordLength 30 -LowerAlphas 5 -UpperAlphas 5 -Numbers 10 -SpecialCharacters 10

Create-ComplexPassword -PasswordLength 16 -LowerAlphas 2 -UpperAlphas 2 -Numbers 2 -SpecialCharacters 2

Create-ComplexPassword -PasswordLength 16 -SpecialCharacters 16



Script:
Function Create-ComplexPassword {
 
Param (
        [parameter()] [ValidateNotNullOrEmpty()] [int] [ValidateScript({$_ -gt 0})] $PasswordLength = 8,
        [parameter()] [ValidateNotNullOrEmpty()] [int] [ValidateScript({$_ -gt 0})] $LowerAlphas = 1,
        [parameter()] [ValidateNotNullOrEmpty()] [int] [ValidateScript({$_ -gt 0})] $UpperAlphas = 1,
        [parameter()] [ValidateNotNullOrEmpty()] [int] [ValidateScript({$_ -gt 0})] $Numbers = 1,
        [parameter()] [ValidateNotNullOrEmpty()] [int] [ValidateScript({$_ -gt 0})] $SpecialCharacters = 1
    )
  
    If (($LowerAlphas + $UpperAlphas + $Numbers + $SpecialCharacters) -gt $PasswordLength) { 
        Throw "The specified sum of the number of upper, lower, numeric, and special characters cannot be greater than the desired length of the password."
    }
    
    # Build the regular expression for pattern matching based on the supplied requirements
    $match = "^"
    
    # Build match for number of required lower alphas
    $match += "(?="
    For ($i=1; $i -le $LowerAlphas; $i++) {
        $match += ".*[a-z]"
    }
    $match += ")"
    
    # Build match for number of required upper alphas
    $match += "(?="
    For ($i=1; $i -le $UpperAlphas; $i++) {
        $match += ".*[A-Z]"
    }
    $match += ")"
    
    # Build match for number of required numbers
    $match += "(?="
    For ($i=1; $i -le $Numbers; $i++) {
        $match += ".*[0-9]"
    }
    $match += ")"
    
    
    # Build match for number of required special characters
    $match += "(?="
    For ($i=1; $i -le $SpecialCharacters; $i++) {
        $match += ".*[^a-zA-Z0-9]"
    }
    $match += ")"
    
    # Build the ending match of the statement to include anything else
    $match += ".*$"

    # Creates random object with seed to guarantee uniqueness
 $objRnd = New-Object System.Random((Get-Date).Millisecond); 

    $iterations = 0

    Do {
        $strPassword = $Null
     For ($i = 0; $i -lt $PasswordLength; $i++) {
       $NextChar = $objRnd.next(33,126)
                $strPassword += ([char]$nextChar)
     }
        $iterations++
    } Until (($strPassword -match $match) -eq $True)

 $strPassword
}



Thursday, June 2, 2011

PowerShell GUIs: Active Directory TreeView

So this is a modified bit of code I wrote to build a tree-view of the local Active Directory OU structure. I'm using this snippet in another piece of code but it *should* work just fine in any domain. Please let me know of any modifications or fixes necessary.



function Add-Node { 
        param ( 
            $selectedNode, 
            $name
        ) 
        $newNode = new-object System.Windows.Forms.TreeNode  
        $newNode.Name = $name 
        $newNode.Text = $name 
        $selectedNode.Nodes.Add($newNode) | Out-Null 
        return $newNode 
} 

function Get-NextLevel {
    param (
        $selectedNode,
        $dn
   )
   
    $OUs = Get-ADObject -Filter 'ObjectClass -eq "organizationalUnit" -or ObjectClass -eq "container"' -SearchScope OneLevel -SearchBase $dn

    If ($OUs -eq $null) {
        $node = Add-Node $selectedNode $dn
    } Else {
        $node = Add-Node $selectedNode $dn
        
        $OUs | % {
            Get-NextLevel $node $_.distinguishedName
        }
    }
}
 
function Build-TreeView { 
    if ($treeNodes)  
    {  
          $treeview1.Nodes.remove($treeNodes) 
        $form1.Refresh() 
    } 
    
    $treeNodes = New-Object System.Windows.Forms.TreeNode 
    $treeNodes.text = "Active Directory Hierarchy" 
    $treeNodes.Name = "Active Directory Hierarchy" 
    $treeNodes.Tag = "root" 
    $treeView1.Nodes.Add($treeNodes) | Out-Null 
     
    $treeView1.add_AfterSelect({ 
        $textbox1.Text = $this.SelectedNode.Name
    }) 
     
    #Generate Module nodes 
    $OUs = Get-NextLevel $treeNodes $strDomainDN
    
    $treeNodes.Expand() 
} 
 
#Generated Form Function 
function GenerateForm { 
 
    #region Import the Assemblies 
    Import-Module ActiveDirectory
    [reflection.assembly]::loadwithpartialname("System.Windows.Forms") | Out-Null 
    [reflection.assembly]::loadwithpartialname("System.Drawing") | Out-Null 
    $objIPProperties = [System.Net.NetworkInformation.IPGlobalProperties]::GetIPGlobalProperties()
    $strDNSDomain = $objIPProperties.DomainName.toLower()
    $strDomainDN = $strDNSDomain.toString().split('.'); foreach ($strVal in $strDomainDN) {$strTemp += "dc=$strVal,"}; $strDomainDN = $strTemp.TrimEnd(",").toLower()
    #endregion 
     
    #region Generated Form Objects 
    $form1 = New-Object System.Windows.Forms.Form 
    $treeView1 = New-Object System.Windows.Forms.TreeView 
    $label1 = New-Object System.Windows.Forms.Label
    $textbox1 = New-Object System.Windows.Forms.TextBox
    $InitialFormWindowState = New-Object System.Windows.Forms.FormWindowState 
    #endregion Generated Form Objects 
     
    #---------------------------------------------- 
    #Generated Event Script Blocks 
    #---------------------------------------------- 
    $button1_OnClick=  
    { 
    $form1.Close() 
     
    } 
     
    $OnLoadForm_StateCorrection= 
    {Build-TreeView 
    } 
     
    #---------------------------------------------- 
    #region Generated Form Code 
    $form1.Text = "Form" 
    $form1.Name = "form1" 
    $form1.DataBindings.DefaultDataSourceUpdateMode = 0 
    $form1.ClientSize = New-Object System.Drawing.Size(400,500) 
     
    $treeView1.Size = New-Object System.Drawing.Size(350,375)
    $treeView1.Name = "treeView1" 
    $treeView1.Location = New-Object System.Drawing.Size(15,15)
    $treeView1.DataBindings.DefaultDataSourceUpdateMode = 0 
    $treeView1.TabIndex = 0 
    $form1.Controls.Add($treeView1)
    
    $label1.Name = "label1" 
    $label1.Location = New-Object System.Drawing.Size(15,400)
    $label1.Size = New-Object System.Drawing.Size(350,20)
    $label1.Text = "Selected Value:"
    $form1.Controls.Add($label1) 
    
    $textbox1.Name = "textbox1" 
    $textbox1.Location = New-Object System.Drawing.Size(15,425)
    $textbox1.Size = New-Object System.Drawing.Size(350,20)
    $textbox1.Text = ""
    $form1.Controls.Add($textbox1) 
     
     
    #endregion Generated Form Code 
     
    #Save the initial state of the form 
    $InitialFormWindowState = $form1.WindowState 
    #Init the OnLoad event to correct the initial state of the form 
    $form1.add_Load($OnLoadForm_StateCorrection) 
    #Show the Form 
    $form1.ShowDialog()| Out-Null 
     
} #End Function 
 
#Call the Function 
GenerateForm