Create Powershell GUI with Visual Studio - visual-studio

I am using Visual Studio to create a GUI environment for all my PowerShell scripts together to create a common platform . I have one problem with the text box . I want to have a text box so all the results appear there . Right now only the last command is appearing there .
Any suggestions ?
<TextBox x:Name="Information" HorizontalAlignment="Left" Margin="10,116,0,0" TextWrapping="Wrap" VerticalAlignment="Top" Grid.ColumnSpan="3" Height="255" Width="678"/>
and I want at this texbox to appear the following messages
$WPFMapping.Add_Click({
{
$WPFInformation.Text = " We Will try to map the drives"}
if (((New-Object System.IO.DriveInfo("N:")).DriveType -ne
"NoRootDirectory"))
{
$WPFInformation.Text = " N drive is already mounted and accessible"}
else {
net use /persistent:yes N: "this drive"
Write-Host "mounting"}
if (((New-Object System.IO.DriveInfo("V:")).DriveType -ne
"NoRootDirectory"))
{
$WPFInformation.Text = " V drive is already mounted and accessible"}
else {
$WPFInformation.Text = " V drive mapping in progress"}
net use /persistent:yes V: "this drive"
$WPFInformation.Text = " V drive mapping has been completed"
})

By setting the text in $WPFInformation.Text for the second drive mapping (V:), you overwrite the text you have set in there earlier for the N: mapping.
To append extra lines to the textbox, use
$WPFInformation.Text += [Environment]::NewLine
$WPFInformation.Text += " V drive is already mounted and accessible"
etc. for every next line you want to add.
You can also use AppendText() to add the new lines in there:
$WPFInformation.AppendText("`r`n V drive is already mounted and accessible")
Where `r`n represents the CRLF newline.
p.s. Of course, make sure the textbox's MultiLine property is set to $WPFInformation.Multiline = $true

Related

print an image from command line and await print job completion on Windows

I needed to write a solution to write data on and then print RFID labels en-masse, each generated as .png images from a template python script and data taken from a database or excel file.
To print the program simply calls the relative system utility (CUPS on unix systems) using subprocess.check_call(print_cmd) passing the image file (saved on a ram-mounted file system for minimal disk usage)
Now, it also needs to run on Windows systems, but there is not really a decent system utility for that, and solutions under a similar question command line tool for print picture? don't account for print-job completion or if the job results in an error, the margins are all screwed and the image is always rotated 90 degrees for some reason.
How can I sanely print an image using a command or a script in Windows and wait for it to complete successfully or return an error if the job results in an error?
Possibly with no dependencies
If you can install dependencies, there are many programs that offer a solution out-of-the-box.
The only sane way i could find to solve this issue with no dependencies is by creating a powershell script to account for this
[CmdletBinding()]
param (
[string] $file = $(throw "parameter is mandatory"),
[string] $printer = "EXACT PRINTER NAME HERE"
)
$ERR = "UserIntervention|Error|Jammed"
$status = (Get-Printer -Name $printer).PrinterStatus.ToString()
if ($status -match $ERR){ exit 1 }
# https://stackoverflow.com/a/20402656/17350905
# only sends the print job to the printer
rundll32 C:\Windows\System32\shimgvw.dll,ImageView_PrintTo $file $printer
# wait until printer is in printing status
do {
$status = (Get-Printer -Name $printer).PrinterStatus.ToString()
if ($status -match $ERR){ exit 1 }
Start-Sleep -Milliseconds 100
} until ( $status -eq "Printing" )
# wait until printing is done
do {
$status = (Get-Printer -Name $printer).PrinterStatus.ToString()
if ($status -match $ERR){ exit 1 }
Start-Sleep -Milliseconds 100
} until ( $status -eq "Normal" )
I would then need to slightly modify the print subprocess call to
powershell -File "path\to\print.ps1" "C:\absolute\path\to\file.png"
Then there are a couple of necessary setup steps:
(discaimer, I don't use windows in english so i don't know how the english thigs are supposed to be called. i will use cursive for those)
create an example image, right click and then select Print
from the print dialog that opens then set up all the default options you want, like orientation, margins, paper type, etc etc for the specific printer you're gonna use.
Go to printer settings, under tools then edit Printer Status Monitoring
edit monitoring frequency to "only during print jobs". it should be disabled by default
in the next tab, modify polling frequency to the minimum available, 100ms during print jobs (you can use a lower one for the while not printing option
Assuming the following:
only your program is running this script
theres always only 1 printing job at a time for a given printer
the printer drivers were not written by a monkey and they actually report the current, correct printer status
This little hack will allow to print an image from a command and await job completion, with error management; and uses only windows preinstalled software
Further optimization could be done by keeping powershell subprocess active and only passing it scripts in the & "path\to\print.ps1" "C:\absolute\path\to\file.png" format, waiting for standard output to report an OK or a KO; but only if mass printing is required.
Having had to work on this again, just wanted to add a simpler solution in "pure" python using the pywin32 package
import time
import subprocess
from typing import List
try:
import win32print as wprint
PRINTERS: List[str] = [p[2] for p in wprint.EnumPrinters(wprint.PRINTER_ENUM_LOCAL)]
PRINTER_DEFAULT = wprint.GetDefaultPrinter()
WIN32_SUPPORTED = True
except:
print("[!!] an error occured while retrieving printers")
# you could throw an exception or whatever
# bla bla do other stuff
if "WIN32_SUPPORTED" in globals():
__printImg_win32(file, printer_name)
def __printImg_win32(file: str, printer: str = ""):
if not printer:
printer = PRINTER_DEFAULT
# verify prerequisites here
# i still do prefer to print calling rundll32 directly,
# because of the default printer settings shenaningans
# and also because i've reliably used it to spool millions of jobs
subprocess.check_call(
[
"C:\\Windows\\System32\\rundll32",
"C:\\Windows\\System32\\shimgvw.dll,ImageView_PrintTo",
file,
printer,
]
)
__monitorJob_win32(printer)
pass
def __monitorJob_win32(printer: str, timeout=16.0):
p = wprint.OpenPrinter(printer)
# wait for job to be sheduled
t0 = time.time()
while (time.time()-t0) < timeout:
ptrr = wprint.GetPrinter(p, 2)
# unsure about those flags, but definitively not errors.
# it seems they are "moving paper forward"
if ptrr["Status"] != 0 and ptrr["Status"] not in [1024,1048576]:
raise Error("Printer is in error (status %d)!" % ptrr["Status"])
if ptrr["cJobs"] > 0:
break
time.sleep(0.1)
else:
raise Error("Printer timeout sheduling job!")
# await job completion
t0 = time.time()
while (time.time()-t0) < timeout:
ptrr = wprint.GetPrinter(p, 2)
if ptrr["Status"] != 0 and ptrr["Status"] not in [1024,1048576]:
raise Error("Printer is in error (status %d)!" % ptrr["Status"])
if ptrr["cJobs"] == 0 and ptrr["Status"] == 0:
break
time.sleep(0.1)
else:
raise Error("Printer timeout waiting for completion!")
wprint.ClosePrinter(p)
return
useful additional resources
Print image files using python
Catch events from printer in Windows
pywin32's win32print "documentation"

Locating and opening programs based on a word in python

I have a script where a user will input a program to open, and that script will open the program. The way I've done it isn't very efficient and so I'm leading into problems.
If I input "Open chrome", the script starts a search for a file called chrome.exe on your computer and then launches it. This works for a few programs like chrome.exe, steam.exe etc, but there are programs where their exe file is named differently.
For example, "Open word". The script will search for a program named word.exe, but the Microsoft Word exe file is actually named WINWORD.EXE, so the search will fail.
So my question is how can I make this more efficient? I know I could add exceptions and find the directory of the file myself but this would be impractical to do for many programs.
with sr.Microphone() as source:
success = False
print (">")
audio = r.listen(source)
try:
print("Processing...")
t = r.recognize_google(audio)
print (": " + t)
except sr.UnknownValueError:
print("Unknown input")
continue
except sr.RequestError as e:
print("An error occured at GAPI\nA common cause is lack of internet connection")
continue
if "open" in t:
t = t.replace("open","")
t = t.replace(" ","")
z = t
speak = "Opening " + z
t = t + ".exe"
print (t)
for a,d,f in os.walk("C:\\"):
for files in f:
if files == t.lower() or files == t.capitalize() or files == t.upper():
s.Speak(speak)
pat = os.path.join(a,files)
print (pat)
sp.call([pat])
success = True
Edit:
I did some research and found out about something called filename pattern searching. I've been unable get it running, any ideas?

Windows duplicating printer with custom name

My organization uses a piece of software that has a printer name hard-coded into it PRN1.
Users are spread out through multiple locations; so it is impossible to just rename a single printer. Our networked printers are mapped by computer location via a login script; and some computers have locally attached printers.
The current proposed solution is to remote into each computer with the user logged in; re-map the users default printer; and manually rename it (Right click->Printer Prop...).
I'm trying to avoid this as we do not use roaming profiles and some users do move from location to location; and the users wouldn't understand why it suddenly isn't working.
Ideally I'd like to create a script that will automatically duplicate the users default printer; and name it PRN1.
$AllPrinters = gwmi win32_printer
$DefaultPrinter = $AllPrinters | where {$_.Default -eq $true}
rundll32 printui.dll,PrintUIEntry /ga /n $DefaultPrinter.SystemName + "\" + $DefaultPrinter.ShareName /z /b"PRN1"
Above is what I currently have; I know I'm not using the rundll32 command properly as the printer isn't being duplicated with the new name.
Any help or assistance would be greatly appreciated.
I use that rundll32 command here and there but never have solid luck with it.
You could do something like this by using wmi entirely. You may want to wrap a try catch around a large chunk of this to suppress errors and log output if users do end up having issues.
$Name = "PRN"
$AllPrinters = gwmi win32_printer
$DefaultPrinter = $AllPrinters | where {$_.Default -eq $true}
$objHelper = [WMICLASS]"\\localhost\root\cimv2:Win32_SecurityDescriptorHelper"
$print = [WMICLASS]"\\localhost\root\cimv2:Win32_Printer"
$print.Scope.Options.EnablePrivileges = $true
$newprinter = $print.createInstance()
$newprinter.drivername = $DefaultPrinter.DriverName
$newprinter.PortName = $DefaultPrinter.PortName
$newprinter.Shared = $false
$newprinter.Location = $DefaultPrinter.Location
$newprinter.Comment = $DefaultPrinter.Comment
$newprinter.DeviceID = $Name
$newprinter.PrintProcessor = $DefaultPrinter.PrintProcessor
$newprinter.PrintJobDataType = $DefaultPrinter.DataType
$newprinter.RawOnly = $DefaultPrinter.RawOnly
$result = $newprinter.Put()

Saving Tab names and ConqueShells along with Vim sessions

Is there any way to get vim to save the tab names (assigned via the Tab Name script) and/or a terminal emulator (set up via the Conque Shell script) upon issuing the :mksession [fileName] command?
Observe below (zoom in), I have a working session on the left, and the same session loaded via the vim -S fileName command, on the right. The assigned tab labels revert to absolute paths, ConqueShell terminal is interpreted as a file.
After learning some basic VimScript I just gave up and used Python instead (to cite one example, you can't save global information to a session if it is a list). Here is a solution I found for saving tab names (will post a solution for ConqueShell if I find one)
Put the following in your .vimrc file and use whatever mapping you want to quickly save and load your sessions
"Tokenize it so it has the following form (without spaces)
"Label1 JJ Label2 JJ Label3 JJ Label4
"Or if you prefer use something other than 'JJ' but DO NOT
"use symbols as they could interfere with the shell command
"line
function RecordTabNames()
"Start at the first tab with no tab names assigned
let g:TabNames = ''
tabfirst
"Iterate over all the tabs and determine whether g:TabNames
"needs to be updated
for i in range(1, tabpagenr('$'))
"If tabnames.vim created the variable 't:tab_name', append it
"to g:TabNames, otherwise, append nothing, but the delimiter
if exists('t:tab_name')
let g:TabNames = g:TabNames . t:tab_name . 'JJ'
else
let g:TabNames = g:TabNames . 'JJ'
endif
"iterate to next tab
tabnext
endfor
endfunction
func! MakeFullSession()
call RecordTabNames()
mksession! ~/.vim/sessions/Session.vim
"Call the Pythin script, passing to it as an argument, all the
"tab names. Make sure to put g:TabNames in double quotes, o.w.
"a tab label with spaces will be passed as two separate arguments
execute "!mksession.py '" . g:TabNames . "'"
endfunc
func! LoadFullSession()
source ~/.vim/sessions/Session.vim
endfunc
nnoremap <leader>mks :call MakeFullSession()<CR>
nnoremap <leader>lks :call LoadFullSession()<CR>
Now create the following text file and put it somewhere in your PATH variable (echo $PATH to get it, mine is at /home/user/bin/mksession.py) and make sure to make it executable (chmod 0700 /home/user/bin/mksession.py)
#!/usr/bin/env python
"""This script attempts to fix the Session.vim file by saving the
tab names. The tab names must be passed at the command line,
delimitted by a unique string (in this case 'JJ'). Also, although
spaces are handled, symbols such as '!' can lead to problems.
Steer clear of symbols and file names with 'JJ' in them (Sorry JJ
Abrams, that's what you get for making the worst TV show in history,
you jerk)
"""
import sys
import copy
if __name__ == "__main__":
labels = sys.argv[1].split('JJ')
labels = labels[:len(labels)-1]
"""read the session file to add commands after tabedit
" "(replace 'USER' with your username)
"
f = open('/home/USER/.vim/sessions/Session.vim', 'r')
text = f.read()
f.close()
"""If the text file does not contain the word "tabedit" that means there
" "are no tabs. Therefore, do not continue
"""
if text.find('tabedit') >=0:
text = text.split('\n')
"""Must start at index 1 as the first "tab" is technically not a tab
" "until the second tab is added
"""
labelIndex = 1
newText = ''
for i, line in enumerate(text):
newText +=line + '\n'
"""Remember that vim is not very smart with tabs. It does not understand
" "the concept of a single tab. Therefore, the first "tab" is opened
" "as a buffer. In other words, first look for the keyword 'edit', then
" "subsequently look for 'tabedit'. However, when being sourced, the
" "first tab opened is still a buffer, therefore, at the end we will
" "have to return and take care of the first "tab"
"""
if line.startswith('tabedit'):
"""If the labelIndex is empty that means it was never set,
" "therefore, do nothing
"""
if labels[labelIndex] != '':
newText += 'TName "%s"\n'%(labels[labelIndex])
labelIndex += 1
"""Now that the tabbed windowing environment has been established,
" "we can return to the first "tab" and set its name. This serves
" "the double purpose of selecting the first tab (if it has not
" "already been selected)
"""
newText += "tabfirst\n"
newText += 'TName "%s"\n'%(labels[0])
#(replace 'USER' with your username)
f = open('/home/USER/.vim/sessions/Session.vim', 'w')
f.write(newText)
f.close()

Is there is a way to change a Windows folder icon using a Perl script?

Is there is a way to change a Windows folder icon using a Perl script?
My intention is to change the ordinary icon of the "xxx_documents" folder to some other icon. I have to run the script in such a way that it takes care for the whole drive.
The drive contains many folders. I have to search for each folder named "documents" (e.g. "xxx_documents" or simply "documents") and change its icon to one from the "%SystemRoot%\system32\SHELL32.dll" library.
Is that possible in Perl? Thanks to all who help me with this.
You sure can do it with Perl. Windows controls directory icons by use of a hidden systemDekstop.ini file in each folder. The contents looks something like this:
[.ShellClassInfo]
IconFile=%SystemRoot%\system32\SHELL32.dll
IconIndex=41
On Windows XP (and I assume on other systems), icon 41 is a tree. Windows requires this file be explicitly set as a system file for it to work, this means we'll need to dig down into Win32API::File to create it:
#!/usr/bin/perl
use strict;
use warnings;
use Win32API::File qw(createFile WriteFile fileLastError CloseHandle);
my $file = createFile(
'Desktop.ini',
{
Access => 'w', # Write access
Attributes => 'hs', # Hidden system file
Create => 'tc', # Truncate/create
}
) or die "Can't create Desktop.ini - " . fileLastError();
WriteFile(
$file,
"[.ShellClassInfo]\r\n" .
"IconFile=%SystemRoot%\\system32\\SHELL32.dll\r\n" .
"IconIndex=41\r\n",
0, [], []
) or die "Can't write Desktop.ini - " . fileLastError();
CloseHandle($file) or die "Can't close Desktop.ini - " . fileLastError();
If you run the code above, it should set the icon for the current directory to a tree. You may need to refresh your directory listing before explorer picks up the change.
Now that we have a way to change icons, we can now just walk through a whole drive and change every folder that matches our pattern. We can do this pretty easily with File::Find, or one of its alternatives (eg, File::Find::Rule, or File::Next):
#!/usr/bin/perl
use strict;
use warnings;
use File::Find qw(find);
use Win32API::File qw(createFile WriteFile fileLastError CloseHandle);
my $topdir = $ARGV[0] or die "Usage: $0 path\n";
find( \&changeIcon, $topdir);
sub changeIcon {
return if not /documents$/i; # Skip non-documents folders
return if not -d; # Skip non-directories.
my $file = createFile(
"$_\\Desktop.ini",
{
Access => 'w', # Write access
Attributes => 'hs', # Hidden system file
Create => 'tc', # Truncate/create
}
) or die "Can't create Desktop.ini - " . fileLastError();
WriteFile(
$file,
"[.ShellClassInfo]\r\n" .
"IconFile=%SystemRoot%\\system32\\SHELL32.dll\r\n" .
"IconIndex=41\r\n",
0, [], []
) or die "Can't write Desktop.ini - " . fileLastError();
CloseHandle($file) or die "Can't close Desktop.ini - " . fileLastError();
}
Unfortunately, I've just discovered that the icon only gets changed if the directory currently has, or once had, an icon... There's clearly an attribute that's being set on the directory itself that causes Windows to look for a Desktop.ini file, but I can't for the life of me figure out what it is. As such, the above solution is incomplete; we also need to find and fix the attributes on the directory where we're adding the icon.
Paul
1.
[.ShellClassInfo]
LocalizedResourceName=#%SystemRoot%\system32\shell32.dll,-21790
InfoTip=#%SystemRoot%\system32\shell32.dll,-12689
IconResource=%SystemRoot%\system32\imageres.dll,-108
IconFile=%SystemRoot%\system32\shell32.dll
IconIndex=-237
2.
[.ShellClassInfo]
LocalizedResourceName=#%SystemRoot%\system32\shell32.dll,-21803
InfoTip=#%SystemRoot%\system32\shell32.dll,-12689
IconResource=%SystemRoot%\system32\imageres.dll,-3
To get the icon to refresh, you will have to invoke some SHChangeNotify voodoo (C++ example, but you get the idea):
int imageIndex = Shell_GetCachedImageIndexW(wPath, GetSyncFolderIconIndex(), 0);
if (imageIndex != -1)
{
// If we don't do this, and we EVER change our icon, Explorer will likely keep
// using the old one that it's already got in the system cache.
SHChangeNotify(SHCNE_UPDATEIMAGE, SHCNF_DWORD | SHCNF_FLUSHNOWAIT, &imageIndex, NULL);
}
SHChangeNotify(SHCNE_UPDATEITEM, SHCNF_PATHW | SHCNF_FLUSHNOWAIT, wPath, NULL);

Resources