Help us understand the problem. What is going on with this article?

Unity(x86/x64)でWindowsメッセージを受け取る方法

More than 3 years have passed since last update.

はじめに

  • Unityでマウスの横スクロールを取得したかっただけなのですが、他にも色々応用が出来そうなので調べてみました。
  • x86/x64両対応です。

擬似コード

test.cs
using UnityEngine;
using UnityEditor;
using System;
using System.Runtime.InteropServices;


public delegate IntPtr WndProcDelegate(IntPtr hWnd, uint msg, IntPtr wParam, IntPtr lParam);

class test : EditorWindow {


        [DllImport("user32.dll", CharSet = CharSet.Auto, ExactSpelling = true)]
        public static extern System.IntPtr GetForegroundWindow();

        [DllImport("user32.dll", EntryPoint = "SetWindowLong")]
        private static extern int SetWindowLong32(HandleRef hWnd, int nIndex, int dwNewLong);
        [DllImport("user32.dll", EntryPoint = "SetWindowLongPtr")]
        private static extern IntPtr SetWindowLongPtr64(HandleRef hWnd, int nIndex, IntPtr dwNewLong);



        [DllImport("user32.dll", EntryPoint = "DefWindowProcA")]
        private static extern IntPtr DefWindowProc(IntPtr hWnd, uint wMsg, IntPtr wParam, IntPtr lParam);

        [System.Runtime.InteropServices.DllImport("user32.dll")]
        private static extern System.IntPtr GetActiveWindow();

        private HandleRef hMainWindow;
        private IntPtr oldWndProcPtr;
        private IntPtr newWndProcPtr;
        private WndProcDelegate newWndProc;

        // P/Invokeの定義 (pinvoke.net参照)
        public static IntPtr SetWindowLongPtr(HandleRef hWnd, int nIndex, IntPtr dwNewLong)
        {
            if (IntPtr.Size == 8)
                return SetWindowLongPtr64(hWnd, nIndex, dwNewLong);
            else
            {
                return new IntPtr(SetWindowLong32(hWnd, nIndex, dwNewLong.ToInt32()));
            }
        }

        const int WM_MOUSEHWHEEL = 0x20E;
        const int WM_NCHITTEST = 0x084;
        const int WM_NCDESTROY = 0x082;
        const int WM_WINDOWPOSCHANGING = 0x046;

        private IntPtr wndProc(IntPtr hWnd, uint msg, IntPtr wParam, IntPtr lParam)
        {
            if (msg == WM_NCDESTROY || msg == WM_WINDOWPOSCHANGING)
            {
                Term();
            }
            if (msg == WM_NCHITTEST || msg == WM_MOUSEHWHEEL)
            {
                float delta = wParam.ToInt32() / 7864320.0f;

                if (delta < 0.0f)
                {
                    inputData.hscroll = -3.0f;
                }
                else if (delta > 0.0f)
                {
                    inputData.hscroll = 3.0f;
                }

            }


            Debug.Log("wndProc msg:0x" + msg.ToString("x4") + " wParam:0x" + wParam.ToString("x4") + " lParam:0x" + lParam.ToString("x4"));

            return DefWindowProc(hWnd, msg, wParam, lParam);
        }

/// <summary>
        /// 表示
        /// </summary>
        [MenuItem("Sample/Window")]
        static void ShowWindow()
        {
            test parent = ScriptableObject.CreateInstance<test>();
            parent.Show();
            parent.Init();


        }
        /// 終了
        ~test()
        {
            Term();
        }

        /// 初期化(EditorWindowをShowした後にコール)
        public void Init()
        {
            // ウインドウプロシージャをフックする
            hMainWindow = new HandleRef(null,GetActiveWindow());
            newWndProc = new WndProcDelegate(wndProc);
            newWndProcPtr = Marshal.GetFunctionPointerForDelegate(newWndProc);
            oldWndProcPtr = SetWindowLongPtr(hMainWindow, -4, newWndProcPtr);

        }
        public void Term()
        {
            SetWindowLongPtr(hMainWindow, -4, oldWndProcPtr);
            hMainWindow = new HandleRef(null, IntPtr.Zero);
            oldWndProcPtr = IntPtr.Zero;
            newWndProcPtr = IntPtr.Zero;
            newWndProc = null;
        }

        void OnGUI()
        {
            // ウィンドウハンドルが切り替わったので初期化 
            if (hMainWindow.Handle == IntPtr.Zero)
            {
                Init();
            }
       }
}

課題

  • アクティブなウィンドウハンドルを取得してるだけなので、ウィンドウがドッキングしてる時しかちゃんと取れない。

    • 一旦WM_NCDESTROYとWM_WINDOWPOSCHANGINGのタイミングで一旦ハンドル等クリアして、OnGUIでハンドルがNullだったら初期化としてみた
  • Unity.GUIとの更新タイミングと整合性を取るために色々ゴニョゴニョしないといけない。

参考リンク

-Windows Touch in the Editor

DandyMania
ただのしがないゲームプログラマです。
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
No comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
ユーザーは見つかりませんでした