首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >问答首页 >如何从WM_DROPFILES发送C#?

如何从WM_DROPFILES发送C#?
EN

Stack Overflow用户
提问于 2017-12-05 00:03:57
回答 1查看 1.7K关注 0票数 0

我希望使用C#代码来模拟用户在单独的进程中将文件拖放到控件上。作为实现这一目标的垫脚石,我试图向我自己的WM_DROPFILES发送一条TextBox消息,并验证DragDrop事件是否被触发。

下面的代码包含一个TextBox和两个按钮,单击button1成功地将textBox1的文本设置为"Hello“。因此,我似乎正确地使用了SendMessage,并且能够通过指针提供参数。将文件从Windows拖放到textBox1上将显示MessageBox,因此textBox1被设置为正确接收拖放文件。但是,当我单击button2时,什么都不会发生。为什么我点击MessageBox时看不到button2呢?

代码语言:javascript
运行
复制
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;
        }
    }
}
EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2017-12-05 03:09:58

您没有看到MessageBox出现的原因很可能是因为TextBox一开始就不处理WM_DROPFILES消息。它通过实现IDropTarget接口(参见WPF文档中的拖放概述 ),使用OLE拖放来实现其drop支持。

自从Windows 95中引入WM_DROPFILES以来,DoDragDrop()就一直受到人们的反对。OLE拖放长期以来一直是Windows上实现拖放的首选方法。Windows仍然支持WM_DROPFILES (但不支持.NET),但只支持与遗留应用程序的向后兼容性。

将项目从Windows拖到其他应用程序使用OLE拖放到引擎盖下,即使接收方没有实现OLE拖放。

如果您将一个IDataObject拖放到已经调用了RegisterDragDrop()的窗口上(就像您的TextBox一样),那么IDataObject将被传递给窗口的IDropTarget接口进行处理。

如果您将一个IDataObject拖放到一个没有实现IDropTarget但已经调用了DragAcceptFiles()的窗口上,或者至少具有WS_EX_ACCEPTFILES窗口样式,那么如果IDataObject中包含CF_HDROP数据,Windows将生成一个WM_DROPFILES消息。

因此,要执行您正在尝试使用TextBox的操作,您需要实现IDropSourceIDataObject接口,然后使用它们调用DoDragDrop()。让Windows为您处理实际的拖放操作。见Shell剪贴板格式处理Shell数据传输方案

现在,尽管如此,如果您仍然决心向另一个进程发送WM_DROPFILES消息(您的问题说这是您的最终目标),那么您可以这样做,并且Windows将为您跨进程边界封送您的DROPFILES结构,但是您需要使用PostMessage()而不是SendMessage() (不确定原因,只知道它是必需的),并且确保只有当PostMessage()失败时才释放DROPFILES。如果DROPFILES成功,Windows将为您释放PostMessage()

但即便如此,也不能保证接收过程实际处理WM_DROPFILES消息(即使处理了,手动WM_DROPFILES消息也可能被UIPI阻塞,除非接收方本身调用ChangeWindowMessageFilter/Ex()以允许WM_DROPFILESWM_COPYGLOBALDATA消息)。接收方可能会期望使用IDataObject。如果接收器甚至支持拖放。

票数 3
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/47644004

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档