Tagged with " homepost"

Calling Maxscript Functions from DotNet

May 11, 2009 by     9 Comments    Posted under: DotNet, Technical Research

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.


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


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.


In a previous post, I worked out a method for using maxscript calls to the colorman interface from the dotnetassembly to get the max UI colours. After this post, the talented Yannick Puech contacted me and pointed me to the CUIUpdater class in managedservices. I knew max must have had something like this, but hadn’t figured it out until now. Thanks a million, Yannick!

Using CUIUpdater

You can’t create an instance of CUIUpdater via dotnetobject. You have to call the getinstance function instead.

dotnetcolorman = (dotnetclass "Managedservices.cuiupdater").getinstance()
showmethods dotnetcolorman
--   .GetButtonDarkShadow()
--   .GetButtonLightShadow()
--   .GetButtonPressedColor()
--   .GetColor aColorIndex
--   .GetControlColor()
--   .GetEditControlColor()
--   .[static]GetInstance()
--   .GetMaxColor id
--   .GetTextColor()

As you can see, there are some useful things in there to call from an assembly.

-- dotNetObject:System.Drawing.Color
-- this is the colour of the interface
-- dotNetObject:System.Drawing.Color
-- the is colour of the text (if you hadn’t already guessed)

Dark UI –

Light UI -Automatically!

download script

XML LayoutPanel – Another Custom control for 3dsMax

Mar 24, 2009 by     No Comments    Posted under: DotNet, Technical Research, User Controls

Recently, I was using a FlowLayoutPanel to provide a UI to load XML presets on a Character Pose system. The FlowlayoutPanel is a really useful UI component available in the DotNet toolbox. It is basically a container for you to place other controls into, and as a result, is perfect for dynamic interfaces. The problem in Max is that DotNet event handlers need to be specified in GLOBAL scope in order for them to register. This creates issues when using them in as part of a custom attribute or modifier plugin, the very thing (regrettably) that I wanted to do.

The way around this to build your own class that inherits the FlowLayoutPanel and adds the handlers outside of Max. Since I was going to the trouble of doing this, I thought I’d add a couple of methods I’d previously put into my HitchHiker control. Here is the control in the interface –

The control to the right is a node storage system for all of the facial bones, and the left hand interface is the custom FlowLayoutPanel. Clicking a button gives access to the XML file where the stored transforms for various facial expressions are stored.

The Panel has a single method, populate <path string> ,which searches the folder and adds a button to the layout pane for each file it finds. It will also look for a related image. This is how I wanted to show the presets. If there is a png file with the same name as the file in the folder, it will put that on the button. If that isn’t present, it checks if there is a file called “Default.png”. This was so I could put a rough icon for the storage type if I wanted. If not, the default XML icon is used. There is a compact (pictured) and larger icon and it depends on the button size if the larger one gets used. Take a look at the class diagram below for more information on other properties you can set –

Setting up the control properties in 3dsMax

Here are some examples of setting properties using this class within 3dsMax – Note the namespace of the control –

dotNetControl XMLPanel “LoneRobot.UI.Character.XMLLayoutPanel” pos:[0,186] width:161 height:336

Also note the use of the + sign when using a custom enumeration in 3dsMax –

XMLPanel.thumbsize = dotNetObject “system.drawing.size” 140 30
XMLPanel.selectedborderwidth = 2
XMLPanel.border = true
XMLPanel.textalign=(dotnetclass “System.Drawing.ContentAlignment”).middleright
XMLPanel.imagealign=(dotnetclass “System.Drawing.ContentAlignment”).middleleft
XMLPanel.sortby=(dotnetclass “LoneRobot.UI.Character.XMLLayoutPanel+SortByOptions”).filename
XMLPanel.sortby=(dotnetclass “LoneRobot.UI.Character.XMLLayoutPanel+SortByOptions”).length
XMLPanel.populate DirectoryPath

Top of the Class

The more observant may have noticed that in the event section of the class diagram was an event called Picked. If you did, then award yourself a biscuit. Picked isn’t a standard dotnet event for the FlowLayoutPanel. It is in XMLLayoutPanel though, so what is this and how does it work?

You may already be familiar with the concept of Object Orientated Programming, or OOP. It’s kind of fundamental to working with DotNet, and getting a handle on it will really improve how you approach a task. If you’re not 100% straight on what all this OOP malarkey is, I’ll try to give a brief explanation. The DotNet framework is a big orchard of classes. Almost everything (with some exceptions naturally!) is a class in dotnet. One way to look at it is that if a class was a banana, the dotnetclass would be a blueprint for what a banana is. A dotnetobject is the actual banana. And there are multiple bananas.

Buttons are classes, Forms are classes, BackgroundWorkers are classes. Each time you use a dotnetobject, you are using an instance of that class.

Now this is all very straightforward so far, and is the sort of thing that really impresses girls when you talk to them about it.

Let’s go on by looking at what happens when you press a button in dotnet. In order to handle the event, you have an eventarg attached to it. This has relevant information pertaining to the button being pressed, like the x and y position of the mouse etc. However, on an XMLLayoutPanel, that sort of information isn’t really useful. What you need is something that will convey the data you need after the event is fired.

You’ll remember at the start that I talked about inheriting classes like the FlowLayoutPanel to make the XMLLayoutPanel. Where it gets more interesting is when you realize that the eventarg is also a class. This means you can inherit them also, and change the functionality to something you want to use.

For the XMLLayoutPanel, all that you really need is the path and filename to the XML file you have picked. Since the system.eventarg tied to a button press doesn’t have that kind of information in it, you can write a custom eventarg to provide it. Once you have defined the event name in Visual Studio, you will need to write the event handler class. Here is the setup of this in VB – don’t worry if it makes no sense, i’ll try to explain the process of what is going on.

Public Class XMLPickedEventArgs
Inherits EventArgs
Private _fileName As String
Public Property FileName() As String
Return _fileName
End Get
Set(ByVal value As String)
_fileName = value
End Set
End Property
Public Class XMLPickedEventArgs
Public Sub New(ByRef control As XMLLayoutPanel, ByVal fileName As String)
control.CurrentItem = fileName
Me.FileName = fileName
End Sub
End Class

XMLPickedEventArgs is created with two constructors, a reference to me ,the base control itself (not the sender in this case, which would be the button) and the Tag property of the button, which is the string path of the XML file. Now that this object has been instantiated, it means the XML filename has been stored in the eventargs filename property, and it can now be passed to raise the event.

If you look at this class in Visual studio, you will see that the control has a new custom registered event in the properties window that isn’t part of the FlowlayoutPanel –

Even better, Intellisense tells us the arguments in our custom class. As the class has just one property, that is all we see. However, this is the only one we need! So from this, we know that if we access the filename property of the eventarg, we will get the path to the XML file we just clicked. This load string can then be handled in max and passed to whatever XML function you wish.

You can do the same when using the class in max by calling showproperties on the eventarg in max – Notice the structure is the same, with sender being the custom control itself, and args being the eventarg. Depending on what you pass from the assembly you could do other things, for example , In the sender variable you could have passed a reference to the button from the event, meaning that you could use the event to change the state of it (i.e. backcolor, border etc), or you could keep the sender as the XMLPanel and add a property to the eventarg to refer to the button. It’s really up to you.

on XMLPanel picked sender args do
showproperties args
listener output –
.FileName : <System.String>
.Empty : <System.EventArgs>, read-only, static

In short, with Visual Studio you can build a control as broad or as specialized as you like. In this case, I really wanted something for this particular task, and was able to build it. The main advantage is it bypasses the event handler issues of the 3dsMax command pane. Of course this approach could be done for a variety of filetypes

I hope this has given you a background as to why you would write your own event handler, and how to use it in Max. Feel free to download the control and use it in your projects. I’m using this control more and more in the utilities I write; I think that it gives a good visual feel to the UI and allows for dynamic layouts to be made where the layout logic is completely handled by the control, not by you.

Which is nice.

download script

Retrieving the 3DSMax File Thumbnail with DotNet

Oct 5, 2008 by     12 Comments    Posted under: 3dsMax, DotNet, Maxscript, Technical Research

Firstly, a small disclaimer – If you are unsure about any of the information contained on this page, Please do not try to implement it.

Before Max opted for DotNet integration, You could use the Windows ActiveX thumbnail component to view the 3dsMax file thumbnail. I have always found this small image to be very useful. However, when looking for a method to retrieve this via DotNet, I found it much more difficult. There is very little written about this, and I was getting nowhere until and I was fortunate enough to have Cuney Tozdas of Splutterfish point me towards two C++ files in the SDK examples. Please note that I am not a programmer, so apologies if there are any inaccuracies in my text.

How Max Stores it

The notes in the SDK pointed me towards the notion of IPropertyStorage and Structured Storage. Basically, the summary info of a Max file is set using this method. So not only is the Thumbnail stored in this part of the file, there are options to store many other pieces of information pertaining to the maxfile. You can see this by picking File>Summary Info in 3dsMax. The bad news is that novices like myself are not going to get near the level of programming needed to access this part of a file without some C++ knowledge.

Fortunately, help is at hand because it just so happened that while researching IPropertyStorage I stumbled across something on from Microsoft. MS Office files also use the same method as 3dsMax to store information in office files, using OLE Structured Storage, and Microsoft have provided a class for dealing with the lower level stuff for you called DSOFile.

DsoFile is a COM class and can be freely downloaded from this link


And the tricky bit….

Com classes need to be registered, which means that you have to use regsrv32 in order to do this. If you use the installer from the Microsoft website it will perform this step for you. However, should you ever move the dll or are interested here are the steps for performing this manually. If anyone knows about building setup projects in Visual Studio to automate this i’d be grateful if they could let me know, as i would much prefer to give an installer for this and save everyone the trouble.

  1. Once your Window’s operating system has loaded completely, click on Start and then click on Run.
  2. Input in the Run field a command that tells your computer to register the DLL file. You will need to input specific information including the path and the file name. The following is a template for the command: regsvr32 “FileName.dll”
    It is important to note that path is the actually location or directory of where the file is located.

  1. Once the command is input into the Run field correctly, press Enter.
  2. Once the DLL has been registered, you should receive a confirmation in the form of a pop up box.

This message will list your newly registered DLL file and confirm that is was successfully registered into the registry.

Once registered, you will now be able to use this class in Visual Studio. I’m not going to go into how to use DSOfile for this, as you can figure this out from the source provided from microsoft. When you add a reference to a COM class in Visual Studio, it creates an interop class that allows the .net framework and the COM class to talk to each other. So as well as registering the DSOfile dll, you will need to load the InterOp class as an assembly in any MaxScript code to use it.

The MaxFileInfo Class

I have written a basic dotnet class with a few properties and an overloaded method to retrieve the Max Thumbnail. Note that when instantiated, the MaxFileInfo class creates a MaxFileinfo dotnetobject. This means that once created, it will automatically reference the information properties. However, the Thumbnail access is fast so it is generated on demand. In my HitchHiker utility at the top It propagates the control almost immediately.

to create a MaxFileInfo object, you provide a string path variable

MaxFileInfo “<Maxfile path and filename>”

I have provided two options – calling Thumbnail on the file will return a DotNet image, useful if you are building a dotNet rollout in max, or by specifying Thumbnail True will copy this to the clipboard and allow you to use getclipboardBitmap() to put onto a MXS button. I have included a small example of the two methods in the download.

Still to do…

This class will only currently work in 32 bit versions of Max and Windows. Someone very kindly recompiled DSOFile for the X64 platform for me and i am working around a couple of issues at the moment, so I will hope to release a working version for the X64 platform very soon. I am slightly behind on this because my Laptop runs 32bit vista and my work machine XPx64, so i don’t get an awful lot of time to test in this environment. I will be upgrading my laptop shortly in order to develop this further, as without 64bit compatibility it is about as much use as a chocolate teapot. Indeed, i wish it was more straightforward.

I will be the first to say that this implementation is far from perfect. There are a couple of other references that need to be addressed depending on the platform you are running. On XP, I sometimes had to provide the stdole.dll also, as well as visualbasic.compatibility.dll as this is used by the class to convert the picture thumbnail to a dotnet image.

If you have any questions or need help with this class, feel free to email me (pete at lonerobot dot com) or PM me via CGTalk. If you want to use this in any scripts, you are freely welcome to do so, although I’d appreciate a mention!

I have also looked into extending this class with a few GDI+ methods, embedding the file information into the image before it returns it with some layout control, as well as a crude resampling routine to resize the thumbnail to something larger.

Included in the download is a very basic utility written in Visual Studio that allows you to browse files via a treeview. It has drag and drop functionality too. Its just provided as is, in the hope it would be useful to someone.

The other thing to add to this class library would be the comprehensive ability to specify the summary properties of a maxfile via dotnet. If you think this would be of particular use to you, please let me know, and I might be able to find the time to write it for you.


MultiThreading in 3D Studio Max using the BackgroundWorker class

Aug 29, 2008 by     10 Comments    Posted under: 3dsMax, DotNet, Maxscript

You will all be aware that when you perform any intensive calculation process within MaxScript that it pretty much ties up that session of max. The solution to this would be to run this process in a separate thread, much like 3DS Max does with all other operations. However, the system.threading class can be tricky to configure. Luckily the DotNet chaps have provided an alternative that does all the hard work for you – The BackgroundWorker.

You create an instance of this class in MXS via the following method –

MainThread = dotnetobject "System.ComponentModel.BackGroundWorker"

This is a small test that demonstrates use of this class. You will notice that when you run the example via MXS, the 3Dsmax UI will be completely tied up. Via the Dotnet method, you can still use the max interface and do other tasks within a single copy of max. It also supports cancellation so you can abort an intensive task should you need to.

The main thread is called via the DoWork() event – specifying this in MaxScript as a dotnethandler allows you to pass a function to operate as the work thread. So you can pretty much put what you like in here and it will run in a thread separate to the UI. This is obviously better suited to work that isn’t viewport related. It is perfect if you want to perform an intensive calculation without starting a separate instance of 3DS Max.

Updating the UI thread from the Worker Thread

Something to understand in VB/C# with this class is that like using other threading methods you have to use delegates if you want to update a UI that was started in a thread different to the one perform the work. If you attempt this in VB/C# it will throw an exception. The backgroundworker class has this delegate functionality built in, providing an easy way to pass progress (and other) information back to the UI thread. I haven’t implemented it here as it didn’t seem to be necessary for the script to work, although i have included it commented out in the code. In VB/C#, The backgroundworker class allows this to happen via the ProgressChanged handler. If you look in the script this can only report back if you set the object’s workerreportsprogress property to true., without this property set will also result in an exception.

Once started with the runworkerasync() function, The mainthread performs the work function. You choose when to update back to the UI thread via the reportprogress method. This takes two arguments, a percentage integer and a userstate object. So, you can put pretty much anything in this object – i have placed an array with various properties in so i can can the userstate[integer]method to retrieve these in the updatethread function. The ProgressChangedEventArgs therefore contains a progresspercentage property to update a progressbar or similar and the userstate property for anything else. For example I have used this to pass the e.progresspercentage argument to the painthandler of a control and use GDI+ to draw a custom progressbar over a bitmap that is loading. You don’t have to provide a userstate property, the eventarg is overloaded (meaning you can choose which arguments to supply) so that you can just return the progress percentage should you wish.

Insert Update Thread pun here…

Whilst the background worker seems to perform well in a VS environment, I had notived a couple of intermittent synchronisation errors when using a this class within 3dsMax. Following a discussion on CGTalk, the dotnet SDK mentions this issue. Here is what it says –

SynchronizingBackgroundWorker Class

Replaces the standard BackgroundWorker provided by the .NET Framework to fix what seems to be a bug causing the BackgroundWorker to fire events in the wrong thread.We have encountered cases where the BackgroundWorker seems to lose track of the main thread or just arbitrarily decide to fire ProgressChanged and RunWorkerCompleted in a new thread rather than in the main thread. This causes synchronization errors.

This replacement class allows the client to specify a ISynchronizeInvoke synchronizer object through which the events will be fired. All Controls implement ISynchronizeInvoke, so any Control should be adequate to act as a synchronizer to invoke the events in the main thread.

I have updated these classes to run with this component by adding a reference to CsharpUtilities in VS. In max, you can just reference it normally like so –

Worker = DotNetObject "CSharpUtilities.SynchronizingBackgroundWorker"

You don’t need to load the assembly as MaxDoes this automatically at startup.

download script