View Full Version : Maxscript, SDK and .NET

04 April 2008, 02:17 PM
Okey lets say I want to make a maxscript function with max sdk that adds .NET Button to form user gives to the maxscript function.

Looking at the source code of maxsdotNet plugin it seems that the argument given to the max sdk function is DotNetObjectWrapper, however max sdk doesn't really have any information on how to handdle these. How would I get the .NET form (or actually MaxCustomControls.MaxForm) out of the wrapper so I can use it as standard .NET stuff?

Do I have to take all classes from maxsdotNet sources and compile them along with my plugin or what? Would that even work?

04 April 2008, 10:51 AM
MaxCustomControls is a standard .NET assembly so you can add it as a .NET dependency in the Project settings.

But I don't really understand what you're trying to do. I think to speed up your script, you can just use a .NET assembly without coding a MAXScript extension that will be really hard to implement.

In fact, you can use the MaxForm in a .NET assembly (C# or VB.NET), build your form with the designer and add methods to be able to change controls values and communicate with them in MAXScript code.

04 April 2008, 03:56 PM
I'm trying to recreate scene explorer, so basicly I have to deal large amount of data (>10 000 objects) from 3ds max. I need solid connection to 3ds max scene, taking name of 10 000 objects and listing them into DataGridView. This takes MINUTES with maxscript dotnet connection. There is no way to speed it up with .NET assemblies since I need to take name of each object from 3ds max, .NET doesn't have any access to 3ds max. Or does it? There is zero documentation about it if it can do it.

So I need 3ds max SDK to connect object data to .NET form. This is still fairly simple. The problem rises when I want to combine maxscript with SDK and .NET since Autodesk decided not to document maxscript .NET connection at all.

In a nutshell. I want to make my own custom .NET maxscript functions. Like:

myAddButtonFunction maxscriptDotNetForm "Hello World button"

which would add .NET button to given .NET form.

04 April 2008, 09:41 AM
What's slow ?
The processing in MAXScript ?
Sending string to DataGridView with MAXScript ?
The building of the .NET form ?

Maybe your example: myAddButtonFunction maxscriptDotNetForm "Hello World button"
isn't meaningful for what you're trying to do, but there's no advantage to code such method in C++ as in C# it would be easier and faster.

For example, SceneExplorer was designed with 2 .NET assemblies : a C# assembly (with core SceneExplorer Framework) and a C++/CLI, mix of managed and unmanaged plugin code. I think that if we want to use MAXScript a lot, we can avoid the C++/CLI stage and use MAXScript extension but without .NET. Why ?:
If the use of a DataGridView is slow, do that work in C# or VB.NET.
If the MAXScript processing of objects is too slow, code a MAXScript extension but without .NET. So, you can communicate between your MAXScript code and the managed code with basic data types:
MAXScript -> MAXScript Extension -> MAXScript -> C# / VB.NET -> MAXScript etc.

C++ SDK + C++/CLI + MAXScript SDK == real pain :D.
As you can see it, the code of the mxsDotNet DLX is really hard to read because MAXScript SDK isn't very easy to use and not well designed, C++/CLI is just glue code; very hard to read and use.

04 April 2008, 01:37 PM
for i in 1 to objects.count do DataGridView.Rows.Item[i-1].Cells.Item[0].Value = objects[i].name

That is slow.

And that I want to speed up by creating custom Maxscript function which does it for me. Unfortunately it I just can't figure out how to get Maxscript .NET Wrapper classes to my project. I tried compiling the mxsdotNet, using the library from that. However I can't seem to figure out how to get namespaces over.

I get this when I use "using namespace MXS_dotNet;"
error C2871: 'MXS_dotNet' : a namespace with this name does not exist

04 April 2008, 02:03 PM
I guess its time to give up on this. Obviously Autodesk never meant anybody to be able to expand their Maxscript .NET connection.

04 April 2008, 03:03 PM
for i in 1 to objects.count do DataGridView.Rows.Item[i-1].Cells.Item[0].Value = objects[i].name
What you can do in this situation is trying to send to a custom .NET assembly method an array of objects names and the DataGridView control. So this heavy loop will be completely done in managed code and should be (for sure!) very much faster than a loop in MAXScript.

I can code this small assembly if you want.

04 April 2008, 03:12 PM
How would you handle maxscript array? I guess you could try converting the whole array into single string but I'm not sure if maxscript string object likes strings with 100 000 chars.

04 April 2008, 03:24 PM
My first try would be to convert the maxscript array to a .NET array. With 10000 objects this can be slow but it's worse the try!

For example, here is how to convert a MAXScript string array to a .NET string array:

fn mxsArrayToDotNetArray mxsArray =
local mxsArrayCount = mxsArray.Count
netArray = dotNetObject "System.String[]" mxsArrayCount

for i = 1 to mxsArrayCount do
local netString = dotNetObject "System.String" mxsArray[i]
local netIndex = dotNetObject "System.Int32" (i-1)
netArray.SetValue netString netIndex


mxsStrArray = #("Hello", "GoodBye", "DotNet", "ADO")
netStrArray = mxsArrayToDotNetArray mxsStrArray

for i = 0 to (netStrArray.Length-1) do
local netIndex = dotNetObject "System.Int32" i
print (netStrArray.GetValue netIndex)

dotNetUtils.AssignColumnFromArray (DataGridView dataGridView) (String[] names) (int column)

Here is how to use the dotNetUtils assembly:

dotNet.loadAssembly "dotNetUtils.dll"
dataGridViewUtils = dotNetClass "dotNetUtils.DataGridViewUtils"
dataGridViewUtils.AssignColumnFromArray dataGridView netStrArray 0

I'm not sure the assembly code is good 'cause I've never used a DataGridView.

04 April 2008, 04:56 PM
You are right, its slightly faster than working directly with DataGridView cell values. However, even with 1 000 objects it crashes max. I'm not sure where the problem is, even increasing the heapsize doesn't help. Maxs dotnet connection just can't handle large amount of data. I wish there would be a way to send sdk pointters to actual dotnet objects with maxscript. It would make everything so easy. Without doing this, there is just no way to get this thing working.

Too bad too sad, thanks for your help anyway.

04 April 2008, 05:13 PM
It crashes max without any error or message ?

04 April 2008, 05:46 PM
Out of curiosity... have you considered not loading 10,000 or 1,000 or even 100 objects into the list?
It would be nice if you could, but let's face it - the user can't display 100 lines on screen as it is (okay, those running at 2560x1600 could).

So perhaps you can just chunk it up into N items maximum at a time, and let any unloading/loading happen dynamically (based on the scrollbar position or... who knows what).

04 April 2008, 07:05 PM
ypuech, it uses 100% of one core and does nothing.

ZeBoxx2, yeah I thought about that. I guess I'll give it a try, it might give annoying delay when scrolling since its all running on the same thread (I think).

One option is to do everything with SDK and .NET but it would mean a lot more work not to mention making changes and customizing the thing would be a pain.

04 April 2008, 05:22 PM
Okey I finally found a way to do it. Maxscript -> SDK -> .NET. However its still pretty slow, and the time to update single cell seems like O(n^2). It also hangs when there are >2000 cells or something like that. Anyways, here are the functions:

#pragma managed
#using <System.dll>
#using <System.Windows.Forms.dll>
using namespace System::Windows::Forms;
namespace BerconExplorer {
void managedUpdateColumn(int handle, int index, char** arr, int length) {
System::Windows::Forms::Control ^ cont = gcnew System::Windows::Forms::Control();
DataGridView ^ dGW = (DataGridView ^)cont->FromHandle(System::IntPtr(handle));
for (int i = 0; i<length; i++)
dGW->Rows[i]->Cells[index]->Value = gcnew System::String(arr[i]) ;
#pragma unmanaged
#include "definsfn.h"
def_visible_primitive(BerconExplorer, "BerconExplorer");
using namespace BerconExplorer;
Value* BerconExplorer_cf(Value **arg_list, int count) {
check_arg_count(BerconExplorer, 3, count);
int handle = arg_list[0]->to_int();
int index = arg_list[2]->to_int();
Array* nameArr = (Array *)arg_list[1];
int length = nameArr->size;
char** stringArr = new char*[length];
for (int i = 0; i<length; i++)
stringArr[i] = nameArr->data[i]->to_string();
BerconExplorer::managedUpdateColumn(handle, index, stringArr, length);
return &ok;

04 April 2008, 08:13 AM
That's a good try Jerry.

But, is DataGridView cells number limited ?
Have you tried to use dGW->Rows->Add() instead of dGW->Rows[i] ?

04 April 2008, 01:36 PM
I create the rows before calling that update. Its kinda weird that the code I posted is actually slower than doing it maxscript, or doing it using the assembly you made. I'm not really sure why. I do have two conversions, first from maxscript string to char* and then to .NET String. But I think thats unavoidable and it shouldn't really be THAT slow.

Anyway, the correct way to work here would probably be using DataGridView in virtual mode, so it'd only update the shown values. I'll have to check out how well that works out.

04 April 2008, 03:14 PM
Okey, I tried the virtual mode and all I can say is WHOA. Its very fast, without any lag even with 10 000 objects.

I also studied what was actually slow in the SDK function. It turns out that reserving memory for new string takes ages! Meaning the gcnew System::String("Hello").

Anyway I wont be needing that any more since using the Virtualmode (only updating the displayed cells) is so much faster.

Thanks for everybody for support.

08 August 2008, 08:50 PM
Hi Bercon,

i'm struggling with a listview. I want it to display a nodeselection (name and wirecolor). It's rather slow when selecting large numbers of nodes. I also stumbled across the virtualmode for the .net listview. But i'm having trouble understanding it.
I'm able to display my testItems in the listview using the virtualmode, like so:
lvPreview = dotNetObject "listview"
lvPreview.virtualmode = true
--the virtualListSize gets set through a callback
--so i'm always getting the correct amount of listviewItems
--in this example i'll just use an arbitrary number
lvPreview.virtualListSize = 26

--the event handler function
function fn_createVirtualItem control arg =
newItem = dotNetObject "listViewItem" "myTestItem"
newItem.subItems.add "theSub"
arg.item = newItem
dotNet.addEventHandler lvPreview "RetrieveVirtualItem" fn_createVirtualItem

So i'm just creating a certain amount of listviewItems which are the same. Could you help me with creating each listviewItem to display the name and wirecolor of each selected object? Somehow i need to get into that eventHandler function and change the properties of the listviewItems, but i don't see how.


CGTalk Moderation
08 August 2008, 08:50 PM
This thread has been automatically closed as it remained inactive for 12 months. If you wish to continue the discussion, please create a new thread in the appropriate forum.