potisanのプログラミングメモ

趣味のプログラマーがプログラミング関係で気になったことや調べたことをいつでも忘れられるようにメモするブログです。はてなブログ無料版なので記事の上の方はたぶん広告です。記事中にも広告挿入されるみたいです。

C# ドロップされたデータのフォーマット形式を列挙する

DragEventArgs eとComTypes.IDataObject型

DragDropイベントなどで使用されるe.DataはSystem.Windows.Forms.IDataObject型ですが、System.Runtime.InteropServices.ComTypes.IDataObject型(以下ComTypes.IDataObject型)へ明示的に変換することができます。このComTypes.IDataObject型を使用することで通常のe.Dataでは隠されたフォーマット形式等を参照することができます。

以下に具体的なコードを示します。Form1はDragDragが有効なフォームで複数行テキストボックスTextBox1が1つ存在します。実行してフォームの枠にエクスプローラーからファイルをドロップするとドロップデータに含まれるフォーマットの形式一覧をテキストボックスに表示します。

using System.Runtime.InteropServices;
using System.Text;
using System.Windows.Forms;
using ComTypes = System.Runtime.InteropServices.ComTypes;

namespace WindowsFormsApp1
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }

        private void Form1_DragOver(object sender, DragEventArgs e)
        {
            e.Effect = DragDropEffects.All;
        }

        private void Form1_DragDrop(object sender, DragEventArgs e)
        {
            var obj = (ComTypes.IDataObject)e.Data;
            var buffer = new StringBuilder();

            var enumerator = obj.EnumFormatEtc(ComTypes.DATADIR.DATADIR_GET);
            var fmts = new ComTypes.FORMATETC[1];
            const int S_OK = 0;
            while (enumerator.Next(1, fmts, null) == S_OK)
            {
                var fmt = fmts[0];
                buffer.AppendLine($"{fmt.cfFormat.ToString("X4")} \"{ClipboardUtility.GetClipboardFormatName(fmt.cfFormat)}\"");
            }
            textBox1.Text = buffer.ToString();
        }
    }

    public static class ClipboardUtility
    {
        private const int ERROR_INSUFFICIENT_BUFFER = 122;

        public static class NativeMethods
        {
            [DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
            public static extern int GetClipboardFormatNameW(uint format, StringBuilder lpszFormatName, int cchMaxCount);
        }

        public static string GetClipboardFormatName(short format)
        {
            var buffer = new StringBuilder();
            for (int l = 256; ; l += 256)
            {
                var copied = NativeMethods.GetClipboardFormatNameW(
                    (uint)format, buffer, l);
                if (Marshal.GetLastWin32Error() == 0)
                    return buffer.ToString(0, copied);
                if (Marshal.GetLastWin32Error() != ERROR_INSUFFICIENT_BUFFER)
                    return null;
            }
        }
    }
}

出力の具体例

デスクトップのファイル・フォルダおよびGoogle ChromeのURLまたは選択範囲(文字列)をドロップした場合の出力例を示します。

デスクトップのファイル・フォルダ(PC等の特殊項目含まず)

C0DA "Shell IDList Array" TYMED_HGLOBAL
C213 "DragImageBits" TYMED_HGLOBAL
C0E9 "DragContext" TYMED_ISTREAM
C214 "DragSourceHelperFlags" TYMED_HGLOBAL
C0E8 "InShellDragLoop" TYMED_HGLOBAL
000F "" TYMED_HGLOBAL
C006 "" TYMED_HGLOBAL
C007 "" TYMED_HGLOBAL

デスクトップのファイル・フォルダ(PC等の特殊項目含む)

C0DA "Shell IDList Array" TYMED_HGLOBAL
C0E3 "Preferred DropEffect" TYMED_HGLOBAL
C213 "DragImageBits" TYMED_HGLOBAL
C0E9 "DragContext" TYMED_ISTREAM
C214 "DragSourceHelperFlags" TYMED_HGLOBAL
C0E8 "InShellDragLoop" TYMED_HGLOBAL

Google Chrome 64ビット (68.0.3440.75)のURL(HTTPS)

C0E9 "DragContext" TYMED_ISTREAM
C213 "DragImageBits" TYMED_HGLOBAL
C347 "text/x-moz-url" TYMED_HGLOBAL
C0DE "FileGroupDescriptorW" TYMED_HGLOBAL
C07B "FileContents" TYMED_HGLOBAL
C0EE "UniformResourceLocatorW" TYMED_HGLOBAL
C0E7 "UniformResourceLocator" TYMED_HGLOBAL
000D "" TYMED_HGLOBAL
0001 "" TYMED_HGLOBAL

Google Chrome 64ビット (68.0.3440.75)の選択範囲(文字列のみ)

C0E9 "DragContext" TYMED_ISTREAM
C213 "DragImageBits" TYMED_HGLOBAL
C344 "chromium/x-renderer-taint" TYMED_HGLOBAL
000D "" TYMED_HGLOBAL
0001 "" TYMED_HGLOBAL
C0E2 "" TYMED_HGLOBAL
C1DC "" TYMED_HGLOBAL

データフォーマット(クリップボードフォーマット)形式の数値と文字列

データフォーマットはクリップボードフォーマットと共通で管理されており、4バイトの数値または文字列へのポインタです。数値から名前を取得するにはWin32 APIGetClipboardFormatNameW(A)関数を使用することができます。クリップボードデータの形式については公式ドキュメントを参照してください。

2021/3/10:この記事は別のブログで投稿した記事を移動したものです。