MaxScript与外部程序通讯
最近项目要求通过java给max发送任务指令,max接收指令执行任务,并且返回执行的结果。不管为什么会有这样的需求,有就要去实现。
1、OLE开启
Max本身提供了一个方式,它可以将自己注册成一个Ole自动化对象,默认是没有开启的,开启这个接口只需要操作注册表即可。将该脚本存放的max的安装路径下的Scripts\StartUp,即max启动时会默认加载的脚本。保证max启动以后会执行该脚本。
关于脚本中内容具体请参考Maxscript的自带文档 OLE Automation 章节。
1 ( 2 /* Dynamically writes the necessary Registry information to allow 3 Simon Felton's MXSCOM bridge to work. 4 IF RUNNING THIS SCRIPT ON AN VERSION OF MAX OLDER THAN MAX 10 5 THE AVG EXTENSION *MUST* BE INSTALLED 6 */ 7 8 local reg_key 9 local max_version = ((maxVersion())[1] / 1000) as string 10 11 fn create_reg_key hkey key_name ®_key key_value_name key_value_type key_value = 12 ( 13 registry.createKey hkey key_name key:®_key 14 registry.setValue reg_key key_value_name key_value_type key_value 15 ) 16 17 fn write_sub_key_data reg_key sub_key_name sub_key_type sub_key_value = 18 ( 19 local sub_key 20 registry.createKey reg_key sub_key_name key:&sub_key 21 registry.setValue sub_key "" sub_key_type sub_key_value 22 ) 23 24 25 -- Establish a root key for generalized Max data 26 create_reg_key HKEY_CURRENT_USER @"Software\Classes\MAX.Application" ®_key "" #REG_SZ "OLE Automation MAX Application" 27 28 -- Add the Clsid information 29 write_sub_key_data reg_key "Clsid" #REG_SZ "{7FA22CB1-D26F-11d0-B260-00A0240CEEA3}" 30 31 -- Add the CurVer information 32 write_sub_key_data reg_key "CurVer" #REG_SZ ("MAX.Application." + max_version) 33 34 -- Establish a new root key for the version of Max being used 35 create_reg_key HKEY_CURRENT_USER (@"Software\Classes\MAX.Application." + max_version) ®_key "" #REG_SZ ("OLE Automation MAX " + max_version + ".0 Application") 36 37 -- Add the Clsid information 38 write_sub_key_data reg_key "Clsid" #REG_SZ "{7FA22CB1-D26F-11d0-B260-00A0240CEEA3}" 39 40 -- Make a new root key for the CLSID data 41 create_reg_key HKEY_CURRENT_USER @"Software\Classes\CLSID\{7FA22CB1-D26F-11d0-B260-00A0240CEEA3}" ®_key "" #REG_SZ ("OLE Automation MAX " + max_version + ".0 Application") 42 43 -- Add sub key data 44 write_sub_key_data reg_key "ProgID" #REG_SZ ("MAX.Application." + max_version) 45 write_sub_key_data reg_key "VersionIndependentProgID" #REG_SZ "MAX.Application" 46 47 -- Register the running of files and executing script code to OLE. 48 registerOLEInterface #( filein, execute, edit, encryptscript ) 49 50 )
2、编写socket通讯dll(C#)
为什么要开启ole,是因为该dll用到了ole。为什么用C#写dll通信,是因为我对C#比较熟悉,还有就是maxscript中虽然可以调用donet的方法,但是语法上写起来很别扭,之前也写过ms一版,有些问题在ms中不好解决,比如在在调用ms中的执行了“importFile (strIn) #noPrompt” 时会发生未知错误。下面会贴上C#和ms两个版本的socket通讯代码(都是demo版本,仅供参考)。这里max都是做服务端,需要循环监听客户端的消息。
1 using System; 2 using System.Collections.Generic; 3 using System.Linq; 4 using System.Net; 5 using System.Net.Sockets; 6 using System.Reflection; 7 using System.Text; 8 using System.Threading; 9 using System.Web.Script.Serialization; 10 11 namespace SocketTest 12 { 13 public class SocketServer 14 { 15 //承载接收数据 16 private static byte[] result = new byte[1024]; 17 //端口 18 private static int myProt = 8889; 19 20 private static Socket serverSocket; 21 public static void InitSocket() 22 { 23 //创建通讯对象 24 serverSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); 25 //绑定IP地址:端口 26 serverSocket.Bind(new IPEndPoint(IPAddress.Any, myProt)); 27 //设定最多10个排队连接请求 28 serverSocket.Listen(5); 29 //通过Clientsoket发送数据 30 Thread myThread = new Thread(ListenClientConnect); 31 myThread.Start(); 32 } 33 /// <summary> 34 /// 监听客户端连接 35 /// </summary> 36 private static void ListenClientConnect() 37 { 38 while (true) 39 { 40 Socket clientSocket = serverSocket.Accept(); 41 try 42 { 43 clientSocket.Send(Encoding.UTF8.GetBytes("第一次握手")); 44 Thread receiveThread = new Thread(ReceiveMessage); 45 receiveThread.Start(clientSocket); 46 } 47 catch (Exception) 48 { 49 50 clientSocket.Shutdown(SocketShutdown.Both); 51 clientSocket.Close(); 52 } 53 54 } 55 } 56 57 /// <summary> 58 /// 接收消息 59 /// </summary> 60 /// <param name="clientSocket"></param> 61 private static void ReceiveMessage(object clientSocket) 62 { 63 Socket myClientSocket = (Socket)clientSocket; 64 while (true) 65 { 66 try 67 { 68 //通过clientSocket接收数据 69 int receiveNumber = myClientSocket.Receive(result); 70 //返回给客户端的字典对象 71 Dictionary<string, object> dicTemp = new Dictionary<string, object>(); 72 if (receiveNumber > 0) 73 { 74 try 75 { 76 //将客户端消息转换成字符 77 string clientResult = Encoding.UTF8.GetString(result, 0, receiveNumber); 78 //json字符串转换成字典对象 79 Dictionary<string, object> dicResult = GetDicByJson(clientResult); 80 81 if (dicResult.Keys.Contains("code")) 82 { 83 //判断是否为java轮询监听 84 if (dicResult["code"].ToString().Equals("0100")) 85 { 86 //返回正常运行消息 87 dicTemp.Add("code", "0002"); 88 89 } 90 //执行任务文件处理的任务命令 91 else if (dicResult["code"].ToString().Equals("0101")) 92 { 93 try 94 { 95 //获取输入输出路径 96 string inFilePath = dicResult["in"].ToString(); 97 string outFilePath = dicResult["out"].ToString(); 98 //判断文件是否存在 99 //if (File.Exists(inFilePath)) 100 //{ 101 //获取max ole对象 102 var com_type = Type.GetTypeFromProgID("Max.Application"); 103 dynamic com_obj = Activator.CreateInstance(com_type); 104 //构建执行参数 105 object[] parameter = new object[1]; 106 //Fncreathouse 自定义ms中的方法 测试的时候可以用box()来替代 sname是参数名称 inFilePath是java传过来的参数值 107 parameter[0] = "Fncreathouse sName:\"" + inFilePath + "\""; 108 //执行方法 109 object result = com_type.InvokeMember("execute", BindingFlags.InvokeMethod | BindingFlags.Instance, System.Type.DefaultBinder, com_obj, parameter); 110 dicTemp.Add("code", result.ToString()); 111 //myClientSocket.Send(Encoding.UTF8.GetBytes("成功了" + result.ToString())); 112 //string filepath = "D:\\2017\\3DMAX\\MXSPyCOM-master\\hello_world.ms"; 113 //com_obj.FileIn(filepath); 114 //} 115 //else 116 //{ 117 // //文件不存在 118 // dicTemp.Add("code", "0004"); 119 //} 120 } 121 catch (Exception) 122 { 123 dicTemp.Add("code", "0005"); 124 } 125 } 126 } 127 else 128 { 129 130 } 131 } 132 catch (Exception) 133 { 134 135 dicTemp.Add("code", "0005"); 136 } 137 //将结果转换成json字符串,返回给客户端 138 string returnStr = GetJsonStrByDic(dicTemp); 139 myClientSocket.Send(Encoding.UTF8.GetBytes(returnStr)); 140 } 141 else 142 { 143 //关闭客户端的连接 144 myClientSocket.Shutdown(SocketShutdown.Both); 145 myClientSocket.Close(); 146 break; 147 } 148 149 150 } 151 catch (Exception) 152 { 153 myClientSocket.Shutdown(SocketShutdown.Both); 154 myClientSocket.Close(); 155 break; 156 } 157 } 158 } 159 160 /// <summary> 161 /// 将字符串转换成字典 162 /// </summary> 163 /// <param name="jsonString"></param> 164 /// <returns></returns> 165 public static Dictionary<string, object> GetDicByJson(string jsonString) 166 { 167 Dictionary<string, object> JsonData = new Dictionary<string, object>(); 168 try 169 { 170 JavaScriptSerializer s = new JavaScriptSerializer(); 171 JsonData = (Dictionary<string, object>)s.DeserializeObject(jsonString); 172 } 173 catch (Exception) 174 { 175 } 176 return JsonData; 177 } 178 /// <summary> 179 /// 字典转换成简单json 180 /// </summary> 181 /// <param name="dic"></param> 182 /// <returns></returns> 183 public static string GetJsonStrByDic(Dictionary<string, object> dic) 184 { 185 string json = string.Empty; 186 try 187 { 188 if (dic.Keys.Count > 0) 189 { 190 json = "{"; 191 foreach (var item in dic.Keys) 192 { 193 //如果是最后一个不加逗号 194 if (item.Equals(dic.Keys.Last())) 195 { 196 json = json + "\"" + item + "\":\"" + dic[item].ToString() + "\""; 197 } 198 else 199 { 200 json = json + "\"" + item + "\":\"" + dic[item].ToString() + "\","; 201 } 202 } 203 json = json + "}"; 204 } 205 } 206 catch (Exception) 207 { 208 209 } 210 return json; 211 } 212 } 213 }
1 /* 2 后台监听7457端口消息 3 */ 4 Global Encoding,IPAddress 5 ( 6 Fn fntest =( 7 --print("准创建box") 8 box() 9 --print("创建完box") 10 11 exportfile "f:\\box.fbx" #noPrompt 12 13 --print("保存文件") 14 15 ) 16 Fn ExecutionOfTasks = 17 ( 18 --theIPAddress = IPAddress.Parse "127.0.0.1" 19 mainTcpListener = DotNetObject "System.Net.Sockets.TcpListener" IPAddress.Any 7455 20 --承载数据接收 21 mainByteStream = DotNetObject "System.Byte[]" 1024 22 23 mainTcpListener.Start() 24 while true do 25 ( 26 27 try 28 ( 29 --获取客户端 30 theSocket = mainTcpListener.AcceptSocket() 31 --与客户端第一次握手 32 theSocket.Send(Encoding.UTF8.GetBytes("I am 3dmax")) 33 while true do 34 ( 35 --获取客户端数 36 num= theSocket.Receive mainByteStream 37 if num>0 then 38 ( 39 --解码客户端数据 40 theString = Encoding.UTF8.GetString(mainByteStream) 41 42 print("收到客户端的消息:"+theString) 43 44 /* 45 for i=1 to 10000000 do 46 ( 47 str="fuck" 48 )*/ 49 --给客户端发送数据* 50 fntest() 51 print("创建box完成") 52 theSocket.Send(Encoding.UTF8.GetBytes("我是主线程")) 53 ) 54 else 55 ( 56 theSocket.Close() 57 exit 58 ) 59 60 ) 61 62 ) 63 catch 64 ( 65 theSocket.Close() 66 67 ) 68 ) 69 70 71 ) 72 73 Fn ListenerTasks = 74 ( 75 theTcpListener = DotNetObject "System.Net.Sockets.TcpListener" IPAddress.Any 7456 76 --承载数据接收 77 secondaryByteStream = DotNetObject "System.Byte[]" 1024 78 79 theTcpListener.Start() 80 while true do 81 ( 82 83 try 84 ( 85 --获取客户端 86 theSocket = theTcpListener.AcceptSocket() 87 --与客户端第一次握手 88 theSocket.Send(Encoding.UTF8.GetBytes("I am 3dmax")) 89 while true do 90 ( 91 --获取客户端数 92 num= theSocket.Receive secondaryByteStream 93 if num>0 then 94 ( 95 --解码客户端数据 96 theString = Encoding.UTF8.GetString(secondaryByteStream) 97 print("收到客户端的消息:"+theString) 98 --给客户端发送数据 99 theSocket.Send(Encoding.UTF8.GetBytes("我辅助线程")) 100 ) 101 else 102 ( 103 theSocket.Close() 104 exit 105 ) 106 107 ) 108 109 ) 110 catch 111 ( 112 theSocket.Close() 113 114 ) 115 ) 116 117 118 ) 119 --IP地址 120 IPAddress = DotNetClass "System.Net.IPAddress" 121 --定义编码解码对象 122 Encoding = DotnetClass "System.Text.Encoding" 123 124 MainThread = DotNetObject "System.ComponentModel.BackgroundWorker" 125 DotNet.AddEventHandler MainThread "DoWork" ExecutionOfTasks 126 MainThread.WorkerSupportsCancellation = true 127 --异步运行 128 MainThread.RunWorkerAsync() 129 130 SecondaryThread = DotNetObject "System.ComponentModel.BackgroundWorker" 131 DotNet.AddEventHandler SecondaryThread "DoWork" ListenerTasks 132 SecondaryThread.WorkerSupportsCancellation = true 133 --异步运行 134 SecondaryThread.RunWorkerAsync() 135 136 137 --异步运行 138 139 /* 140 windows.processPostedMessages() 141 BackgroundWorker.CancelAsync() 142 BackgroundWorker.Dispose() 143 */ 144 )
3、使用ms加载dll
下面的脚本要在max启动的时候执行,建议放在max的安装目录下的Scripts\StartUp
1 Fn GetDotNetAssemblyByFile dllFileName = 2 ( 3 local result 4 DotNetAssembly = dotNetClass "System.Reflection.Assembly" 5 6 tempFolder = SysInfo.TempDir 7 sourceFileName = GetFilenameFile dllFileName 8 tempPrefix = (GenClassID returnValue:true)[1] as string 9 tempFileName = tempFolder + tempPrefix + sourceFileName + GetFilenameType dllFileName 10 CopyFile dllFileName tempFileName 11 result = DotNetAssembly.LoadFile tempFileName 12 result 13 ) 14 15 DotNetActivator = DotNetClass "System.Activator" 16 --根据实际路径填写 17 TestAssembly = GetDotNetAssemblyByFile @"C:\Program Files\Autodesk\3ds Max 2016\scripts\Startup\SocketBy3DMAX.dll" 18 TestClassType = TestAssembly.GetType("SocketTest.SocketServer") 19 TestClassObject = DotNetActivator.CreateInstance TestClassType 20 TestClassObject.InitSocket()