Multi threaded processes in .NET via MAXScript

Hi there everyone,

I’m having a problem with setting up a dotNET process in a separate thread from 3ds Max so that I can continue working in Max while the process finishes in the background.

I read Pete Addington’s BackgroundWorker post on his site, but I don’t think I’m using it right in conjunction with dotNET processes.

Here’s the part I’m having trouble with: I want to run a separate exe file that converts texture files, but as soon as I associate a process with it and run a loop for every texture I want, it blocks Max and I can’t use it. Also a CMD appears every time it goes through the texture files and runs the individual processes, which’d be nice to get rid of too.

Here’s the snippet that takes care of the process:

local dNProcess = dotNetObject “System.Diagnostics.Process”
local dNBGWorker = dotNetObject “System.ComponentModel.BackGroundWorker”

	dNBGWorker.RunWorkerAsync
	(
		for i = 1 to filteredSceneTex.count do
		(	
			procArgs = "-memory 256 etc..."
			dNProcess.StartInfo.CreateNoWindow = True -- I thought this'd hide the CMD popping up, but it doesn't
			dNProcess.StartInfo.FileName = "C:\converter.exe" -- the program to run
			dNProcess.StartInfo.Arguments = procArgs -- passing it these arguments
			dNProcess.Start() -- starting the process
			dNProcess.WaitForExit() -- waits for the process to finish before going on with the loop, which essentially blocks this thread
		)
	)

Any idea how to make it run as I’d expect? So that the conversion loop runs in a separate thread as converting large texture files can actually take some time.

Thanks a lot in advance!

C# is pretty finicky about suppressing the cmd window when using Proc. While I haven’t tried it specifically in Max, I think if you add a

StartInfo.UseShellExecute = false;

the window should stop popping up. Taken from Mike Stall’s wonderful blog:

UseShellExecute=false means that Process.Start basically becomes a direct call to kernel32!CreateProcess. Practically, this means that now you just need to figure out how various ProcessStartInfo properties map to kernel32!CreateProcess flags. Presumably ProcessStartInfo defaults most flags to 0. The CreateNoWindow property translates to a CREATE_NO_WINDOW flag being passed to kernel32!CreateProcess, which means the console app runs without a window at all. The default is to share console windows and you need to pass the CREATE_NEW_CONSOLE flag to give the child a new console.

Another way to have the loop not stall out on you during the WaitForExit() might be to check for the target file of your converter.exe to have it modify date changed or check if it exists if you are creating a new file. Doing that instead of the WaitForExit() may alleviate some issues.

Again, I don’t have Max in front of me atm to test these .Net suggestions, but I think they should do the trick for you.

edit
I re-read your question. I think your blocking issue may be MaxScript related and not .Net. If you could modify the convertor.exe to take multiple file inputs, or easier, just dynamically write and execute a batch/python file in MaxScript to do the conversion entirely independent of Max, that may be a better solution.

[QUOTE=sl4ppy;3903]C# is pretty finicky about suppressing the cmd window when using Proc. While I haven’t tried it specifically in Max, I think if you add a

StartInfo.UseShellExecute = false;

the window should stop popping up. Taken from Mike Stall’s wonderful blog:

Another way to have the loop not stall out on you during the WaitForExit() might be to check for the target file of your converter.exe to have it modify date changed or check if it exists if you are creating a new file. Doing that instead of the WaitForExit() may alleviate some issues.

Again, I don’t have Max in front of me atm to test these .Net suggestions, but I think they should do the trick for you.

edit
I re-read your question. I think your blocking issue may be MaxScript related and not .Net. If you could modify the convertor.exe to take multiple file inputs, or easier, just dynamically write and execute a batch/python file in MaxScript to do the conversion entirely independent of Max, that may be a better solution.[/QUOTE]

The UseShellExecute property works perfectly! Thanks a lot!

However, it still hangs Max’s thread… I think I’m forcing dotNET in a wrong direction here with the BGWorker and the Process. I have a feeling I’m mixing two different things here, but as I’m not that experienced with dotNET, I don’t know where to look.

Anyways, your procedure also helped a lot, so, again, thanks a lot! :wink:

Hi Loocas,

I’d agree with chris, It’s probably a max issue - It is still picky about just how much of the interface it actually frees up - the BGW does seem to operate aside from the UI thread, so that if you are running computational operations you are fine, but the moment you use any MXS calls it limits you again. If what you are doing can be moved into a separate program as mentioned that would be the way to go. If you could provide a little more information about what sort of textures you are converting, I may be able to add a method into my Photoshop Interop wrapper that can do it, which you could trigger via a maxscript.

[QUOTE=LoneRobot;3926]Hi Loocas,

I’d agree with chris, It’s probably a max issue - It is still picky about just how much of the interface it actually frees up - the BGW does seem to operate aside from the UI thread, so that if you are running computational operations you are fine, but the moment you use any MXS calls it limits you again. If what you are doing can be moved into a separate program as mentioned that would be the way to go. If you could provide a little more information about what sort of textures you are converting, I may be able to add a method into my Photoshop Interop wrapper that can do it, which you could trigger via a maxscript.[/QUOTE]

Hmm… this is bad news :frowning:

The conversion is partially based on proprietary programs. I’m converting all the bitmaps in 3ds Max to a finalRender Block Bitmap format via Cebas’ fMapConvert that I got over at the beta forums and that’s why I’m sayint it’s “partially proprietary” as it’s not publicly available, yet.

The problem is, it’s just an EXE that performs a conversion FROM TO format with this syntax: fMapConvert C:\myBitmap.jpg D:\myBitmap.bbt with a few optional parameters.

I tried calling it via DOSCommand, but that caused to run the program as many times as many textures I had, which was bad as it wasted tons of memory. So I tried generating a FOR loop in DOS, which was a pain, but in the end it worked. The only down side was that I couldn’t pass just a string, but instead, the FOR loop required me to write the list of textures to convert in an external text file, which was not an option.

Running a ShellLaunch was the same as opening the exe as many times as many textures I had. The problem, again, was memory. The textures are quite large and the bbt files are even larger.

I haven’t tried the HiddenDOSCommand yet, but will give it a shot.

So I tried the dotNET PROCESS which allowed me to wait for one process to finish before the other started, which was great and saves tons of times. But I’m not able to detach the processing from the main 3ds Max thread so it blocks Max.

Another way could be writing just a connection that’d pass the generated list of texture files (actual texture paths) and run them extrenally via Python for example, with PythonNET or IronPython connections so I could call dotNET from that. Or screw dotNET and let it run via Python alone. But agai, it adds a bit more to the management side and I can’t ensure all the machines the script will be run on will have Python installed with all the extensions I’ll need. :frowning:

This is a bit of a bummer right now… I though dotNET could take care of the threading for me.

What about just a vanilla console c# app that meets all your extension requirements launched from Max that passes in the list of input and output files as arguments (or reads them from a text file created by MaxScript?)

[QUOTE=sl4ppy;3932]What about just a vanilla console c# app that meets all your extension requirements launched from Max that passes in the list of input and output files as arguments (or reads them from a text file created by MaxScript?)[/QUOTE]

Well… I don’t know C# at all… :confused:

bah, if you can write Python, you can write C#. :wink:

Have you tried declaring the dotNetObject “System.Diagnostics.Process” inside the background thread instead of out? Just a thought.

[QUOTE=Kameleon;3936]Have you tried declaring the dotNetObject “System.Diagnostics.Process” inside the background thread instead of out? Just a thought.[/QUOTE]

Unfortunately the same result :frowning: