尝试使用EasyHook实现完善光标映射

This commit is contained in:
Xu Liu 2021-03-12 19:37:40 +08:00
commit 16fdb53c18
21 changed files with 569 additions and 38 deletions

5
.gitignore vendored
View file

@ -10,6 +10,11 @@
*.userosscache
*.sln.docstates
# EasyHook
EasyHook*.dll
EasyHook*.exe
EasyLoad*.dll
# User-specific files (MonoDevelop/Xamarin Studio)
*.userprefs

View file

@ -0,0 +1,67 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
<ProjectGuid>{983647DC-E3C2-4658-852B-FF4D146F8134}</ProjectGuid>
<OutputType>Library</OutputType>
<AppDesignerFolder>Properties</AppDesignerFolder>
<RootNamespace>CursorHook</RootNamespace>
<AssemblyName>CursorHook</AssemblyName>
<TargetFrameworkVersion>v4.7.2</TargetFrameworkVersion>
<FileAlignment>512</FileAlignment>
<Deterministic>true</Deterministic>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<DebugSymbols>true</DebugSymbols>
<DebugType>full</DebugType>
<Optimize>false</Optimize>
<OutputPath>..\build\x64\Debug\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<DebugType>pdbonly</DebugType>
<Optimize>true</Optimize>
<OutputPath>bin\Release\</OutputPath>
<DefineConstants>TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
</PropertyGroup>
<ItemGroup>
<Reference Include="EasyHook, Version=2.7.7097.0, Culture=neutral, PublicKeyToken=4b580fca19d0b0c5, processorArchitecture=MSIL">
<HintPath>..\packages\EasyHook.2.7.7097\lib\net40\EasyHook.dll</HintPath>
</Reference>
<Reference Include="System" />
<Reference Include="System.Core" />
<Reference Include="System.Runtime.Remoting" />
<Reference Include="System.Xml.Linq" />
<Reference Include="System.Data.DataSetExtensions" />
<Reference Include="Microsoft.CSharp" />
<Reference Include="System.Data" />
<Reference Include="System.Net.Http" />
<Reference Include="System.Xml" />
</ItemGroup>
<ItemGroup>
<Compile Include="InjectionEntryPoint.cs" />
<Compile Include="NativeMethods.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="ServerInterface.cs" />
</ItemGroup>
<ItemGroup>
<Content Include="EasyHook32.dll" />
<Content Include="EasyHook32Svc.exe" />
<Content Include="EasyHook64.dll" />
<Content Include="EasyHook64Svc.exe" />
<Content Include="EasyLoad32.dll" />
<Content Include="EasyLoad64.dll" />
</ItemGroup>
<ItemGroup>
<None Include="packages.config" />
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
</Project>

View file

@ -0,0 +1,141 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading;
namespace Magpie.CursorHook {
/// <summary>
/// EasyHook will look for a class implementing <see cref="EasyHook.IEntryPoint"/> during injection. This
/// becomes the entry point within the target process after injection is complete.
/// </summary>
public class InjectionEntryPoint : EasyHook.IEntryPoint {
/// <summary>
/// Reference to the server interface within FileMonitor
/// </summary>
private ServerInterface _server = null;
/// <summary>
/// Message queue of all files accessed
/// </summary>
private Queue<string> _messageQueue = new Queue<string>();
private IntPtr _hwndHost;
/// <summary>
/// EasyHook requires a constructor that matches <paramref name="context"/> and any additional parameters as provided
/// in the original call to <see cref="EasyHook.RemoteHooking.Inject(int, EasyHook.InjectionOptions, string, string, object[])"/>.
///
/// Multiple constructors can exist on the same <see cref="EasyHook.IEntryPoint"/>, providing that each one has a corresponding Run method (e.g. <see cref="Run(EasyHook.RemoteHooking.IContext, string)"/>).
/// </summary>
/// <param name="context">The RemoteHooking context</param>
/// <param name="channelName">The name of the IPC channel</param>
public InjectionEntryPoint(
EasyHook.RemoteHooking.IContext context,
string channelName, IntPtr hwndHost) {
_hwndHost = hwndHost;
// Connect to server object using provided channel name
_server = EasyHook.RemoteHooking.IpcConnectClient<ServerInterface>(channelName);
// If Ping fails then the Run method will be not be called
_server.Ping();
}
/// <summary>
/// The main entry point for our logic once injected within the target process.
/// This is where the hooks will be created, and a loop will be entered until host process exits.
/// EasyHook requires a matching Run method for the constructor
/// </summary>
/// <param name="context">The RemoteHooking context</param>
/// <param name="channelName">The name of the IPC channel</param>
public void Run(
EasyHook.RemoteHooking.IContext context,
string channelName, IntPtr hwndHost) {
// Injection is now complete and the server interface is connected
_server.IsInstalled(EasyHook.RemoteHooking.GetCurrentProcessId());
// Install hooks
// SetCursor https://docs.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-setcursor
var setCursorHook = EasyHook.LocalHook.Create(
EasyHook.LocalHook.GetProcAddress("user32.dll", "SetCursor"),
new SetCursor_Delegate(SetCursor_Hook),
this);
// Activate hooks on all threads except the current thread
setCursorHook.ThreadACL.SetExclusiveACL(new int[] { 0 });
_server.ReportMessage("SetCursor钩子安装成功");
NativeMethods.EnumChildWindows(IntPtr.Zero, (IntPtr hWnd, int lParam) => {
NativeMethods.SetClassLong(hWnd, NativeMethods.GCLP_HCURSOR,
NativeMethods.LoadCursor(IntPtr.Zero, NativeMethods.IDC_ARROW));
NativeMethods.EnumChildWindows(hWnd, (IntPtr hWnd1, int lParam1) => {
NativeMethods.SetClassLong(hWnd, NativeMethods.GCLP_HCURSOR,
NativeMethods.LoadCursor(IntPtr.Zero, NativeMethods.IDC_ARROW));
return true;
}, 0);
return true;
}, 0);
NativeMethods.SetCursor(NativeMethods.LoadCursor(IntPtr.Zero, NativeMethods.IDC_ARROW));
try {
// Loop until FileMonitor closes (i.e. IPC fails)
while (true) {
Thread.Sleep(500);
string[] queued = null;
lock (_messageQueue) {
queued = _messageQueue.ToArray();
_messageQueue.Clear();
}
// Send newly monitored file accesses to FileMonitor
if (queued != null && queued.Length > 0) {
_server.ReportMessages(queued);
} else {
_server.Ping();
}
}
} catch {
// Ping() or ReportMessages() will raise an exception if host is unreachable
}
// Remove hooks
setCursorHook.Dispose();
// Finalise cleanup of hooks
EasyHook.LocalHook.Release();
}
// The SetCursor delegate, this is needed to create a delegate of our hook function
[UnmanagedFunctionPointer(CallingConvention.StdCall,
CharSet = CharSet.Unicode,
SetLastError = true)]
delegate IntPtr SetCursor_Delegate(IntPtr hCursor);
// The SetCursor hook function. This will be called instead of the original SetCursor once hooked.
IntPtr SetCursor_Hook(IntPtr hCursor) {
_server.ReportMessage("SetCursor前:" + hCursor.ToString());
if (!NativeMethods.PostMessage(_hwndHost, NativeMethods.WM_COPYDATA, hCursor, IntPtr.Zero)) {
_server.ReportMessage("error: " + Marshal.GetLastWin32Error().ToString());
}
var r = NativeMethods.SetCursor(hCursor);
NativeMethods.PostMessage(_hwndHost, NativeMethods.WM_COPYDATA, NativeMethods.GetCursor(), IntPtr.Zero);
_server.ReportMessage("SetCursor后:" + NativeMethods.GetCursor().ToString());
return r;
//return IntPtr.Zero;
}
}
}

View file

@ -0,0 +1,58 @@
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Runtime.InteropServices;
using System.Text;
namespace Magpie.CursorHook {
// Win32 API
public static class NativeMethods {
[DllImport("user32.dll", CharSet = CharSet.Unicode)]
public static extern IntPtr SetCursor(IntPtr hCursor);
[DllImport("user32.dll", CharSet = CharSet.Unicode)]
public static extern IntPtr GetCursor();
[DllImport("user32.dll", CharSet = CharSet.Unicode)]
public static extern IntPtr LoadCursor(IntPtr hInstance, IntPtr lpCursorName);
public readonly static IntPtr IDC_ARROW = new IntPtr(32512);
public readonly static IntPtr IDC_HAND = new IntPtr(32649);
[DllImport("user32.dll", CharSet = CharSet.Unicode)]
public static extern IntPtr SetClassLong(IntPtr hWnd, int nIndex, IntPtr dwNewLong);
public readonly static int GCLP_HCURSOR = -12;
[DllImport("user32.dll", CharSet = CharSet.Unicode)]
public static extern int ShowCursor(bool bShow);
public delegate bool EnumWindowsProc(IntPtr hwnd, int lParam);
[DllImport("user32.dll", CharSet = CharSet.Unicode)]
public static extern bool EnumChildWindows(IntPtr hWndParent, EnumWindowsProc lpEnumFunc, int lParam);
[DllImport("user32.dll", CharSet = CharSet.Unicode)]
public static extern bool PostMessage(IntPtr hWnd, uint Msg, IntPtr wParam, IntPtr lParam);
public readonly static uint WM_COPYDATA = 0x004A + 1;
[StructLayout(LayoutKind.Sequential)]
public struct POINT {
public int x;
public int y;
}
[StructLayout(LayoutKind.Sequential)]
public struct CURSORINFO {
public int cbSize;
public int flags;
public IntPtr hCursor;
public POINT ptScreenPos;
}
[DllImport("user32.dll", CharSet = CharSet.Unicode)]
public static extern bool GetCursorInfo(ref CURSORINFO pci);
}
}

View file

@ -0,0 +1,36 @@
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
// 有关程序集的一般信息由以下
// 控制。更改这些特性值可修改
// 与程序集关联的信息。
[assembly: AssemblyTitle("CursorHook")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")]
[assembly: AssemblyProduct("CursorHook")]
[assembly: AssemblyCopyright("Copyright © 2021")]
[assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")]
// 将 ComVisible 设置为 false 会使此程序集中的类型
//对 COM 组件不可见。如果需要从 COM 访问此程序集中的类型
//请将此类型的 ComVisible 特性设置为 true。
[assembly: ComVisible(false)]
// 如果此项目向 COM 公开,则下列 GUID 用于类型库的 ID
[assembly: Guid("983647dc-e3c2-4658-852b-ff4d146f8134")]
// 程序集的版本信息由下列四个值组成:
//
// 主版本
// 次版本
// 生成号
// 修订号
//
//可以指定所有这些值,也可以使用“生成号”和“修订号”的默认值
//通过使用 "*",如下所示:
// [assembly: AssemblyVersion("1.0.*")]
[assembly: AssemblyVersion("1.0.0.0")]
[assembly: AssemblyFileVersion("1.0.0.0")]

View file

@ -0,0 +1,55 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Magpie.CursorHook {
/// <summary>
/// Provides an interface for communicating from the client (target) to the server (injector)
/// </summary>
public class ServerInterface : MarshalByRefObject {
public void IsInstalled(int clientPID) {
Console.WriteLine("Hook 成功");
}
/// <summary>
/// Output messages to the console.
/// </summary>
/// <param name="clientPID"></param>
/// <param name="fileNames"></param>
public void ReportMessages(string[] messages) {
for (int i = 0; i < messages.Length; i++) {
Console.WriteLine(messages[i]);
}
}
public void ReportMessage(string message) {
Console.WriteLine(message);
unsafe {
var ci = new NativeMethods.CURSORINFO {
cbSize = sizeof(NativeMethods.CURSORINFO)
};
NativeMethods.GetCursorInfo(ref ci);
Console.WriteLine("另一进程:" + ci.hCursor.ToString());
}
}
/// <summary>
/// Report exception
/// </summary>
/// <param name="e"></param>
public void ReportException(Exception e) {
Console.WriteLine("IPC出错" + e.ToString());
}
/// <summary>
/// Called to confirm that the IPC channel is still open / host application has not closed
/// </summary>
public void Ping() {
}
}
}

View file

@ -0,0 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="EasyHook" version="2.7.7097" targetFramework="net472" />
</packages>

View file

@ -10,30 +10,50 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Magpie", "Magpie\Magpie.csp
{8FC22A64-6D09-478B-9980-608D27601EF2} = {8FC22A64-6D09-478B-9980-608D27601EF2}
EndProjectSection
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CursorHook", "CursorHook\CursorHook.csproj", "{983647DC-E3C2-4658-852B-FF4D146F8134}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Debug|x64 = Debug|x64
Debug|x86 = Debug|x86
Release|Any CPU = Release|Any CPU
Release|x64 = Release|x64
Release|x86 = Release|x86
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{8FC22A64-6D09-478B-9980-608D27601EF2}.Debug|Any CPU.ActiveCfg = Debug|Win32
{8FC22A64-6D09-478B-9980-608D27601EF2}.Debug|x64.ActiveCfg = Debug|x64
{8FC22A64-6D09-478B-9980-608D27601EF2}.Debug|x64.Build.0 = Debug|x64
{8FC22A64-6D09-478B-9980-608D27601EF2}.Debug|x86.ActiveCfg = Debug|Win32
{8FC22A64-6D09-478B-9980-608D27601EF2}.Debug|x86.Build.0 = Debug|Win32
{8FC22A64-6D09-478B-9980-608D27601EF2}.Release|Any CPU.ActiveCfg = Release|Win32
{8FC22A64-6D09-478B-9980-608D27601EF2}.Release|x64.ActiveCfg = Release|x64
{8FC22A64-6D09-478B-9980-608D27601EF2}.Release|x64.Build.0 = Release|x64
{8FC22A64-6D09-478B-9980-608D27601EF2}.Release|x86.ActiveCfg = Release|Win32
{8FC22A64-6D09-478B-9980-608D27601EF2}.Release|x86.Build.0 = Release|Win32
{C75EC8D6-FF40-4307-9B46-EA760DC5E4C9}.Debug|Any CPU.ActiveCfg = Debug|x86
{C75EC8D6-FF40-4307-9B46-EA760DC5E4C9}.Debug|x64.ActiveCfg = Debug|x64
{C75EC8D6-FF40-4307-9B46-EA760DC5E4C9}.Debug|x64.Build.0 = Debug|x64
{C75EC8D6-FF40-4307-9B46-EA760DC5E4C9}.Debug|x86.ActiveCfg = Debug|x86
{C75EC8D6-FF40-4307-9B46-EA760DC5E4C9}.Debug|x86.Build.0 = Debug|x86
{C75EC8D6-FF40-4307-9B46-EA760DC5E4C9}.Release|Any CPU.ActiveCfg = Release|x86
{C75EC8D6-FF40-4307-9B46-EA760DC5E4C9}.Release|x64.ActiveCfg = Release|x64
{C75EC8D6-FF40-4307-9B46-EA760DC5E4C9}.Release|x64.Build.0 = Release|x64
{C75EC8D6-FF40-4307-9B46-EA760DC5E4C9}.Release|x86.ActiveCfg = Release|x86
{C75EC8D6-FF40-4307-9B46-EA760DC5E4C9}.Release|x86.Build.0 = Release|x86
{983647DC-E3C2-4658-852B-FF4D146F8134}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{983647DC-E3C2-4658-852B-FF4D146F8134}.Debug|Any CPU.Build.0 = Debug|Any CPU
{983647DC-E3C2-4658-852B-FF4D146F8134}.Debug|x64.ActiveCfg = Debug|Any CPU
{983647DC-E3C2-4658-852B-FF4D146F8134}.Debug|x64.Build.0 = Debug|Any CPU
{983647DC-E3C2-4658-852B-FF4D146F8134}.Debug|x86.ActiveCfg = Debug|Any CPU
{983647DC-E3C2-4658-852B-FF4D146F8134}.Debug|x86.Build.0 = Debug|Any CPU
{983647DC-E3C2-4658-852B-FF4D146F8134}.Release|Any CPU.ActiveCfg = Release|Any CPU
{983647DC-E3C2-4658-852B-FF4D146F8134}.Release|Any CPU.Build.0 = Release|Any CPU
{983647DC-E3C2-4658-852B-FF4D146F8134}.Release|x64.ActiveCfg = Release|Any CPU
{983647DC-E3C2-4658-852B-FF4D146F8134}.Release|x64.Build.0 = Release|Any CPU
{983647DC-E3C2-4658-852B-FF4D146F8134}.Release|x86.ActiveCfg = Release|Any CPU
{983647DC-E3C2-4658-852B-FF4D146F8134}.Release|x86.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE

View file

@ -0,0 +1,41 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Diagnostics;
using System.Runtime.Remoting;
using EasyHook;
using System.IO;
using System.Reflection;
using Magpie.CursorHook;
using System.Runtime.Remoting.Channels.Ipc;
namespace Magpie {
public class CursorHookInjector {
private string channelName = null;
private readonly int targetPID;
private IpcServerChannel ipcServer;
public CursorHookInjector(int targetPID, IntPtr hwndSrc) {
Debug.Assert(targetPID > 0);
this.targetPID = targetPID;
// Create the IPC server using the FileMonitorIPC.ServiceInterface class as a singleton
ipcServer = RemoteHooking.IpcCreateServer<ServerInterface>(ref channelName, WellKnownObjectMode.Singleton);
// Get the full path to the assembly we want to inject into the target process
string injectionLibrary = Path.Combine(Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location), "CursorHook.dll");
EasyHook.RemoteHooking.Inject(
targetPID, // ID of process to inject into
injectionLibrary, // 32-bit library to inject (if target is 32-bit)
injectionLibrary, // 64-bit library to inject (if target is 64-bit)
channelName, // the parameters to pass into injected library
hwndSrc // ...
);
}
}
}

View file

@ -64,11 +64,15 @@
<ApplicationIcon>logo.ico</ApplicationIcon>
</PropertyGroup>
<ItemGroup>
<Reference Include="EasyHook, Version=2.7.7097.0, Culture=neutral, PublicKeyToken=4b580fca19d0b0c5, processorArchitecture=MSIL">
<HintPath>..\packages\EasyHook.2.7.7097\lib\net40\EasyHook.dll</HintPath>
</Reference>
<Reference Include="Gma.System.MouseKeyHook, Version=5.6.130.0, Culture=neutral, processorArchitecture=MSIL">
<HintPath>..\packages\MouseKeyHook.5.6.0\lib\net40\Gma.System.MouseKeyHook.dll</HintPath>
</Reference>
<Reference Include="System" />
<Reference Include="System.Core" />
<Reference Include="System.Runtime.Remoting" />
<Reference Include="System.Xml.Linq" />
<Reference Include="System.Data.DataSetExtensions" />
<Reference Include="Microsoft.CSharp" />
@ -80,6 +84,7 @@
<Reference Include="System.Xml" />
</ItemGroup>
<ItemGroup>
<Compile Include="CursorHookInjector.cs" />
<Compile Include="MainForm.cs">
<SubType>Form</SubType>
</Compile>
@ -119,7 +124,31 @@
<None Include="App.config" />
</ItemGroup>
<ItemGroup>
<Content Include="EasyHook32.dll">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
<Content Include="EasyHook32Svc.exe">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
<Content Include="EasyHook64.dll">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
<Content Include="EasyHook64Svc.exe">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
<Content Include="EasyLoad32.dll">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
<Content Include="EasyLoad64.dll">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
<Content Include="logo.ico" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\CursorHook\CursorHook.csproj">
<Project>{983647dc-e3c2-4658-852b-ff4d146f8134}</Project>
<Name>CursorHook</Name>
</ProjectReference>
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
</Project>

View file

@ -104,7 +104,7 @@ namespace Magpie {
this.tkbFrameRate.TabIndex = 3;
this.tkbFrameRate.TickFrequency = 10;
this.tkbFrameRate.Value = 60;
this.tkbFrameRate.ValueChanged += new System.EventHandler(this.TkbFrameRate_ValueChanged);
this.tkbFrameRate.Scroll += new System.EventHandler(this.TkbFrameRate_Scroll);
//
// lblFrameRate
//

View file

@ -17,13 +17,12 @@ namespace Magpie {
{
""effect"": ""scale"",
""type"": ""mitchell"",
""scale"": [0,0],
""useSharperVersion"": true
""scale"": [0,0]
},
{
""effect"": ""sharpen"",
""type"": ""adaptive"",
""curveHeight"": 0.2
""curveHeight"": 0.3
}
]";
private static readonly string CommonEffectJson = @"[
@ -44,6 +43,8 @@ namespace Magpie {
IKeyboardMouseEvents keyboardEvents = null;
CursorHookInjector cursorHookInjector = null;
public MainForm() {
InitializeComponent();
@ -78,6 +79,11 @@ namespace Magpie {
Settings.Default.Save();
}
private void TkbFrameRate_Scroll(object sender, EventArgs e) {
lblFrameRate.Text = tkbFrameRate.Value.ToString();
Settings.Default.FrameRate = (uint)tkbFrameRate.Value;
}
private void TxtHotkey_TextChanged(object sender, EventArgs e) {
keyboardEvents?.Dispose();
keyboardEvents = Hook.GlobalEvents();
@ -94,7 +100,12 @@ namespace Magpie {
if(!NativeMethods.HasMagWindow()) {
if(!NativeMethods.CreateMagWindow(frameRate, effectJson, false)) {
MessageBox.Show("创建全屏窗口失败:" + NativeMethods.GetLastErrorMsg());
return;
}
int pid = NativeMethods.GetSrcPID();
IntPtr hwndHost = NativeMethods.GetHostWnd();
cursorHookInjector = new CursorHookInjector(pid, hwndHost);
} else {
NativeMethods.DestroyMagWindow();
}
@ -145,10 +156,5 @@ namespace Magpie {
tsmiMainForm.PerformClick();
}
}
private void TkbFrameRate_ValueChanged(object sender, EventArgs e) {
lblFrameRate.Text = tkbFrameRate.Value.ToString();
Settings.Default.FrameRate = (uint)tkbFrameRate.Value;
}
}
}

View file

@ -22,6 +22,9 @@ namespace Magpie {
[DllImport("user32", CharSet = CharSet.Unicode)]
public static extern int RegisterWindowMessage(string message);
[DllImport("user32.dll", CharSet = CharSet.Unicode)]
public static extern IntPtr SetCursor(IntPtr hCursor);
[DllImport("Runtime", CallingConvention = CallingConvention.StdCall)]
public static extern bool CreateMagWindow(
@ -44,5 +47,14 @@ namespace Magpie {
public static string GetLastErrorMsg() {
return Marshal.PtrToStringUni(GetLastErrorMsgNative());
}
[DllImport("Runtime", CallingConvention = CallingConvention.StdCall)]
public static extern int GetSrcPID();
[DllImport("Runtime", CallingConvention = CallingConvention.StdCall)]
public static extern IntPtr GetSrcWnd();
[DllImport("Runtime", CallingConvention = CallingConvention.StdCall)]
public static extern IntPtr GetHostWnd();
}
}

View file

@ -1,4 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="EasyHook" version="2.7.7097" targetFramework="net472" />
<package id="MouseKeyHook" version="5.6.0" targetFramework="net472" />
</packages>

View file

@ -19,7 +19,7 @@ public:
if (FAILED(hr)) {
return hr;
}
*ppOutput = new Anime4KUpscaleDenoiseConvReduceTransform();
return S_OK;

View file

@ -17,21 +17,20 @@ public:
_cursorSize.cx = GetSystemMetrics(SM_CXCURSOR);
_cursorSize.cy = GetSystemMetrics(SM_CYCURSOR);
_d2dBmpNormalCursor = _CursorToD2DBitmap(LoadCursor(NULL, IDC_ARROW));
_d2dBmpHandCursor = _CursorToD2DBitmap(LoadCursor(NULL, IDC_HAND));
if (!noDisturb) {
// 保存替换之前的 arrow 光标图像
ComPtr<ID2D1Bitmap> arrowImg = _CursorToD2DBitmap(LoadCursor(NULL, IDC_ARROW));
HCURSOR tptCursor = _CreateTransparentCursor();
Debug::ThrowIfWin32Failed(
SetSystemCursor(CopyCursor(tptCursor), OCR_HAND),
L"ÉèÖà OCR_HAND ʧ°Ü"
);
Debug::ThrowIfWin32Failed(
SetSystemCursor(tptCursor, OCR_NORMAL),
SetSystemCursor(_CreateTransparentCursor(), OCR_NORMAL),
L"设置 OCR_NORMAL 失败"
);
// tptCursor ±»Ïú»Ù
_hCursorArrow = LoadCursor(NULL, IDC_ARROW);
_cursorMap.emplace(_hCursorArrow, arrowImg);
// 限制鼠标在窗口内
RECT r{ lroundf(srcRect.left), lroundf(srcRect.top), lroundf(srcRect.right), lroundf(srcRect.bottom) };
@ -69,7 +68,10 @@ public:
void DrawCursor() {
CURSORINFO ci{};
ci.cbSize = sizeof(ci);
Debug::ThrowIfWin32Failed(GetCursorInfo(&ci), L"GetCursorInfo ʧ°Ü");
Debug::ThrowIfWin32Failed(
GetCursorInfo(&ci),
L"GetCursorInfo 失败"
);
if (ci.hCursor == NULL) {
return;
@ -98,18 +100,33 @@ public:
targetScreenPos.y + _cursorSize.cy - ii.yHotspot
};
if (ci.hCursor == LoadCursor(NULL, IDC_ARROW)) {
_d2dDC->DrawBitmap(_d2dBmpNormalCursor.Get(), &cursorRect);
} else if (ci.hCursor == LoadCursor(NULL, IDC_HAND)) {
_d2dDC->DrawBitmap(_d2dBmpHandCursor.Get(), &cursorRect);
auto it = _cursorMap.find(ci.hCursor);
if (it != _cursorMap.end()) {
_d2dDC->DrawBitmap(it->second.Get(), &cursorRect);
} else {
Debug::WriteLine(L"未找到");
try {
ComPtr<ID2D1Bitmap> c = _CursorToD2DBitmap(ci.hCursor);
_d2dDC->DrawBitmap(c.Get(), &cursorRect);
} catch (magpie_exception) {
ComPtr<ID2D1Bitmap> cursorBmp = _CursorToD2DBitmap(ci.hCursor);
_d2dDC->DrawBitmap(cursorBmp.Get(), &cursorRect);
// 添加进表
_cursorMap[ci.hCursor] = cursorBmp;
} catch (...) {
// 如果出错,只绘制普通光标
Debug::WriteLine(L"_CursorToD2DBitmap 出错");
_d2dDC->DrawBitmap(_d2dBmpNormalCursor.Get(), &cursorRect);
it = _cursorMap.find(_hCursorArrow);
if (it == _cursorMap.end()) {
return;
}
cursorRect = {
targetScreenPos.x,
targetScreenPos.y,
targetScreenPos.x + _cursorSize.cx,
targetScreenPos.y + _cursorSize.cy
};
_d2dDC->DrawBitmap(it->second.Get(), &cursorRect);
}
}
}
@ -167,8 +184,8 @@ private:
IWICImagingFactory2* _wicImgFactory;
ID2D1DeviceContext* _d2dDC;
ComPtr<ID2D1Bitmap> _d2dBmpNormalCursor = nullptr;
ComPtr<ID2D1Bitmap> _d2dBmpHandCursor = nullptr;
HCURSOR _hCursorArrow{};
std::map<HCURSOR, ComPtr<ID2D1Bitmap>> _cursorMap;
SIZE _cursorSize{};

View file

@ -65,3 +65,28 @@ API_DECLSPEC void WINAPI DestroyMagWindow() {
API_DECLSPEC const WCHAR* WINAPI GetLastErrorMsg() {
return Debug::GetLastErrorMessage().c_str();
}
API_DECLSPEC HWND WINAPI GetSrcWnd() {
if (MagWindow::$instance == nullptr) {
return NULL;
}
return MagWindow::$instance->GetSrcWnd();
}
API_DECLSPEC UINT32 WINAPI GetSrcPID() {
HWND hwndSrc = GetSrcWnd();
DWORD pid = 0;
GetWindowThreadProcessId(hwndSrc, &pid);
return pid;
}
API_DECLSPEC HWND WINAPI GetHostWnd() {
if (MagWindow::$instance == nullptr) {
return NULL;
}
return MagWindow::$instance->GetHostWnd();
}

View file

@ -38,6 +38,14 @@ public:
CoUninitialize();
}
HWND GetSrcWnd() {
return _hwndSrc;
}
HWND GetHostWnd() {
return _hwndHost;
}
private:
MagWindow(
HINSTANCE hInstance,

View file

@ -7,6 +7,7 @@
// Mitchell-Netravali 缩放算法,一种双三次插值,可以获得平滑的边缘
// 可选是否使用更锐利的版本,默认为否
// (经测试两种版本几乎没有区别)
class MitchellNetravaliScaleEffect : public EffectBase {
public:
IFACEMETHODIMP Initialize(
@ -50,7 +51,7 @@ public:
enum PROPS {
PROP_SCALE = 0,
PROP_USE_SHARPER_VERSION = 1
PROP_USE_SHARPER_VERSION = 0
};
static HRESULT Register(_In_ ID2D1Factory1* pFactory) {

View file

@ -241,7 +241,9 @@
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">false</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">false</ExcludedFromBuild>
</None>
<None Include="common.hlsli" />
<None Include="common.hlsli">
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">false</ExcludedFromBuild>
</None>
<None Include="cpp.hint" />
<None Include="packages.config" />
</ItemGroup>
@ -262,9 +264,6 @@
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">false</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">false</ExcludedFromBuild>
</FxCompile>
<FxCompile Include="Anime4KDeblurKernelShader.hlsl">
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">false</ExcludedFromBuild>
</FxCompile>
<FxCompile Include="Anime4KUpscaleConv4x3x3x1Shader.hlsl">
<ShaderType Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">Pixel</ShaderType>
<ShaderType Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">Pixel</ShaderType>
@ -411,6 +410,12 @@
<ShaderType Condition="'$(Configuration)|$(Platform)'=='Release|x64'">Pixel</ShaderType>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">false</ExcludedFromBuild>
</FxCompile>
<FxCompile Include="Anime4KDeblurKernelShader.hlsl">
<ShaderType Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">Pixel</ShaderType>
<ShaderType Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">Pixel</ShaderType>
<ShaderType Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">Pixel</ShaderType>
<ShaderType Condition="'$(Configuration)|$(Platform)'=='Release|x64'">Pixel</ShaderType>
</FxCompile>
</ItemGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<ImportGroup Label="ExtensionTargets">

View file

@ -1,9 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup>
<FxCompile Include="Anime4KDeblurKernelShader.hlsl">
<Filter>着色器\缩放\Anime4K</Filter>
</FxCompile>
<FxCompile Include="Anime4KUpscaleConv4x3x3x1Shader.hlsl">
<Filter>着色器\缩放\Anime4K</Filter>
</FxCompile>
@ -67,6 +64,9 @@
<FxCompile Include="Lanczos6ScaleShader.hlsl">
<Filter>着色器\缩放</Filter>
</FxCompile>
<FxCompile Include="Anime4KDeblurKernelShader.hlsl">
<Filter>着色器\缩放\Anime4K</Filter>
</FxCompile>
</ItemGroup>
<ItemGroup>
<ClCompile Include="GUIDs.cpp">