I'm executing a PowerShell script in C# using the "PowerShellInstance" class but I'm having an issue with the commandlet "Clear-Host". I get some errors related to the fact that the host program or the command type does not support user interaction.
However, it must be possible to execute this script directly via PowerShell (without using C#), therefore, I want the "Clear-Host" command to execute only if the script has been triggered from within a PowerShell windows and not when it's triggered from the C# code.
So far, the only way I found is to wrap it in a "Try { } Catch { }" block but I'm wondering if there is a better solution... Something like "if ($host.IsOk) { }".
Thanks
OK, if I understand correctly, you want something like this:
if($host.UI.RawUI.WindowTitle -match "PowerShell")
{
Clear-Host
}
else
{
"The script was executed from C#"
}
Related
I am trying to silently send a synchronous Lisp command to autocad from c# code.
Here's how we send a Synchronous command to autocad.
string textToSend = $"(command-s \"_.-layout\" \"_t\" {filePath} {layoutName})";
object acadDoc = Application.DocumentManager.MdiActiveDocument.GetAcadDocument();
acadDoc.GetType().InvokeMember("SendCommand", BindingFlags.InvokeMethod, null, acadDoc, new[] { textToSend + "\n" });
The command works but the problem is that the command ends up in autocad's command line and clogs up the history of the drafters using our extensions.
We tried modifying system variables CMDECHO, CMDDIA, and NOMUTT without success
Directly in autocad's command line manually
With the c# method SetSystemVariable()
The same way we called our InvokeMember("SendCommand")
In the same Lisp command where we do our action
I looked at the InvokeMember parameters but didn't see anything that might affect the display of the command like there exists for the ActiveDocument.SendTextToExecute() asynchronous command.
How do we send synchronous Lisp commands to autocad from c# code silently?
Ps: The reason why I am not using WBlockCloneObjects() is because it makes our apps extremely unstable. I am not really interested in opening that whole can of worms in this issue, I'm only stating this to explain why I ended up with the solution from my question.
The title of my question was misleading. I didn't need to run lisp code silently, I needed to run commands in acad's command line silently. It just happened to be that acad's command line accepts lisp code so that's what I was using.
Instead of running lisp code, I used the method ActiveDocument.Editor.Command() to send my command to autocad. This method is synchronous and is affected by the system variable cmdecho.
I encountered a subsequent problem; because I was calling this method from a button in the banner with RelayCommand, Editor.Command was throwing the exception eInvalidInput because I was in the Application context instead of the Document context.
The best way to handle this was to split my first method in two and call the second method with ActiveDocument.SendStringToExecute() which uses the command line so I end up in the Document context. But because SendstringToExecute() is async, I had to rework my method's logic a bit.
Here is the final result (simplified)
private Layout Layout;
private RelayCommand _Command_Test;
public RelayCommand Command_Test
{
get
{
if (_Command_Test == null)
{
_Command_Test = new RelayCommand(
(param) =>
{
FirstMethod();
},
(param) =>
{
return true;
}
);
}
return _Command_Test;
}
}
[CommandMethod("FirstMethod")]
public void FirstMethod()
{
// Many actions and processing
Layout = new Layout(/*With stuff*/);
Application.DocumentManager.MdiActiveDocument.SendStringToExecute("SecondMethod ", true, false, false);
}
[CommandMethod("SecondMethod")]
public void SecondMethod()
{
short cmdecho = (short)Application.GetSystemVariable("cmdecho");
Application.SetSystemVariable("cmdecho", 0);
Editor ed = Application.DocumentManager.MdiActiveDocument.Editor;
ed.Command("_.-layout", "_t", Layout.FilePath, Layout.Name);
Application.SetSystemVariable("cmdecho", cmdecho);
// Actions that need to be ran after the command
}
I had to split my first method differently to have two sets of synchronous actions.
Because CommandMethod can't receive parameters, I had to store information in the fields of the class.
I skipped it in the code shown here but I, of course, used try-catches and transactions to handle potential issues.
I made a custom Debug.Log using Conditional attribute like this
[Conditional("DEBUG_BUILD"), Conditional("UNITY_EDITOR")]
public static void Print(object str)
{
UnityEngine.Debug.Log(str);
}
And I use it like this
void Something()
{
CustomDebug.Print("Error");
}
But when I click the console, It open the custom log script not the script that caused error.
I wonder if I can make link the console with error script.
It's simply returning the last most call in the stack, which is going to be the code in your custom Debug script.
You can get around this easily by compiling this into it's own assembly though, move your code to a Managed Unity DLL, then it will ignore the DLL level stack trace and only return the location your custom method was called from.
Maybe you can try Application.logMessageReceived to implement your custom debug log.
And you can use UnityEngine.Debug.Log() or MonoBehaviour.print() to record logs. It still can link to the scripts.
So I'm working with cmdlets in C# and using a base cmdlet which derives from PSCmdlet.
Like this: Child <- Parent <- PSCmdlet.
We login to a system using methods in the Parent cmdlet. But sometimes things don't always go right with exceptions, crashes and so on. So it doesn't really logout properly.
My question is as follows:
Is there a method existing that I can implement/override in the Parent cmdlet that will run no matter what happens so that we properly can logout? Kind of like try finally.
I have checked out EndProcessing() and StopProcessing() but they aren't really up to the task in unforeseen situations.
Edit: To clarify, there are multiple cmdlets and the situation is not just about login. There are multiple exceptions that can occur based on code and user-input.
The solution for my problem, as #PetSerAl suggested, was to implement IDisposable in the Parent class.
Using Dispose() I could then add whatever methods needed to finalize the cmdlet no matter what happens.
This sounds like a good case to use Try-catch:
try
{
UserLogin();
OtherMethod();
}
catch(UnauthorizedException au)
{
Console.WriteLine("Unauthorized user... shutting down");
//Closing Code...
Environment.Exit(0);
}
catch(OtherException oe)
{
//Closing Code...
Environment.Exit(0);
}
...
Where you would (if not already) throw the UnauthorizedException() exception within UserLogin() or which ever method you are using to test credentials.
This concept can also be applied to the other areas of code that you are expecting exceptions to be thrown. If the exceptions are because of code not working properly, Try-Catch should not be used to suppress that error as it should be corrected. Exceptions/errors based off of User-Input could be wrapped with Try-Catch.
I need to terminate the whole script execution if command let encounters the error. The whole script after that must never execute. In my case, If I use ThrowTerminatingError, it just stops the current commandlet to be executed further and the rest of the script executes which I don't want.
I already used "PipelineStoppedException" but this doesn't tell anything in the error stream and no exception can be a catch. Because Invoke call successfully terminates.
How can I achieve this? I want to terminate the whole script execution with the exception message. Following is the C# code which I am using from commandlet but it is still allowing rest of scripts to continue execution further.
Edit
protected override void BeginProcessing()
{
base.BeginProcessing();
if (SomeLogicTrue())
{
var errorRecord = PowershellErrorHandler.GetErrorRecord(new PipelineStoppedException("access is not supported for this context."), "InvalidExecutionContext");
ThrowTerminatingError(errorRecord);
}
}
Set the error action preference to "Stop" at the beginning of your script.
$ErrorActionPreference = "Stop"
Now, even if the script throws a none terminating error, it will stop at once.
I'd suggest you to read An Introduction to Error Handling in PowerShell as well.
I have this snippet on Windows (VS2017 Community) on Unity 5.6:
public static void setClipboardStr(string str)
{
try
{
if (Clipboard.ContainsText())
{
// ...doesn't matter if true or false -
// from here on, I can't copy+paste inside
// the game or outside until I close the app.
// If I had an error instead of try/catch or
// check if it contains text, the error will
// remain UNTIL I REBOOT (beyond the app closing).
}
}
catch(Exception ex)
{
Debug.LogError(ex);
}
}
Whenever I use Clipboard in any form, even when checking if it's text or not, it destroys the clipboard until I close the app. Now, is this a Unity bug? VS bug? Is there something I'm not understanding? What should I use, instead?
Clipboard.ContainsText is from the System.Windows.Forms namespace. These are not supported in Unity. One would be lucky to get it to compile and extremely luck to get work properly since Unity uses Mono. Also, this is not portable so don't use anything from this namespace in Unity.
What should I use, instead?
Write to clipboard:
GUIUtility.systemCopyBuffer = "Hello";
Read from clipboard:
string clipBoard = GUIUtility.systemCopyBuffer;
This should work. If not, you can implement your own clipboard API from scratch using their C++ API. You do have to do this for each platform.