I'm writing a PowerShell script that uses the reflection APIs to get all the namespaces in an assembly. Anyways, that's not relevant. Here is the relevant portion of the code:
function Get-Namespaces($assembly)
{
$assemblyClass = [Reflection.Assembly]
$assemblyObject = $assemblyClass::ReflectionOnlyLoadFrom($assembly)
$types = $assemblyObject.GetTypes() # This is the part that's having issues
return $types | ? IsPublic | select Namespace -Unique
}
cd $PSScriptRoot
$assemblies = #()
$assemblies += Get-WpfAssemblies
$assemblies += Get-UwpAssembly
$namespaces = $assemblies | % {
% { Get-Namespaces $_ }
}
For some reason, the part that initializes $types seems to be having issues; specifically, it's telling me to catch the exception and check the LoaderExceptions property of the caught exception for more information. So when I try to do just that:
try { $assemblyObject.GetTypes() } catch { echo $_.LoaderExceptions }
and run it, the script prints nothing.
Why does this happen and how can I fix it?
For people who would like to try out the script in its entirety, I've made a publicly available GitHub gist. (Note that it will only work if you have the Windows 10 dev tools installed, but I'm sure reasonably experienced PowerShell users can modify the script to run on their machines.)
Unfortunately, I'm not on a Windows PC to try this, but with some google searching, it looks like the correct syntax should be:
try {
....
} catch [System.Reflection.ReflectionTypeLoadException] {
echo $_.LoaderExceptions
}
Check out http://www.vexasoft.com/blogs/powershell/7255220-powershell-tutorial-try-catch-finally-and-error-handling-in-powershell. Seems to have some good information on exception handling in PowerShell.
The (topmost) exception you're catching is probably an ErrorRecord, which doesn't have a property LoaderExceptions. PowerShell expands missing properties to $null values, which get converted to an empty string for output. You can check the exception type as well as its properties and methods by inspecting the current object in the catch block with the Get-Member cmdlet:
try { $assemblyObject.GetTypes() } catch { Get-Member -InputObject $_ }
Since PowerShell has a tendency of hiding relevant information in nested exceptions you may want to do something like this to unroll them:
try {
...
} catch {
$_.InvocationInfo.Line.Trim() + "`n"
$_.InvocationInfo.PositionMessage + "`n"
$e = $_.Exception
do {
$e.Message
if ($e.LoaderExceptions) { $e.LoaderExceptions }
$e = $e.InnerException
} while ($e)
}
The problem was that PowerShell was interpreting what was echoed as a return value:
function Generate-ErrorMessage
{
try
{
blah
}
catch
{
echo $_
}
}
$message = Generate-ErrorMessage # Will contain some PowerShell message about not being able to find 'blah'
The solution was to use Console.WriteLine directly:
function Generate-ErrorMessage
{
try
{
blah
}
catch
{
[Console]::WriteLine($_)
}
}
Generate-ErrorMessage # prints the message to the console
Not as pretty, but it works as expected.
EDIT: Write-Host also works:
try { blah }
catch { Write-Host $_ }
For other commands, you can take a look here.
EDIT 2: In fact, Out-Host is even better for logging:
try { blah }
catch { $_ | gm | Out-Host } # displays more detailed info than Write-Host
Related
I have recently installed VS 2019 and opened up my project I created in VS 2017. The software works fine but there is a bug in VS with lambda captured variables. MS apparently is aware of said issue, but I was wondering if anyone else had come across this recently and if you have, have you managed to solve it?
Example bit of code from my project, the intellisense has flagged up every line where "[this]" appears. The error / bug reads
lambda captured variable of type "MainPage^*" cannot be copied to closure class field of type "MainPage^"
if (_serialPort1 != nullptr)
{
concurrency::create_task(WriteToSerialDeviceAsync(cancellationTokenSource_serialPort1->get_token(),
Arduino_Device.Outgoing_Bytes, PORT_1)).then([this](concurrency::task<void> previousTask) {
try
{
previousTask.get();
}
catch (Platform::COMException^ ex)
{
this->DataStreamWindow->Text += "\r\n!EXCEPTION CAUGHT! " + ex->Message;
}
});
}
Ok, I managed to stumble upon a somewhat ugly hack to fix this.
Rather than pass [this] into the lambda, I added the line auto _this = this; prior to creating any tasks. This did however mean that any variables which were accessed using this->SomeVariable became _this->SomeVariable.
So my example above now looks like this.
if (_serialPort1 != nullptr)
{
auto _this = this;
concurrency::create_task(WriteToSerialDeviceAsync(cancellationTokenSource_serialPort1->get_token(),
Arduino_Device.Outgoing_Bytes, PORT_1)).then([_this](concurrency::task<void> previousTask) {
try
{
previousTask.get();
}
catch (Platform::COMException^ ex)
{
_this->DataStreamWindow->Text += "\r\n!EXCEPTION CAUGHT! " + ex->Message;
}
});
}
Hope this is of use.
If so then why copying outside the task? You could do
if (_serialPort1 != nullptr)
{ concurrency::create_task(WriteToSerialDeviceAsync(cancellationTokenSource_serialPort1->get_token(),
Arduino_Device.Outgoing_Bytes, PORT_1)).then([_this = this](concurrency::task<void> previousTask) {
try
{
previousTask.get();
}
catch (Platform::COMException^ ex)
{
_this->DataStreamWindow->Text += "\r\n!EXCEPTION CAUGHT! " + ex->Message;
}
});
}
But based on your problem this is not the proper solution. You better find what's wrong with your project migration to VS 2019.
Is there a way to proceed with a test even if assertSee returns with an error.
This is my current code:
$browser->visit('https://urltotest.co.uk/find-an-engineer/')
->type("EngineerId", $engineerId)
->click('#checkEngineer');
$test = $browser->assertSee("Engineer cannot be found");
What I would like to be able to do is go:
if ($test == false) {
return false;
} else {
return $browser->text('.engineer-search-results-container .search-result .col-md-8 .row .col-xs-10 h3');
}
But when the assertSee fails all I get back is:
Did not see expected text [Engineer cannot be found] within element [body].
As an exception. Any way I can proceed if it can't find that element?
You should be able to achieve this using a try catch to catch the exception:
try {
$browser->assertSee("Engineer cannot be found");
} catch(\Exception $e) {
return false;
}
To note, I do not know if there is a method such as $browser->fail() (like phpunit's $this->fail()) which will fail the test for you.
I am trying to clear up what gets written in the console when not debugging so for example when running in release it will not show up. So for that i use this method for the Logging that should not be shown
public static void DebugWriteConsole(string s)
{
Debug.WriteLine(DateTime.UtcNow.ToString("yyyy-MM-dd HH:mm:ss | ") + s);
if (Log.logger != null)
{
Log.Info(s);
}
}
And it works in that regard that it doesn't show up when running in release but the problem i have is that i run the application with -c so it runs in a console window but when running in debug the Debug.WritLine only prints into the vs output window and nothing in the console window. Anyone know how to solve this?
Has been explained in the Microsoft Docs in the TraceListeners collection topic
You can use TextWriterTraceListener and specify the System.Console.Out as the stream where you want to write (or any other suitable stream instance)
TextWriterTraceListener myWriter = new TextWriterTraceListener(System.Console.Out);
Debug.Listeners.Add(myWriter);
or just use ConsoleTraceListener.
ConsoleTraceListener trc = new ConsoleTraceListener();
Debug.Listeners.Add(trc);
Another option is to use a pragma directive to overcome the NET Core problem
public static void DebugWriteConsole(string s)
{
#if DEBUG
Console.WriteLine(DateTime.UtcNow.ToString("yyyy-MM-dd HH:mm:ss | ") + s);
#endif
Debug.WriteLine(DateTime.UtcNow.ToString("yyyy-MM-dd HH:mm:ss | ") + s);
if (Log.logger != null)
{
Log.Info(s);
}
}
Steve way will probably work for most but if you are using .Net Core and have this problem i found a solution like this since .Net Core doesn't have Debug.Listeners A way to do this is by writing the method likes this
[Conditional("DEBUG")]
public static void DebugWriteConsole(string s)
{
Console.WriteLine(DateTime.UtcNow.ToString("yyyy-MM-dd HH:mm:ss | ") + s);
if (Log.logger != null)
{
Log.Info(s);
}
}
still using the Console.WriteLine but putting the Conditional attribute on the method
Anyone know how to obtain the current DSC working folder?
On the server, when DSC runs, it generates a folder stucture like the following:
C:\Packages\Plugins\Microsoft.Powershell.DSC\2.18.0.0\DSCWork\DSC.0
The only trouble is when DSC gets a new version, it increments the ".X" folder.. However, when DSC runs, there is no clear way how to resolve the current folder from a script block predictably:
Script GetDscCurrentPath
{
TestScript = {
return $false
}
SetScript ={
Write-Verbose (Get-Item 'C:\%path_to_dsc%\FileInsideMyDscAsset.txt')
}
GetScript = { #{Result = "GetDscCurrentPath"} }
}
Thoughts? Thanks in advance!!!
The DSC Extension does not expose a variable or function to get the current working directory, but since your script / module is in the directory you should be able to use the PSScriptRoot to get the directory. Here is an example:
Write-Verbose -Message "PSScriptRoot: $PSScriptRoot" -Verbose
# Note PSScriptRoot will change when the extension executes
# your configuration function. So, you need to save the value
# when your configuration is loaded
$DscWorkingFolder = $PSScriptRoot
configuration foo {
Script GetDscCurrentPath
{
TestScript = {
return $false
}
SetScript ={
Write-Verbose $using:DscWorkingFolder
}
GetScript = { #{Result = $using:DscWorkingFolder} }
}
}
I'm really weak in Powershell scripting and hoping someone can put the following pieces together to help me solve the following problem.
Background:
Whenever I update my NuGet packages, they often will touch the app.config and web.config files in my project, thereby, changing the formatting.
This has been reported here without a solution.
Nuget and web.config formatting
http://nuget.codeplex.com/workitem/1511
Goal:
Ultimately, what I'd like to do is have a context menu in Visual Studio (e.g. at the solution level)
that gets a list of all app.config and web.config
and apply the CTRL+K+D to format the config files
(where we have it set to have one attribute per line)
Basically, I'm looking for a modified script that I can hook up to Visual Studio context menu (at solution level) that will read only the relevant config files. OR, alternatively, executed on file save (for relevant config files).
THANKS!
My starting point is from
a) Mark Melville showing how to execute a powershell script from within the context menu in Visual Studio
How do I run a PowerShell script from Visual Studio 2010
b) and Scripts from Phil Haack and David Fowl
http://haacked.com/archive/2011/05/22/an-obsessive-compulsive-guide-to-source-code-formatting.aspx/
https://gist.github.com/davidfowl/984358
For reference, cut and pasted from above links,
Adding context menu to Visual Studio
Add an "External Tool". Go to Tools > External Tools. Add a new one with these settings:
Title: Run with Powershell
Command: powershell.exe
Arguments: -ExecutionPolicy RemoteSigned -File "$(ItemPath)"
Initial Directory: $(ItemDir)
Check "Use Output Window"
If you plan to use scripts that require arguments, check "Prompt For Arguments"
Take note of the position your tool is in the list (1,2, etc...) Click OK.
Now go to Tools > Customize, Commands tab, select Context Menu, and
choose "Project and Solution Context Menus | Item".
Click "Add Command..".
Choose Tools category, and choose "External Command X"
where x is the position your tool was in the list.
Click OK.
Move it to the position you want in the menu, and click "Modify Selection"
to give it a friendly name,
add keyboard shortcuts, etc..
Click Close.
Right click your .ps1 file in the solution explorere and enjoy. (NOTE: I also did this for cmd.exe to run .bat files.)
For reference, cut and pasted from above links,
powershell script to format document
# Print all project items
Recurse-Project -Action {param($item) "`"$($item.ProjectItem.Name)`" is a $($item.Type)" }
# Function to format all documents based on https://gist.github.com/984353
function Format-Document {
param(
[parameter(ValueFromPipelineByPropertyName = $true)]
[string[]]$ProjectName
)
Process {
$ProjectName | %{
Recurse-Project -ProjectName $_ -Action { param($item)
if($item.Type -eq 'Folder' -or !$item.Language) {
return
}
$window = $item.ProjectItem.Open('{7651A701-06E5-11D1-8EBD-00A0C90F26EA}')
if ($window) {
Write-Host "Processing `"$($item.ProjectItem.Name)`""
[System.Threading.Thread]::Sleep(100)
$window.Activate()
$Item.ProjectItem.Document.DTE.ExecuteCommand('Edit.FormatDocument')
$Item.ProjectItem.Document.DTE.ExecuteCommand('Edit.RemoveAndSort')
$window.Close(1)
}
}
}
}
}
function Recurse-Project {
param(
[parameter(ValueFromPipelineByPropertyName = $true)]
[string[]]$ProjectName,
[parameter(Mandatory = $true)]$Action
)
Process {
# Convert project item guid into friendly name
function Get-Type($kind) {
switch($kind) {
'{6BB5F8EE-4483-11D3-8BCF-00C04F8EC28C}' { 'File' }
'{6BB5F8EF-4483-11D3-8BCF-00C04F8EC28C}' { 'Folder' }
default { $kind }
}
}
# Convert language guid to friendly name
function Get-Language($item) {
if(!$item.FileCodeModel) {
return $null
}
$kind = $item.FileCodeModel.Language
switch($kind) {
'{B5E9BD34-6D3E-4B5D-925E-8A43B79820B4}' { 'C#' }
'{B5E9BD33-6D3E-4B5D-925E-8A43B79820B4}' { 'VB' }
default { $kind }
}
}
# Walk over all project items running the action on each
function Recurse-ProjectItems($projectItems, $action) {
$projectItems | %{
$obj = New-Object PSObject -Property #{
ProjectItem = $_
Type = Get-Type $_.Kind
Language = Get-Language $_
}
& $action $obj
if($_.ProjectItems) {
Recurse-ProjectItems $_.ProjectItems $action
}
}
}
if($ProjectName) {
$p = Get-Project $ProjectName
}
else {
$p = Get-Project
}
$p | %{ Recurse-ProjectItems $_.ProjectItems $Action }
}
}
# Statement completion for project names
Register-TabExpansion 'Recurse-Project' #{
ProjectName = { Get-Project -All | Select -ExpandProperty Name }
}