Redirect and parse in realtime stdout of an long running process in vb.net - vb.net

This code executes "handbrakecli" (a command line application) and places the output into a string:
Dim p As Process = New Process
p.StartInfo.FileName = "handbrakecli"
p.StartInfo.Arguments = "-i [source] -o [destination]"
p.StartInfo.UseShellExecute = False
p.StartInfo.RedirectStandardOutput = True
p.Start
Dim output As String = p.StandardOutput.ReadToEnd
p.WaitForExit
The problem is that this can take up to 20 minutes to complete during which nothing will be reported back to the user. Once it's completed, they'll see all the output from the application which includes progress details. Not very useful.
Therefore I'm trying to find a sample that shows the best way to:
Start an external application (hidden)
Monitor its output periodically as it displays information about it's progress (so I can extract this and present a nice percentage bar to the user)
Determine when the external application has finished (so I can't continue with my own applications execution)
Kill the external application if necessary and detect when this has happened (so that if the user hits "cancel", I get take the appropriate steps)
Does anyone have any recommended code snippets?

The StandardOutput property is of type StreamReader, which has methods other than ReadToEnd.
It would be more code, but if you used the Read method, you could do other things like provide the user with the opportunity to cancel or report some type of progress.
Link to Read Method with code sample:
http://msdn.microsoft.com/en-us/library/ath1fht8(v=VS.90).aspx
Edit:
The Process class also has a BeginOutputReadLine method which is an asynchronous method call with callback.
http://msdn.microsoft.com/en-us/library/system.diagnostics.process.beginoutputreadline(v=VS.90).aspx

Related

GMOD Lua: How can I pass variables between scripts?

Im new to Lua and the gmod gamemode creation, and I'm having a bit of trouble. I want to deactivate a HUD when the game starts. I have 2 files, one the init.lua file, where a function is called that the game starts (there I want to change the value of HUD.lua) and a HUD.lua file, where the HUD is drawn and it contains the variable I want to change.
I tried multiple approaches, like referencing the script like:
hud.gameBegan = true
, but that didn't worked, so I tried also this putting into my init.lua:
SetNWBool("gameBegan", true)
and then I put this into the HUD.lua:
gameBegan = GetNWBool("gameBegan")
Lastly I tried this:
hud = AddCSLuaFile("hud.lua")
hud:gameChanged(true)
Unfortunatly, neither of these approaches worked for me, can somebody help me?
I would suggest keeping a table on the GM table for your gamemode to hold game states. This would then be synced between the server and the client using network messages.
Essentially how it will work is once the game starts, the server will send a network message to every player telling them that game started is true. When a new player joins, they will also need to know whether the game has started yet or not, and so we will also have to send a network message from the server to any new player that joins, telling them whether game started is true or false.
Once the game ends we need to also inform every player that the game has ended.
To start we need to store the states somewhere, and since whether a game has started or not relates to the gamemode it may be best to store it on the GAMEMODE table, and it also needs to be defined for the server and each player, so we should use the GAMEMODE table in gamemode/shared.lua:
GAMEMODE.States = GAMEMODE.States or {}
GAMEMODE.States.GameStarted = false
In your gamemode/init.lua file (which runs on the server) you may then add something like this:
util.AddNetworkString("MyGamemode.GameStartedSync")
function GM:SetGameStarted(hasStarted)
GAMEMODE.States.GameStarted = hasStarted
-- We have updated the state on the server, now update it
-- for each player on their client
net.Start("MyGamemode.GameStartedSync")
net.WriteBool(hasStarted)
net.Broadcast()
end
function GM:PlayerInitialSpawn(ply, transition)
BaseClass.PlayerInitialSpawn(self, ply, transition)
-- Player has joined, so send them the current game state
net.Start("MyGamemode.GameStartedSync")
net.WriteBool(GAMEMODE.States.GameStarted)
net.Send(ply)
end
If you already have a GM:PlayerInitialSpawn(ply) function then simply merge the contents of that one with yours.
(Note you will need DEFINE_BASECLASS("gamemode_base") at the top of your init.lua file to make BaseClass available if you don't already.)
In you gamemode/cl_init.lua file you need to now write the code on the player's client that can receive the network message:
net.Receive("MyGamemode.GameStartedSync", function()
local hasStarted = net.ReadBool()
GAMEMODE.States.GameStarted = hasStarted
end)
You can then set the sate of whether the game has started using GAMEMODE:SetGameStarted(true) or GAMEMODE:SetGameStarted(false) in any serverside script. And its value can be used with GAMEMODE.States.GameStarted on both the client and the server.
e.g.
if GAMEMODE.States.GameStarted then
DrawMyHud()
end
I am not familiar with GMod but maybe this can work?
In your init.lua file, you can use the include function to include the HUD.lua file and then set the variable to deactivate the HUD.
Here is an example:
include("HUD.lua") -- include the HUD file
HUDEnabled = false -- set the variable to false
In your HUD.lua file, you can then check the value of the variable before drawing the HUD:
if HUDEnabled then
-- code to draw HUD
end
You can also move the variable in the HUD.lua file so you can use it in the HUD.lua file and init.lua file.
-- HUD.lua
local HUDEnabled = true
if HUDEnabled then
-- code to draw HUD
end
-- init.lua
include("HUD.lua")
HUDEnabled = false

What is the best way in VB.Net to launch bat scripts one after the other?

I've got a VB.Net program that calls a bat script, waits for it to succeed or timeout, do some actions, calls another bat script, waits for it to succeed or timeout and so on.
What I have done is declaring the following elements :
Dim mainProcessHandler As New ProcessStartInfo()
Dim mainProcess As Process
And use them all along the program like that :
mainProcessHandler.FileName = "something_01out18.bat"
mainProcessHandler.Arguments = "-d"
mainProcessHandler.WindowStyle = ProcessWindowStyle.Hidden
mainProcess = Process.Start(mainProcessHandler)
Then further :
mainProcessHandler.FileName = "something_02out18.bat"
mainProcessHandler.Arguments = "-s"
mainProcessHandler.WindowStyle = ProcessWindowStyle.Normal
mainProcess = Process.Start(mainProcessHandler)
And a third, a fourth, etcetera: I've got multiple calls such as described above and can't convince myself that's the way to do it.
What is the correct way in VB.Net to handle this issue?
The answer to this question is that the submitted code is suitable to launch bat scripts one after the other. Any reflexion about which other way to do it could be better may lead to an opinion-based discussion which is improper on stackoverflow.

AHK: Manage multiple scripts

I got many scripts. I want to be able to manage them all in 1 in script.
What I want is that the main script will activate a certain script, then when the secondary script is done, it returns a value to the main script. After that, the main script calls another secondary script, etc...
Is there a proper way to do this?
More precise question:
Is it possible to activate a AHK script from another script AHK?
At the moment, to detect that at a secondary script is complete, the way I currently use is that right before the end of the secondary script, I press a combinaison of keys that the main script will detect. And once detected, it will increase a main script variable by one and this will trigger the activation of the next script. Is there a better way to achieve this?
The main script could call the other scripts using RunWait. The scripts could then communicate back before terminating themselves.
The best option for communication would be to use OnMessage.
The following is a working example from the documentation:
; Example: Send a string of any length from one script to another. This is a working example.
; To use it, save and run both of the following scripts then press Win+Space to show an
; InputBox that will prompt you to type in a string.
; Save the following script as "Receiver.ahk" then launch it:
#SingleInstance
OnMessage(0x4a, "Receive_WM_COPYDATA") ; 0x4a is WM_COPYDATA
return
Receive_WM_COPYDATA(wParam, lParam)
{
StringAddress := NumGet(lParam + 2*A_PtrSize) ; Retrieves the CopyDataStruct's lpData member.
CopyOfData := StrGet(StringAddress) ; Copy the string out of the structure.
; Show it with ToolTip vs. MsgBox so we can return in a timely fashion:
ToolTip %A_ScriptName%`nReceived the following string:`n%CopyOfData%
return true ; Returning 1 (true) is the traditional way to acknowledge this message.
}
; Save the following script as "Sender.ahk" then launch it. After that, press the Win+Space hotkey.
TargetScriptTitle = Receiver.ahk ahk_class AutoHotkey
#space:: ; Win+Space hotkey. Press it to show an InputBox for entry of a message string.
InputBox, StringToSend, Send text via WM_COPYDATA, Enter some text to Send:
if ErrorLevel ; User pressed the Cancel button.
return
result := Send_WM_COPYDATA(StringToSend, TargetScriptTitle)
if result = FAIL
MsgBox SendMessage failed. Does the following WinTitle exist?:`n%TargetScriptTitle%
else if result = 0
MsgBox Message sent but the target window responded with 0, which may mean it ignored it.
return
Send_WM_COPYDATA(ByRef StringToSend, ByRef TargetScriptTitle) ; ByRef saves a little memory in this case.
; This function sends the specified string to the specified window and returns the reply.
; The reply is 1 if the target window processed the message, or 0 if it ignored it.
{
VarSetCapacity(CopyDataStruct, 3*A_PtrSize, 0) ; Set up the structure's memory area.
; First set the structure's cbData member to the size of the string, including its zero terminator:
SizeInBytes := (StrLen(StringToSend) + 1) * (A_IsUnicode ? 2 : 1)
NumPut(SizeInBytes, CopyDataStruct, A_PtrSize) ; OS requires that this be done.
NumPut(&StringToSend, CopyDataStruct, 2*A_PtrSize) ; Set lpData to point to the string itself.
Prev_DetectHiddenWindows := A_DetectHiddenWindows
Prev_TitleMatchMode := A_TitleMatchMode
DetectHiddenWindows On
SetTitleMatchMode 2
SendMessage, 0x4a, 0, &CopyDataStruct,, %TargetScriptTitle% ; 0x4a is WM_COPYDATA. Must use Send not Post.
DetectHiddenWindows %Prev_DetectHiddenWindows% ; Restore original setting for the caller.
SetTitleMatchMode %Prev_TitleMatchMode% ; Same.
return ErrorLevel ; Return SendMessage's reply back to our caller.
}
Well, I'm not sure why you'd want to make one script run another one... but here are a few other methods:
Include a script in another one
but, you know you can include a script inside another one, right? That is, you can use another scripts functions in your main script.
Make sure a particular script is loaded
"I got many scripts" too. Sometimes I need to make sure that a particular one is included before I can use it, so I put this at the top:
;make sure core.ahk is loaded since it is required
#include c:\ahk\core.ahk
And you don't have to worry about it getting included more than once (unless you need it) because:
#Include ensures that FileName is included only once, even if multiple inclusions are encountered for it. By contrast, #IncludeAgain allows
multiple inclusions of the same file, while being the same as #Include
in all other respects.
Now, when I include file.ahk in main.ahk, I am assured of no problems using the functions from core.ahk that file.ahk requires. And even if I include core.ahk again in main.ahk it is no worry (unless it contains subroutines instead of just functions - in which case they get run at the point where they were included, so it's best not to put subroutines in your ahk libraries).
Use good ole' RUN on Scripts
Aside from that, you know you can always use the run command to launch an ahk script. You don't have to do all that fancy WM_SENDMESSAGE stuff.
Communicate betweenst scripts using a hidden GUI
Another way for two scripts to communicate between each other is for script #1 to keep open a hidden GUI window that has an edit box and a submit button. This window will never be shown. Now, Script #2 hunts for that message box, uses send to put a string in the edit box, and then control-click to push the submit button. Now script #1 has just received input from script #2. And you don't even have to hunt for the window if you put the windows hwnd value in both scripts (so they already know it ahead of time). This works like a charm.
Tell if a script has completed
If you use ahk's run command, there is an parameter that will give you back the PID of that process (PID = Process ID). You can use this PID to check to see if the script is running, and you can use it to terminate the process.
Also, if you use runwait - the script using that command will pause and wait for the runn-ed process to complete and close before continuing.
theoretically you could also use a file object between the scripts as a sort of stdin/stdout method as when opening a file with the file object you can set it as shared.
You could also set an environment variable and pass the name of the variable to the script ,given that you have setup argument handling in the target script, which then sets the environment variable value on closing. using RunWait and this you could find out what the return result of the script is after it runs.
Lastly, look into using a function as that is probably the "best practice" for what you are trying to do. Since a function can do anything a script can do and you can pass it an array to operate on or with using ByRef on the array param. This would mean that you don't have to write in a bunch of parameters when writing the function and the variables would release memory once the function is complete, automatically. You can even write your functions in a separate file and use #Include to use them in your script.

Maven an java.io.scanner(System.in)

I'm running into issues with my project. When running in Netbeans it seems to work fine with user interaction. However when I run using mvn test it does not. I see the command line menu but I am not prompted to make a selection. When I force terminate the project, I get an error about No Line Found.
Any Ideas? I'm stumped.
The line that isn't working is essentially:
System.out.print("1) Print String\n"
+ "0) Exit\n"
+ "Enter Selection: ");
String line = (new java.util.Scanner(System.in)).nextLine();
I see the output Similar to this:
1) Print String
0) Exit
But I don't see "Enter Selection: " and it doesn't prompt for the String input. I terminate and get "No Line Found" though after I cancel the execution I see the whole string int he "Test Results window".
It's abnormal for unit tests to pause for user interaction. I wouldn't be surprised if it acts strangely. I expect the testing libraries don't really anticipate this sort of thing.
In practice, one should not interact with user while performing JUnit tests. Tests should be designed to operate automatically and continuously. If you want to test underlying code with two separate values, two tests should be implemented and call each the underlying code with their own value. This should cover for the two options offered to your user.

"The directory name is invalid" error on Process.Start?

I am writing a launcher program, and when I go to start the process I get the "The directory name is invalid" error. Here is the code that is launching the process:
Const DEBUG_ROOT = _
"Z:\Kiosk_JC\KioskSignIn.root\KioskSignIn\KioskSignIn\KioskSignIn\bin\Debug"
Dim oKiosk As New System.Diagnostics.Process
oKiosk.StartInfo.UserName = oEnc.Decrypt(Username)
oKiosk.StartInfo.Password = oEnc.DecryptSecure(Password)
oKiosk.StartInfo.Domain = oEnc.Decrypt(Domain)
''// The AddBS function appends a '\' to the passed string if it is not present
oKiosk.StartInfo.WorkingDirectory = AddBS(DEBUG_ROOT)
oKiosk.StartInfo.FileName = "KioskSignIn.exe"
oKiosk.StartInfo.UseShellExecute = False
Dim proc As Process = Nothing
proc = System.Diagnostics.Process.Start(oKiosk.StartInfo)
I saw on another question here that I needed to set the WorkingDirectory (before I started searching I was getting the error). Even though I have this property set, I am still getting the error. Any thoughts?
More info
I should also note that my Z:\ is a on my network. I have a function that resolves a path to UNC. When I ran this function on DEBUG_ROOT, I get the same error.
I tried moving the application to c:\kiosk. Same result. I am logged in as the user I am impersonating, so I have access to all shares and files.
Here is the link, for some reason the URL formating wants to consume all the text after the link is designated:
Referred Post
Mapped drives are per-user. You are likely starting the process with a different user.
Sounds like the process can't see the Z: drive or doesn't have security access. What user context does the app run under? Perhaps the Z: drive is not available in that context.
I got the same error as you do. most likely the user you use to run the process does not have access to specified resource (exe file)
try to move your exe to some other location and/or give your user access rights to the file.