环境:
- window11
- 3dsmax2026
- .net8
- vs2026
写这个的原因:
看到autodesk官网上明确声明:3dmax2026的一项重大变更是.net的支持从4.8转向了.net8,但网络并没有看到相关的入门博客,问chatgpt耗费了我1个多小时,最后还让我用c++开发。。。
最后,我只能扒官方的文档一步步尝试,还好最后成功了。
参考:
第一个例子:编写一个跟随3dsmax启动的插件
代码:
MyFirst3dMaxPlugin.slnx
xml
<Solution>
<Configurations>
<Platform Name="Any CPU" />
<Platform Name="x64" />
</Configurations>
<Project Path="MyFirst3dMaxPlugin/MyFirst3dMaxPlugin.csproj">
<Platform Solution="*|x64" Project="x64" />
</Project>
</Solution>
MyFirst3dMaxPlugin.csproj
xml
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net8.0-windows</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<Platforms>x64</Platforms>
</PropertyGroup>
<ItemGroup>
<!-- 修改为自己3dmax的安装目录 -->
<Reference Include="Autodesk.Max">
<HintPath>D:\AutoDesk\3ds Max 2026\Autodesk.Max.dll</HintPath>
</Reference>
</ItemGroup>
</Project>
AssemblyFunctions.cs
csharp
using System.Reflection;
namespace MyFirst3dMaxPlugin
{
public class AssemblyFunctions
{
//下面是四个3dmax支持的声明周期函数
//注意: 我是向d盘写入文件, 如果向c盘写入可能会有权限的问题
public static void AssemblyMain()
{
File.AppendAllText("d:\\demo.txt", "AssemblyMain-" + DateTime.Now.ToString() + "\r\n");
// 如果想在3dmax启动的时候调试的话 加上下面一行, 这样3dmax启动的时候会有弹框提示
//System.Diagnostics.Debugger.Launch();
}
public static void AssemblyShutdown()
{
File.AppendAllText("d:\\demo.txt", "AssemblyShutdown-" + DateTime.Now.ToString() + "\r\n");
}
public static void AssemblyLoad()
{
File.AppendAllText("d:\\demo.txt", "AssemblyLoad-" + DateTime.Now.ToString() + "\r\n");
}
public static void AssemblyInitializationCleanup()
{
File.AppendAllText("d:\\demo.txt", "AssemblyInitializationCleanup-" + DateTime.Now.ToString() + "\r\n");
}
}
}
编译运行
编译后直接放到3dmax的bin/assemblies目录:

可以将 ·MyFirst3dMaxPlugin.deps.json· 一并拷贝过去
这样打开3dmax后就可以看到写入的日志了:

当我们关掉3dmax后再看日志:

第二个例子:写一个内嵌的web服务器
在第一个例子基础上,在3dsmax2026启动时就开启一个web服务器。
代码:
改造 MyFirst3dMaxPlugin.csproj
xml
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net8.0-windows</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<Platforms>x64</Platforms>
</PropertyGroup>
<ItemGroup>
<!-- 修改为自己3dmax的安装目录 -->
<Reference Include="Autodesk.Max">
<HintPath>D:\AutoDesk\3ds Max 2026\Autodesk.Max.dll</HintPath>
</Reference>
<!-- web框架 -->
<FrameworkReference Include="Microsoft.AspNetCore.App" />
</ItemGroup>
</Project>
新增 Program.cs
csharp
using Microsoft.AspNetCore.Builder;
namespace MyFirst3dMaxPlugin
{
public class Program
{
public static void Main()
{
var builder = WebApplication.CreateBuilder();
var app = builder.Build();
app.MapGet("/", () =>
{
return "hello-" + DateTime.Now;
});
app.Run("http://localhost:5000");
}
}
}
改造 AssemblyFunctions.cs
csharp
using System.Reflection;
namespace MyFirst3dMaxPlugin
{
public class AssemblyFunctions
{
//下面是四个3dmax支持的声明周期函数
//注意: 我是向d盘写入文件, 如果向c盘写入可能会有权限的问题
public static void AssemblyMain()
{
File.AppendAllText("d:\\demo.txt", "AssemblyMain-" + DateTime.Now.ToString() + "\r\n");
// 如果想在3dmax启动的时候调试的话 加上下面一行, 这样3dmax启动的时候会有弹框提示
//System.Diagnostics.Debugger.Launch();
// 第一种方法: 程序集解析事件
AppDomain.CurrentDomain.AssemblyResolve += (sender, args) =>
{
string assemblyName = new AssemblyName(args.Name).Name;
string depsPath = Path.Combine(
Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location),
"MyFirst3dMaxPlugin-Dependency",
assemblyName + ".dll"
);
if (File.Exists(depsPath))
return Assembly.LoadFrom(depsPath);
return null;
};
//// 第二种方法: 手动加载所有dll
//string assemblyDir = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);
//string depsDir = Path.Combine(assemblyDir, "MyFirst3dMaxPlugin-Dependency");
//// 加载所有依赖DLL
//foreach (string dll in Directory.GetFiles(depsDir, "*.dll"))
//{
// try
// {
// Assembly.LoadFrom(dll);
// }
// catch (Exception ex)
// {
// // 处理加载失败
// File.AppendAllText("d:\\demo.txt", "AssemblyMain-处理加载失败-" + dll + "-" + DateTime.Now.ToString() + "\r\n");
// }
//}
Task.Run(() => Program.Main());
}
public static void AssemblyShutdown()
{
File.AppendAllText("d:\\demo.txt", "AssemblyShutdown-" + DateTime.Now.ToString() + "\r\n");
}
public static void AssemblyLoad()
{
File.AppendAllText("d:\\demo.txt", "AssemblyLoad-" + DateTime.Now.ToString() + "\r\n");
}
public static void AssemblyInitializationCleanup()
{
File.AppendAllText("d:\\demo.txt", "AssemblyInitializationCleanup-" + DateTime.Now.ToString() + "\r\n");
}
}
}
编译运行
发布配置:

将 Pulish 下的内容整体拷贝到 bin/assemblies\MyFirst3dMaxPlugin-Dependency

还有将 MyFirst3dMaxPlugin.dll 拷贝到 bin/assemblies 下
现在让我们再打开3dsmax2026,然后看下浏览器:

现在,一个内嵌的web服务器已经诞生了。
第三个例子:写一个自定义菜单项
目标:点击这个菜单后向场景中添加一个茶壶
代码:
新建一个工程ActionPluginDemo,直接上代码:
ActionPluginDemo.csproj
csharp
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net8.0-windows</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<Platforms>x64</Platforms>
<!-- 修改为自己3dmax的安装目录 -->
<ADSK_3DSMAX_x64_2026>D:\AutoDesk\3ds Max 2026</ADSK_3DSMAX_x64_2026>
</PropertyGroup>
<ItemGroup>
<Reference Include="Autodesk.Max">
<HintPath>$(ADSK_3DSMAX_x64_2026)\Autodesk.Max.dll</HintPath>
<Private>False</Private>
</Reference>
<Reference Include="CSharpUtilities">
<HintPath>$(ADSK_3DSMAX_x64_2026)\CSharpUtilities.dll</HintPath>
<Private>False</Private>
</Reference>
<Reference Include="ManagedServices">
<HintPath>$(ADSK_3DSMAX_x64_2026)\ManagedServices.dll</HintPath>
<Private>False</Private>
</Reference>
<Reference Include="MaxCustomControls">
<HintPath>$(ADSK_3DSMAX_x64_2026)\MaxCustomControls.dll</HintPath>
<Private>False</Private>
</Reference>
<Reference Include="UiViewModels">
<HintPath>$(ADSK_3DSMAX_x64_2026)\UiViewModels.dll</HintPath>
<Private>False</Private>
</Reference>
<Reference Include="WPFCustomControls">
<HintPath>$(ADSK_3DSMAX_x64_2026)\WPFCustomControls.dll</HintPath>
<Private>False</Private>
</Reference>
</ItemGroup>
<!-- 自动将生成的 ActionPluginDemo.dll 拷贝到 bin\assemblies -->
<Target Name="PostBuild" AfterTargets="PostBuildEvent">
<Copy SourceFiles="$(TargetPath)"
DestinationFolder="$(ADSK_3DSMAX_x64_2026)\bin\assemblies\"
SkipUnchangedFiles="true" />
</Target>
</Project>
MaxDotNetCUIDemo.cs
csharp
using Autodesk.Max;
using UiViewModels.Actions;
namespace ActionPluginDemo
{
public class MaxDotNetCUIDemo : CuiActionCommandAdapter
{
// 动作显示的文本
public override string ActionText
{
get { return InternalActionText; }
}
// 动作所属的分类
public override string Category
{
get { return InternalCategory; }
}
// 执行动作时的逻辑
public override void Execute(object parameter)
{
var global = GlobalInterface.Instance;
var coreInterface = global.COREInterface13;
try
{
// 创建茶壶几何体
IClass_ID teapotClassId = global.Class_ID.Create(
(uint)BuiltInClassIDA.TEAPOT_CLASS_ID,
(uint)BuiltInClassIDB.TEAPOT_CLASS_ID);
// 创建几何体实例
object obj = coreInterface.CreateInstance(
SClass_ID.Geomobject,
teapotClassId as IClass_ID);
if (obj == null)
{
coreInterface.PushPrompt("无法创建茶壶对象");
return;
}
// 将对象转换为 IObject
var teapotObject = obj as IObject;
if (teapotObject == null)
{
coreInterface.PushPrompt("无法将对象转换为 IObject");
return;
}
// 创建场景节点
IINode node = global.COREInterface.CreateObjectNode(teapotObject);
// 正确的方法:将节点添加到场景中
coreInterface.RootNode.AttachChild(node, false);
// 获取参数块
IAnimatable anim = teapotObject as IAnimatable;
if (anim != null)
{
IIParamBlock2 pb = anim.GetParamBlock(0);
if (pb != null)
{
// 设置茶壶参数
// 参数索引和意义:
// 0: 半径 (Radius)
// 1: 分段数 (Segments)
// 2: 平滑 (Smooth)
// 3: 生成贴图坐标 (Generate Mapping Coords)
pb.SetValue(0, coreInterface.Time, 25.0f, 0); // 半径25单位
pb.SetValue(1, coreInterface.Time, 4, 1); // 分段数4
pb.SetValue(2, coreInterface.Time, 1, 2); // 平滑
pb.SetValue(3, coreInterface.Time, 0, 3); // 不生成贴图坐标
}
}
// 选择新创建的节点
coreInterface.SelectNode(node, true);
// 重绘视图
coreInterface.RedrawViews(coreInterface.Time, RedrawFlags.Normal, null);
coreInterface.PushPrompt("茶壶创建成功!");
}
catch (Exception ex)
{
coreInterface.PushPrompt($"Error: {ex.Message}");
}
}
// 内部动作文本
public override string InternalActionText
{
get { return "Jackletter-ActionText!"; }
}
// 内部分类名称
public override string InternalCategory
{
get { return "Jackletter-Category"; }
}
}
}
编译运行
编译完成后,已自动将 ActionPluginDemo.dll 丢进 bin\assemblies目录,如下:

然后,启动3dmax
配置菜单
启动后,点击 "自定义"-> "菜单编辑器",在弹出的窗口中搜索 "jackletter"(类别切换成所有类别),会看到下面收到的我们定义的插件信息,将它拖拽到右面,然后点击保存,在弹框提示中选择"保存",我们可以在文件弹出的菜单配置文件保存框中写入自己喜欢的名字(我写的是test),主要步骤看下图:

观察菜单效果
配置完成后,我们回到3dsmax软件,点击主菜单"文件",会看到我们的插件,点击它,效果如下:

补充项:如何调试代码
- 对于3dmax刚启动时的代码
看代码注释,只要将System.Diagnostics.Debugger.Launch();放开就行了。 - 对于webapi或其他的代码,可以采用附加到进程的方法,如下:
快捷键:ctrl+alt+p

然后打个断点:

然后浏览器访问以下就可以了:
