LoginSignup
13
7

More than 5 years have passed since last update.

UnityでのWindowsのコモンダイアログを利用したファイル選択

Last updated at Posted at 2019-01-29

概要

Unity から Windows のコモンダイアログ Comdlg32.dll の GetOpenFileName() を呼び出した方法のメモです。

背景

Unity上でファイル選択をする場合、Unity Simple File Browser1 やその他アセットを利用する方法があります。
あるいは Mono の System.Windows.Forms. OpenFileDialog を利用する方法2もありますが、DLLのコピーが必要です。

また、どちらもWindowsのダイアログそのものではないため使い勝手が異なります。
そこで、Windowsのダイアログを利用する方法を記しておきます。

方法

Stack Overflow の投稿3 などを参考に、下記のファイル OpenFileName.cs を用意しました。

OpenFileName.cs
using System;
using System.Runtime.InteropServices;

[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]
public class OpenFileName
{
    public int structSize = 0;
    public IntPtr dlgOwner = IntPtr.Zero;
    public IntPtr instance = IntPtr.Zero;
    public string filter = null;
    public string customFilter = null;
    public int maxCustFilter = 0;
    public int filterIndex = 0;
    public string file = null;
    public int maxFile = 0;
    public string fileTitle = null;
    public int maxFileTitle = 0;
    public string initialDir = null;
    public string title = null;
    public int flags = 0;
    public short fileOffset = 0;
    public short fileExtension = 0;
    public string defExt = null;
    public IntPtr custData = IntPtr.Zero;
    public IntPtr hook = IntPtr.Zero;
    public string templateName = null;
    public IntPtr reservedPtr = IntPtr.Zero;
    public int reservedInt = 0;
    public int flagsEx = 0;

    public static readonly int OFN_ALLOWMULTISELECT = 0x00000200;
    public static readonly int OFN_CREATEPROMPT = 0x00000200;
    public static readonly int OFN_DONTADDTORECENT = 0x02000000;
    public static readonly int OFN_ENABLEHOOK = 0x00000020;
    public static readonly int OFN_ENABLEINCLUDENOTIFY = 0x00400000;
    public static readonly int OFN_ENABLESIZING = 0x00800000;
    public static readonly int OFN_ENABLETEMPLATE = 0x00000040;
    public static readonly int OFN_ENABLETEMPLATEHANDLE = 0x00000080;
    public static readonly int OFN_EXPLORER = 0x00080000;
    public static readonly int OFN_EXTENSIONDIFFERENT = 0x00000400;
    public static readonly int OFN_FILEMUSTEXIST = 0x00001000;
    public static readonly int OFN_FORCESHOWHIDDEN = 0x10000000;
    public static readonly int OFN_HIDEREADONLY = 0x00000004;
    public static readonly int OFN_LONGNAMES = 0x00200000;
    public static readonly int OFN_NOCHANGEDIR = 0x00000008;
    public static readonly int OFN_NODEREFERENCELINKS = 0x00100000;
    public static readonly int OFN_NOLONGNAMES = 0x00040000;
    public static readonly int OFN_NONETWORKBUTTON = 0x00020000;
    public static readonly int OFN_NOREADONLYRETURN = 0x00008000;
    public static readonly int OFN_NOTESTFILECREATE = 0x00010000;
    public static readonly int OFN_NOVALIDATE = 0x00000100;
    public static readonly int OFN_OVERWRITEPROMPT = 0x00000002;
    public static readonly int OFN_PATHMUSTEXIST = 0x00000800;
    public static readonly int OFN_READONLY = 0x00000001;
    public static readonly int OFN_SHAREAWARE = 0x00004000;
    public static readonly int OFN_SHOWHELP = 0x00000010;

    public OpenFileName()
    {
        this.structSize = Marshal.SizeOf(this);
        this.filter = "All Files\0*.*\0\0";
        this.file = new string('\0', 4096);
        this.maxFile = this.file.Length;
        this.fileTitle = new string('\0', 256);
        this.maxFileTitle = this.fileTitle.Length;
        this.title = "Open";
        this.flags = OFN_EXPLORER
            | OFN_FILEMUSTEXIST
            | OFN_PATHMUSTEXIST
            | OFN_NOCHANGEDIR;
    }

    [DllImport("Comdlg32.dll", SetLastError = true, ThrowOnUnmappableChar = true, CharSet = CharSet.Auto)]
    public static extern bool GetOpenFileName([In, Out] OpenFileName lpOfn);

    /// <summary>
    /// このメソッドでダイアログが開く
    /// </summary>
    /// <returns>ファイルが選択されればそのパス、非選択ならnullを返す</returns>
    public static string ShowDialog()
    {
        OpenFileName ofn = new OpenFileName();
        if (GetOpenFileName(ofn))
        {
            return ofn.file;
        }
        return null;
    }
}

上記を用意した後、例えば下記のようなスクリプトを適当なオブジェクトにアタッチすることで試せます。

TestScene.cs
using UnityEngine;

public class TestScene : MonoBehaviour {
    private void OnGUI()
    {
        if (GUI.Button(new Rect(100, 100, 200, 40), "Open"))
        {
            string path = OpenFileName.ShowDialog();
            Debug.Log(path);
        }
    }
}

制限事項

Windows 専用となります。
この実装例では単一ファイルの選択のみ可能です。
ダイアログが開いている間、Unity側の動作は停止します。

このコードは Windows 10 64ビットでのみテストしました。

Unityエディタ上で再生してダイアログを開き、閉じる前にUnityエディタをアクティブにすると、カレントディレクトリが変更されたと怒られエディタを強制終了させられました。

補足(2019/2/2追記)

  • flags に設定する OFN_***** の内容についてはMSDNやそれを訳していただいた記事4が参考になります。
  • ここで載せているのはとても簡易的なものです。実用するなら有料アセットも探した方が良いかと思います。無料でネイティブダイアログを出せるものだとUnityStandaloneFileBrowser5というものがありました。Mac、Linux、WebGLにも対応していて良さそうです!(私自身はそう試せていませんが)

参考資料


  1. yasirkula. Unity Simple File Browser, GitHub. (accessed 2019-01-29) 

  2. @koukiwf. UnityでOpenFileDialog(外部ファイルの読み込み), Qiita. (accessed 2019-01-29) 

  3. Arie. Using GetOpenFileName instead of OpenFileDialog, Stack Overflow. (accessed 2019-01-29) 

  4. すなのかたまり. OPENFILENAME構造体, Tips of VC++. (accessed 2019-02-02) 

  5. gkngkc. UnityStandaloneFileBrowser, GitHub. (accessed 2019-02-02) 

13
7
2

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
13
7