Notifications
Clear all

[Closed] Problem with "custom" hiddenDosCommand

Hey guys!

I really need your help on this one.

So, let’s see. Basically what I’m trying to do is to pass commands to dos through maxscript.
So as you know there are a couple of ways to do this:

ShellLaunch
DosCommand
HiddenDosCommand

ShellLaunch and DosCommand are a problem for two reasons. First they are not hidden, which is bad :P. But they also don’t work at all, when I have to pass multiple strings. Someone in the forums confirmed this, I read it not too long ago, but I couldn’t find the post.
HiddenDosCommand should not be a problem, but, it doesn’t seem to work properly with 3dsMax 9. Yes I have AVG installed, but it’s giving me a CreateProcess error. I also noticed when uninstalling TexTools from a 3dsMax 9 installation, the same error pops up when it attempts to delete folders.

So my solution (or so I thought), was to grab this guys function:
http://mmw2008.5d6d.com/archiver/tid-16583.html
and change it a bit so it matches my needs:

fn HiddenDotNetCommand Command=
(
	Process = DotNetObject "System.Diagnostics.Process"
	Process.StartInfo.WindowStyle   =  (DotNetClass  "System.Diagnostics.ProcessWindowStyle").Hidden
	Process.StartInfo.CreateNoWindow   =   true
	Process.StartInfo.UseShellExecute   =   false
	Process.StartInfo.RedirectStandardInput = true;
	Process.StartInfo.RedirectStandardOutput = true;
	Process.StartInfo.FileName = "cmd.exe"
	Process.Start()
	Process.StandardInput.WriteLine(Command)
	Process.StandardInput.WriteLine("exit")
	Process.WaitForExit()
)

Now I have tested it a couple of times here.
Windows 7 – 64bit
all 3dsMax versions, 3dsMax 9 SP2 – 3dsMax 2012
I have also tested it on another PC:
Windows 32-bit 3dsMax 9 SP2

In both cases it works 100%

But, now I have had two reports, where I have been able to confirm that this function does not work!
The specs of the PCs were:
-Windows 7 – 64bit, 3dsMax 2012
-Windows XP – 32bit, 3dsMax 2011
Since I don’t have access to those PCs it’s hard for me to debug, I also don’t know enough dotnet yet to be able to understand what might be wrong.

Do you guys have any idea why this would not work on some PCs?

26 Replies

i don’t see any problem in the code. i think that the dos command itself doesn’t execute right on those machines for any reason. ask the person who reported the problem to executed the command directly using the command prompt window, and give you a result.

1 Reply
(@norman3d)
Joined: 11 months ago

Posts: 0

Yes I tried this. I’m 100% sure it has nothing to do with the command being wrong, no problems with quotation or anything like that.

wow thanks! O_O

Do you mind helping me out one last time? I’m trying to pass this command with your function:

"C:\\md5.exe" -o"C:\md5hash.tmp" "C:\TheFile.exe"

The md5hash.tmp should be created as a result of this command.
However I’m not sure if I’m doing it right since the file is not being created:

dnp.execute ("\"" + "C:\\md5.exe" + "\"") "" ("-o" + "\"" + "C:\\md5hash.tmp" + "\"" + " " + "\"" + "C:\\TheFile.exe" + "\"")

I ran into problems handling error output that was returned by the Perforce command line processor. I ran into the kinds of deadlock issue discussed here reading stderr

I eventually gave up on a pure MXS solution and started using asynchronous reads through a delegate in a C# snippet:

fn CreateDotNetProcessor = 
(
	if dotnet.GetType "DotNetProcessor" == undefined do
	(
		format "Constructing .NET Processor...
"
		-- If layout of this text looks ragged, press ctrl-F11 to switch to a monospaced font...
		sb = ""
		sb +="    using System;																			
"
		sb +="    using System.Text;																	
"
		sb +="    using System.Diagnostics;																
"
		sb +="																							
"
		sb +="    class DotNetProcessor																	
"
		sb +="    {																						
" 
		sb +="																							
"
		sb +="		private static StringBuilder stdOut = null;											
"
		sb +="		private static StringBuilder stdErr = null;											
"
		sb +="																							
"
		sb +="		public String Output() { return stdOut == null ? \"\" : stdOut.ToString(); }		
"
		sb +="		public String Errors() { return stdErr == null ? \"\" : stdErr.ToString(); }		
"
		sb +="																							
"
		sb +="		public void Execute(String filename, String args, String stdinbuff)					
"
		sb +="		{																					
"
		sb +="			Process p = new System.Diagnostics.Process();									
"
		sb +="																							
"
		sb +="			stdErr = new StringBuilder();													
"
		sb +="			stdOut = new StringBuilder();													
"
		sb +="			                               													
"
		sb +="			p.EnableRaisingEvents = true;													
"
		sb +="			p.StartInfo.FileName = filename;												
"
		sb +="			p.StartInfo.Arguments = args;													
"
		sb +="			p.StartInfo.WindowStyle = System.Diagnostics.ProcessWindowStyle.Hidden	;		
"
		sb +="			p.StartInfo.UseShellExecute = false;											
"
		sb +="			p.StartInfo.RedirectStandardOutput = true;										
"
		sb +="			p.StartInfo.RedirectStandardError = true;										
"
		sb +="			p.StartInfo.RedirectStandardInput = stdinbuff.Length > 0;						
"
		sb +="																							
"
		sb +="			p.StartInfo.CreateNoWindow = true;												
"
		sb +="																							
"
		sb +="			p.OutputDataReceived += new DataReceivedEventHandler(OutputHandler);			
"
		sb +="			p.ErrorDataReceived += new DataReceivedEventHandler(ErrorHandler);				
"
		sb +="																							
"
		sb +="			p.Start();																		
"
		sb +="																							
"
		sb +="			if (stdinbuff.Length > 0)														
" 
		sb +="			{																				
"
		sb +="				p.StandardInput.Write(stdinbuff);											
"
		sb +="				p.StandardInput.Close();													
"
		sb +="			}																				
"
		sb +="																							
"
		sb +="			stdErr = new StringBuilder();													
"
		sb +="			stdOut = new StringBuilder();													
"
		sb +="																							
"
		sb +="			p.BeginOutputReadLine();														
"
		sb +="			p.BeginErrorReadLine();															
"
		sb +="																							
"
		sb +="			p.WaitForExit();																
"
		sb +="			p.Close();																		
"	
		sb +="		}																					
"
		sb +="																							
"
		sb +="		private static void OutputHandler(object sendingProcess, DataReceivedEventArgs d)	
"
		sb +="		{																					
"
		sb +="			if (!String.IsNullOrEmpty(d.Data)) stdOut.Append(d.Data + \"\
\");				
"
		sb +="		}																					
"
		sb +="																							
"
		sb +="		private static void ErrorHandler(object sendingProcess, DataReceivedEventArgs d)	
"
		sb +="		{																					
"
		sb +="			if (!String.IsNullOrEmpty(d.Data)) stdErr.Append(d.Data + \"\
\");				
"
		sb +="		}																					
"
		sb +="   }																						
"

		csharpProvider = dotnetobject "Microsoft.CSharp.CSharpCodeProvider"
		compilerParams = dotnetobject "System.CodeDom.Compiler.CompilerParameters"
		
		compilerParams.ReferencedAssemblies.Add("System.dll");
		
		compilerParams.GenerateInMemory = true
		compilerResults = csharpProvider.CompileAssemblyFromSource compilerParams #(sb)
		
		if (compilerResults.Errors.Count > 0 ) then
		(
			errs = stringstream ""
			for i = 0 to (compilerResults.Errors.Count-1) do
			(
				err = compilerResults.Errors.Item[i]
				format "Error:% Line:% Column:% %
" err.ErrorNumber err.Line \                                              
													 err.Column err.ErrorText to:errs 
			)
			MessageBox (errs as string) title: "Errors encountered while compiling C# code"
			format "%
" errs
			return undefined
		)
	)
	dotnetobject "DotNetProcessor"	
)

-- Tests
(
	dnp = CreateDotNetProcessor()
	
	-- Run a dir command
	dnp.execute "cmd.exe" "/K dir c:\\" ""
	format "output:
%" (dnp.output())
	format "errors:
%" (dnp.errors())
		
	-- Read from stdin
	dnp.execute "cmd.exe" "" "echo foo
echo fee
"
	format "output:
%" (dnp.output())
	format "errors:
%" (dnp.errors())
		
	-- Launch an app and wait for it to complete
	dnp.execute "calc.exe" "" ""
	format "output:
%" (dnp.output())
	format "errors:
%" (dnp.errors())	
		
	-- Do some ftp fiddling (using stdin)
	dnp.execute "ftp" "" "status
! echo \"Escape to the shell!
!dir c:\\\"
status
quit"
	format "output:
%" (dnp.output())
	format "errors:
%" (dnp.errors())	

	-- Query Perforce (if you have it)
	dnp.execute "p4" "info" ""
	format "output:
%" (dnp.output())
	format "errors:
%" (dnp.errors())	
)

It’s not pretty, but it stopped hanging…
.biddle

[edit: missing a semi-colon]

Mike Biddlecombe
                   Animation Programmer
                   United Front Games
           		         Vancouver,  		 		         Canada

I didn’t know you were in Vancouver these days Mike!

1 Reply
(@biddle)
Joined: 10 months ago

Posts: 0

Go Canucks Go!

I think all you need to do is swap the ‘args’ that are passed to the executable and the ‘stdin’ input that will be sent to the program once it’s up and running (which is usually not required and can be left empty)

exe = "C:\md5.exe"
args = "-o\"C:\\md5hash.tmp\" \"C:\\TheFile.exe\""
input = ""

dp.execute exe args input

See if this works. Cheers!

fn HiddenDotNetCommand Command=
(
	Process = DotNetObject "System.Diagnostics.Process"
	Process.StartInfo.WindowStyle   =  (DotNetClass  "System.Diagnostics.ProcessWindowStyle").Hidden
	Process.StartInfo.FileName = "cmd.exe"
	Process.StartInfo.Arguments = "/C " + Command
	Process.Start()
)
1 Reply
(@norman3d)
Joined: 11 months ago

Posts: 0

Hey Kameleon, just a quick update. Seems like your code, just like the one I was using, is not working on the guy’s machine. But biddle’s code seems to work 100% of time across all machines so far.

How about if you remove the StartInfo ? Just run the Process.Start (“cmd.exe /C ” + Command) ?

1 Reply
(@norman3d)
Joined: 11 months ago

Posts: 0

I tried this…

fn HiddenDotNetCommand Command=
(
	Process = DotNetObject "System.Diagnostics.Process"
	Process.StartInfo.WindowStyle   =  (DotNetClass  "System.Diagnostics.ProcessWindowStyle").Hidden
	Process.Start(Command)
)

HiddenDotNetCommand ("cmd.exe /C " + "\"" + getDir #userscripts + "\\md5.exe" + "\"" + " " + "-o\"" + "C:\\md5hash.tmp" + "\"" + " " + "\"" + (getDir #userscripts + "\	heFile.exe") + "\"")

and I get the following error:

-- Runtime error: dotNet runtime exception: The system cannot find the file specified

Can’t you start the md5.exe process directly using Process.Start?
Or if you’re specifically after MD5, try this: http://msdn.microsoft.com/en-us/library/system.security.cryptography.md5.aspx

1 Reply
(@norman3d)
Joined: 11 months ago

Posts: 0

Yes this works, but trying to pass arguments directly through Process.Start instead of using Process.StartInfo.Arguments fails.

I have no problem using biddle’s code, it really does seem to work

I need it for more than MD5 hash, but as a matter of fact, I tried this code by Kameleon and it seemed to work, except with 3dsMax 9, that’s why I switched to command line tool. (I can’t really remember what the error message was)

My guess is that you have something wrong when constructing the string, I saw that you have the file between “” and the arguments out which is correct but maybe debugging that whole string again might help to find a working solution.

About the md5 function in max 9 not working maybe it’s some conversion types that work differently in Max 9, I remember having those kind of problems.

1 Reply
(@norman3d)
Joined: 11 months ago

Posts: 0

I don’t think the string was the problem. The function works fine on 95% of the machines, so the string can’t be “randomly” wrong. As a matter of fact I’m using this with MaxScriptManager, and a couple hundred people have installed it successfully. Only two have reported having a problem, and I’ve been able to pin point that the function was the root of the problem. As to why it’s causing problems in some machines. I’m absolutely clueless. :surprised

Page 1 / 2