【发布时间】:2017-02-06 21:27:11
【问题描述】:
我正在编写一个允许其他程序写入的 .Net WPF 应用程序。设计人员想要实现的功能之一是在第三方程序中使用自定义光标来替换文本编辑器“I-Beam”。
我使用的第一个第三方程序是 MS Word 2013,使用 .Net 的 Office Interop 东西。我的程序的应用程序域中有一个全局 C# DLL,它包含游标资源。
Interop 不允许您分配自定义光标(尽管它允许您切换光标)。所以经过大量的挖掘,我尝试了一些低级的玩弄。
在互操作插件中,我添加了这个类:
public static class CursorHook
{
private static IntPtr programCursor = IntPtr.Zero;
private static IntPtr systemCursor = LoadCursor(IntPtr.Zero, OCR_IBEAM);
public static void Init()
{
programCursor = Marshal.AllocHGlobal(resourceCursor.marker_cursor2.Length);
Marshal.Copy(resourceCursor.marker_cursor2, 0, programCursor, resourceCursor.marker_cursor2.Length);
}
public static void Start()
{
SetSystemCursor(programCursor, OCR_IBEAM);
}
public static void Stop()
{
SetSystemCursor(systemCursor, OCR_IBEAM);
}
public static void Dispose()
{
Marshal.FreeHGlobal(programCursor);
}
private static IntPtr _hookID = IntPtr.Zero;
private const int OCR_IBEAM = 32513;
[DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
private static extern bool SetSystemCursor(IntPtr hCursor, uint id);
[DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
private static extern IntPtr LoadCursor(IntPtr hInstance, int id);
[DllImport("user32.dll")]
private static extern IntPtr SetCursor(IntPtr hCursor);
}
在我的插件类中,我这样做:
public partial class ThisAddIn
{
//Stuff
private void ThisAddIn_Startup(object sender, System.EventArgs e)
{
CursorHook.Init();
CursorHook.Start();
this.Application.WindowDeactivate += new WordInterop.ApplicationEvents4_WindowDeactivateEventHandler(Deactivated);
this.Application.WindowActivate += new WordInterop.ApplicationEvents4_WindowActivateEventHandler(Activated);
//Stuff
}
private void ThisAddIn_Shutdown(object sender, System.EventArgs e)
{
CursorHook.Stop();
CursorHook.Dispose();
}
//stuff
private void Activated(WordInterop.Document Doc, WordInterop.Window Wn) { CursorHook.Start(); }
private void Deactivated(WordInterop.Document Doc, WordInterop.Window Wn) { CursorHook.Stop(); }
//stuff
}
然而,光标并没有改变(即使调用了代码)。我错过了什么?
顺便说一句,我理解静态类如何可能不是资源的最佳利用的大局。但现在,我只是想让光标做我想做的事,完成后我会继续优雅。
提前致谢!
编辑
我已将代码修改为如下所示:
public static class CursorHook
{
private static IntPtr ptr_IBeam = IntPtr.Zero;
private static IntPtr programCursor = IntPtr.Zero;
private static IntPtr systemCursor = IntPtr.Zero;
private static Cursor ProgramCursor = null;
public static void Init()
{
using(MemoryStream ms = new MemoryStream(resourceCursor.marker_cursor2))
{
ProgramCursor = new Cursor(ms);
}
programCursor = ProgramCursor.Handle;
systemCursor = Cursors.IBeam.CopyHandle();
}
public static void Start()
{
SetSystemCursor(programCursor, UNS_OCR_IBEAM);
}
public static void Stop()
{
SetSystemCursor(systemCursor, UNS_OCR_IBEAM);
}
public static void Dispose()
{
}
private const int OCR_IBEAM = 32513;
private const uint UNS_OCR_IBEAM = OCR_IBEAM;
private static IntPtr PTR_OCR_IBEAM = new IntPtr(OCR_IBEAM);
[DllImport("user32.dll", CharSet = CharSet.Auto)]
private static extern bool SetSystemCursor(IntPtr hCursor, uint id);
}
现在一切都变得简单多了,这主要归功于 HansPassant 在下面的评论。现在它实际上也有点效果了!
然而,仍然有两个错误。
1) 图标没有颜色。我知道这是自定义图标的问题,并且有解决方法,所以我会自己研究。
我遇到了什么问题: 2) 打开 Word 时图标会变成我的自定义图标,在 Word 外部单击时会变回默认图标。但是在此之后它将拒绝更改回我的图标。每次我“激活”MS Word 时,SetSystemCursor() 都会返回 false。为什么?
【问题讨论】:
-
可以安全地假设目标窗口处理WM_SETCURSOR,并根据需要设置自己的光标,从而取消您更改光标形状的天真尝试。
-
除非我试图更改 SYSTEM 光标,因此目标窗口的作用无关紧要。
-
假设目标窗口正在使用SYSTEM光标。
-
@IInspectable 公平点。不过,它是 MS Word。当我通过控制面板将系统光标更改为自定义光标时,MS Word 会使用它。
-
错误检查不是可选的。 SetSystemCursor 可以返回 false,你永远不会知道。 LoadCursor 的第二个参数是 IntPtr,而不是 int。 Marshal.AllocHGlobal() + Copy() 不正确,请改用 Icon.Handle 属性。
标签: c# winapi office-interop low-level