question

Martin C3 avatar image
1 Like"
Martin C3 asked Phil BoBo commented

How to let runprogram() wait for batch file (.bat) to finish

Hello,

I have a question about running batch files (.bat) with the runprogram() command. I call the batch file from my script, but I want the script to wait until the batch file has finished before the script executes the next command. How can I achieve this?

Example:

runprogram("/.../Run_Forecast.bat");

//Execute AFTER batch file has finished
pt("Batch file completed");

Any help would be greatly appreciated, thanks :)

Martin

FlexSim 18.0.3
scriptwaitrunprogrambatch file.bat
5 |100000

Up to 12 attachments (including images) can be used with a maximum of 23.8 MiB each and 47.7 MiB total.

1 Answer

Phil BoBo avatar image
5 Likes"
Phil BoBo answered Phil BoBo commented

I'm not aware of a good way to do this from FlexScript, but you can do it using C++.

The FlexScript command runprogram() is a wrapper around the C++ Windows API WinExec function.

The FlexScript command rundocument() is a wrapper around the C++ Windows API ShellExecute function.

Neither of those commands alone will wait for the spawned process to finish.

In C++, you can use the ShellExecuteEx function in conjunction with the WaitForSingleObject function to execute a batch script and wait for it to finish before moving on. See How to wait for ShellExecute to run?

Something like this:

SHELLEXECUTEINFO sei = {0};
sei.cbSize = sizeof(SHELLEXECUTEINFO);
sei.fMask = SEE_MASK_NOCLOSEPROCESS;
sei.hwnd = NULL;
sei.lpVerb = NULL;
sei.lpFile = "cmd.exe";
sei.lpParameters = apchar(concat("/C \"", modeldir(), "Run_Forecast.bat\""));
sei.lpDirectory = NULL;
sei.nShow = SW_SHOW;
sei.hInstApp = NULL;
ShellExecuteEx(&sei);
WaitForSingleObject(sei.hProcess, INFINITE);
CloseHandle(sei.hProcess);

You could add a C++ user command using the DLL Maker, and call that command from your FlexScript function in order to execute a batch file and not return until it has completed.

wait-for-bat-script-video.zip


· 4
5 |100000

Up to 12 attachments (including images) can be used with a maximum of 23.8 MiB each and 47.7 MiB total.

Martin C3 avatar image Martin C3 commented ·

Hello,

with some further help I was finally able to make this solution into a working DLL with a relative path to the bat file. I made some changes to the code @phil.bobo suggested.

This is the modified code I used to create the DLL with the DLL Maker:

// C LIBRARY FOR INPUT/OUTPUT STREAMS
#include <stdio.h>
// IN ORDER TO HAVE ACCESS TO FLEXSIM FUNCTIONS, INCLUDE FLEXSIMDEFS.H
#include "FlexsimDefs.h"
#include <shellapi.h>

visible double dllcommand1(FLEXSIMINTERFACE)
{	
	SHELLEXECUTEINFO sei = { 0 };
	sei.cbSize = sizeof(SHELLEXECUTEINFO);
	sei.fMask = SEE_MASK_NOCLOSEPROCESS;
	sei.hwnd = NULL;
	sei.lpVerb = "open";
	sei.lpFile = "Run_Forecast.bat";
	sei.lpParameters = NULL;
	sei.lpDirectory = apchar(modeldir());
	sei.nShow = SW_SHOW;
	sei.hInstApp = NULL;
	ShellExecuteEx(&sei);
	WaitForSingleObject(sei.hProcess, INFINITE);
	CloseHandle(sei.hProcess);

	return 0;
}

I attached a test model with the working DLL, bat file and cpp file.

dll-works.zip

Thanks for your help! :)

Martin

2 Likes 2 ·
dll-works.zip (327.0 KiB)
Stefan Haeussler avatar image Stefan Haeussler Martin C3 commented ·

Thank you for your instructions on that topic - they are very helpful.

I used your code to call CPLEX via a batch file and wanted to add my experiences to this topic: The procedure does not work with Flexsim 16, but it certainly does with Flexsim 17 update 2.

Best regards,

Stefan

0 Likes 0 ·
Phil BoBo avatar image Phil BoBo ♦♦ Stefan Haeussler commented ·

The procedure here is just describing adding C++ code via an external DLL. You can do that with any version of FlexSim.

The DLLMaker project is version-controlled. If you get the tip, then it will only work with 17.2 or later. If you want to target an older version, then you need to get an older version of the DLLMaker project, not the tip:

https://bitbucket.org/flexsim/flexsim-dll-maker/commits/all

1 Like 1 ·
Martin C3 avatar image Martin C3 commented ·

***This issue has been resolved, see my later comment***

Hello phil,

first of all thank you very much for providing such a detailed solution. I have created a DLL with the DLL Maker and implemented the code you provided. The DLL builds and can be attached to a user command in Flexsim. However, when the user command is executed cmd opens, but the batch file is not executed. When I close the cmd manually the command returns and executes the rest of the script. So in principal it works, just that the batch file is not executed.

Changing the code like this seems to resolve the issue:

#include <stdio.h>
#include "FlexsimDefs.h"
#include <shellapi.h>

visible double dllcommand1(FLEXSIMINTERFACE)
{	
	SHELLEXECUTEINFO sei = { 0 };
	sei.cbSize = sizeof(SHELLEXECUTEINFO);
	sei.fMask = SEE_MASK_NOCLOSEPROCESS;
	sei.hwnd = NULL;
	sei.lpVerb = "open";
	sei.lpFile = "C:/Users/Martin/Documents/FlexSim 2018 Projects/Run_Forecast.bat";
	sei.lpParameters = NULL;
	sei.lpDirectory = NULL;
	sei.nShow = SW_SHOW;
	sei.hInstApp = NULL;
	ShellExecuteEx(&sei);
	WaitForSingleObject(sei.hProcess, INFINITE);
	CloseHandle(sei.hProcess);

	return 0;
}<br>

This this command will execute the batch file and will wait to return until it has finished. The only caveat here is that I have to use the absolute filepath. I have tried to use the relative path similar to the first solution. The issue is that modeldir() returns the directory with backslashes.

I have tried this crude approach to fix this in the DLL:

But this does not build. How can I get this relative path to work?

P.S. I added two test models that I used.

Model 1 (Original) uses a DLL with your original solution. When I try to run the user command that attaches to the DLL only the cmd opens.

Model 2 (Modified) uses my altered solution that works, but has the filepath for the bat-file hardcoded in the DLL. It would therefore only work on my machine. I included the altered mydll.cpp template that I used to create the DLL.

dll-test.zip

1 Like 1 ·
dll-test.zip (656.2 KiB)
relative-path.png (4.6 KiB)