2017-12-05 97 views
0

私はC#コードを使用して、ユーザーが別のプロセスでファイルをコントロールにドラッグアンドドロップするのをシミュレートしたいと考えています。この目標の踏み台として、自分のTextBoxにWM_DROPFILESメッセージを送信し、DragDropイベントがトリガーされていることを確認しようとしています。C#からWM_DROPFILESを送信するにはどうすればよいですか?

1つのTextBoxと2つのボタンを含むフォームの下のコードでは、button1をクリックするとtextBox1のテキストが正常に「Hello World」に設定されます。それで、私はSendMessageを正しく使用していると思われ、ポインタを介して引数を提供できるようになっています。 Windowsエクスプローラからテキストボックス1にファイルをドラッグ&ドロップするとMessageBoxが表示されるので、ドラッグアンドドロップされたファイルを正しく受け取るようにtextBox1が設定されます。しかし、button2をクリックすると何も起こりません。 button2をクリックしたときにMessageBoxが表示されないのはなぜですか?

using System; 
using System.Data; 
using System.Linq; 
using System.Runtime.InteropServices; 
using System.Windows.Forms; 

namespace StackOverflow 
{ 
    public partial class BadDragDrop : Form 
    { 
     #region WINAPI 

     [Serializable] 
     [StructLayout(LayoutKind.Sequential)] 
     struct POINT 
     { 
      public Int32 X; 
      public Int32 Y; 
     } 

     [Serializable] 
     [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)] 
     class DROPFILES 
     { 
      public Int32 size; 
      public POINT pt; 
      public Int32 fND; 
      public Int32 WIDE; 
     } 

     const uint WM_DROPFILES = 0x0233; 
     const uint WM_SETTEXT = 0x000C; 

     [DllImport("Kernel32.dll", SetLastError = true)] 
     static extern int GlobalLock(IntPtr Handle); 

     [DllImport("Kernel32.dll", SetLastError = true)] 
     static extern int GlobalUnlock(IntPtr Handle); 

     [DllImport("user32.dll", SetLastError = true)] 
     static extern IntPtr SendMessage(IntPtr hWnd, uint Msg, IntPtr wParam, IntPtr lParam); 

     #endregion 

     public BadDragDrop() 
     { 
      InitializeComponent(); 
      textBox1.AllowDrop = true; 
     } 

     private void button1_Click(object sender, EventArgs e) 
     { 
      string textToSet = "Hello world\0"; 
      IntPtr p = Marshal.AllocHGlobal(textToSet.Length); 
      Marshal.Copy(textToSet.Select(c => (byte)c).ToArray(), 0, p, textToSet.Length); 
      int success = GlobalUnlock(p); 
      SendMessage(textBox1.Handle, WM_SETTEXT, IntPtr.Zero, p); 
      Marshal.FreeHGlobal(p); 
     } 

     private void button2_Click(object sender, EventArgs e) 
     { 
      string filePath = @"C:\Windows\win.ini" + "\0\0"; 

      DROPFILES s = new DROPFILES() 
      { 
       size = Marshal.SizeOf<DROPFILES>(), 
       pt = new POINT() { X = 10, Y = 10 }, 
       fND = 0, 
       WIDE = 0, 
      }; 

      int wparamLen = s.size + filePath.Length; 

      IntPtr p = Marshal.AllocHGlobal(wparamLen); 
      int iSuccess = GlobalLock(p); 

      Marshal.StructureToPtr(s, p, false); 
      Marshal.Copy(filePath.Select(c => (byte)c).ToArray(), 0, p + s.size, filePath.Length); 

      iSuccess = GlobalUnlock(p); 

      var verify = new byte[wparamLen]; 
      Marshal.Copy(p, verify, 0, wparamLen); 

      var ipSuccess = SendMessage(textBox1.Handle, WM_DROPFILES, p, IntPtr.Zero); 
      Marshal.FreeHGlobal(p); 
     } 

     private void textBox1_DragDrop(object sender, DragEventArgs e) 
     { 
      MessageBox.Show(this, "Drag drop!"); 
     } 

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

答えて

1

TextBoxはそもそもWM_DROPFILESメッセージを処理しないので、あなたがあなたのMessageBoxが表示されていない理由がありそうです。 IDropTargetインターフェイス(WPFドキュメントのDrag and Drop Overviewを参照)を実装する代わりに、OLEドラッグ& Dropを使用してドロップサポートを実装します。 DoDragDrop()が95 OLEドラッグ&ドロップは非常に長い時間のためにWindows上でドラッグ&ドロップ&を実装するために方法が好まれている帰りのWindowsで導入されて以来

WM_DROPFILESは、これまでに廃止されました。 WM_DROPFILESは、Windowsではサポートされていますが(.NETではなく)、従来のアプリケーションとの下位互換性のみがサポートされています。

Windowsエクスプローラのアイテムを他のアプリケーションにドラッグすると、OLEドラッグ&ドロップが受信者にOLEドラッグ&ドロップを実装していない場合でもドロップされます。

あなたは(あなたのTextBoxが持っているように)&がそれに呼ばれるRegisterDragDrop()を持っていたウィンドウにIDataObjectをドラッグ&ドロップした場合、IDataObjectは取り扱いのためのウィンドウのIDropTargetインタフェースに渡されます。

あなたがIDropTargetを実装していないが、それに呼ばれるDragAcceptFiles()があった、または少なくともWS_EX_ACCEPTFILESウィンドウスタイルを持つウィンドウ上に&ドロップIDataObjectをドラッグするとIDataObjectCF_HDROPデータが含まれている場合、WindowsはWM_DROPFILESメッセージを生成します。その中に。

だから、あなたはあなたのTextBoxにしようとしている何をするために、あなたはIDropSourceIDataObjectインタフェースを実装する必要があり、その後、彼らとDoDragDrop()を呼び出します。 Windowsが実際のドラッグアンドドロップを処理するようにします。 Shell Clipboard FormatsおよびHandling Shell Data Transfer Scenariosを参照してください。

これで、まだWM_DROPFILESメッセージを別のプロセス(あなたの質問があなたの最終目標です)に送信することが決定された場合、それを行うことができ、WindowsはDROPFILESの構造境界を越えて構造体をマーシャリングしますあなたはSendMessage()の代わりにPostMessage()を使用する必要があります(その理由だけは分かりません)。が失敗した場合にのみ、DROPFILESを解放してください。 PostMessage()が成功した場合、WindowsはDROPFILESを解放します。

しかし、その後も、受信側のプロセスが実際に(とそれがない場合でも、受信機がWM_DROPFILESWM_COPYGLOBALDATAメッセージを許可するように自分自身にChangeWindowMessageFilter/Ex()を呼び出していない限り、あなたのマニュアルWM_DROPFILESメッセージは、UIPIによってブロックされる可能性がありますWM_DROPFILESメッセージを処理するという保証はありません)。受信機が代わりにIDataObjectを予期しているかもしれません。受信機がドラッグをサポートしていても、&のドロップは全くありません。

関連する問題