当前位置:首页 >> 破解资源

去除友善串口调试助手注册提示弹窗


工作原因,经常使用串口调试助手。虽然网上完全免费的串口调试助手很多,但是用习惯了友善,不想换其他的工具。
这个软件30天试用期过后,会不定时弹出注册提示窗口,有些烦人。于是想着去除掉。
因自己不会分析exe进行脱壳破解一类的,只有用偏门的思路,快速解决问题,忘坛友们不要嘲笑去除友善串口调试助手注册提示弹窗

主界面.jpg


二、针对上述注册提示窗口(模态窗口,本窗口不关闭,主窗口无法使用),我首先想到的方法是:使用系统的FindwindowFindWindowEx函数,查找窗口,并调用SendMessage关闭。
按上述方法进行分析,
1.首先查看FindwindowFindWindowEx函数的参数
[C#] 纯文本查看 复制代码
 /// <summary>/// 查找窗口[/font][font=&quot;]/// </summary>/// <param name="lpClassName">窗口类名称</param>[/font][font=&quot;]/// <param name="lpWindowName">窗口标题名称</param>[/font][font=&quot;]/// <returns></returns>[DllImport("user32.dll", EntryPoint ="FindWindow")]private extern static IntPtr FindWindow(string lpClassName,string lpWindowName);/// <summary>/// 查找窗口[/font][font=&quot;]/// </summary>/// <param name="hwndParent">父窗体句柄</param>[/font][font=&quot;]/// <param name="hwndChildAfter">子窗体句柄</param>[/font][font=&quot;]/// <param name="strClass">窗口类名</param>[/font][font=&quot;]/// <param name="strWindow">窗口标题</param>[/font][font=&quot;]/// <returns></returns>[DllImport("user32.dll")]private extern static IntPtr FindWindowEx(IntPtr hwndParent,IntPtr hwndChildAfter,string strClass, string strWindow);

可以看出,调用这两个函数需要知道窗口的类名或者窗口标题名称。另外从助手界面可以看出两个窗口的名称都是一样的,看看两个窗口的类名是否一样,不一样的话,就可以直接查到这个窗口。使用VS自带的spy++查找窗口的句柄和类名,上图a.主窗口句柄
去除友善串口调试助手注册提示弹窗

主窗体句柄.jpg


b.注册提示窗口句柄
去除友善串口调试助手注册提示弹窗

注册提示窗体句柄1.jpg


从上面两个窗口可以得到的数据:
主窗口
句柄:000A0FAE(10进制:659374)
标题:友善串口调试助手
类:Qt5QWindowIcon
注册提示窗口
句柄:001E0FA2(10进制:1970082)
标题:友善串口调试助手
类:Qt5QWindowIcon
发现两个窗口的类名称也都一样,没办法直接按名称或类名关闭窗体了。
观察这两个窗口句柄,发现注册提示窗口的句柄值要大于主窗口的句柄值,仔细想想,除了第一次打开程序先弹出的注册提示窗口外,其它提示窗口的都是在主窗口出现之后出现的,后续提示窗口的句柄肯定大于主窗口的句柄,所以可以通过判断句柄值大小来确定哪个是我们的目标窗口。
2.实际上在提示窗口出现的情况下,多次调用FindWindow函数,每次获得的窗口句柄始终是弹出窗口的句柄,得不到主窗口的句柄;将FindWindow得到的弹出窗口句柄传入FindWindowEx的主窗体句柄参数位置可实现查找到主窗体。比较两个窗体的句柄大小,然后关闭弹出窗口句柄。
[C#] 纯文本查看 复制代码
 private void StartDetect2()       {           string winname = "友善串口调试助手";           string winclsname = "Qt5QWindowIcon";           _ThreadDetect = new Thread(() =>           {                while (true)                {                    Application.DoEvents();                    Thread.Sleep(1000);                    IntPtr hwnd1 =FindWindow(winclsname, winname); //通过窗体类名称和窗体名称查找句柄,精确过滤。如果只写窗口名称,也会查到文件夹名称为友善串口调试助手的窗口。影响后面的判断。                    if(hwnd1!=IntPtr.Zero)                    {                        IntPtr hwnd2 =FindWindowEx(hwnd1, IntPtr.Zero, winclsname, winname);                        if(hwnd2!=IntPtr.Zero)                        {                            if (hwnd1.ToInt64()> hwnd2.ToInt64())                            {                               SendMessage(hwnd1, 16, 0, 0);//关闭窗口,通过发送消息的方式                             }                            else                            {                               SendMessage(hwnd2, 16, 0, 0);//关闭窗口,通过发送消息的方式                             }                        }                    }                }           });           _ThreadDetect.Start();       }

3.上述方法试验后发现:只能在打开主窗口后,弹出注册提示窗之前,运行该方法才会有效,并且只能关闭一次弹出窗口。
分析原因,因为,弹出窗口出现后,FindWindow获取到的就是弹窗的句柄,该句柄值大于主窗口句柄,导致FindWindowEx查不到主窗口句柄。
三、通过上面的分析发现上面的思路达不到目的,于是考虑使用:查询桌面所有窗口句柄,然后将窗口名称和类名称都符合要求的窗口添加进一个List,然后再判断句柄大小,关闭大句柄的窗口。
直接上核心代码:
[C#] 纯文本查看 复制代码
usingSystem.Runtime.InteropServices;usingSystem.Threading;usingSystem.Diagnostics;usingSystem.IO;///<summary>        /// 发送系统消息(可用来发送关闭窗口命令)        /// </summary>        /// <paramname="hWnd"></param>        /// <paramname="msg"></param>        /// <paramname="wParam"></param>        /// <paramname="lParam"></param>        /// <returns></returns>        [DllImport("User32.dll",EntryPoint = "SendMessage")]        private static extern intSendMessage(IntPtr hWnd, int msg, uint wParam, uint lParam);        /// <summary>        /// //用来遍历所有窗口         /// </summary>        /// <paramname="lpEnumFunc"></param>        /// <paramname="lParam"></param>        /// <returns></returns>        [DllImport("user32.dll")]        private static extern boolEnumWindows(WNDENUMPROC lpEnumFunc, int lParam);        /// <summary>        /// 获取窗口Text        /// </summary>        /// <paramname="hWnd"></param>        /// <paramname="lpString"></param>        /// <paramname="nMaxCount"></param>        /// <returns></returns>        [DllImport("user32.dll")]         private static extern intGetWindowTextW(IntPtr hWnd, [MarshalAs(UnmanagedType.LPWStr)]StringBuilderlpString, int nMaxCount);        /// <summary>        /// 获取窗口类名         /// </summary>        /// <paramname="hWnd"></param>        /// <paramname="lpString"></param>        /// <paramname="nMaxCount"></param>        /// <returns></returns>        [DllImport("user32.dll")]         private static extern intGetClassNameW(IntPtr hWnd, [MarshalAs(UnmanagedType.LPWStr)]StringBuilderlpString, int nMaxCount);         /// <summary>        /// 声明一个委托函数用于 Win32 API - EnumWindows 的回调函数:        /// </summary>        /// <paramname="hWnd"></param>        /// <paramname="lParam"></param>        /// <returns></returns>        private delegate bool WNDENUMPROC(IntPtr hWnd,int lParam);  //IntPtr hWnd用int也可以/// <summary>        /// 自定义一个结构体,用来保存句柄信息        /// </summary>        public struct WindowInfo        {            public IntPtr hWnd;            public string szWindowName;            public string szClassName;        }/// <summary>        /// 获取所有标题相同的窗体句柄及窗体类名        /// </summary>        /// <paramname="windowText">窗体标题名称</param>        /// <paramname="clsText">窗体类名称</param>        /// <returns></returns>        public List<WindowInfo>GetAllWindows(string windowText,string clsText)        {             //用来保存窗口对象 列表            List<WindowInfo> wndList =new List<WindowInfo>();            //enum all desktop windows             EnumWindows(delegate(IntPtr hWnd, intlParam)             {                 WindowInfo wnd = newWindowInfo();                 StringBuilder sb = newStringBuilder(256);                //get window name                  GetWindowTextW(hWnd, sb,sb.Capacity);                 wnd.szWindowName =sb.ToString();                //get window class                 GetClassNameW(hWnd, sb,sb.Capacity);                wnd.szClassName =sb.ToString();                if((wnd.szWindowName==windowText)&& (wnd.szClassName==clsText))                {                    //get hwnd                     wnd.hWnd = hWnd;                    //add it into list                     wndList.Add(wnd);                 }                return true;             }, 0);            return wndList;         }Thread_ThreadDetect;  //创建一个线程/// <summary>        /// 开始检测        /// </summary>        private void StartDetect()        {            string winname = "友善串口调试助手";            string winclsname ="Qt5QWindowIcon";            _ThreadDetect = new Thread(() =>            {                while (true)                {                    Application.DoEvents();                    Thread.Sleep(1000);                    List<WindowInfo>winInfo = GetAllWindows(winname, winclsname);                    if (winInfo.Count >= 2)                    {                        long h1 =winInfo[0].hWnd.ToInt64();                        long h2 =winInfo[1].hWnd.ToInt64();                        if (h1 > h2)                        {                           Console.WriteLine("找到窗口,句柄为:"+ h1.ToString());                           SendMessage(winInfo[0].hWnd, 16, 0, 0);//关闭窗口,通过发送消息的方式                         }                        else                        {                           Console.WriteLine("找到窗口,句柄为:"+ h2.ToString());                           SendMessage(winInfo[1].hWnd, 16, 0, 0);//关闭窗口,通过发送消息的方式                         }                    }                }            });_ThreadDetect.IsBackground = true;            _ThreadDetect.Start();        }private voidfrmCloseOtherForm_Load(object sender, EventArgs e)        {            //将本程序exe放在友善串口助手根目录,打开本程序自动打开友善串口助手            if(File.Exists("SerialPortUtility.exe"))            {                Process.Start("SerialPortUtility.exe");            }            StartDetect();            this.ShowInTaskbar = false;        }

去除友善串口调试助手注册提示弹窗

主窗口.png

主窗口


图二:弹出窗口样式
去除友善串口调试助手注册提示弹窗

弹窗.png

弹窗


主代码修改如下:
[C#] 纯文本查看 复制代码
private const int GWL_STYLE = (-16) ;            //窗口样式/// <summary>        /// 获得所属句柄窗体的样式函数        /// </summary>        /// <param name="hWnd"></param>        /// <param name="nIndex"></param>        /// <returns></returns>        [DllImport("user32.dll", SetLastError = true)]        static extern int GetWindowLong(IntPtr hWnd, int nIndex);//获得所属句柄窗体的样式函数  /// <summary>/// 开始检测/// </summary>private void StartDetect(){    string winname = "友善串口调试助手";    //string winclsname = "Qt5QWindowIcon";    _ThreadDetect = new Thread(() =>    {        while (true)        {            Application.DoEvents();            Thread.Sleep(500);            List<WindowInfo> winInfo = GetAllWindows2(winname, "96CC0000");            if (winInfo.Count > 0) SendMessage(winInfo[0].hWnd, 16, 0, 0);//关闭窗口,通过发送消息的方式                    }    });    _ThreadDetect.IsBackground = true;    _ThreadDetect.Start();}public List<WindowInfo> GetAllWindows2(string windowText, string clsText)        {            //用来保存窗口对象 列表            List<WindowInfo> wndList = new List<WindowInfo>();            //enum all desktop windows             EnumWindows(delegate(IntPtr hWnd, int lParam)            {                WindowInfo wnd = new WindowInfo();                StringBuilder sb = new StringBuilder(256);                //get window name                  GetWindowTextW(hWnd, sb, sb.Capacity);                wnd.szWindowName = sb.ToString();                               Int32 windowStyle = GetWindowLong(hWnd, -16);                int value = int.Parse(clsText, NumberStyles.AllowHexSpecifier);                if ((wnd.szWindowName == windowText) && (windowStyle == value))                {                    //get hwnd                     wnd.hWnd = hWnd;                    wndList.Add(wnd);                }                return true;            }, 0);            return wndList;        }