Eding.ICU域名工具箱
因为需要实现OAuth授权,并且需要通过自定义URL协议再不同的WPF窗体间传输Windows消息,所以通过实际调试得到了以下完善的代码。
第一步、注册自定义URL协议
public static void RegisterCustomUrlProtocol()
// 使用 HKEY_CURRENT_USER\Software\Classes 分支创建注册表键
RegistryKey? currentUserKey = Registry.CurrentUser.OpenSubKey("Software", true);
if (currentUserKey != null)
RegistryKey classesKey = currentUserKey.CreateSubKey("Classes");
RegistryKey key = classesKey.CreateSubKey("myapp");
// 设置注册表键的值
key.SetValue("", "URL: Custom Protocol");
key.SetValue("URL Protocol", "");
// 创建 "shell\open\command" 子键并设置值
RegistryKey commandKey = key.CreateSubKey(@"shell\open\command");
string executablePath = Assembly.GetEntryAssembly()?.Location ?? string.Empty;
string exePath = executablePath.Replace(".dll", ".exe");
commandKey.SetValue("", "\"" + exePath + "\" \"%1\"");
第二步、向运行实例发送消息
public static class NativeMethods
// Windows消息常量
public const int WM_COPYDATA = 0x004A;
[DllImport("user32.dll", EntryPoint = "SendMessage")]
public static extern bool SendMessage(IntPtr hWnd, uint Msg, IntPtr wParam, IntPtr lParam);
// 结构体用于封装
[StructLayout(LayoutKind.Sequential)]
public struct COPYDATASTRUCT
public IntPtr dwData; // 传递的数据
public int cbData; // 数据大小
public IntPtr lpData; // 指向数据的指针
public static void SendArgsToRunningInstance(string[] args)
// 获取正在运行的实例的进程ID
var currentProcess = Process.GetCurrentProcess();
foreach (var process in Process.GetProcessesByName(currentProcess.ProcessName))
if (process.Id != currentProcess.Id)
// 向正在运行的实例发送参数
string arguments = string.Join("||", args);
byte[] data = Encoding.Unicode.GetBytes(arguments);
// 分配内存并将数据复制到内存中
IntPtr lpData = Marshal.AllocHGlobal(data.Length);
Marshal.Copy(data, 0, lpData, data.Length);
// 构造 COPYDATASTRUCT 结构体
COPYDATASTRUCT cds = new()
dwData = IntPtr.Zero,
cbData = data.Length,
lpData = lpData
//使用Marshal将struct转化为IntPtr
IntPtr cdsPtr = Marshal.AllocHGlobal(Marshal.SizeOf(cds));
Marshal.StructureToPtr(cds, cdsPtr, false);
SendMessage(process.MainWindowHandle, WM_COPYDATA, IntPtr.Zero, cdsPtr);
// 释放分配的内存
Marshal.FreeHGlobal(lpData);
Marshal.FreeHGlobal(cdsPtr);
catch (Exception ex)
MessageBox.Show("将参数发送到正在运行的实例时出错: " + ex.Message, "Error", MessageBoxButton.OK, MessageBoxImage.Error);
第三步、添加消息处理钩子
void WSInitialized(object sender, EventArgs e)
HwndSource? hs = PresentationSource.FromVisual(this) as HwndSource;
hs?.AddHook(new HwndSourceHook(WndProc));
private bool _isMessageHandled = false;
第四步、处理Windows消息
private IntPtr WndProc(IntPtr hwnd, int msg, IntPtr wParam, IntPtr lParam, ref bool handled)
switch (msg)
case WM_COPYDATA:
if (!_isMessageHandled && lParam != IntPtr.Zero)
object? structObj = Marshal.PtrToStructure(lParam, typeof(COPYDATASTRUCT));
if (structObj != null)
COPYDATASTRUCT cds = (COPYDATASTRUCT)structObj;
string strResult = Marshal.PtrToStringUni(cds.lpData, cds.cbData / 2);
// 调用异步方法处理消息
Task handleTask = OAuth.HandleCustomUrlProtocol(strResult, this);
// 在异步操作完成后根据结果设置_isMessageHandled变量的值
handleTask.ContinueWith(task =>
_isMessageHandled = task.Result;
break;
return IntPtr.Zero;
声明:本站所有文章,如无特殊说明或标注,均为本站原创发布。任何个人或组织,在未征得本站同意时,禁止复制、盗用、采集、发布本站内容到任何网站、书籍等各类媒体平台。如若本站内容侵犯了原著者的合法权益,可联系我们进行处理。