How to make delphi VCL application run from the command line - batch-file

I've got a VCL application in Delphi 10.2.
User should choose a few settings and then press the "Run" button.
Now I want this app to run automatically once a day (using Task Scheduler in Windows) with the settings that the user set up already in the app. So I need a solution to run the "Run" button routine from the command line.
How can I make this app not to open the main form, but to run behind the scenes, using the chosen settings?
As far as I understand, I'm supposed to make another unit that would run some scripts from the main VCL App, would get the settings and would pass them as parameters to a function which will call the "Run" button routine.
But here I cannot figure out how can I run this unit instead of the main form when Task Scheduler is running the app and not the user.
Or maybe there is a different solution?
Can anyone help please?

You can use a command-line parameter for the exe and run the application accordingly.
To read the command line parameter use the function ParamStr (when you only use one command line parameter, you can use ParamStr(1) to read the first and only parameter).
When you need to have more command line parameters, then use ParamCount to iterate through all the available parameters.
Depending on the way you want to run your ‘Run’ code and have your config form shown, you can edit the projects .dpr file to show/create the main form when you run the app in ‘config’ mode and in the other case execute the ‘run’ code.
program MyProgram;
uses
Vcl.Forms,
ConfigFormUnit in 'ConfigFormUnit.pas' {ConfigForm},
RunCodeUnit in 'RunCodeUnit.pas';
{$R *.res}
begin
Application.Initialize;
if ParamStr(1) = 'run' then
ExecuteRunCode
else begin
Application.MainFormOnTaskbar := True;
Application.CreateForm(TConfigForm, ConfigForm);
end;
Application.Run;
end.
The routine ExecuteRunCode is a public routine in one of the units, which contains the code you want to run in 'Run' mode. (in the example the unit RunCodeUnit.pas).
unit RunCodeUnit;
interface
procedure ExecuteRunCode;
implementation
procedure ExecuteRunCode;
begin
// Code to run
end;
end.
Of course, you can create two separate programs, each having its own specific code, but this way you can use a single application if you want/need.

Probably you want to create a command line only version of your application. This is named "console mode" application in Delphi terminology (File / new / console application).
You can access command line parameters using ParamStr and ParamCount.
If you need both command line and GUI application, put all not GUI code into one unit, encapsulating it in a class or as standard function. The you can use that unit from the GUI application and from the console mode application.
By the way, it is a good design to always separate user interface from business code. You have a good example of that need here: you either use the code from a GUI application or from a command line application and you could as well use it from a service application or inside a DLL.

Related

Only allow page.save_screenshot if a window exists?

My Capybara tests are designed to call page.save_screenshot if an example fails. This is configured in a config.after(:each) block in my spec_helper.
However, not all of my tests always have a window open. Long story short, some of my tests may make a few requests using rest-client and then pass/fail based on some rspec expectations. When these type of tests fail, a browser window opens because of the call to page.save_screenshot.
Is there a way to add a conditional so that saving a screenshot is only attempted when there is a window (headless or non-headless) open?
You can probable do something like
page.save_screenshot if page.current_window.exists?
however the best options would really be to make tests that don't use the browser a different type of test (not system or feature) or add metadata to the test so that metadata can be used to determine whether or not the test uses the browser
# In your tests
it 'does something not using the browser', :no_browser do
...
end
# In your spec_helper
config.after(:each) do |example|
page.save_screenshot unless example.metadata[:no_browser]
end

Accessing a grid control with UIAutomation in .NET 5

We've got a C# GUI application (WPF) that for various reasons uses .NET Core 3.1. In that application we have a custom grid control that we would like to talk to using UIAutomation.
Our test harness application, a console app using .NET 5.0, does this:
Uses Process.Start() to run our main application.
Creates an instance of CUIAutomation.
Passes the handle of the main application's main window to IUIAutomation.ElementFromHandle() to get an IUIAutomationElement object.
Calls FindAll(TreeScope.TreeScope_Children) recursively to find all the elements. Those elements that have an automation ID are stored in a dictionary.
That all works great. In the test harness, we can load the main application, and then find the element with automation ID "ExitButton" and click on it. Woohoo!
So back in the main application, we have a grid. We assign the grid an automation ID, and when we access that element in the test harness, we can see the grid's OnCreateAutomationPeer() being called. It returns a custom automation peer object (derives from FrameworkElementAutomationPeer, also implements IGridProvider), and our peer object responds to a few basic requests (IsControlElementCore, GetAutomationIdCore) so we know the test harness & peer are talking.
So now the challenge.
How do we get our test harness to do any grid stuff?
From reading online, it seems like we need to call something like
gridElement.GetCurrentPropertyValue(GridPattern.ColumnCountProperty)
but no matter what we try, we cannot find anything named "GridPattern". It doesn't show up in any of the Windows UIAutomation DLLs, nothing in Nuget seems to help.
Where is GridPattern defined? Or are we getting at the grid testing functionality the wrong way?
EDIT #1
By manually editing our test harness' CSPROJ file and adding a reference to Microsoft.WindowsDesktop.App.WPF (as described in this question) the compiler now knows what GridPattern is. However, we're still not sure how to access grid functionality in our test harness.
EDIT #2
The compiler lets us call
GetCurrentPattern(GridPatternIdentifiers.ColumnCountProperty.Id)
in our test harness, but at runtime it throws an exception "Value does not fall within the expected range."
EDIT #3
Calling GetCurrentPropertyValue(GridPatternIdentifiers.ColumnCountProperty.Id) in the test harness works correctly to retrieve the value of the ColumnCount property on the peer object in the main application
EDIT #4
Calling GetCurrentPattern(GridPatternIdentifiers.Pattern.Id) in our test harness doesn't throw an exception, but returns an object of type System.__ComObject which is not obviously useful. Simply casting the __ComObject to a GridPattern throws the expected exception "Cannot convert type 'System.__ComObject' to 'System.Windows.Automation.GridPattern'"
EDIT #5
From looking through the source code, we discovered the GUID of UIAutomationClient.IUIAutomationGridPattern. Passing this to GetCurrentPatternAs(GridPatternIdentifiers.Pattern.Id, gridPatternGUID) works, in that it doesn't fail, but rather returns the IntPtr value 1526502600256.
Only remaining question:
There is a function GridPattern.GetItem() used to retrieve an automation element the represents a specific cell. We still don't know how to get an actual GridPattern object in the test harness.
Finally found the last piece of the magic incantation:
UIAutomationClient.IUIAutomationGridPattern gridPattern = transmitterGrid.Auto.GetCurrentPattern(GridPatternIdentifiers.Pattern.Id) as UIAutomationClient.IUIAutomationGridPattern;
This gives me a gridPattern object that, when called from the test harness, invokes the matching function in the peer in the main application.

How to set V$SESSION.PROGRAM via OCI?

Using the JDBC thin client, it is possible for the client to configure what is displayed in the PROGRAM column of the V$SESSION view (this is done by setting the CONNECTION_PROPERTY_THIN_VSESSION_PROGRAM connection property).
Is there a similar capability for a C program using OCI?
There are ways to set CLIENT_INFO, MODULE and ACTION as well as V$SESSION_CONNECT_INFO.DRIVER_INFO using the session handle, but I could find nothing for PROGRAM.
I haven't tried it, but here's an archived blog post (with example C code) on the topic. The author says you can do it by overwriting argv[0] early in your program, and OCI will send that program name to the server, e.g.
prglen = strlen(argv[0]);
strncpy(argv[0], "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx", prglen);

Runnig N times the simulation in anylogic

In my anylogic Project, I want to terminate my execution and run the simulation for N times. in each of the simulation I store my output in an excel file which depends on the run count.
Instead of stopping and running by my click, I want to do it automatically. How can I do that?
I try to use an event and write by while loop (myparm<=N) and in loop I wrote getEngine().run, but it didn't work!
if it is possible please help me.
Thanks
Below is an overview of a methodology of how you can do it using the existing simulation framework used by AnyLogic
You need to make use of the simulation setup in order to run multiple runs of the model and save the output. My suggested setup will be the following:
Have a button on your Simulation Experiment page (The first page you see when running the model) that you will use to start off the multiple model runs. In here you set the engine to not run in real time mode by using
getEngine().setRealTimeMode(false);
you might also want to set the initial seed and some other model parameters that you might also want to change and perhaps save after model execution. When you have setup the model the way you want use run() to start running the model.
Now under the Simulation Experiment setup page under the 'Java actions' section you need to specify what the model must do after it finished running the model. In the 'After simulation run' section write some code to save the data from the model into your Excel files. To access variables and objects from the model use root, e.g.
saveSomeData(root.myDataset);
where saveSomeData is a function on the Simulation page to save my data set found on the model, called myDataset, to an Excel file. It would be great to also save the seed and the specific parameters, if you changed any, to the Excel file for future reference.
Once you have saved the data output from the model you can specify a new seed and perhaps change parameters again and then call the run() again to run the model for another iteration. When the model has finished running it will again call the 'After simulation run' code here, so do put a stop condition otherwise it will just continue running one iteration after the other. You can access the number of model runs by using
getEngine().getRunCount()
Also, your model needs to have some stop condition, otherwise once it starts running it will never stop. You can specify this in the Simulation Experiment page under the 'Model time' section or programatically in your model using
finishSimulation();
In order to run the model cyclically, please use the following code in the Action field of a timeout triggered event or On destroy field of the top-level agent:
new Thread(){
public void run(){
// stops the model
getExperiment().stop();
try {
// delay
this.sleep(1000);
} catch(Exception e) {};
// runs it again
((Simulation) getExperiment()).button.action();
}
}.start();
The model results should be written to the Excel file before executing this code.
As Jaco-Ben suggested, you can specify getEngine().getRunCount() as condition of restarting the Simulation experiment.

Create Console like Progress Window

I am replacing many batch files, that do almost the exact same thing, with one WPF executable. I have the program written, but I am having troubles with my "console" like display.
Within the program I call an executable to perform a task. This executable sends it's output messages to the console. I am able to redirect those to my "console" like display with the following.
Process p = new Process();
p.StartInfo.RedirectStandardOutput = true;
p.StartInfo.RedirectStandardError = true;
p.StartInfo.UseShellExecute = false;
p.StartInfo.CreateNoWindow = true;
p.StartInfo.FileName = MyExecutable;
p.StartInfo.Arguments = MyArguments;
p.Start();
while (!p.StandardOutput.EndOfStream)
{
string _output = p.StandardOutput.ReadLine();
// This is my display string ObservableCollection
_displayString.Add(new DisplayData { _string = _output, _color = System.Windows.Media.Brushes.Black, _fontSize = 12 });
// This is to redirect to a Console & what I want to get rid of
Console.WriteLine(_output);
}
p.WaitForExit();
When the executable is done running I use a MessageBox to ask the user if they want to run the executable again. The problem is that when the MessageBox is up the user is unable to scroll on my "console" like window to make an informative decision. To temporarily work around this I launch a console at start up, see this, and write the output stream from the process running the executable to the console so the user can scroll through the information and make a decision.
I am using a listbox to display a collection of textblocks, see this. I am not attached to anything for making the display, or MessageBox'es.
How can I make a "console" like display that will take a user input (MessageBox, direct input, etc...), but also allow them to scroll through the data before they make their decision?
EDIT:
As to the comments I have been receiving I am not clear on what I am trying to accomplish. I have a batch file that runs a executable multiple times with different arguments per run. There is a lot of setup before and between the executable calls. I have created an executable to replace many flavors of a similar batch file with drop down menus for the user to change settings at run time. When the user likes their settings they click a "Start" button, and away it goes doing setups and prompting questions as it goes and then finally runs executable for the first time.
My issue is when the called executable, inside mine, is done running the user needs to decide if they want to run it again for different reasons. I need to prompt the user "Run again - 'Yes' or 'No'?", and that is where I am running into problems. A MessageBox doesn't allow me to scroll on my Progress Window. I have tried a Modeless dialog box, but with Show() the program continues on, and ShowDialog() is the same issue as the MessageBox.
Any suggestions on how to do this would be appreciated.
You are in Windows, but trying to use DOS paradigm. Windows is event-based system! You need to write "event handlers" but not put all your code in one function.
However, there is a trick, which allows to show Modal (read "blocking your code") dialog in Modeless (read "not blocking your window"). Not sure how to implement this in WPF, but idea is to re-enable your window (which acts as parent for your dialog). You need to do this in your dialog event handler (WM_INITDIALOG equivalent?).
Also (in WinAPI) you may run dialog with NULL as parent window.

Resources