Winform 从 WIn32.dll 调用函数时出现无效句柄错误



我正在构建一个需要剪贴板组件的Winform应用程序,因此我需要调用user32.dll 。现在,当我启动应用程序时,我收到以下错误System.UnauthorizedAccessException: 'Access is denied. (Exception from HRESULT: 0x80070005 (E_ACCESSDENIED))'

经过进一步调查,事实证明,当我尝试使用SetClipboardViewer()注册剪贴板查看器时,我从 user32 收到一个invalid Handle错误代码.dll 。

using System;
using System.Drawing;
using System.Runtime.InteropServices;
using System.Windows.Forms;
using System.Threading;
namespace NiceClip
{
public partial class MainForm : Form
{
[DllImport("User32.dll")]
protected static extern int SetClipboardViewer(int hWndNewViewer);
[DllImport("User32.dll")]
protected static extern int GetLastError();
[DllImport("User32.dll", CharSet = CharSet.Auto)]
public static extern bool ChangeClipboardChain(IntPtr hWndRemove, IntPtr hWndNewNext);
[DllImport("user32.dll", CharSet = CharSet.Auto)]
public static extern int SendMessage(IntPtr hwnd, int wMsg, IntPtr wParam, IntPtr lParam);
IntPtr nextClipboardViewer;
NotifyIcon niceClipIcon;
Icon niceClipIconImage;
ContextMenu contextMenu = new ContextMenu();
bool reallyQuit = false;
bool isCopying = false;
public MainForm()
{
InitializeComponent();
nextClipboardViewer = (IntPtr)SetClipboardViewer((int)this.Handle);
if (this.clipboardHistoryList.Items.Count > 0)
this.clipboardHistoryList.SetSelected(0, true);
clipboardHistoryList.Select();
this.TopMost = true;
niceClipIconImage = Properties.Resources.clipboard;
niceClipIcon = new NotifyIcon
{
Icon = niceClipIconImage,
Visible = true
};
MenuItem quitMenuItem = new MenuItem("Quit");
MenuItem showFormItem = new MenuItem("NiceClip");
this.contextMenu.MenuItems.Add(showFormItem);
this.contextMenu.MenuItems.Add("-");
this.contextMenu.MenuItems.Add(quitMenuItem);
niceClipIcon.ContextMenu = contextMenu;
quitMenuItem.Click += QuitMenuItem_Click;
showFormItem.Click += ShowForm;
}
protected override void OnHandleCreated(EventArgs e)
{
base.OnHandleCreated(e);
}
/// <summary>
/// Takes care of the external DLL calls to user32 to receive notification when
/// the clipboard is modified. Passes along notifications to any other process that
/// is subscribed to the event notification chain.
/// </summary>
protected override void WndProc(ref System.Windows.Forms.Message m)
{
const int WM_DRAWCLIPBOARD = 0x308;
const int WM_CHANGECBCHAIN = 0x030D;
int error = Marshal.GetLastWin32Error(); // Error is code 1400 (invalid window handle)
switch (m.Msg)
{
case WM_DRAWCLIPBOARD:
if (!isCopying)
AddClipBoardEntry();
break;
case WM_CHANGECBCHAIN:
if (m.WParam == nextClipboardViewer)
nextClipboardViewer = m.LParam;
else
SendMessage(nextClipboardViewer, m.Msg, m.WParam,
m.LParam);
break;
default:
base.WndProc(ref m);
break;
}
}
/// <summary>
/// Adds a clipboard history to the clipboard history list.
/// </summary>
private void AddClipBoardEntry()
{
if (Clipboard.ContainsText()) // FAILS HERE
{
string clipboardText = Clipboard.GetText();
if (!String.IsNullOrEmpty(clipboardText))
{
clipboardHistoryList.Items.Insert(0, clipboardText);
toolStripStatusLabel.Text = "Entry added in the clipboard history.";
deleteButton.Enabled = true;
}
}
else
{
toolStripStatusLabel.Text = "History entry was not added because it was null or empty";
}
}

int error = Marshal.GetLastWin32Error();行返回错误代码 1400,即无效的窗口句柄,请参阅此处。

可能的解决方案

我已经验证了螺纹单元设置是否正确

namespace NiceClip
{
static class Program
{
[STAThread]        // Here
static void Main()
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Application.Run(new MainForm());
}
}
}

我也尝试使用GC.KeepAlive()这样它就不会收集表单或它的句柄,但我真的不明白为什么会这样。

当我调试应用程序时,我观察到,就在调用SetClipBoardViewer()之前,表单句柄有一个值(我认为)是有效的,至少它不是null0x0000所以我不明白为什么句柄会被视为无效。

注意

  • 该应用程序之前在同一台计算机上编译过,我只是 已经有一段时间没有工作了,现在它不起作用。
  • 完整的应用程序在 GitHub 上以当前状态在此提交时可用(减去我为调试目的添加的几行和一些注释,以帮助你们理解这一点)。

我发现了错误(来自 GIT 的源代码)。更改应用程序设置。

转到Properties-->Security并更改为"这是一个完全信任的应用程序"。

此更改后,应用程序可以正常启动,剪贴板功能正常工作。

最新更新