diff --git a/RevokeMsgPatcher.MultiInstance/FormMultiInstance.Designer.cs b/RevokeMsgPatcher.MultiInstance/FormMultiInstance.Designer.cs index 11b3e4a..1bd18e7 100644 --- a/RevokeMsgPatcher.MultiInstance/FormMultiInstance.Designer.cs +++ b/RevokeMsgPatcher.MultiInstance/FormMultiInstance.Designer.cs @@ -28,6 +28,7 @@ /// private void InitializeComponent() { + this.components = new System.ComponentModel.Container(); this.txtPath = new System.Windows.Forms.TextBox(); this.lblPathTag = new System.Windows.Forms.Label(); this.btnChoosePath = new System.Windows.Forms.Button(); @@ -38,6 +39,8 @@ this.linkLabel1 = new System.Windows.Forms.LinkLabel(); this.label3 = new System.Windows.Forms.Label(); this.label4 = new System.Windows.Forms.Label(); + this.button1 = new System.Windows.Forms.Button(); + this.mutexHandleCloseTimer = new System.Windows.Forms.Timer(this.components); ((System.ComponentModel.ISupportInitialize)(this.startNum)).BeginInit(); this.SuspendLayout(); // @@ -94,7 +97,7 @@ 0, 0}); this.startNum.Minimum = new decimal(new int[] { - 2, + 1, 0, 0, 0}); @@ -102,7 +105,7 @@ this.startNum.Size = new System.Drawing.Size(55, 21); this.startNum.TabIndex = 12; this.startNum.Value = new decimal(new int[] { - 2, + 1, 0, 0, 0}); @@ -146,11 +149,26 @@ this.label4.TabIndex = 16; this.label4.Text = "注意:\r\n1. 使用本程序多开前不能存在正在运行的微信进程。\r\n2. 多开的成功率取决于你的机器性能,不保证每次都能成功"; // + // button1 + // + this.button1.Location = new System.Drawing.Point(369, 190); + this.button1.Name = "button1"; + this.button1.Size = new System.Drawing.Size(75, 23); + this.button1.TabIndex = 17; + this.button1.Text = "button1"; + this.button1.UseVisualStyleBackColor = true; + this.button1.Click += new System.EventHandler(this.button1_Click); + // + // mutexHandleCloseTimer + // + this.mutexHandleCloseTimer.Tick += new System.EventHandler(this.mutexHandleCloseTimer_Tick); + // // FormMultiInstance // this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 12F); this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; - this.ClientSize = new System.Drawing.Size(472, 133); + this.ClientSize = new System.Drawing.Size(472, 259); + this.Controls.Add(this.button1); this.Controls.Add(this.label4); this.Controls.Add(this.linkLabel1); this.Controls.Add(this.label3); @@ -181,6 +199,8 @@ private System.Windows.Forms.LinkLabel linkLabel1; private System.Windows.Forms.Label label3; private System.Windows.Forms.Label label4; + private System.Windows.Forms.Button button1; + private System.Windows.Forms.Timer mutexHandleCloseTimer; } } diff --git a/RevokeMsgPatcher.MultiInstance/FormMultiInstance.cs b/RevokeMsgPatcher.MultiInstance/FormMultiInstance.cs index 1f566ef..5508f15 100644 --- a/RevokeMsgPatcher.MultiInstance/FormMultiInstance.cs +++ b/RevokeMsgPatcher.MultiInstance/FormMultiInstance.cs @@ -47,25 +47,21 @@ namespace RevokeMsgPatcher.MultiInstance { if (File.Exists(txtPath.Text)) { - // 检测微信进程是否存在 - Process[] ps = Process.GetProcessesByName("WeChat"); - if (ps.Length > 0) - { - DialogResult result = MessageBox.Show("当前存在运行中的微信进程,请先关闭当前微信才能使用该功能。点击【确定】强制关闭当前所有微信进程并进行多开,点击【取消】不做任何处理。", "当前存在运行中的微信进程", MessageBoxButtons.OKCancel, MessageBoxIcon.Question); - if (result == DialogResult.OK) - { - foreach (Process p in ps) - p.Kill(); - } - else - { - return; - } - } + Process[] processes = Process.GetProcessesByName("WeChat"); + ProcessUtil.CloseMutexHandle(processes); // 启动多个实例 for (int i = 0; i < startNum.Value; i++) { - Process.Start(txtPath.Text); + //var t = new Task(() => + //{ + // Process newInstance = Process.Start(txtPath.Text); + // newInstance.WaitForInputIdle(); + // ProcessUtil.CloseMutexHandle(newInstance); + //}); + //t.Start(); + Process newInstance = Process.Start(txtPath.Text); + //newInstance.WaitForInputIdle(); + //ProcessUtil.CloseMutexHandle(newInstance); } } } @@ -107,5 +103,16 @@ namespace RevokeMsgPatcher.MultiInstance } return null; } + + private void button1_Click(object sender, EventArgs e) + { + Process[] processes = Process.GetProcessesByName("WeChat"); + ProcessUtil.CloseMutexHandle(processes); + } + + private void mutexHandleCloseTimer_Tick(object sender, EventArgs e) + { + + } } } diff --git a/RevokeMsgPatcher.MultiInstance/FormMultiInstance.resx b/RevokeMsgPatcher.MultiInstance/FormMultiInstance.resx index 1af7de1..06e74a4 100644 --- a/RevokeMsgPatcher.MultiInstance/FormMultiInstance.resx +++ b/RevokeMsgPatcher.MultiInstance/FormMultiInstance.resx @@ -117,4 +117,7 @@ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + 17, 17 + \ No newline at end of file diff --git a/RevokeMsgPatcher.MultiInstance/ProcessUtil.cs b/RevokeMsgPatcher.MultiInstance/ProcessUtil.cs new file mode 100644 index 0000000..7818c38 --- /dev/null +++ b/RevokeMsgPatcher.MultiInstance/ProcessUtil.cs @@ -0,0 +1,229 @@ +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.IO; +using System.Linq; +using System.Runtime.InteropServices; +using System.Text; +using System.Threading.Tasks; + +namespace RevokeMsgPatcher.MultiInstance +{ + /// + /// 微信多开原理: + /// https://mp.weixin.qq.com/s/bb7XMxop7e8rd7YqQ88nyA + /// 参考: + /// https://stackoverflow.com/questions/54872228/c-sharp-how-to-find-all-handles-associated-with-current-process + /// https://hintdesk.com/2010/05/22/c-get-all-handles-of-a-given-process-in-64-bits/ + /// + public class ProcessUtil + { + /// + /// https://www.geoffchappell.com/studies/windows/km/ntoskrnl/api/ex/sysinfo/handle_table_entry.htm?ts=0,242 + /// + [StructLayout(LayoutKind.Sequential, Pack = 1)] + public struct SYSTEM_HANDLE_INFORMATION + { // Information Class 16 + public ushort ProcessID; + public ushort CreatorBackTrackIndex; + public byte ObjectType; + public byte HandleAttribute; + public ushort Handle; + public IntPtr Object_Pointer; + public IntPtr AccessMask; + } + + private enum OBJECT_INFORMATION_CLASS : int + { + ObjectBasicInformation = 0, + ObjectNameInformation = 1, + ObjectTypeInformation = 2, + ObjectAllTypesInformation = 3, + ObjectHandleInformation = 4 + } + + [StructLayout(LayoutKind.Sequential, Pack = 1)] + private struct OBJECT_NAME_INFORMATION + { // Information Class 1 + public UNICODE_STRING Name; + } + + [StructLayout(LayoutKind.Sequential)] + private struct UNICODE_STRING + { + public ushort Length; + public ushort MaximumLength; + public IntPtr Buffer; + } + + [Flags] + private enum PROCESS_ACCESS_FLAGS : uint + { + All = 0x001F0FFF, + Terminate = 0x00000001, + CreateThread = 0x00000002, + VMOperation = 0x00000008, + VMRead = 0x00000010, + VMWrite = 0x00000020, + DupHandle = 0x00000040, + SetInformation = 0x00000200, + QueryInformation = 0x00000400, + Synchronize = 0x00100000 + } + + [DllImport("ntdll.dll")] + private static extern uint NtQuerySystemInformation(int SystemInformationClass, IntPtr SystemInformation, int SystemInformationLength, ref int returnLength); + + [DllImport("kernel32.dll")] + private static extern IntPtr OpenProcess(PROCESS_ACCESS_FLAGS dwDesiredAccess, [MarshalAs(UnmanagedType.Bool)] bool bInheritHandle, int dwProcessId); + + [DllImport("kernel32.dll", SetLastError = true)] + [return: MarshalAs(UnmanagedType.Bool)] + private static extern bool DuplicateHandle(IntPtr hSourceProcessHandle, IntPtr hSourceHandle, IntPtr hTargetProcessHandle, out IntPtr lpTargetHandle, uint dwDesiredAccess, [MarshalAs(UnmanagedType.Bool)] bool bInheritHandle, uint dwOptions); + + [DllImport("kernel32.dll")] + private static extern IntPtr GetCurrentProcess(); + + [DllImport("ntdll.dll")] + private static extern int NtQueryObject(IntPtr ObjectHandle, int ObjectInformationClass, IntPtr ObjectInformation, int ObjectInformationLength, ref int returnLength); + + [DllImport("kernel32.dll")] + private static extern bool CloseHandle(IntPtr hObject); + + [DllImport("kernel32.dll")] + private static extern bool GetHandleInformation(IntPtr hObject, out uint lpdwFlags); + + private const uint STATUS_INFO_LENGTH_MISMATCH = 0xC0000004; + private const int DUPLICATE_CLOSE_SOURCE = 0x1; + private const int DUPLICATE_SAME_ACCESS = 0x2; + + private const int CNST_SYSTEM_HANDLE_INFORMATION = 0x10; + private const int OBJECT_TYPE_MUTANT = 17; + + public static List GetHandles(Process process) + { + List aHandles = new List(); + int handle_info_size = Marshal.SizeOf(new SYSTEM_HANDLE_INFORMATION()) * 20000; + IntPtr ptrHandleData = IntPtr.Zero; + try + { + ptrHandleData = Marshal.AllocHGlobal(handle_info_size); + int nLength = 0; + + while (NtQuerySystemInformation(CNST_SYSTEM_HANDLE_INFORMATION, ptrHandleData, handle_info_size, ref nLength) == STATUS_INFO_LENGTH_MISMATCH) + { + handle_info_size = nLength; + Marshal.FreeHGlobal(ptrHandleData); + ptrHandleData = Marshal.AllocHGlobal(nLength); + } + + long handle_count = Marshal.ReadIntPtr(ptrHandleData).ToInt64(); + IntPtr ptrHandleItem = ptrHandleData + Marshal.SizeOf(ptrHandleData); + + for (long lIndex = 0; lIndex < handle_count; lIndex++) + { + SYSTEM_HANDLE_INFORMATION oSystemHandleInfo = new SYSTEM_HANDLE_INFORMATION(); + oSystemHandleInfo = (SYSTEM_HANDLE_INFORMATION)Marshal.PtrToStructure(ptrHandleItem, oSystemHandleInfo.GetType()); + ptrHandleItem += Marshal.SizeOf(new SYSTEM_HANDLE_INFORMATION()); + if (oSystemHandleInfo.ProcessID != process.Id) { continue; } + aHandles.Add(oSystemHandleInfo); + } + } + catch (Exception ex) + { + throw ex; + } + finally + { + Marshal.FreeHGlobal(ptrHandleData); + } + return aHandles; + } + + public static void FindAndCloseWeChatMutexHandle(SYSTEM_HANDLE_INFORMATION systemHandleInformation, Process process) + { + IntPtr ipHandle = IntPtr.Zero; + IntPtr openProcessHandle = IntPtr.Zero; + IntPtr hObjectName = IntPtr.Zero; + try + { + PROCESS_ACCESS_FLAGS flags = PROCESS_ACCESS_FLAGS.DupHandle | PROCESS_ACCESS_FLAGS.VMRead; + openProcessHandle = OpenProcess(flags, false, process.Id); + // 通过 DuplicateHandle 访问句柄 + if (!DuplicateHandle(openProcessHandle, new IntPtr(systemHandleInformation.Handle), GetCurrentProcess(), out ipHandle, 0, false, DUPLICATE_SAME_ACCESS)) + { + return; + } + + int nLength = 0; + hObjectName = Marshal.AllocHGlobal(256 * 1024); + + // 查询句柄名称 + while ((uint)(NtQueryObject(ipHandle, (int)OBJECT_INFORMATION_CLASS.ObjectNameInformation, hObjectName, nLength, ref nLength)) == STATUS_INFO_LENGTH_MISMATCH) + { + Marshal.FreeHGlobal(hObjectName); + if (nLength == 0) + { + Console.WriteLine("Length returned at zero!"); + return; + } + hObjectName = Marshal.AllocHGlobal(nLength); + } + OBJECT_NAME_INFORMATION objObjectName = new OBJECT_NAME_INFORMATION(); + objObjectName = (OBJECT_NAME_INFORMATION)Marshal.PtrToStructure(hObjectName, objObjectName.GetType()); + + if (objObjectName.Name.Buffer != IntPtr.Zero) + { + string strObjectName = Marshal.PtrToStringUni(objObjectName.Name.Buffer); + Console.WriteLine(strObjectName); + // \Sessions\1\BaseNamedObjects\_WeChat_App_Instance_Identity_Mutex_Name + if (strObjectName.Contains("_Instance_Identity_Mutex_Name")) + { + // 通过 DuplicateHandle DUPLICATE_CLOSE_SOURCE 关闭句柄 + IntPtr mHandle = IntPtr.Zero; + if (DuplicateHandle(openProcessHandle, new IntPtr(systemHandleInformation.Handle), GetCurrentProcess(), out mHandle, 0, false, DUPLICATE_CLOSE_SOURCE)) + { + CloseHandle(mHandle); + } + } + } + } + catch (Exception ex) + { + Console.WriteLine(ex.Message); + } + finally + { + Marshal.FreeHGlobal(hObjectName); + CloseHandle(ipHandle); + CloseHandle(openProcessHandle); + } + } + + /// + /// 关闭微信的互斥句柄 + /// + /// 微信的进程 + /// + public static void CloseMutexHandle(Process[] processes) + { + foreach (Process process in processes) + { + CloseMutexHandle(process); + } + } + + public static void CloseMutexHandle(Process process) + { + List aHandles = GetHandles(process); + foreach (SYSTEM_HANDLE_INFORMATION handle in aHandles) + { + // Mutant 类型的句柄 + if (handle.ObjectType == OBJECT_TYPE_MUTANT) + { + FindAndCloseWeChatMutexHandle(handle, process); + } + } + } + } +} \ No newline at end of file diff --git a/RevokeMsgPatcher.MultiInstance/RevokeMsgPatcher.MultiInstance.csproj b/RevokeMsgPatcher.MultiInstance/RevokeMsgPatcher.MultiInstance.csproj index 2ed2fcd..0f89bf3 100644 --- a/RevokeMsgPatcher.MultiInstance/RevokeMsgPatcher.MultiInstance.csproj +++ b/RevokeMsgPatcher.MultiInstance/RevokeMsgPatcher.MultiInstance.csproj @@ -51,6 +51,7 @@ FormMultiInstance.cs +