• 1/3

Embedding a Web Server in 3ds Max using .NET

Posted by Christopher Diggins, 9 October 2012 3:24 pm

Controlling a .NET Application Remotely using a Web Server

The problem of controlling a .NET application such as 3ds Max from another process, or over a network, is similar to the challenge of controlling Python from C# just in the other direction. In this blog post I describe how to embed a simple web-server implemented in C# in 3ds Max that executes MAXScript code.  

I have attached both the source code, and the .NET plug-in as a DLL.
Download WebServer.zip

To load the web-server plug-in you have to copy the assembly (MAXScriptWebServer.DLL) in the bin/assemblies sub-folder of 3ds Max 2013. Next you need start up 3ds Max in Administrator Mode. Now when you go to the "Customize menu" you should see the user action in the “.NET samples” category. You can then associate it with a short-cut key or menu option. For more information on registering custom actions see the 3ds Max documentation.

Why use a Web Server and not Sockets?

At first it might seem like overkill to embed a web-server in your application when you just want to send messages to it. As one of my colleagues asked, why not just use sockets?

When you get rigtht down to it, a web-server is effectively a wrapper around one end of a TCP/IP socket that uses HTTP as the application level communication protocol. Using HTTP provides you with a well-defined and widely available standard for sending data to and from a server, with protocols for both error handling and content encoding. For me the biggest win is the fact that I can use any web-browser as a client!

Fundamental Classes

Okay, now that we have gotten past your irrational fear of embedding a web-server in your .NET application let’s look at the building blocks:

  • System.Net.HttpListener – receiving and responding to HTTP requests
  • System.Web.HttpUtility – parsing query string data
  • System.Windows.Threading.Dispatcher – executing code on the main thread
  • System.Thread.Thread – executing http listening on a separate thread
  • Autodesk.Max.IGlobal – accessing the core interfaces of the 3ds Max .NET API, for example to execute MAXScript code.

Basics of Creating a CUI Action

I opted to create the server a custom .NET user action for 3ds Max 2013. To create a custom .NET action you have to create a class derived from UiViewModels.Actions.CUICommandAdapter. This is an abstract class that requires you to implement five methods:

  • string ActionText – A string property that represents the name of the action as shown on menus and buttons.
  • string InternalActionText – A non-localized version of ActionText
  • void Execute(object parameter) – The function called when the action is triggered by 3ds Max. For example if a menu item selected or  short-cut key us pressed.
  • string Category – The name of the category the action can be found in the customize dialog.
  • string InternalCategory – A non-localized version of Category.

In your project you will have to first reference the following assemblies which can be found in the 3ds Max application folder:

  • Autodesk.Max.dll
  • UiViewModels.dll

It is very important that when you create the reference in your proejct that you set the “copy local” property to false. Be sure you also don’t make the same mistake I did and I link to the wrong version of 3ds Max.

Now you will need to add references to the following Microsoft assemblies:

  • PresentationCore
  • System.Web
  • System.Windows.Forms
  • WindowsBase

Finally you should set the output folder of the project to the “bin/assemblies” sub-folder of 3ds Max.

For more information on writing .NET plug-ins see the 3ds Max SDK documentation.

Starting the Web Server

When you start the web-server from the menu (or shortcut key depending on how you registered it) you will see the followigntext in the MAXScript listener:

  Starting HTTP listener
  HTTP listener started
  Thread started

You can now point your browser to: http://localhost:8080/test and you will see something similar to the following screen shot:


Using the web Server from MAXScript

The user action class library can also be loaded from MAXScript. This can be useful if you want the server to start up with 3ds Max. The following code shows how to launch the web-server from a MAXScript script:

fn startWebServer = (
    Assembly = dotNetClass "System.Reflection.Assembly"
    maxroot = pathConfig.GetDir #maxroot
    Assembly.LoadFile (maxroot + "\bin\assemblies\MaxScriptWebServer.dll")
    cls = dotNetClass "MaxScriptWebServer.MaxScriptWebServer"
    server = dotNetObject cls
    print "Created: " + server.actionText
    server.execute undefined
)
startWebServer ()

Highlights of the Web Server Code

The web-server is implemented primarily by the System.Net.HttpListener class. The implementation is based on the example shown on MSDN. The three most important things to note about the code are:

  1. We want the web-server to run on its own thread so that 3ds Max remains responsive.
  2. The MAXScript code comes from an HTML form URL encoded and has to be decoded. 
  3. We want the MAXScript code to be executed on the main thread.

First, to get the web-server to run on its own thread we use the System.Thread.Thread class.

   var t = new Thread(() => ListenLoop());
   t.Start();

Next to decode the MAXScript code sent from the web-page we need to use System.Web.HttpUtility to retrieve and decode the form text.

        public static string GetFormData(HttpListenerRequest request, string formName)
        {
            if (request == null || !request.HasEntityBody)
                return "";
            var body = request.InputStream;
            var encoding = request.ContentEncoding;
            var reader = new System.IO.StreamReader(body, encoding);
            var text = reader.ReadToEnd();
            body.Close();
            reader.Close();
            var queryVars = HttpUtility.ParseQueryString(text, encoding);
            return queryVars[formName];
        }

Finally to execute the MAXScript code on the main thread we use a System.Windows.Threading.Dispatcher

  // Execute the evaluation of the code as MAXScript on the main thread.
  Action a = () => global.ExecuteMAXScriptScript(code, false, null);
  dispatcher.Invoke(a);

Next Steps

If you are interested in a more sophisticated mechanism for controlling 3ds Max you might want to look into using XML RPC to control 3ds Max using the XML-RPC.NET library.

If you need full bi-directional communication between a server application and multiple clients and for some reason polling is not acceptable then I would recommend investigating using HTML5 Web sockets.

2 Comments

mhoward

Posted 14 January 2013 4:34 pm

Chris, when using ExecuteMAXScriptScript(), is there any way of collecting or catching errors that are being reported to the MaxScript listener?

samsfisher

Posted 22 September 2013 12:56 pm

Hi,

I am trying to develop a way to send maxscript from a .net application to 3dsmax. how do i go about it without using a plugin approach?

WIll Autodesk.max.remoting come in to help? i spent hours searching the net but no avail.

if you could shed light onto how i can use remoting that would be great

Sam

Add Your Comment

You must be logged in to post a comment.

Please only report comments that are spam or abusive.