Shell.Application & errors - powershell

To clarify, I am specifically asking about how to HANDLE errors when using Shell.Application. Or more to the point, CAN one handle errors, and IF SO, how. The font example is just that, an example of the current situation I am trying to solve. But the question remains, can one handle (not avoid, handle) an error when using Shell.Application? There may well be some subtlety to the answer, I.e in general you can, but specifically not in the fonts example. That seems to be the case, and I am inclined to decide that Shell.Application is old, broken technology that should simply not be used, because it can't be robust in all use cases, which to me says it's potentially fragile in any use case.
I am attempting to refine my use of Shell.Application.CopyHere(), specifically for use installing fonts. What I hope to do is address the occasional error where a font file is corrupt or otherwise not a valid font file.
So far as I can tell from this there really is no way to address this. I can use argument value 16 Respond with "Yes to All" for any dialog box that is displayed. to perhaps get past the error, but with no return code I have no way to log a resulting error. Using .CopyHere() in PowerShell with a Try/Catch doesn't work either. Is this just old technology from a time when Microsoft just accepted things failing ungracefully? Or am I missing a technique that solves the issue?
EDIT: Based on that link I provided, I tried the 1024 argument, Do not display a user interface if an error occurs. Like so
$fontFolder.CopyHere($fontFilePath, 1024)
Doesn't seem to do what it says it does, since I am seeing a dialog that says
Cannot install bogus.ttf
The file ... does not appear to be a valid font.
So, not only not able to get a meaningful error back, but the presence of an error disrupts execution of the script and requires user interaction. Ugh.
EDIT 2:
Not really a minimal code example, since my question is CAN this be done, even before how. But this is what I just tried.
fontFilePath = '\\px\Rollouts\Misc\Fonts\bogus.ttf'
$fontFolderPath = "$env:windir\Fonts"
$fontFolder = $(New-Object -ComObject:Shell.Application).Namespace($fontFolderPath)
$fontFolder.CopyHere($fontFilePath, 1024)
Based on what that 1024 argument claims to do, I would expect this to fail, but also not stop processing while a dialog waits for user interaction.
Also, worth noting that bogus.ttf is simply an empty text file renamed with TTF extension. So, guaranteed not to successfully install a font.

It seems that the Fonts folder, while it does implement Folder.CopyHere, does not evaluate the flags passed in the second parameter. Doesn't seem there are any other standard utilities to install fonts programmatically, or verify the integrity of the TTF non-interactively.
So this leaves us the option of rolling our own Font registration using the Win32 API. Basically, you have to copy the font to the Fonts folder:
# For .NET versions earlier than 4.0, hardcode to C:\Windows\Fonts
# Fonts special folder is new in 4.0
$fontDir = [System.Environment]::GetFolderPath([System.Environment.SpecialFolder]::Fonts)
Copy-Item C:\Path\To\Font.ttf "${fontDir}\Font.ttf"
This next call isn't strictly necessary, as it only adds the font temporarily to your user session, but it does function as an integrity check to see if the font is valid and can be imported. You need to P/Invoke AddFontResource from the Win32 API:
Add-Type -Name Gdi32 -Namespace Win32 -MemberDefinition #"
[System.Runtime.InteropServices.DllImport("gdi32.dll")]
public static extern int AddFontResource(string lpszFilename);
"#
# Returns 1 on success or 0 on failure, if you want your error checking here
[Win32.Gdi32]::AddFontResource("${fontDir}\FontFileName.ttf")
And then register it at the following registry key. This piece is necessary for persisting the font you copied to $fontDir:
New-ItemProperty -Path 'HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Fonts' -Name "FontName" -Value "FontFileName.ttf"
You can programmatically get the FontName using the PrivateFontCollection class, and the Families[x].Name property under that, where x is the index of the font in the collection you want the name for.
To address your edit, there isn't going to be a "one size fits all" approach when it comes to handling errors in Shell.Application. There are multiple reasons to avoid using Shell.Application, such as:
APIs are wrought with legacy behavior, such as using modal dialogs to report errors
Windows standards are implemented inconsistently, such as Fonts ignoring the documented CopyFile flags
Not available in a non-interactive session
The first two points are cases the example in your question hits. The third comes into play in many automation scenarios, especially when a non-interactive service accounts are used to execute commands. There are very few cases where something can only be done via Shell.Application, and as such it's almost always best to avoid it if there is an alternative API. Your font installation scenario is an excellent example of this.

Related

Ignore or bypass errors phpcs

How to bypass or ignore specific errors/warnings in vscode?, I am using phpcs.
What you are looking for is to ignore the warning and/or errors, that are notified by the phpcs in the console of the vscode.
For Warnings
Use the following config in your settings.json
"phpcs.showWarnings": false,
this will remove all the warnings displayed in the output console.
For Errors
You should go trough the DOCS for complete details but to remove the errors related to the Doc block and the formatting standards you can set the
"phpcs.errorSeverity": 6,
Although it is mostly used for testing or code reviews to check for total warnings and errors by setting different values for both, but for development i dont do that and keep it to the default value that is 5 but you can get rid of those errors above in your image.
The vscode-phpcs refers to the GitHub project squizlabs/PHP_CodeSniffer, which integrates PHP_CodeSniffer into VSCode.
Its readme mentions the setting phpcs.ignorePatterns:
An array of glob patterns to skip files and folders that match when linting your documents.
{
"phpcs.ignorePatterns": [
"*/ignored-file.php",
"*/ignored-dir/*"
]
}
That refers to PHP CodeSniffer --ignore option.
That is not what you want exactly, since it ignores all errors on a given set of file.
But you could use PHP CodeSniffer syntax to ignore errors:
Ignoring Parts of a File
Some parts of your code may be unable to conform to your coding standard. For example, you might have to break your standard to integrate with an external library or web service.
To stop PHP_CodeSniffer generating errors for this code, you can wrap it in special comments. PHP_CodeSniffer will then hide all errors and warnings that are generated for these lines of code.
$xmlPackage = new XMLPackage;
// phpcs:disable
$xmlPackage['error_code'] = get_default_error_code_value();
$xmlPackage->send();
// phpcs:enable
Again, not exactly what you want, since you have to specify that on a file-by-file basis
You can disable multiple error message codes, sniff, categories, or standards by using a comma separated list.
You can also selectively re-enable just the ones you want.
The following example disables the entire PEAR coding standard, and all the Squiz array sniffs, before selectively re-enabling a specific sniff. It then re-enables all checking rules at the end.
// phpcs:disable PEAR,Squiz.Arrays
$foo = [1,2,3];
bar($foo,true);
// phpcs:enable PEAR.Functions.FunctionCallSignature
bar($foo,false);
// phpcs:enable

GoCD - How to display PowerShell output in color?

As the title says, how do we display console output from PowerShell scripts in color? We're using v16.12 of GoCD.
Edit: Due to company policy we're not allowed to use write-host. Please don't ask why :)
The answer is absolutely not related to GoCD.
What about
[System.Console]::BackgroundColor="blue"
[System.Console]::ForegroundColor="yellow"
[System.Console]::WriteLine("yellow on blue");
If you want a potentially easier way to handle those colors, you could install, and use the TMOutput module, a module I've written. It uses System.Console; however, it does so with less coding on your part.
Here's a link to a post about it: http://tommymaynard.com/script-sharing-write-output-gets-foreground-and-background-colors-and-more-2016/. On that page, is a link to the module on Microsoft's PowerShell Gallery. If you use PowerShellGet, you can download and install it using the below command.
Install-Module -Name TMOutput
Edit: Another thing the module is going to do for you, is reset the colors back to what they were before you put what you wanted in color. You'd have to do that yourself if you just use [System.Console] on your own.
This is only arbitrarily not Write-Host, but it is another option that might satisfy the requirements.
$Host.UI.Write('Cyan','DarkMagenta','Text to display')
Overloads:
void Write(string value)
void Write(System.ConsoleColor foregroundColor, System.ConsoleColor backgroundColor, string value)
Also while not directly related, it's worth noting that if your requirements are actually to satisfy PSScriptAnalyzer rules you can wrap any Write-Host commands in a function that uses the verb Show (e.g. Show-GoCDOutput) and PSScriptAnalyzer will skip it.

AutoHotKey ControlGet

I have a script that is throwing and error when I issue a ControlGet.
OtPtVar := AfxWnd422
strMPP := Material Planning Profile
ControlFocus,%OtPtVar%,,%strMPP%
Control,Show,,%OtPtVar%,,%strMPP%
These all work but when it gets to the next line it throws an error:
ControlGet,sysList,List,,%OtptVar%,,%strMPP%
The control is a grid with five fields row number,Part,Description,Time,Lateness.
`ControlFocus,%OtPtVar%,,%strMPP%`
`Control,Show,,%OtPtVar%,,%strMPP%`
`ControlGet,sysList,List,,%OtPtVar%,,%strMPP%`
The error occurs on the last line.
Thank you.
After searching the AutoHotKey site I found to the following:
Those Afx window classes were custom classes distributed with early versions of MFC, before the "Common Controls" concept was introduced.
Many people have tried to "read" text from them but you CAN'T, not with messages anyway!
They're almost certainly owner-drawn - the text is not delivered by messages, it's "hand-drawn" into the window's DC
...
Messages are a dead-end I think - those who have pursued this problem must have already clocked up several hundreds of hours of monitoring message traffic between them!
So its not possible to get a hold of the control.
ControlGet's List subcommand can only handle standard listview controls, which usually have the class 'SysListView32'.
Did you try AccViewer just in case.
This link mentions textcapturex by Deskperience Software in case that helps.
"AfxWnd42s" Please Help - AutoIt General Help and Support - AutoIt Forums
https://www.autoitscript.com/forum/topic/49057-afxwnd42s-please-help/
There are theoretically process hacker methods,
but I'm not an expert on those, and wouldn't recommend such methods.
And yes, unfortunately when I really needed to, I used OCR once.
Potentially you can do OCR, recreate the text in the same font,
and check for an exact image match as a double-check.
Also, if possible, set the font to one where characters such as capital i and small L are unambiguous.

Is it possible to use a control without putting it on a form in VB6?

I'm pretty sure about the answer to this, but I'm trying a variety of things to get a very stubborn project to work. One idea was to try to run code through a control without defining it on a form.
So, for example, my original code looked like this:
frmProcess.MyViewer.MaxPageSize = 100
frmProcess.MyViewer.ResetPages
frmProcess.MyViewer.AddPageToView "C:\TestPage1.txt"
I've changed it to:
Dim objViewer As MyViewer
objViewer.MaxPageSize = 100
objViewer.ResetPages
objViewer.AddPageToView "C:\TestPage1.txt"
I get an error window with "Run-time error '91': Object variable or With block variable not set".
But there doesn't seem to be a way to 'set' this control. Is this just impossible, or is there another way to do it that doesn't require a form?
EDIT: I ended up abandoning this entire path of activity, as an alternate solution was found that got around the problem I was having with this form freezing. I don't want to delete this question in case someone else comes along and can benefit from the answers, which are potentially useful.
Try this on a form.
Dim objViewer As MyViewer
Set objViewer = Controls.Add("MyViewer", "MyViewer1")
objViewer.MaxPageSize = 100
objViewer.ResetPages
objViewer.AddPageToView "C:\TestPage1.txt"
I've had similar situations in the past. If all else fails and you have to use a form you can do something crude like
1) Set the .Left property of the control to a negative number (like -10000) so the control doesn't appear on the form, the user can not see it
2) Make the entire form not visible..
ActiveX controls normally expect a number of services from their containers, for example persistence. They are also "packaged and marked" in ways that set the kinds of instantiation they support.
See Introduction to ActiveX Controls.
While it is perfectly possible for a control to be created in such a way as to make many of the available services optional, most controls are created from template code that requires a number of them. And most controls that are "visible at runtime" are going to require container services.
However that doesn't mean a control can't be designed to support containerless instantiation. A well known example of such a control is Microsoft Script Control 1.0 (MSScriptControl.ScriptControl) which can be used either way.

Creating a shadow copy using the "Backup" context in a PowerShell

I am in the process of writing a PowerShell script for backing up a Windows computer using rsync. To this end, I am attempting to use WMI from said script to create a non-persistent Shadow copy with writer participation (as is apparently recommended for backups).
I found out from another question (Accessing Volume Shadow Copy (VSS) Snapshots from powershell) a way to create a shadow copy in general, but the example given there uses "ClientAccessible" as the context parameter, which results in the creation of a persistent Shadow Copy, without writer participation.
While searching for a solution, I have found that I could use the following command to obtain a list of contexts, which I assume are understood by WMI:
Get-WmiObject win32_shadowcontext | Out-GridView
It does the list have a context named "Backup", which is conveniently what I want. I proceeded to attempt creating a non-persistent shadow copy using that context:
$shadow = (Get-WmiObject -list win32_shadowcopy).Create("C:\", "Backup")
However, this seems to fail and the content of the $shadow variable is set to
ReturnValue : 5
ShadowID : {00000000-0000-0000-0000-000000000000}
According to the relevant documentation (Create method of the Win32_ShadowCopy class), the return value means "Unsupported shadow copy context."
I couldn't find any relevant documentation as to why this context is unsupported or whether it is possible to use it at all. I have also tried the "FileShareBackup" and "AppRollback" contexts without success.
I assume I am either missing something obvious, or that for some reason, WMI really doesn't support anything else than "clientAccessible" when creating shadow copies, or that this is OS-dependent (I am testing this on Windows 7, 64-bit)
How can I get this to work?
Okay, Technoob1984 here with the scoop. See my attached screen shot.
This one is tricky, because you have to use x64 version of Powershell (located under system32 not wow64)
The Shadow Copy Context are the .properties of the object.
Also I used the static method in my screenshots below.
https://learn.microsoft.com/en-us/previous-versions/windows/desktop/vsswmi/create-method-in-class-win32-shadowcopy
# get existing shadow copies
$shadow = get-wmiobject win32_shadowcopy
"There are {0} shadow copies on this sytem" -f $shadow.count
""
# get static method
$class=[WMICLASS]"root\cimv2:win32_shadowcopy"
# create a new shadow copy
"Creating a new shadow copy"
$class.create("C:\", "ClientAccessible")
# Count again
$shadow = get-wmiobject win32_shadowcopy
so in the example there, you would want to use $class.Properties to see what you can use as a Shadow Context.
See my screen shot:
So Shadow Context is 'Caption, Count, Description' and anything else under the 'Name:' value of .Properties. I do not see 'Backup' as one of the options.
Enjoy
Your $shadow has a 5 on return value looking at the error message, your shadow id has all zeros , you would need to add a 1 or a 2 to the end of the volume shadow copy in the registry using binary or dword.
find the folder in the registry named volsnap in your regedit search .volsnap.sys is found in the C:\Windows\System32\drivers directory. The file size is 52,352 bytes.The volsnap file contains Microsoft's digital signature make sure its the correct bytes.
This confirms its authenticity. volsnap.sys appears to be a file that was compressed by an EXE-Packer. This technique is often used by trojans to keep the file size small and also hamper debugging efforts.
However, this in itself is not sufficient reason to presume malicious intent, since even well-intentioned, professional software producers take advantage of compressed files. For this reason, 2% of all experts consider this file to be a possible threat. The probability that it can cause harm is high. Please consider the additional Comments from other users.
shadow id default
00000000-0000-0000-0000-000000000000
00000000-0000-0000-0000-000000000005
if it already has a 5 which it probably doesn't change it to 1
or create new code
Shadow id $shadow 00000000-0000-0000-0000-0000000000001
not exactly as shown.you may have to try different wording I'm not sure if $will work, if not, try the js standalone version.