工具方案分享_传递消息到3dsMax

  • 分享通过C#从外部程序中,向3dsMax传递消息的做法
  • 此方法是从SublimeText软件里的插件SendTo3dsMax中学到的,它使用Python编写,我这里用C#重新实现了

  • 分成3个步骤,附了一个重要的辅助类WindowsEnumerator

步骤1,通过C#获得当前打开的3dsMax主窗口句柄

public static IntPtr[] Get3dsMaxInstances()
{
    return (
        from p in Process.GetProcesses()
        where p.MainWindowTitle.Contains(@"Autodesk 3ds Max")
        select p.MainWindowHandle
    ).ToArray();
}

步骤2,查找子窗口MaxscriptListener的句柄

  • 选择其中一个3dsMax实例,查找子窗口MaxscriptListener
  • 需要用到WindowsEnumerator类,见 附1 WindowsEnumerator
public static IntPtr WHndMsl = (IntPtr)0; 

// 通过传入的3dsMax主窗口句柄,查找窗口MaxscriptListener的句柄
private static void FindAndMergeMaxscriptListener(IntPtr WHndMax)
{  
    var enumerator = new WindowsEnumerator();
    foreach (var child in enumerator.GetChildWindows((int)WHndMax))
    {
                if (child.ClassName.Contains("MXS_Scintilla"))
                {
                    WHndMsl = (IntPtr)child.hWnd;

                    // MSLProcessID
                    var procIdMsl = new IntPtr();
                    WindowsEnumerator.GetWindowThreadProcessId(WHndMsl, procIdMsl);

                    // 当前程序的线程
                    var procIdCurr = Thread.CurrentThread.ManagedThreadId;

                    // 合并线程
                    WindowsEnumerator.AttachThreadInput((uint)procIdMsl, (uint)procIdCurr, true);

                    break;
                }
    }
}

步骤3,向MaxscriptListener 传递消息

public static int WM_SETTEXT    = 0x000C;
public static int WM_CHAR    = 0x0102;
public static int VK_RETURN    = 0x0D; 

// 传入MaxscriptListener的句柄,以及一句Maxscript命令
/*
    -- cmd例子
    SendMessageTo3dsMax(WHndMsl, " print \"Hello World.\" ")

*/
public static void SendMessageTo3dsMax(IntPtr WHndMsl, string cmd)
{
    // 需要加上 ; 才有用
    var text = cmd + ";";
    WindowsEnumerator.SendMessage(WHndMsl, (uint)WM_SETTEXT, IntPtr.Zero, text);

    // 需要传入回车键
    WindowsEnumerator.SendMessage(WHndMsl, (uint)WM_CHAR, VK_RETURN, 0);

}


附1,WindowsEnumerator

using System;
using System.Collections.Generic;
using System.Runtime.InteropServices;
using System.Text;

namespace GenericLibrary.Library
{
    /// <summary>
    /// Enumerate top-level and child windows
    /// Fix: 2015.05.27.
    /// 
    /// </summary>
    /// <example>
    /// WindowsEnumerator enumerator = new WindowsEnumerator(); 
    /// foreach (ApiWindow top in enumerator.GetTopLevelWindows()) 
    /// { 
    ///    Console.WriteLine(top.MainWindowTitle); 
    ///        foreach (ApiWindow child in enumerator.GetChildWindows(top.hWnd))  
    ///            Console.WriteLine(" " + child.MainWindowTitle); 
    /// } 
    /// </example>
    public class WindowsEnumerator
    {

        private delegate int EnumCallBackDelegate(int hWnd, int lParam);

        [DllImport("user32", CharSet = CharSet.Ansi, SetLastError = true, ExactSpelling = true)]
        private static extern int EnumWindows(EnumCallBackDelegate lpEnumFunc, int lParam);

        [DllImport("user32", CharSet = CharSet.Ansi, SetLastError = true, ExactSpelling = true)]
        private static extern int EnumChildWindows(int hWndParent, EnumCallBackDelegate lpEnumFunc, int lParam);

        [DllImport("user32", EntryPoint = "GetClassNameA", CharSet = CharSet.Ansi, SetLastError = true, ExactSpelling = true)]
        private static extern int GetClassName(int hWnd, StringBuilder lpClassName, int nMaxCount);

        [DllImport("user32", CharSet = CharSet.Ansi, SetLastError = true, ExactSpelling = true)]
        private static extern int IsWindowVisible(int hWnd);

        [DllImport("user32", CharSet = CharSet.Ansi, SetLastError = true, ExactSpelling = true)]
        private static extern int GetParent(int hWnd);

        // -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
        // SendMessages

        [DllImport("user32", EntryPoint = "SendMessageA", CharSet = CharSet.Ansi, SetLastError = true, ExactSpelling = true)]
        private static extern Int32 SendMessage(Int32 hWnd, Int32 wMsg, Int32 wParam, Int32 lParam);

        [DllImport("user32", EntryPoint = "SendMessageA", CharSet = CharSet.Ansi, SetLastError = true, ExactSpelling = true)]
        private static extern Int32 SendMessage(Int32 hWnd, Int32 wMsg, Int32 wParam, StringBuilder lParam);

        // public static SendMessage

        //[DllImport("user32.dll", EntryPoint = "SendMessage", CharSet = CharSet.Auto)]
        //public static extern bool SendMessage(IntPtr hWnd, uint Msg, int wParam, StringBuilder lParam);

        //[DllImport("user32.dll", SetLastError = true)]
        //public static extern IntPtr SendMessage(int hWnd, int Msg, int wparam, int lparam);

        [DllImport("user32.dll", CharSet = CharSet.Auto)]
        public static extern IntPtr SendMessage(IntPtr hWnd, uint msg, IntPtr wParam, string lParam);

        [DllImport("User32.DLL")]
        public static extern int SendMessage(IntPtr hWnd, UInt32 msg, Int32 wParam, Int32 lParam);

        // -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 

        [DllImport("user32.dll")]
        public static extern int GetWindowText(IntPtr hwnd, string lpString, int cch);

        internal delegate int WindowEnumProc(IntPtr hwnd, IntPtr lparam);

        [DllImport("user32.dll")]
        internal static extern bool EnumChildWindows(IntPtr hwnd, WindowEnumProc func, IntPtr lParam);

        // 通过句柄获得线程 
        [DllImport("user32.dll")]
        public static extern Int32 GetWindowThreadProcessId(IntPtr hWnd, out Int32 lpdwProcessId);

        [DllImport("user32.dll", SetLastError = true)]
        public static extern uint GetWindowThreadProcessId(IntPtr hWnd, out uint lpdwProcessId);

        // When you don't want the ProcessId, use this overload and pass IntPtr.Zero for the second parameter
        [DllImport("user32.dll")]
        public static extern uint GetWindowThreadProcessId(IntPtr hWnd, IntPtr processId);

        // 合并线程
        [DllImport("user32.dll")]
        public static extern bool AttachThreadInput(uint idAttach, uint idAttachTo, bool fAttach);

        // RegisterWindowMessage  
        [DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Auto)]
        public static extern int RegisterWindowMessage(string lpString);

        // -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --

        [DllImport("user32.dll")]
        public static extern int SetWindowLong(IntPtr hWnd, int nIndex, int dwNewLong);

        [DllImport("user32.dll")]
        public static extern int GetWindowLong(IntPtr hWnd, int nIndex);

        [DllImport("user32.dll", SetLastError = true)]
        public static extern IntPtr SetParent(IntPtr hWndChild, IntPtr hWndNewParent);

        // -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --  

        // Top-level windows.
        // Child windows.
        // Get the window class.
        // Test if the window is visible--only get visible ones.
        // Test if the window's parent--only get the one's without parents.
        // Get window text length signature.
        // Get window text signature.

        private List<ApiWindow> _listChildren = new List<ApiWindow>();
        private readonly List<ApiWindow> _listTopLevel = new List<ApiWindow>();
        private string _topLevelClass = "";
        private string _childClass = "";

        /// <summary>
        /// Get all top-level window information
        /// </summary>
        /// <returns>List of window information objects</returns>
        public List<ApiWindow> GetTopLevelWindows()
        {
            EnumWindows(EnumWindowProc, 0);

            return _listTopLevel;

        }

        public List<ApiWindow> GetTopLevelWindows(string className)
        {
            _topLevelClass = className;

            return GetTopLevelWindows();
        }

        /// <summary>
        /// Get all child windows for the specific windows handle (hwnd).
        /// </summary>
        /// <returns>List of child windows for parent window</returns>
        public List<ApiWindow> GetChildWindows(Int32 hwnd)
        {

            // Clear the window list.
            _listChildren = new List<ApiWindow>();

            // Start the enumeration process.
            EnumChildWindows(hwnd, EnumChildWindowProc, 0);

            // Return the children list when the process is completed.
            return _listChildren;
        }

        public List<ApiWindow> GetChildWindows(Int32 hwnd, string childClass)
        {

            // Set the search
            _childClass = childClass;
            return GetChildWindows(hwnd);

        }

        /// <summary>
        /// Callback function that does the work of enumerating top-level windows.
        /// </summary>
        /// <param name="hwnd">Discovered Window handle</param>
        /// <param name="lParam"></param>
        /// <returns>1=keep going, 0=stop</returns>
        private Int32 EnumWindowProc(Int32 hwnd, Int32 lParam)
        {
            // Eliminate windows that are not top-level.
            if (GetParent(hwnd) == 0 && Convert.ToBoolean(IsWindowVisible(hwnd)))
            {

                // Get the window title / class name.
                var window = GetWindowIdentification(hwnd);

                // Match the class name if searching for a specific window class.
                if (_topLevelClass.Length == 0 || window.ClassName.ToLower() == _topLevelClass.ToLower())
                {
                    _listTopLevel.Add(window);
                }
            }

            // To continue enumeration, return True (1), and to stop enumeration 
            // return False (0).
            // When 1 is returned, enumeration continues until there are no 
            // more windows left.

            return 1;

        }

        /// <summary>
        /// Callback function that does the work of enumerating child windows.
        /// </summary>
        /// <param name="hwnd">Discovered Window handle</param>
        /// <param name="lParam"></param>
        /// <returns>1=keep going, 0=stop</returns>
        private Int32 EnumChildWindowProc(Int32 hwnd, Int32 lParam)
        {

            var window = GetWindowIdentification(hwnd);

            // Attempt to match the child class, if one was specified, otherwise
            // enumerate all the child windows.
            if (_childClass.Length == 0 || window.ClassName.ToLower() == _childClass.ToLower())
            {
                _listChildren.Add(window);
            }
            return 1;

        }

        /// <summary>
        /// Build the ApiWindow object to hold information about the Window object.
        /// </summary>
        private ApiWindow GetWindowIdentification(int hwnd)
        {

            const Int32 WM_GETTEXT = 13;
            const Int32 WM_GETTEXTLENGTH = 14;

            var window = new ApiWindow();
            var title = new StringBuilder();

            // Get the size of the string required to hold the window title.
            var size = SendMessage(hwnd, WM_GETTEXTLENGTH, 0, 0);

            // If the return is 0, there is no title.
            if (size > 0)
            {
                title = new StringBuilder(size + 1);
                SendMessage(hwnd, WM_GETTEXT, title.Capacity, title);
            }

            // Get the class name for the window.
            var classBuilder = new StringBuilder(64);
            GetClassName(hwnd, classBuilder, 64);

            // Set the properties for the ApiWindow object.
            window.ClassName = classBuilder.ToString();
            window.MainWindowTitle = title.ToString();
            window.hWnd = hwnd;

            return window;
        }
    }

    public class ApiWindow
    {
        public string MainWindowTitle = "";
        public string ClassName = "";
        public int hWnd;
    }
}


附,完成品工具的展示

使用WPF编写的文件目录树,用于查看文件夹的内容,并且可以直接读取文件,让Max接受相关命令
当前演示的是 读取Bip文件直接 设置到3dsMax中的骨骼上进行动画预览

发表评论

电子邮件地址不会被公开。 必填项已用*标注