Calling Maxscript Functions from DotNet

29 votes
Author: 
LoneRobot

Calling Maxscript Functions from DotNet

I thought I’d draw attention to a few enhancements that Autodesk has made
with the new release in the hope that it will help you with your
scripts. The private/public methods in struct declaration in MXS was a
welcome addition, but I was hoping for a few extras in the dotnet area
too. The main one to note is -

dotNet.setLifetimeControl {<dotNetObject> | <dotNetClass>} {#mxs | #dotnet}

This addresses the problem with
assigning event handlers to dotnet objects in max -They would be
removed as soon as Max triggered a garbage collect. I’m pleased about
this - I had resorted to inherting controls that contained the
handler’s functionality in order to use them in Max, this means that
now it is possible to avoid the problem of non-functioning controls. It
was something that I (and probably many others) had reported as a bug,
and despite the fact that I’m sure it would have been noticed anyway,
It is good that you feel that the bug submission system does actually
get read by somebody. Good job Autodesk! Have a biscuit!

I don’t tend to read The Area a great deal, but I stumbled upon a blog
by the Audodesk SDK chap Chris Diggins. He wrote that there are two
really great addtions to the dotnet toolkit this release - the most
important being the ability to invoke maxscript from within a Dotnet
assembly.

Max automatically loads an assembly called ManagedServices.dll, and in this release two classes have been added -

  • MaxscriptSDK

  • PathSDK

PathSDK is the equivalent of the old getdir
#scripts method in MXS, so is useful when you want to tie a path to a
Max install but you dont want to hardcode it, but MaxscriptSDK is a
great addition potentially to anyone thinking about tinkering with
3dsMax gubbins from dotnet.

 

ManagedServices.MaxscriptSDK.ExecuteMaxscriptCommand(MXSstring)

This method allows you to pass a
Maxscript command from an assembly as a string. In Chris D’s article he
mentioned that he’d written a script manager tool in C#. Since I
thought i’d look into this I thought i’d have a go at writing one in
VB. (I hope you don’t mind Chris!)

This is what i came up with - It’s
a custom user control which has an inherited treeview and some radio
buttons tied to the new PathSDK members. The thing to remember with
radiobuttons in dotnet is that they have a Flat style property, so can
be changed from the default radiobutton look. This means you can build
a sort of flat tabpanel very easily that contains the same sort of
functionality as a normal one, but with a more attactive look. The
treeview has the search functionality built in, and also has an
embedded imagelist so that each script type can have a different icon.
You’ll notice the encrypted scripts have a little padlock on them.

Running a maxscript from the
utility is easy with the new methods - just passing a FileIn method was
the ticket - Note the VB special characters - these are so that you can
pass a string within a string for the filename -

 

ManagedServices.MaxscriptSDK.ExecuteMaxscriptCommand(”FileIn ” & Chr(34) & e.DirectoryName & Chr(34))

This is all very straightforward.
However, when it came to handling Macroscripts, I needed to do
something else, as there are two ways of running a macro -

  • macros.run <category_string> <name_string>
  • macros.run <macro_id_integer>

This would need some info passed back to the
assembly from max - It seemed that the best way to do it was to grab
the macro id as an integer and run it via that. Fortunately, there are
a couple of methods to allow you to pass values from maxscript back
into your dotnet assembly.

  • .ExecuteColorMaxscriptQuery()
  • .ExecuteFloatMaxscriptQuery()
  • .ExecuteIntMaxscriptQuery()
  • .ExecuteStringMaxscriptQuery()
  • .ExecuteBooleanMaxscriptQuery()

Since the macroID is an integer,
the ExecuteIntMaxscriptQuery is the method we want. However, when
sending the fileIn call via the MaxscriptSDK method, It wasn’t returing
the MacroID integer, and needed a bit more coaxing to get the value
back into the assembly. Using the MaxscriptSDK methods, clicking the
tree registers a Maxscript function that takes the script path and
returns the result of a FileIn call (which does in fact give the
macroID, which is what we want. Once that is back into the dotnet
assembly, you can run it via the method listed above. While this isn’t
tricky, getting this syntax correct (including cases) seemded to be
crucial in getting it working. Although it seemed a little unnecessary,
the function is set to undefined afterwards to avoid having an
unnecessary global floating around. So the full eventhandler in VB was
as follows -

Private Sub DirectoryTree_DirectorySelected(ByVal sender As System.Object, ByVal e As MXSDotNet.DirectorySelectedEventArgs) Handles MXSDirectoryTree.DirectorySelected
Dim fileinfo As New DirectoryInfo(e.DirectoryName)
If Not fileinfo.Attributes = FileAttributes.Directory Then
If ((Control.ModifierKeys And Keys.Control) = Keys.Control) Then
Select Case fileinfo.Extension
Case Is = ".mse"
ManagedServices.MaxscriptSDK.ExecuteMaxscriptCommand("messageBox " & Chr(34) & "This is an encrypted script, so you won't be able to edit it" & Chr(34) & "Title:" & Chr(34) & "Maxscript messagebox called via DotNet" & Chr(34))
Case Else
ManagedServices.MaxscriptSDK.ExecuteMaxscriptCommand("edit " & Chr(34) & e.DirectoryName & Chr(34))
End Select
Else
Select Case fileinfo.Extension
Case Is = ".ms"
ManagedServices.MaxscriptSDK.ExecuteMaxscriptCommand("FileIn " & Chr(34) & e.DirectoryName & Chr(34))
ManagedServices.MaxscriptSDK.ExecuteMaxscriptCommand("Print " & Chr(34) & "MaxScript Run Via DotNet - " & e.DirectoryName & Chr(34))
Case Is = ".mse"
ManagedServices.MaxscriptSDK.ExecuteMaxscriptCommand("FileIn " & Chr(34) & e.DirectoryName & Chr(34))
ManagedServices.MaxscriptSDK.ExecuteMaxscriptCommand("Print " & Chr(34) & "Encrypted MaxScript Run Via DotNet - " & e.DirectoryName & Chr(34))
Case Is = ".mcr"
ManagedServices.MaxscriptSDK.ExecuteMaxscriptCommand("Fn GenerateMCRint Val = (Return (FileIn Val))")
Dim macroID As Integer = ManagedServices.MaxscriptSDK.ExecuteIntMaxscriptQuery("GenerateMCRint " & Chr(34) & e.DirectoryName & Chr(34))
ManagedServices.MaxscriptSDK.ExecuteMaxscriptCommand("macros.run " & macroID.ToString)
ManagedServices.MaxscriptSDK.ExecuteMaxscriptCommand("GenerateMCRint = undefined")
Case Else
End Select
End If
End If

You might notice the ability to CTRL click the tree to open the
script for editing, but the nice thing is that it’s all handled in the
assembly. You can download the control at the end of the article. The
nice thing about this is the code in max -

dotnet.loadassembly "C:Program FilesAutodesk3ds Max 2010ScriptsLoneRobotClassLibMXSDotNet"
rollout MXSviaDotNet "MXSviaDotNet" width:419 height:548
(
dotNetControl MaxTV "MXSDotNet.MXSBot" pos:[0,0] width:420 height:546
)
createdialog MXSviaDotNet

Minimal, eh? While this is a basic
example of what this class can do, I’m really excited about the
potential use of this new stuff in the DotNet arsenal.

Grabbing the UI Color from max as the assembly loads

One thing that these new methods
can allow you to do is probe max for the UI colors and set these in
your custom control. Normally you would have to set this in MXS in the
open handler of the dialog, and there was usually a slight pause as it
would do so. Using the following VB code implemented a method to grab
this color and build a dotnetcolor from the r,g,b float values.
Incidentaly, there looks like a method for color retrieval in this new
class, but I’m buggered if i can get it to work. I’ve posted on Chris
Diggin’s Blog on the Area to see if he can help out.

ManagedServices.MaxscriptSDK.ExecuteMaxscriptCommand("maxBackColor = colorMan.getColor #background")
ManagedServices.MaxscriptSDK.ExecuteMaxscriptCommand("maxForeColor = colorMan.getColor #text")
Dim br As Single = ManagedServices.MaxscriptSDK.ExecuteFloatMaxscriptQuery("(maxBackColor[1] * 255.0f)")
Dim bg As Single = ManagedServices.MaxscriptSDK.ExecuteFloatMaxscriptQuery("(maxBackColor[2] * 255.0f)")
Dim bb As Single = ManagedServices.MaxscriptSDK.ExecuteFloatMaxscriptQuery("(maxBackColor[3] * 255.0f)")
Dim Backcolor As Drawing.Color = Drawing.Color.FromArgb(CInt(br), CInt(bg), CInt(bb))
Dim fr As Single = ManagedServices.MaxscriptSDK.ExecuteFloatMaxscriptQuery("(maxForeColor[1] * 255.0f)")
Dim fg As Single = ManagedServices.MaxscriptSDK.ExecuteFloatMaxscriptQuery("(maxForeColor[2] * 255.0f)")
Dim fb As Single = ManagedServices.MaxscriptSDK.ExecuteFloatMaxscriptQuery("(maxForeColor[3] * 255.0f)")
Dim Forecolor As Drawing.Color = Drawing.Color.FromArgb(CInt(fr), CInt(fg), CInt(fb))
Me.MXSDirectoryTree.ProjectPath = ManagedServices.PathSDK.GetDirectoryPath(ManagedServices.PathSDK.DirectoryID.Scripts)
Me.ForeColor = Forecolor
Me.BackColor = Backcolor

What this does is automatically set the ui colors of the control without interaction, much like how the maxform class does it.

Dark UI -

Light UI -Automatically!

Video URL: