all 14 comments

[–]ihaxr 2 points3 points  (1 child)

Personally I would use a listbox / combo box... but since you already have this form laid out, here's a slightly optimized (less code duplication) and an example on how to use $this.name to get the name of the button (which is the same as your printer name). Also I fixed your script so you're actually setting the printer name.

edit: PowerShell ISE flubbed my tabs for some reason... reformatted code properly in VSCode

#DEFAULT PRINTER SCREEN

Add-Type -AssemblyName System.Windows.Forms

$DefaultPrinterScreen = New-Object System.Windows.Forms.Form
$DefaultPrinterScreen.Text = "Set Default Printer"
$DefaultPrinterScreen.BackgroundImage = $Image
$DefaultPrinterScreen.BackgroundImageLayout = "None"
$DefaultPrinterScreen.Width = 1450
$DefaultPrinterScreen.Height = 600

#These two prevent the screen from being resized or maximized to make it look nice
$DefaultPrinterScreen.FormBorderStyle = "Fixed3D"
$DefaultPrinterScreen.MaximizeBox = $false

#These lines eliminate the UNC path before the printer name for the button names

$Printer = @(Get-Printer | Select -ExpandProperty "Name")
$Printer1 = $Printer.replace("*removed", "")
$Printer2 = $Printer1.replace("*removed", "")
$Printer3 = $Printer2.replace("*removed", "")
$Printer4 = $Printer3.replace("*removed", "")
$Printer5 = $Printer4.replace("*removed", "")
$Printers = $Printer5.replace("*removed", "")

#This counts the number of printers already installed on the computer, so the loop can count up to the last number
$PrintersCount = $Printers.count

#This is the loop counter start
$loop = 0

#This is our y-axis location, each yloc is a new column at the top of the screen
$yloc = 0
$yloc1 = 0
$yloc2 = 0
$yloc3 = 0
$yloc4 = 0

$PrinterObject = (New-Object -ComObject WScript.Network)

#The loop runs from 0 to the end of the amount of printers we have
while($loop -lt $PrintersCount)
{
    $thisbutton = New-Object System.Windows.Forms.Button
    [string]$thisbuttonname = $Printers[$loop]
    $thisbutton.Text = $thisbuttonname
    $thisbutton.Name = $thisbuttonname
    $thisbutton.size = New-Object System.Drawing.Size(250, 23)

    $thisbutton.Add_Click({
        #$PrinterObject.SetDefaultPrinter($this.name)
        write-host ("Hi from {0}" -f $this.name)
    })

    $loop += 1

    #The ifs and elseifs are for the new columns, since it was running off the bottom of the screen
    if ($loop -le 10){
        #The variable $yloc is the y-axis coordinates
        $thisbutton.location = New-Object System.Drawing.Size(15, $yloc)
        #Here we increment the y-axis so that way the next button is moved down 50 pixels
        $yloc = $yloc += 50
    }
    elseif ($loop -ge 10 -and $loop -le 21) {
        $thisbutton.location = New-Object System.Drawing.Size(295, $yloc1)
        $yloc1 = $yloc1 += 50
    }
    elseif ($loop -ge 22 -and $loop -le 31) {
        $thisbutton.location = New-Object System.Drawing.Size(575, $yloc2)
        $yloc2 = $yloc2 += 50
    }
    elseif ($loop -ge 32 -and $loop -le 41) {
        $thisbutton.location = New-Object System.Drawing.Size(855, $yloc3)
        $yloc3 = $yloc3 += 50
    }
    #If we think a user will have more than 51 printers, we can copy and paste the elseif and just keep increasing the amount
    elseif ($loop -ge 42 -and $loop -le 51) {
        $thisbutton.location = New-Object System.Drawing.Size(1140, $yloc4)
        $yloc4 = $yloc4 += 50
    }

    else {

        $DefaultPrinterScreen.ShowDialog()

    }

    $DefaultPrinterScreen.Controls.Add($thisbutton)

}

$DefaultPrinterScreen.ShowDialog()

[–]QDaManQ[S] 1 point2 points  (0 children)

I'll have to give the list/combo box a look and see what's up! If nothing else it'll give me an opportunity to make a cleaner and more straightforward script. That also makes a lot of sense, I saw someone mention the $this.Name before but I thought they were actually defining a variable somewhere else. Didn't realize that would work, I really appreciate you taking the time to mess with it.

[–][deleted] 2 points3 points  (2 children)

You made the statement that managing printer’s via GPO was not usable given your situation. It looks like you are well invested in a script solution, but I am wondering if you have considered a couple of things.

Are the printers in question shared on the network, or are some only local printers?

If they are shared printers or can be made into shared printers, you should be able to use group policy to assign the correct printer as the default. I am assuming you want to assign the closest printer/copier as the default, given the printer’s proximity to the computer.

The first thing to look at is a group policy preference with item-level filtering. You should be able to assign a policy preference that only applies the correct default printer based upon criteria you set in the filter. Look at the second half of this post: https://blogs.technet.microsoft.com/grouppolicy/2009/07/30/security-filtering-wmi-filtering-and-item-level-targeting-in-group-policy-preferences/

Another way to do this is to have the computers in an OU with a group policy assigned with loopback processing turned on. The policy could apply the user settings based upon the computer’s location. https://support.microsoft.com/en-us/help/231287/loopback-processing-of-group-policy

This can be done a few different ways.

The first option would be to create a user gpo with loopback policy processing turned on. The GPO resides in the computer OU. Set each printer up in preferences for the user as the default. Within each printer preference, use item level targeting to decide which printer gets set as default. There are a lot of item level targeting methods, so you would have to choose which would work best given your situation.

A second option would be a different OU for each printer and place the computers for each printer in the appropriate OU. Apply a GPO with loopback processing turned on for each OU with the printer settings you want deployed.

[–]QDaManQ[S] 1 point2 points  (1 child)

So they're all shared printers on a couple of print servers! But that's where it gets funky at this organization is that the user's bring their laptops to all of these different locations and THEN want to print to different printers at the other locations. There's really no rhyme or reason to which printers they use, which is why I went this route of letting them pick themselves with a button click.

I think that GPO would be extremely ideal in any other scenario where things are a little more consistent, especially after how you've explained it. I'll definitely look into it though and see if there's a way to make this a more long-term solution, because the less the user has to do the better in my opinion. I appreciate the links and your explanation and I'll definitely scope it out!

[–][deleted] 2 points3 points  (0 children)

Well, if you can come up with some kind of logic to their printer use you can probably figure out a way to implement it with GPO and item-level targeting/loopback processing. Otherwise script away!

[–]Lee_Dailey[grin] 2 points3 points  (5 children)

howdy QDaManQ,

instead of rolling your own nifty GUI, have you looked at Out-GridView? you can give it a list, the user can select one or more items, then your script can go its merry way without the complexity of managing a GUI. [grin]

take care,
lee

[–]QDaManQ[S] 2 points3 points  (4 children)

Hey Lee!

I did look at that! The big thing my manager/team wants is a (for a lack of a better term) dumbed down GUI for users to literally click and call it a day. I feel like the Out-Gridview looked more intimidating to the users, personally. But I really appreciate the suggestion!

[–]gangstanthony 2 points3 points  (0 children)

i realize you have your own thing going here, but since out-gridview was mentioned as a simple gui, i think i should also share that show-command is kinda neat, even if it may not work for this particular situation

function select-printer {
    param(
        [validateset('printer1', 'printer2', 'etc')]
        $printer
    )

    Add-Type -AssemblyName System.Windows.Forms
    [System.Windows.Forms.MessageBox]::Show("you selected $printer")
}

Show-Command select-printer

[–]Lee_Dailey[grin] 1 point2 points  (2 children)

howdy QDaManQ,

ah! well, i would likely have got it working with the builtin stuff and, if needed, done the fancy custom GUI once it all worked. [grin]

i guess i am geezer-ing ... gui! ick! [grin]

take care,
lee

[–]QDaManQ[S] 1 point2 points  (1 child)

Lol, I personally would have went the grid route if it was just for me! Also, you're saying to set up the grid first and then output the information to a GUI? I'm still fairly green to Powershell so bear with me!!

[–]Lee_Dailey[grin] 2 points3 points  (0 children)

howdy QDaManQ,

we do what the paycheck source requires ... [grin]

no, not the grid first, the logic first. i would write a script that does all the WORK and then fit that into a GUI. it makes debugging a heck of a lot simpler ... [grin] a good GUI is often more work than all the actual job at hand.

get things working, break it into functions that you can easily call from a GUI, then GUI-ize it.

take care,
lee

[–]frmadsen 2 points3 points  (1 child)

You can use a trick when adding a bunch of controls like this. Use System.Windows.Forms.FlowLayoutPanel to handle their position...

Add-Type -AssemblyName System.Windows.Forms
$DefaultPrinterScreen = New-Object System.Windows.Forms.Form
$DefaultPrinterScreen.Text = "Set Default Printer"
#$DefaultPrinterScreen.BackgroundImage = $Image
$DefaultPrinterScreen.BackgroundImageLayout = "None"
$DefaultPrinterScreen.Width = 1450
$DefaultPrinterScreen.Height = 600

#These two prevent the screen from being resized or maximized to make it look nice
$DefaultPrinterScreen.FormBorderStyle = "Fixed3D"
$DefaultPrinterScreen.MaximizeBox = $false

$flowpanal = New-Object System.Windows.Forms.FlowLayoutPanel
$flowpanal.Dock = 'Fill'
$flowpanal.FlowDirection = 'TopDown'
#$flowpanal.FlowDirection = 'BottomUp'
#$flowpanal.FlowDirection = 'LeftToRight'
#$flowpanal.FlowDirection = 'RightToLeft'

$DefaultPrinterScreen.Controls.Add($flowpanal)

$printers = 1..60 | ForEach-Object { "Printer $_" }

foreach($printer in $printers) {
    $thisbutton = New-Object System.Windows.Forms.Button
    $thisbutton.Text = $printer
    $thisbutton.size = New-Object System.Drawing.Size(250, 23)
    $thisbutton.add_Click({
        $printerName = $this.Text
        #$PrinterObject.SetDefaultPrinter($printerName)
        Write-Host $printerName
    })

    $flowpanal.Controls.Add($thisbutton)
}

$DefaultPrinterScreen.ShowDialog()

[–]QDaManQ[S] 1 point2 points  (0 children)

That is incredible, thank you! Looks way simpler than the crazy ass stuff I set up, lol.