LOGIN  |  REGISTER
Smart Living Made Brilliant!
CASTLEOS FORUM

HomeScripting

A forum for information about scripting with CastleOS. Get samples, suggestions, and other help here

IO Redirection Example Messages in this topic - RSS

Nick Bento
Nick Bento
Posts: 221


12/31/2014
Nick Bento
Nick Bento
Posts: 221
Hello All,
I figured I'd post an example of how to do IO redirection with C#. This is useful for having castleOS access output from another program. For an example, I'll discuss a program I threw together with some help from examples I found via google (many are from MSDN and dotnetperls.com, as well as stackoverflow.com. These are all excellent sources to check out for example codes, questions, and how-to's) which gives me an overview of my systems.
The forum posts I used from there to get me on the right path are the following, if you'd like to try and write a similar program:
http://stackoverflow.com/questions/262280/how-can-i-know-if-a-process-is-running
http://stackoverflow.com/questions/1393711/get-free-disk-space
http://stackoverflow.com/questions/278071/how-to-get-the-cpu-usage-in-c

Here is sample output from running the program i wrote:
Processor utilization is at 32.88%. Available system memory is 4.8 Giggabytes. Could not scan drive for storage space. Plex Media Server is not currently running.

Now what would be neat is if we have CastleOS speak this back to me when I ask "System Status" (or whatever custom command I'd like).
We can do that! We do the following:
  • Take our compiled program we wrote (SystemStatus.exe), and place it into C:\ProgramData\CastleOS\CastleOS Kinect Service\Scripts

  • Write a C# script to access it, retrieve the output of it, and then have CastleOSKinectService speak this. We also place that script into C:\ProgramData\CastleOS\CastleOS Kinect Service\Scripts. In this example we call this script SystemStatus-Interface.cs

  • Go into the Kinect Configurator, create the custom command we want recognized (in my case, it is "System status"), and assign the SystemStatus-Interface.cs script to it, then save.

I have attached the interface script I wrote, using the example from http://www.dotnetperls.com/redirectstandardoutput and modifying it a little bit to work with CastleOS. I'll give a brief overview of how it works and what's going on.


There are 4 'using' statements. Using is like an import, it tells the code what libraries to look into for commands. We use System, System.Diagnostics (which is for querying CPU/Memory status), System.IO (used to check disk space, and also related to Memory/CPU), and the CastleOSKinectService library, which is where our CastleOS API calls come from (in this case, the ScriptingAPI.Speak method).

The structure of a script for CastleOS is generally of the form public class [your_class_name], which then encompasses the public void Main(string[] args){} method, which is required for all scripts. This is what's referred to as the entry point of the script, it's the code that gets executed to get things rolling.

Within this Main method is the code that will run. The first block of code makes a call to the ScriptingAPI.Speak method, to have castleOS speak "Scanning Systems"; this is mainly to let us know that the script is running, as the call to our SystemStatus program takes a few seconds to execute and return.
We then use ProcessStartInfo to call upon our SystemStatus.exe. Note that we tell it to not use Shell Execution (it will run in the background instead of launching a new shell window), and we tell it we want to Redirect its output (this will capture the output into a variable we can read from once the program finishes).

We then launch the process. When we use the 'using' keyword in the code block, we basically create a temporary variable on the fly that we can use for other processing; once that block of code finishes executing, the variable is no longer used and is deallocated. In this case, we're creating a temporary variable of type Process called 'process', and assigning it the result of running our process we created just before this code (which we called 'start').

Within that using block, we then use what's called a StreamReader that we are calling 'reader' to read the result of the process we ran. We read the result by looking at process.StandardOutput, which is the captured output of SystemStatus.exe that we ran. Then, we tell the reader to read all that data into a string variable we call 'result'.

Now that we have the string we want to speak stored into that 'result' variable, we can have the CastleOS Speak method simply speak that string by passing it as an argument to the method:
ScriptingAPI.Speak(result);

This will have the CastleOSKinectService speak "Processor utilization is at 32.88%. Available system memory is 4.8 Giggabytes. Could not scan drive for storage space. Plex Media Server is not currently running." as in the example above.

This is a slightly more complex example of what can be done with the scripting system. If you have any questions feel free to ask! smile
edited by nikku on 12/31/2014
edited by nikku on 12/31/2014

--
-Nick B.


0 link
Chris Cicchitelli
Chris Cicchitelli
Administrator
Posts: 3350


12/31/2014
Chris Cicchitelli
Chris Cicchitelli
Administrator
Posts: 3350
Nick, thanks for posting this! I've pinned it to the top of the board. This is a great example of how to do some more advanced automation. With this method the sky is the limit!
0 link
Dominique Garcia
Dominique Garcia
Posts: 55


1/3/2015
Dominique Garcia
Dominique Garcia
Posts: 55
Yes, thank you for this Nick.

I think what we need for CastleOS is some sort of engine generator. An engine that writes the script for you after the users basically plugs in the correct information. Having something like this would make it more likely for users of CastleOS who have no knowledge of actual scripting, to get started with custom commands. I feel the scripting portion of CastleOS is its biggest crutch. Realistically, not everyone who will find CastleOS desirable will be C# programmers. There will be people who want it to work with an easy way to customize it out-the-box.

I have some (not a lot) of experience with scripting and my biggest problem is syntax. Event code, and task code, and of course how to tie them in together. Recently I have begun a learning binge on scripting. I want to learn so I can use CasteOS to its fullest potential (plus teach others on how to script). I also have other programs that open up with scripting so CastleOS is not my only reason as to why I am learning.
edited by dominique on 1/3/2015
0 link
Nick Bento
Nick Bento
Posts: 221


1/3/2015
Nick Bento
Nick Bento
Posts: 221
I think the thing here is that CastleOS is trying to best cater to both audiences; On the one side we have people who really just want a product jam packed with features that has a pretty simple setup and just works out of the box, and on the other side you have people who want to add tons of their own features and expand the capabilities of the software beyond its out of box features. I think it does a great job of both personally smile.
That said I feel in my opinion that the introduction of the scripting engine isn't really a crutch, but more of a potential to improve the software even further. Some of the biggest home automation softwares out there have a scripting engine like castleOS's (not naming any, but many use VB.NET and C#, and some even use C++). I actually was using a software before this that was heavy on VB.NET, and had an even more complicated scripting API than CaslteOS's, haha.
Having an engine generator is a bit of a difficult problem, mainly due to the problem domain. There are literally infinite possible scripts you can write, an engine generator wouldn't really be able to cover every scenario. It's possible a generator could be made to create basic use cases, such as setting up speech events or opening programs, but beyond that I couldn't really see how it would work. I'd be curious to see some examples of what you have in mind? I could potentially create a program that would take in arguments and generate the appropriate script for simple stuff, but anything complex like the couple examples I've posted wouldn't necessarily be possible (mainly because you'd need to compile the code, and a generator wouldn't realistically be able to generate that kind of code from just a few arguments).

--
-Nick B.
0 link
mario rodriguez
mario rodriguez
Posts: 21


1/3/2015
mario rodriguez
mario rodriguez
Posts: 21
hello I am unable to execute any program with this script..the only thing I can here is the scanning system.. .. this is the code I am using please advise.

-----------------------------
using System;
using System.Diagnostics;
using System.IO;
using CastleOSKinectService;
public class SystemStatusInterface
{
public void Main(string[] args)
{
//
// Setup the process with the ProcessStartInfo class.
//
ScriptingAPI.Speak("Scanning systems.");
ProcessStartInfo start = new ProcessStartInfo();
start.FileName = @"C:\elpis\elpis.exe"; // Specify exe name.
start.UseShellExecute = false;
start.RedirectStandardOutput = true;
//
// Start the process.
//
using (Process process = Process.Start(start))
{
//
// Read in all the text from the process with the StreamReader.
//
using (StreamReader reader = process.StandardOutput)
{
string result = reader.ReadToEnd();
ScriptingAPI.Speak(result);
}
}
}
}
0 link
mario rodriguez
mario rodriguez
Posts: 21


1/3/2015
mario rodriguez
mario rodriguez
Posts: 21
this is a different directory .. stil not executing the program.. I am using "elpis" to open pandora as an .exe

--------------------------------

using System;
using System.Diagnostics;
using System.IO;
using CastleOSKinectService;
public class SystemStatusInterface
{
public void Main(string[] args)
{
//
// Setup the process with the ProcessStartInfo class.
//
ScriptingAPI.Speak("Scanning systems.");
ProcessStartInfo start = new ProcessStartInfo();
start.FileName = @"C:\ProgramData\CastleOS\CastleOS Kinect Service\Elpis\Elpis.exe"; // Specify exe name.
start.UseShellExecute = false;
start.RedirectStandardOutput = true;
//
// Start the process.
//
using (Process process = Process.Start(start))
{
//
// Read in all the text from the process with the StreamReader.
//
using (StreamReader reader = process.StandardOutput)
{
string result = reader.ReadToEnd();
ScriptingAPI.Speak(result);
}
}
}
}
0 link
Nick Bento
Nick Bento
Posts: 221


1/3/2015
Nick Bento
Nick Bento
Posts: 221
Hey,
This script opens a program in the background, then speaks its output. It's not for launching a program in the foreground, you would need slightly different code to achieve that.

--
-Nick B.
0 link
Dominique Garcia
Dominique Garcia
Posts: 55


1/3/2015
Dominique Garcia
Dominique Garcia
Posts: 55
Nick Bento wrote:
Hey,
This script opens a program in the background, then speaks its output. It's not for launching a program in the foreground, you would need slightly different code to achieve that.


This is an example of what I was talking about. Status or event code versus code that accomplish some sort of task. People with little programming knowledge do not know the difference. I am not personally attacking hormones, however I will use his post as an example. To him he does not know there needs to be a completely different set of code to accomplish a task.

We need some sort of roadmap here. Even someone with a little experience is finding it difficult without a roadmap.

We already have an example of code that ask for a status. I.E. System status or in the other thread Plex on deck status

Can you show us an example of a task? For example launching a program and then also executing keyboard shortcuts of whatever is launched?
0 link
Chris Cicchitelli
Chris Cicchitelli
Administrator
Posts: 3350


1/3/2015
Chris Cicchitelli
Chris Cicchitelli
Administrator
Posts: 3350
Hey Guys, again I want to suggest you bring these types of questions to the C# forums at Microsoft and StackOverflow. This really isn't a CastleOS, or even scripting, question. What you're looking for info on is really outside the scope of CastleOS. It's not that we don't want to help, but those two forums have thousands of users daily living and breathing this. They'll be able to help far more than those here...
edited by ccicchitelli on 1/3/2015
+1 link
mario rodriguez
mario rodriguez
Posts: 21


1/3/2015
mario rodriguez
mario rodriguez
Posts: 21
Hello Chris, I have been looking online for codes to open .exe programs but no luck yet.. can someone here help me out on this? I am sure is similar to the code to get system status?
0 link
Chris Cicchitelli
Chris Cicchitelli
Administrator
Posts: 3350


1/3/2015
Chris Cicchitelli
Chris Cicchitelli
Administrator
Posts: 3350
+1 link
mario rodriguez
mario rodriguez
Posts: 21


1/28/2015
mario rodriguez
mario rodriguez
Posts: 21
Hello Chris, Nick and I have been exchanging codes to open any .exe progam but have not been successful. CastleOS can process the command like open paint.exe. windows event viewer looks normal with no errors, task manager will open the program in the background but does not actually open. CastleOS looks like it does not allow the program to launch in the foreground. I am not sure if this is a bug? please advise.
0 link
mario rodriguez
mario rodriguez
Posts: 21


1/28/2015
mario rodriguez
mario rodriguez
Posts: 21
this is the code I am using.. also I am able to run the code in visual basic its able to open the program no problem ;

using System;
using System.Diagnostics;
using System.IO;
using CastleOSKinectService;

public class LaunchPaint
{
public void Main(string[] args)
{
ScriptingAPI.Speak("Launching Paint.");
Process.Start(@"mspaint.exe");
}
}
0 link
Nick Bento
Nick Bento
Posts: 221


1/29/2015
Nick Bento
Nick Bento
Posts: 221
Just to add some more data, I also tried to make a sort of 'delegate' program that would launch another program if provided with it's name/arguments, and called that from the script within CastleOS, and the same result occurred. It seems that the process may be launching under the context of the service, and is not visible to the foreground user (as the process does run and appears in task manager, but you can't interact with it; it's a background process at that point). I tried to do some digging to see if there was a way to get around that, but couldn't figure anything out :-/

From what I read this may be by design. It seems that from Windows Vista forward, services are not allowed to interact with the desktop and do not have a UI, so if a service tries to launch an application, it will launch but will not have a UI or anything a user can interact with it seems. Not sure if you have any ideas of a way around that. Only thing I could think of is having some sort of startup program that can receive commands (perhaps via network messages or a RESTful type of interface) that would allow it to kick off programs, since it is in the foreground and can handle that without being constrained to the restrictions of a service. If you think that would be the best solution to the problem I could try to code something up to handle that smile

Did some more digging and I think the solution I'm thinking of may be the best route. Seems Microsoft highly frowns against services launching applications. I'll build a simple application that can receive a command over a socket to launch an application. Should only take me a few days to cobble together in my free time.
edited by nikku on 1/29/2015
edited by nikku on 1/29/2015

--
-Nick B.
0 link
Chris Cicchitelli
Chris Cicchitelli
Administrator
Posts: 3350


1/29/2015
Chris Cicchitelli
Chris Cicchitelli
Administrator
Posts: 3350
Hey Guys, for non GUI apps, you can use the process start method mentioned above. But you are correct that GUI apps are restricted for security reasons. It is possible, but it's much more tricky. You'll need to wrap a C++ method in C# and then call that method (CreateProcessAsUser) instead of Process.Start.

More info:
http://stackoverflow.com/questions/267838/how-can-a-windows-service-execute-a-gui-application
http://stackoverflow.com/questions/418119/c-sharp-run-windows-form-application-from-service-and-in-vista
http://stackoverflow.com/questions/9095909/createprocessasuser-creating-window-in-active-session
0 link
Nick Bento
Nick Bento
Posts: 221


1/30/2015
Nick Bento
Nick Bento
Posts: 221
Hey All,
I wrote a pair of programs that will get around the issue, and are actually pretty neat/useful in general!
It consists of a Server (CastleOSAppLaunchServer.exe) and a client (CastleOSAppLaunchClient.exe). You run the server app on whatever machine you want to have applications launch on. Then, on whatever machine you want to be able to launch voice commands to launch apps with (machine with Kinect), you place the client program on it (I would put it in the scripts folder for simplicity). Then in your custom script, you just use Process.Start to call the client app and pass it the ip address of the machine running the server, along with the name of the program you want to launch and any arguments you want to pass it. For example, I can put the following in my script to launch internet explorer and have it navigate to google.com:
Process.Start(@".\CastleOSAppLaunchClient.exe", "192.168.0.130 iexplore.exe www.google.com");

The neat thing about this is that I could launch apps on devices that aren't even running the CastleOS Kinect Service (for instance, I could have it launch internet explorer on my windows 8.1 tablet and have it navigate to the CastleOS Portal for my system by saying "Jarvis, Bring up the control interface") smile

Also of note, if you wanted the server program to automatically run when the system starts, it would have to be added to the startup programs for the user that logs into the system, it can't be used as a service (otherwise we'd just be right back to our original problem haha!)

Try them out and let me know if it helps, thanks!

Also this was a quick buildout, I plan to recode the server portion to run in the system tray instead of a console window, but figured for now I'd post this version for people to try out and report any issues smile

EDIT: I'm rewriting/renaming parts of the app as I found a bug, and also realised I probably shouldn't use the CastleOS name in it, particularly without expressed permission wink
edited by nikku on 1/30/2015
edited by nikku on 1/30/2015
edited by nikku on 1/30/2015

--
-Nick B.
+1 link
Landon Beasley
Landon Beasley
Posts: 11


1/30/2015
Landon Beasley
Landon Beasley
Posts: 11
Nick
That is great! thank you for making up such a cool app!


Landon
0 link
Nick Bento
Nick Bento
Posts: 221


1/30/2015
Nick Bento
Nick Bento
Posts: 221
No Problem. I found a bug in that version though so I removed it. I'm gonna upload a better version later on that will be much better smile
I'll edit my original post once it's ready.
edited by nikku on 1/30/2015

--
-Nick B.
+1 link
mario rodriguez
mario rodriguez
Posts: 21


1/30/2015
mario rodriguez
mario rodriguez
Posts: 21
Thanks Nick! hope this works smile
0 link
Nick Bento
Nick Bento
Posts: 221


1/30/2015
Nick Bento
Nick Bento
Posts: 221
This is an update of my original post with the new Application and fixes. The server now runs in the system tray instead of a console window smile

Hey All,
I wrote a pair of programs that will get around the issue, and are actually pretty neat/useful in general!
It consists of a Server (RemoteAppLaunchServer.exe inside the RemoteAppLaunchServer folder) and a client (RemoteAppLaunchClient.exe). You place the RemoteAppLaunchServer folder (which includes the executable as well as assistance files it needs to run) on whatever machine you want to have applications launch on and execute the exe. This will launch an application that shows up in your system tray. If you click the little icon (it looks like a computer), it has a Console you can look at to make sure things are working. Then, on whatever machine you want to be able to launch voice commands to launch apps with (machine with Kinect), you place the client program on it (I would put it in the scripts folder for simplicity). Then in your custom script, you just use Process.Start to call the client app and pass it the ip address of the machine running the server, along with the name of the program you want to launch and any arguments you want to pass it. For example, I can put the following in my script to launch internet explorer and have it navigate to google.com:
Process.Start(@".\RemoteAppLaunchClient.exe", "192.168.0.130 iexplore.exe www.google.com");

The neat thing about this is that I could launch apps on devices that aren't even running the CastleOS Kinect Service (for instance, I could have it launch internet explorer on my windows 8.1 tablet and have it navigate to the CastleOS Portal for my system by saying "Jarvis, Bring up the control interface")

Also of note, if you wanted the server program to automatically run when the system starts, it would have to be added to the startup programs for the user that logs into the system, it can't be used as a service (otherwise we'd just be right back to our original problem haha!)

Try them out and let me know if it helps, thanks!

Also this was a quick buildout, I plan to recode the server portion to run in the system tray instead of a console window, but figured for now I'd post this version for people to try out and report any issues

--
-Nick B.


Attachments:
RemoteAppLaunch.zip
+1 link
12