From db0b527dde5cb11677307de9dab0b1568457d1ea Mon Sep 17 00:00:00 2001 From: qwqcode <1149527164@qq.com> Date: Mon, 31 Dec 2018 02:13:05 +0800 Subject: [PATCH] New Title Bar UI, Update CefSharp to v69. --- LICENSE | 14 +- Nacollector/Browser/CrBrowser.cs | 40 +- .../Handler/ChromeWidgetMessageInterceptor.cs | 150 +++++++ .../Browser/Handler/DownloadHandler.cs | 4 +- .../Browser/Handler/DragDropHandler.cs | 67 +++ Nacollector/Browser/Handler/MenuHandler.cs | 4 +- Nacollector/ClassDiagram1.cd | 2 + Nacollector/JsActions/AppAction.cs | 99 +++++ Nacollector/JsActions/TaskControllerAction.cs | 59 +++ Nacollector/MainForm.Designer.cs | 13 +- Nacollector/MainForm.cs | 166 +------- Nacollector/Nacollector.csproj | 93 ++-- Nacollector/Program.cs | 1 + Nacollector/Properties/AssemblyInfo.cs | 6 +- Nacollector/Resources/StartingImg.png | Bin 16986 -> 17296 bytes Nacollector/Resources/html_res/app.html | 17 +- Nacollector/Resources/html_res/assets/app.css | 104 ++++- Nacollector/Resources/html_res/assets/app.js | 20 +- .../Resources/html_res/assets/icons/close.svg | 1 + .../html_res/assets/icons/maximize.svg | 1 + .../html_res/assets/icons/minimize.svg | 1 + .../html_res/assets/icons/unmaximize.svg | 1 + Nacollector/Ui/FormBase.cs | 321 ++++++++++++++ Nacollector/Util/Native.cs | 396 ++++++++++++++++++ Nacollector/packages.config | 1 - 25 files changed, 1359 insertions(+), 222 deletions(-) create mode 100644 Nacollector/Browser/Handler/ChromeWidgetMessageInterceptor.cs create mode 100644 Nacollector/Browser/Handler/DragDropHandler.cs create mode 100644 Nacollector/ClassDiagram1.cd create mode 100644 Nacollector/JsActions/AppAction.cs create mode 100644 Nacollector/JsActions/TaskControllerAction.cs create mode 100644 Nacollector/Resources/html_res/assets/icons/close.svg create mode 100644 Nacollector/Resources/html_res/assets/icons/maximize.svg create mode 100644 Nacollector/Resources/html_res/assets/icons/minimize.svg create mode 100644 Nacollector/Resources/html_res/assets/icons/unmaximize.svg create mode 100644 Nacollector/Ui/FormBase.cs create mode 100644 Nacollector/Util/Native.cs diff --git a/LICENSE b/LICENSE index 210663c..16e9177 100644 --- a/LICENSE +++ b/LICENSE @@ -1,6 +1,6 @@ 《Nacollector 用户使用许可协议》 -Copyright (C) 2018 Zneiat +Copyright (C) 2018 qwqcode 本协议保存于 GitHub 仓库和您本地 Nacollector 运行目录中。 @@ -16,9 +16,9 @@ Copyright (C) 2018 Zneiat 一、【许可使用】 - 1.1 通过任何途径下载 Nacollector 原版程序(原版程序是指在 https://github.com/Zneiat/Nacollector/releases 上发布的程序),在不违反本协议的前提下可自行使用。 + 1.1 通过任何途径下载 Nacollector 原版程序(原版程序是指在 https://github.com/qwqcode/Nacollector/releases 上发布的程序),在不违反本协议的前提下可自行使用。 - 1.2 其它参考 Nacollector 及其衍生品的源代码的程序,须征 Nacollector 原作者 Zneiat 同意后,在标示原有版权信息的情况下使用。 + 1.2 其它参考 Nacollector 及其衍生品的源代码的程序,须征 Nacollector 原作者 qwqcode 同意后,在标示原有版权信息的情况下使用。 二、【禁止使用】 @@ -30,7 +30,7 @@ Copyright (C) 2018 Zneiat 2.4 禁止未经作者允许,私自将 Nacollector 及其衍生品的源代码闭源。 - 2.5 Nacollector 的软件图标(app_ico.ico)版权归 Zneiat 所有,禁止未经作者允许,私自用于除 Nacollector 之外的任何地方。 + 2.5 Nacollector 的软件图标(app_ico.ico)版权归 qwqcode 所有,禁止未经作者允许,私自用于除 Nacollector 之外的任何地方。 三、【许可终止】 @@ -40,15 +40,15 @@ Copyright (C) 2018 Zneiat 四、【权利保留】 - 未明确声明的一切其它权利均为 Zneiat 所保留,对本协议的一切解释权归 Zneiat 所有。 + 未明确声明的一切其它权利均为 qwqcode 所保留,对本协议的一切解释权归 qwqcode 所有。 五、【责任限度】 - 在适用法律所允许的最大范围内,Nacollector 作者 Zneiat 绝不就因使用 Nacollector 造成的任何特殊、意外、非直接或间接的损害承担任何责任。 + 在适用法律所允许的最大范围内,Nacollector 作者 qwqcode 绝不就因使用 Nacollector 造成的任何特殊、意外、非直接或间接的损害承担任何责任。 六、【协议的生效与变更】 - 6.1 作者 Zneiat 有权在必要时修改本协议条款。您可以在相关页面(如:https://github.com/Zneiat/Nacollector)查阅最新版本的协议条款。 + 6.1 作者 qwqcode 有权在必要时修改本协议条款。您可以在相关页面(如:https://github.com/qwqcode/Nacollector)查阅最新版本的协议条款。 6.2 本协议条款变更后,如果您继续使用 Nacollector 及其衍生品,即视为您已接受修改后的协议。如果您不接受修改后的协议,那么视为您放弃一切许可(参见 3.1)。 diff --git a/Nacollector/Browser/CrBrowser.cs b/Nacollector/Browser/CrBrowser.cs index f504f46..9ef5910 100644 --- a/Nacollector/Browser/CrBrowser.cs +++ b/Nacollector/Browser/CrBrowser.cs @@ -1,6 +1,7 @@ using CefSharp; using CefSharp.WinForms; using Nacollector.Browser.Handler; +using Nacollector.Util; using System; using System.Collections.Generic; using System.Drawing; @@ -14,10 +15,13 @@ namespace Nacollector.Browser { public class CrBrowser { + private MainForm form; private ChromiumWebBrowser browser; - public CrBrowser(string address) + public CrBrowser(MainForm form, string address) { + this.form = form; + // 初始化浏览器 browser = new ChromiumWebBrowser(address); @@ -33,6 +37,7 @@ public CrBrowser(string address) browser.MenuHandler = new MenuHandler(); browser.LifeSpanHandler = new LifeSpanHandler(); browser.LoadHandler = new LoadHandler(); + browser.DragHandler = new DragDropHandler(); browser.FrameLoadEnd += new EventHandler(Browser_onFrameLoadEnd); browser.IsBrowserInitializedChanged += new EventHandler(Browser_onIsBrowserInitializedChanged); @@ -45,8 +50,39 @@ private void Browser_onFrameLoadEnd(object sender, FrameLoadEndEventArgs e) } // 浏览器初始化完毕时执行 - private void Browser_onIsBrowserInitializedChanged(object sender, IsBrowserInitializedChangedEventArgs e) + private void Browser_onIsBrowserInitializedChanged(object sender, IsBrowserInitializedChangedEventArgs args) { + if (args.IsBrowserInitialized) + { + // 设置鼠标按下操作 + ChromeWidgetMessageInterceptor.SetupLoop(browser, (message) => + { + Point point = new Point(message.LParam.ToInt32()); + if (((DragDropHandler)browser.DragHandler).draggableRegion.IsVisible(point)) + { + // 若现在鼠标指针在可拖动区域内 + if (message.Msg == (int)WindowMessages.WM_LBUTTONDBLCLK) // 鼠标左键双击 + { + form.Invoke((MethodInvoker)delegate + { + form.ToggleMaximize(); + }); + } + else if (message.Msg == (int)WindowMessages.WM_LBUTTONDOWN) // 鼠标左键按下 + { + NativeMethods.ReleaseCapture(); + form.SendHandleMessage(); // 执行 模拟标题栏拖动 + } + else if (message.Msg == (int)WindowMessages.WM_RBUTTONDOWN) // 鼠标右键按下 + { + form.Invoke((MethodInvoker)delegate + { + form.ShowSystemMenu(point); + }); + } + } + }); + } } /// diff --git a/Nacollector/Browser/Handler/ChromeWidgetMessageInterceptor.cs b/Nacollector/Browser/Handler/ChromeWidgetMessageInterceptor.cs new file mode 100644 index 0000000..d52d659 --- /dev/null +++ b/Nacollector/Browser/Handler/ChromeWidgetMessageInterceptor.cs @@ -0,0 +1,150 @@ +using CefSharp.WinForms; +using Nacollector.Util; +using System; +using System.Runtime.InteropServices; +using System.Text; +using System.Threading; +using System.Threading.Tasks; +using System.Windows.Forms; + +namespace Nacollector.Browser.Handler +{ + /// + /// Intercepts Windows messages sent to the ChromiumWebBrowser control's widget sub-window. + /// + /// It is necessary to listen to the widget sub-window because it receives all Windows messages + /// and forwards them to CEF, rather than the ChromiumWebBrowser.Handle. + /// + /// The supplied Action delegate is fired upon each message. + /// + class ChromeWidgetMessageInterceptor : NativeWindow + { + private readonly ChromiumWebBrowser browser; + private Action forwardAction; + + private ChromeWidgetMessageInterceptor(ChromiumWebBrowser browser, IntPtr chromeWidgetHostHandle, Action forwardAction) + { + AssignHandle(chromeWidgetHostHandle); + + this.browser = browser; + browser.HandleDestroyed += BrowserHandleDestroyed; + + this.forwardAction = forwardAction; + } + + /// + /// Asynchronously wait for the Chromium widget window to be created for the given ChromiumWebBrowser, + /// and when created hook into its Windows message loop. + /// + /// The browser to intercept Windows messages for. + /// This action will be called whenever a Windows message is received. + internal static void SetupLoop(ChromiumWebBrowser browser, Action forwardAction) + { + Task.Factory.StartNew(() => + { + try + { + bool foundWidget = false; + while (!foundWidget) + { + browser.Invoke((Action)(() => + { + IntPtr chromeWidgetHostHandle; + if (ChromeWidgetHandleFinder.TryFindHandle(browser, out chromeWidgetHostHandle)) + { + foundWidget = true; + new ChromeWidgetMessageInterceptor(browser, chromeWidgetHostHandle, forwardAction); + } + else + { + // Chrome hasn't yet set up its message-loop window. + Thread.Sleep(10); + } + })); + } + } + catch + { + // Errors are likely to occur if browser is disposed, and no good way to check from another thread + } + }); + } + + private void BrowserHandleDestroyed(object sender, EventArgs e) + { + ReleaseHandle(); + + browser.HandleDestroyed -= BrowserHandleDestroyed; + forwardAction = null; + } + + protected override void WndProc(ref Message m) + { + base.WndProc(ref m); + + if (forwardAction != null) + { + forwardAction(m); + } + } + } + + class ChromeWidgetHandleFinder + { + private delegate bool EnumWindowProc(IntPtr hwnd, IntPtr lParam); + + [DllImport("user32")] + [return: MarshalAs(UnmanagedType.Bool)] + private static extern bool EnumChildWindows(IntPtr window, EnumWindowProc callback, IntPtr lParam); + + private readonly IntPtr mainHandle; + private string seekClassName; + private IntPtr descendantFound; + + private ChromeWidgetHandleFinder(IntPtr handle) + { + this.mainHandle = handle; + } + + private IntPtr FindDescendantByClassName(string className) + { + descendantFound = IntPtr.Zero; + seekClassName = className; + + EnumWindowProc childProc = new EnumWindowProc(EnumWindow); + EnumChildWindows(this.mainHandle, childProc, IntPtr.Zero); + + return descendantFound; + } + + [DllImport("user32.dll", CharSet = CharSet.Auto)] + public static extern int GetClassName(IntPtr hWnd, StringBuilder lpClassName, int nMaxCount); + + private bool EnumWindow(IntPtr hWnd, IntPtr lParam) + { + StringBuilder buffer = new StringBuilder(128); + GetClassName(hWnd, buffer, buffer.Capacity); + + if (buffer.ToString() == seekClassName) + { + descendantFound = hWnd; + return false; + } + + return true; + } + + /// + /// Chrome's message-loop Window isn't created synchronously, so this may not find it. + /// If so, you need to wait and try again later. + /// + public static bool TryFindHandle(ChromiumWebBrowser browser, out IntPtr chromeWidgetHostHandle) + { + var browserHandle = browser.Handle; + var windowHandleInfo = new ChromeWidgetHandleFinder(browserHandle); + const string chromeWidgetHostClassName = "Chrome_RenderWidgetHostHWND"; + chromeWidgetHostHandle = windowHandleInfo.FindDescendantByClassName(chromeWidgetHostClassName); + return chromeWidgetHostHandle != IntPtr.Zero; + } + } +} diff --git a/Nacollector/Browser/Handler/DownloadHandler.cs b/Nacollector/Browser/Handler/DownloadHandler.cs index 6668581..b07df5f 100644 --- a/Nacollector/Browser/Handler/DownloadHandler.cs +++ b/Nacollector/Browser/Handler/DownloadHandler.cs @@ -15,7 +15,7 @@ public class DownloadHandler : IDownloadHandler public event EventHandler OnDownloadUpdatedFired; // 下载之前(单个下载任务 只会执行一次,从这里获取 downloadItem.SuggestedFileName) - public void OnBeforeDownload(IBrowser browser, DownloadItem downloadItem, IBeforeDownloadCallback callback) + public void OnBeforeDownload(IWebBrowser chromiumWebBrowser, IBrowser browser, DownloadItem downloadItem, IBeforeDownloadCallback callback) { var handler = OnBeforeDownloadFired; if (handler != null) @@ -39,7 +39,7 @@ public void OnBeforeDownload(IBrowser browser, DownloadItem downloadItem, IBefor } // 当下载任务信息更新 - public void OnDownloadUpdated(IBrowser browser, DownloadItem downloadItem, IDownloadItemCallback callback) + public void OnDownloadUpdated(IWebBrowser chromiumWebBrowser, IBrowser browser, DownloadItem downloadItem, IDownloadItemCallback callback) { var handler = OnDownloadUpdatedFired; if (handler != null) diff --git a/Nacollector/Browser/Handler/DragDropHandler.cs b/Nacollector/Browser/Handler/DragDropHandler.cs new file mode 100644 index 0000000..4b07d96 --- /dev/null +++ b/Nacollector/Browser/Handler/DragDropHandler.cs @@ -0,0 +1,67 @@ +using CefSharp; +using CefSharp.Enums; +using System; +using System.Collections.Generic; +using System.Drawing; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Nacollector.Browser.Handler +{ + /** + * 获取可拖拽区域 + */ + public class DragDropHandler : IDragHandler + { + public Region draggableRegion = new Region(); + public event Action RegionsChanged; + + public bool OnDragEnter(IWebBrowser browserControl, IBrowser browser, IDragData dragData, DragOperationsMask mask) + { + return false; + } + + public void OnDraggableRegionsChanged(IWebBrowser browserControl, IBrowser browser, IList regions) + { + if (browser.IsPopup == false) + { + draggableRegion = null; + if (regions.Count > 0) + { + foreach (var region in regions) + { + // Console.WriteLine(region.X + " - " + region.Y + " - " + region.Width + " - " + region.Height); + var rect = new Rectangle(region.X, region.Y, region.Width, region.Height); + + if (draggableRegion == null) + { + draggableRegion = new Region(rect); + } + else + { + if (region.Draggable) + { + draggableRegion.Union(rect); + } + else + { + //In the scenario where we have an outer region, that is draggable and it has + // an inner region that's not, we must exclude the non draggable. + // Not all scenarios are covered in this example. + draggableRegion.Exclude(rect); + } + } + } + } + + RegionsChanged?.Invoke(draggableRegion); + } + } + + public void Dispose() + { + RegionsChanged = null; + } + } +} diff --git a/Nacollector/Browser/Handler/MenuHandler.cs b/Nacollector/Browser/Handler/MenuHandler.cs index b3ba6ef..49cb2ae 100644 --- a/Nacollector/Browser/Handler/MenuHandler.cs +++ b/Nacollector/Browser/Handler/MenuHandler.cs @@ -81,7 +81,9 @@ void IContextMenuHandler.OnBeforeContextMenu(IWebBrowser browserControl, IBrowse model.AddItem(CefMenuCommand.ReloadNoCache, "刷新 (ReloadNoCache)"); model.AddItem((CefMenuCommand)FeedbackProject, "反馈问题"); model.AddItem((CefMenuCommand)LinkToZneiatProject, "开源项目"); - // model.AddItem((CefMenuCommand)ShowDevTools, "检查 (ShowDevTools)"); +# if DEBUG + model.AddItem((CefMenuCommand)ShowDevTools, "检查 (ShowDevTools)"); +# endif } } diff --git a/Nacollector/ClassDiagram1.cd b/Nacollector/ClassDiagram1.cd new file mode 100644 index 0000000..7b89419 --- /dev/null +++ b/Nacollector/ClassDiagram1.cd @@ -0,0 +1,2 @@ + + \ No newline at end of file diff --git a/Nacollector/JsActions/AppAction.cs b/Nacollector/JsActions/AppAction.cs new file mode 100644 index 0000000..5070094 --- /dev/null +++ b/Nacollector/JsActions/AppAction.cs @@ -0,0 +1,99 @@ +using CefSharp; +using Nacollector.Browser; +using Nacollector.Util; +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Drawing; +using System.IO; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using System.Windows.Forms; + +namespace Nacollector.JsActions +{ + /// + /// App 杂项操作 + /// + class AppAction + { + private MainForm _form; + private CrBrowser crBrowser; + + public AppAction(MainForm form, CrBrowser crBrowser) + { + this._form = form; + this.crBrowser = crBrowser; + } + + public void appClose() + { + _form.Invoke((MethodInvoker)delegate { + Application.Exit(); + }); + } + + public void appMaxMini() + { + _form.Invoke((MethodInvoker)delegate { + _form.ToggleMaximize(); + }); + } + + public void appMin() + { + _form.Invoke((MethodInvoker)delegate { + if (!_form.ShowInTaskbar) + { + _form.Hide(); + } + else + { + _form.WindowState = FormWindowState.Minimized; + } + }); + } + + // 获取程序版本 + public string getVersion() + { + crBrowser.RunJS($"AppConfig.updateCheckUrl=\"{GlobalConstant.UpdateCheckUrl}\";AppConfig.updateCheckToken=\"{GlobalConstant.UpdateCheckToken}\""); + return System.Reflection.Assembly.GetExecutingAssembly().GetName().Version.ToString(); + } + + public void showDevTools() + { + crBrowser.GetBrowser().ShowDevTools(); + } + + // 采集是否使用IE代理请求 + public void _utilsReqIeProxy(bool isEnable) + { + Utils.ReqIeProxy = isEnable; + } + + // 日志文件清理 + public void logFileClear() + { + Logging.Clear(); + } + + // 升级操作 + public void appUpdateAction(string srcUrl, string updateType) + { + if (!File.Exists(Path.Combine(Application.StartupPath, "Naupdater.exe"))) + { + MessageBox.Show("升级 Naupdater.exe 模块丢失,无法升级", "未找到 Naupdater.exe", MessageBoxButtons.OK, MessageBoxIcon.Error); + return; + } + + Process process = new Process(); + process.StartInfo.FileName = "Naupdater.exe"; + process.StartInfo.WorkingDirectory = Application.StartupPath; + process.StartInfo.Arguments = $"-{updateType} \"{srcUrl}\""; + // MessageBox.Show($"-{updateType} \"{srcUrl}\""); + process.Start(); + } + } +} diff --git a/Nacollector/JsActions/TaskControllerAction.cs b/Nacollector/JsActions/TaskControllerAction.cs new file mode 100644 index 0000000..9e53be5 --- /dev/null +++ b/Nacollector/JsActions/TaskControllerAction.cs @@ -0,0 +1,59 @@ +using Nacollector.Browser; +using Nacollector.Spiders; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading; +using System.Threading.Tasks; + +namespace Nacollector.JsActions +{ + /// + /// 任务控制器操作 + /// + class TaskControllerAction + { + private MainForm _form; + private CrBrowser crBrowser; + + public TaskControllerAction(MainForm form, CrBrowser crBrowser) + { + this._form = form; + this.crBrowser = crBrowser; + } + + private Dictionary taskThreads = new Dictionary(); + + // 创建新任务 + public void createTask(string taskId, string className, string classLabel, string parmsJsonStr) + { + // 配置 + var settings = new SpiderSettings() + { + TaskId = taskId, + ClassName = className, + ClassLabel = classLabel, + ParmsJsonStr = parmsJsonStr + }; + // 创建任务执行线程 + var thread = new Thread(new ParameterizedThreadStart(_form.StartTask)) + { + IsBackground = true + }; + thread.Start(settings); + // 加入 Threads Dictionary + taskThreads.Add(taskId, thread); + } + + // 终止任务 + public bool abortTask(string taskId) + { + if (!taskThreads.ContainsKey(taskId)) + return false; + + taskThreads[taskId].Abort(); + return true; + } + } +} diff --git a/Nacollector/MainForm.Designer.cs b/Nacollector/MainForm.Designer.cs index b3a0ab8..9d6f9f4 100644 --- a/Nacollector/MainForm.Designer.cs +++ b/Nacollector/MainForm.Designer.cs @@ -33,14 +33,12 @@ private void InitializeComponent() // // ContentPanel // - this.ContentPanel.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) - | System.Windows.Forms.AnchorStyles.Left) - | System.Windows.Forms.AnchorStyles.Right))); this.ContentPanel.BackColor = System.Drawing.Color.FromArgb(((int)(((byte)(51)))), ((int)(((byte)(51)))), ((int)(((byte)(51))))); - this.ContentPanel.Location = new System.Drawing.Point(0, 24); + this.ContentPanel.Dock = System.Windows.Forms.DockStyle.Fill; + this.ContentPanel.Location = new System.Drawing.Point(0, 0); this.ContentPanel.Margin = new System.Windows.Forms.Padding(0); this.ContentPanel.Name = "ContentPanel"; - this.ContentPanel.Size = new System.Drawing.Size(1150, 678); + this.ContentPanel.Size = new System.Drawing.Size(1150, 698); this.ContentPanel.TabIndex = 3; // // MainForm @@ -48,13 +46,16 @@ private void InitializeComponent() this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 12F); this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; this.ClientSize = new System.Drawing.Size(1150, 698); + this.ControlBox = false; + this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.Sizable; this.Controls.Add(this.ContentPanel); this.Icon = global::Nacollector.Properties.Resources.app_ico; this.Name = "MainForm"; + this.Opacity = 0D; this.StartPosition = System.Windows.Forms.FormStartPosition.CenterScreen; this.Text = "Nacollector"; this.FormClosing += new System.Windows.Forms.FormClosingEventHandler(this.MainForm_FormClosing); - this.Load += new System.EventHandler(this.MainForm_Load); + this.Load += new System.EventHandler(this.SplashScreen_MainForm_Load); this.ResumeLayout(false); } diff --git a/Nacollector/MainForm.cs b/Nacollector/MainForm.cs index 8828131..3167e73 100644 --- a/Nacollector/MainForm.cs +++ b/Nacollector/MainForm.cs @@ -1,9 +1,10 @@ using CefSharp; using CefSharp.WinForms; -using MaterialSkin; using Nacollector.Browser; using Nacollector.Browser.Handler; +using Nacollector.JsActions; using Nacollector.Spiders; +using Nacollector.Ui; using Nacollector.Util; using Newtonsoft.Json; using Newtonsoft.Json.Linq; @@ -16,6 +17,7 @@ using System.Drawing; using System.IO; using System.Linq; +using System.Runtime.InteropServices; using System.Text; using System.Threading; using System.Threading.Tasks; @@ -23,7 +25,7 @@ namespace Nacollector { - public partial class MainForm : MaterialSkin.Controls.MaterialForm + public partial class MainForm : FormBase { public static MainForm _mainForm; public static CrBrowser crBrowser; @@ -32,26 +34,11 @@ public partial class MainForm : MaterialSkin.Controls.MaterialForm public MainForm() { _mainForm = this; - - InitSkin(); // 初始化窗体皮肤 + InitializeComponent(); // 初始化控件 InitBrowser(); // 初始化浏览器 } - private void InitSkin() - { - MaterialSkinManager skinManager = MaterialSkinManager.Instance; - skinManager.AddFormToManage(this); - skinManager.Theme = MaterialSkinManager.Themes.DARK; - skinManager.ColorScheme = new ColorScheme(Primary.Blue800, Primary.Blue800, Primary.Blue500, Accent.LightBlue200, TextShade.WHITE); - } - - private void MainForm_Load(object sender, EventArgs e) - { - // 程序启动画面 - SetIsStarting(true); - } - private void InitBrowser() { // 初始化内置浏览器 @@ -61,104 +48,20 @@ private void InitBrowser() Application.Exit(); // 退出程序 } - crBrowser = new CrBrowser(htmlPath); - crBrowser.GetBrowser().RegisterAsyncJsObject("AppAction", new AppActionForJs()); - crBrowser.GetBrowser().RegisterAsyncJsObject("TaskController", new TaskControllerForJs()); - crBrowser.GetBrowser().FrameLoadEnd += new EventHandler(Browser_FrameLoadEnd); // 浏览器初始化完毕时执行 - - crDownloads = new CrDownloads(crBrowser); - - ContentPanel.Controls.Add(crBrowser.GetBrowser()); - } - - private void Browser_FrameLoadEnd(object sender, FrameLoadEndEventArgs e) - { - // 关闭程序启动画面 - SetIsStarting(false); - } - - /// - /// 程序JS操作 - /// - public class AppActionForJs - { - // 获取程序版本 - public string getVersion() - { - crBrowser.RunJS($"AppConfig.updateCheckUrl=\"{GlobalConstant.UpdateCheckUrl}\";AppConfig.updateCheckToken=\"{GlobalConstant.UpdateCheckToken}\""); - return System.Reflection.Assembly.GetExecutingAssembly().GetName().Version.ToString(); - } - - public void showDevTools() - { - crBrowser.GetBrowser().ShowDevTools(); - } - - // 采集是否使用IE代理请求 - public void _utilsReqIeProxy(bool isEnable) - { - Utils.ReqIeProxy = isEnable; - } - - // 日志文件清理 - public void logFileClear() - { - Logging.Clear(); - } + crBrowser = new CrBrowser(this, htmlPath); - // 升级操作 - public void appUpdateAction(string srcUrl, string updateType) - { - if (!File.Exists(Path.Combine(Application.StartupPath, "Naupdater.exe"))) - { - MessageBox.Show("升级 Naupdater.exe 模块丢失,无法升级", "未找到 Naupdater.exe", MessageBoxButtons.OK, MessageBoxIcon.Error); - return; - } - - Process process = new Process(); - process.StartInfo.FileName = "Naupdater.exe"; - process.StartInfo.WorkingDirectory = Application.StartupPath; - process.StartInfo.Arguments = $"-{updateType} \"{srcUrl}\""; - // MessageBox.Show($"-{updateType} \"{srcUrl}\""); - process.Start(); - } - } - - /// - /// 任务控制器 - /// - public class TaskControllerForJs - { - private Dictionary taskThreads = new Dictionary(); + // Need Update: https://github.com/cefsharp/CefSharp/issues/2246 - // 创建新任务 - public void createTask(string taskId, string className, string classLabel, string parmsJsonStr) - { - // 配置 - var settings = new SpiderSettings() - { - TaskId = taskId, - ClassName = className, - ClassLabel = classLabel, - ParmsJsonStr = parmsJsonStr - }; - // 创建任务执行线程 - var thread = new Thread(new ParameterizedThreadStart(_mainForm.StartTask)); - thread.IsBackground = true; - thread.Start(settings); - // 加入 Threads Dictionary - taskThreads.Add(taskId, thread); - } + //For legacy biding we'll still have support for + CefSharpSettings.LegacyJavascriptBindingEnabled = true; + crBrowser.GetBrowser().RegisterAsyncJsObject("AppAction", new AppAction(this, crBrowser)); + crBrowser.GetBrowser().RegisterAsyncJsObject("TaskController", new TaskControllerAction(this, crBrowser)); - // 终止任务 - public bool abortTask(string taskId) - { - if (!taskThreads.ContainsKey(taskId)) - return false; + crBrowser.GetBrowser().FrameLoadEnd += new EventHandler(SplashScreen_Browser_FrameLoadEnd); // 浏览器初始化完毕时执行 - taskThreads[taskId].Abort(); - return true; - } + crDownloads = new CrDownloads(crBrowser); + + ContentPanel.Controls.Add(crBrowser.GetBrowser()); } /// @@ -216,41 +119,6 @@ public void StartTask(object obj) Utils.ReleaseMemory(true); } - - private Form startingForm; - - /// - /// 设置程序启动画面 - /// - public void SetIsStarting(bool isStarting) - { - if (this.InvokeRequired) { this.Invoke(new SetIsStartingDelegate(SetIsStarting), new object[] { isStarting }); return; } - - if (isStarting) - { - startingForm = new Form - { - Size = new Size(640, 400), - TopMost = true, - ControlBox = false, - ShowInTaskbar = false, - AutoSizeMode = AutoSizeMode.GrowAndShrink, - FormBorderStyle = FormBorderStyle.None, - StartPosition = FormStartPosition.CenterScreen, - BackgroundImageLayout = ImageLayout.Zoom, - BackgroundImage = Properties.Resources.StartingImg - }; - startingForm.Show(); - this.Opacity = 0; - } else - { - startingForm.Hide(); - this.Opacity = 1; - } - - } - public delegate void SetIsStartingDelegate(bool isStarting); - private void MainForm_FormClosing(object sender, FormClosingEventArgs e) { // 是否退出弹窗 @@ -263,9 +131,13 @@ private void MainForm_FormClosing(object sender, FormClosingEventArgs e) DialogResult dr = MessageBox.Show(dialogTxt, "退出 Nacollector", MessageBoxButtons.OKCancel); if (dr == DialogResult.OK) + { e.Cancel = false; + } else + { e.Cancel = true; + } } } } \ No newline at end of file diff --git a/Nacollector/Nacollector.csproj b/Nacollector/Nacollector.csproj index 44b60ef..0ee2bf3 100644 --- a/Nacollector/Nacollector.csproj +++ b/Nacollector/Nacollector.csproj @@ -76,9 +76,6 @@ ..\packages\CsQuery.1.3.4\lib\net40\CsQuery.dll True - - ..\packages\MaterialSkin.Updated.0.2.2\lib\MaterialSkin.dll - ..\packages\Newtonsoft.Json.10.0.3\lib\net45\Newtonsoft.Json.dll True @@ -108,12 +105,19 @@ + + + + Form + + + Form @@ -121,6 +125,7 @@ MainForm.cs + @@ -146,9 +151,6 @@ LICENSE PreserveNewest - - Always - Always @@ -182,33 +184,18 @@ Always - + Always Always - - Always - - - Always - - - Always - Always Always - - Always - - - Always - Always @@ -218,13 +205,10 @@ Always - + Always - - Always - - + Always @@ -239,6 +223,18 @@ Always + + Always + + + Always + + + Always + + + Always + Always @@ -251,6 +247,43 @@ Always + + + Always + + + Always + + + Always + + + Always + + + Always + + + Always + + + Always + + + Always + + + Always + + + Always + + + Always + + + Always + MainForm.cs @@ -271,12 +304,6 @@ Settings.settings True - - Always - - - Always - Always diff --git a/Nacollector/Program.cs b/Nacollector/Program.cs index a9f8729..18a5ad2 100644 --- a/Nacollector/Program.cs +++ b/Nacollector/Program.cs @@ -1,4 +1,5 @@ using CefSharp; +using CefSharp.WinForms; using Microsoft.Win32; using Nacollector.Util; using System; diff --git a/Nacollector/Properties/AssemblyInfo.cs b/Nacollector/Properties/AssemblyInfo.cs index 61e99c6..c37c855 100644 --- a/Nacollector/Properties/AssemblyInfo.cs +++ b/Nacollector/Properties/AssemblyInfo.cs @@ -11,7 +11,7 @@ [assembly: AssemblyConfiguration("")] [assembly: AssemblyCompany("")] [assembly: AssemblyProduct("Nacollector")] -[assembly: AssemblyCopyright("Copyright © Zneiat")] +[assembly: AssemblyCopyright("Copyright © qwqaq.com")] [assembly: AssemblyTrademark("")] [assembly: AssemblyCulture("")] @@ -33,7 +33,7 @@ // 可以指定所有值,也可以使用以下所示的 "*" 预置版本号和修订号 // 方法是按如下所示使用“*”: : // [assembly: AssemblyVersion("1.0.*")] -[assembly: AssemblyVersion("1.2.0.0")] -[assembly: AssemblyFileVersion("1.2.0.0")] +[assembly: AssemblyVersion("1.3.0.0")] +[assembly: AssemblyFileVersion("1.3.0.0")] [assembly: NeutralResourcesLanguage("zh-Hans")] diff --git a/Nacollector/Resources/StartingImg.png b/Nacollector/Resources/StartingImg.png index e2a182505ead96f168f31ffd4970f11bdf16f8a7..0137d628d754b2eef680a9c41e6a9e8cff9a5320 100644 GIT binary patch delta 15467 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 kjAY{EF5?B`1$PI -
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/Nacollector/Resources/html_res/assets/app.css b/Nacollector/Resources/html_res/assets/app.css index 2627589..8109305 100644 --- a/Nacollector/Resources/html_res/assets/app.css +++ b/Nacollector/Resources/html_res/assets/app.css @@ -33,19 +33,24 @@ button, input, select, textarea { } .wrap { + position: fixed; + top: 65px; + left: 0; + right: 0; overflow-x: hidden; overflow-y: auto; - width: 100%; - height: 100%; + height: calc(100vh - 75px); + width: 100vw; opacity: 0; transition: opacity .4s cubic-bezier(0.65, 0.05, 0.36, 1), background .4s cubic-bezier(0.65, 0.05, 0.36, 1); + background: #333333; } .container { overflow: hidden; padding-right: 15px; padding-left: 15px; - padding-top: 75px; + padding-top: 35px; padding-bottom: 35px; margin-right: auto; margin-left: auto; @@ -180,7 +185,7 @@ button, input, select, textarea { } /* user-select disable */ -.top-nav-bar, .big-header, .app-select, .app-form label, ::-webkit-input-placeholder { +.title-bar, .top-nav-bar, .big-header, .app-select, .app-form label, ::-webkit-input-placeholder { -webkit-user-select: none; -moz-user-select: none; -ms-user-select: none; @@ -261,16 +266,95 @@ button, input, select, textarea { } } +/* Title Bar */ +.title-bar { + position: fixed; + top: 0; + left: 0; + right: 0; + height: 25px; + width: 100vw; + background: #1565c0; + z-index: 99999; +} + +.title-bar.blur { + /* background: #1565c0; */ +} + +.title-bar-inner { + height: 100%; + width: 100%; + -webkit-app-region: drag; + user-select: none; + display: flex; + justify-content: center; + align-items: center; + position: relative; +} + +.title-bar .controls { + display: flex; + width: 120px; + height: 100%; + justify-content: space-between; + position: absolute; + right: 7px; +} + +.title-bar .controls .window-icon-bg { + display: inline-block; + -webkit-app-region: no-drag; + height: 100%; + width: 33.34%; + transition: background .2s ease-in-out; +} + +.title-bar .controls .window-icon { + height: 100%; + width: 100%; + -webkit-mask-size: 23.1%; + background-color: #ffffff; +} + +.title-bar .controls .window-minimize { + -webkit-mask: url(./icons/minimize.svg) no-repeat 50% 50%; +} + +.title-bar .controls .window-maximize { + -webkit-mask: url(./icons/maximize.svg) no-repeat 50% 50%; +} + +.title-bar .controls .window-unmaximize { + -webkit-mask: url(./icons/unmaximize.svg) no-repeat 50% 50%; +} + +.title-bar .controls .window-close { + -webkit-mask: url(./icons/close.svg) no-repeat 50% 50%; +} + +.title-bar .controls .window-icon-bg:hover { + background-color: hsla(0,0%,100%,.1); +} + +.title-bar .controls .window-icon-bg.red:hover { + background-color: rgba(232,17,35,.9); +} + /* Nav Bar */ .top-nav-bar { z-index: 999; position: fixed; height: 40px; - width: 100%; + left: 0; + right: 0; + width: 100vw; + top: 25px; background: #1565c0; display: flex; align-items: center; box-shadow: rgba(0, 0, 0, 0.12) 0px 1px 6px, rgba(0, 0, 0, 0.12) 0px 1px 4px; + -webkit-app-region: drag; } .top-nav-bar .left-items, .top-nav-bar .right-items { @@ -303,6 +387,7 @@ button, input, select, textarea { float: right; overflow: hidden; border-radius: 0; + -webkit-app-region: no-drag; } .top-nav-bar .right-items .nav-btns .btn-group { @@ -310,6 +395,7 @@ button, input, select, textarea { padding-right: 15px; margin-right: 15px; border-right: 1px solid rgba(255, 255, 255, 0.08); + -webkit-app-region: no-drag; } .top-nav-bar .right-items .nav-btns .btn-group:last-child { @@ -1034,16 +1120,16 @@ button, input, select, textarea { .sidebar-layer .sidebar-block { position: fixed; z-index: 998; - height: 100%; width: 360px; - top: 0px; + top: 65px; + height: calc(100vh - 65px); right: 0px; color: rgba(0, 0, 0, 0.87); background: rgb(255, 255, 255); transition: transform 450ms cubic-bezier(0.23, 1, 0.32, 1) 0ms; box-shadow: rgba(0, 0, 0, 0.16) 0px 3px 10px, rgba(0, 0, 0, 0.23) 0px 3px 10px; border-radius: 0px; - padding: 40px 0 0 0; + padding: 0; transform: translate(370px, 0px); /* 360px + 10px */ } @@ -1313,7 +1399,7 @@ button, input, select, textarea { max-width: 600px; width: 100%; padding-right: 60px; - z-index: 1001; + z-index: 99999; } .notify-layer .notify-item { diff --git a/Nacollector/Resources/html_res/assets/app.js b/Nacollector/Resources/html_res/assets/app.js index 0954662..fe5df29 100644 --- a/Nacollector/Resources/html_res/assets/app.js +++ b/Nacollector/Resources/html_res/assets/app.js @@ -1,6 +1,6 @@ /** - * Created by Zneiat on 2017/7/15. - * https://github.com/Zneiat/Nacollector + * Created by qwqcode on 2017/7/15. + * https://github.com/qwqcode/Nacollector */ /** @@ -432,7 +432,7 @@ window.TaskGen = { // 取消显示 dropdown-option dropdownOptionHide(); // 当前 li 置顶 - $(this).insertBefore(dropdownOptionDom.find('li:first-child')); + // $(this).insertBefore(dropdownOptionDom.find('li:first-child')); }); li.appendTo(dropdownOptionDom); }); @@ -1113,10 +1113,10 @@ AppNavbar.panel = { }; // 自动调整位置 panelObj.setPosition = function () { - var position = $.getPosition(btnDom); + var position = btnDom[0].getBoundingClientRect(); var panelWidth = $(panelSel).outerWidth(); $(panelSel) - .css('top', position['top'] + 'px') + .css('top', position['top'] - 25 + 'px') .css('left', position['right'] - panelWidth + 'px'); }; // 显示 @@ -1634,12 +1634,12 @@ window.setting = { AppAction.getVersion().then(function (version) { infoAppVersion.text(version); }); - itemAt(groupAbout).infoShow('作者', 'ZNEIAT'); + itemAt(groupAbout).infoShow('作者', 'qwqcode'); itemAt(groupAbout).infoShow('联系', '1149527164@qq.com'); - itemAt(groupAbout).infoShow('博客', 'http://www.qwqaq.com'); - itemAt(groupAbout).infoShow('GitHub', 'Zneiat/Nacollector'); - itemAt(groupAbout).infoShow('', '您使用 Nacollector 即视为您已阅读并同意本《Nacollector 用户使用许可协议》的约束'); - itemAt(groupAbout).infoShow('', 'Nacollector Copyright (C) 2018 Zneiat'); + itemAt(groupAbout).infoShow('博客', 'qwqaq.com'); + itemAt(groupAbout).infoShow('GitHub', 'qwqcode/Nacollector'); + itemAt(groupAbout).infoShow('', '您使用 Nacollector 即视为您已阅读并同意本《Nacollector 用户使用许可协议》的约束'); + itemAt(groupAbout).infoShow('', 'Nacollector Copyright (C) 2018 qwqaq.com'); } }; diff --git a/Nacollector/Resources/html_res/assets/icons/close.svg b/Nacollector/Resources/html_res/assets/icons/close.svg new file mode 100644 index 0000000..3b02b08 --- /dev/null +++ b/Nacollector/Resources/html_res/assets/icons/close.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/Nacollector/Resources/html_res/assets/icons/maximize.svg b/Nacollector/Resources/html_res/assets/icons/maximize.svg new file mode 100644 index 0000000..6f76d58 --- /dev/null +++ b/Nacollector/Resources/html_res/assets/icons/maximize.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/Nacollector/Resources/html_res/assets/icons/minimize.svg b/Nacollector/Resources/html_res/assets/icons/minimize.svg new file mode 100644 index 0000000..869a1c1 --- /dev/null +++ b/Nacollector/Resources/html_res/assets/icons/minimize.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/Nacollector/Resources/html_res/assets/icons/unmaximize.svg b/Nacollector/Resources/html_res/assets/icons/unmaximize.svg new file mode 100644 index 0000000..0ac7922 --- /dev/null +++ b/Nacollector/Resources/html_res/assets/icons/unmaximize.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/Nacollector/Ui/FormBase.cs b/Nacollector/Ui/FormBase.cs new file mode 100644 index 0000000..51c58d1 --- /dev/null +++ b/Nacollector/Ui/FormBase.cs @@ -0,0 +1,321 @@ +using CefSharp; +using CefSharp.WinForms; +using Nacollector.Browser; +using Nacollector.Browser.Handler; +using Nacollector.JsActions; +using Nacollector.Spiders; +using Nacollector.Ui; +using Nacollector.Util; +using Newtonsoft.Json; +using Newtonsoft.Json.Linq; +using System; +using System.Collections; +using System.Collections.Generic; +using System.ComponentModel; +using System.Data; +using System.Diagnostics; +using System.Drawing; +using System.IO; +using System.Linq; +using System.Runtime.InteropServices; +using System.Text; +using System.Threading; +using System.Threading.Tasks; +using System.Windows.Forms; + + +namespace Nacollector.Ui +{ + /// + /// Based on http://customerborderform.codeplex.com/ + /// + public partial class FormBase : Form + { + public void DecorationMouseDown(HitTestValues hit, Point p) + { + NativeMethods.ReleaseCapture(); + var pt = new POINTS { X = (short)p.X, Y = (short)p.Y }; + NativeMethods.SendMessage(Handle, (int)WindowMessages.WM_NCLBUTTONDOWN, (int)hit, pt); + } + + public void DecorationMouseDown(HitTestValues hit) + { + DecorationMouseDown(hit, MousePosition); + } + + public void DecorationMouseUp(HitTestValues hit, Point p) + { + NativeMethods.ReleaseCapture(); + var pt = new POINTS { X = (short)p.X, Y = (short)p.Y }; + NativeMethods.SendMessage(Handle, (int)WindowMessages.WM_NCLBUTTONUP, (int)hit, pt); + } + + public void DecorationMouseUp(HitTestValues hit) + { + DecorationMouseUp(hit, MousePosition); + } + + protected override void OnHandleCreated(EventArgs e) + { + base.OnHandleCreated(e); + + if (!DesignMode) + SetWindowRegion(Handle, 0, 0, Width, Height); + } + + protected static int MakeLong(short lowPart, short highPart) + { + return (int)(((ushort)lowPart) | (uint)(highPart << 16)); + } + + public void ShowSystemMenu(Point pos) + { + NativeMethods.SendMessage(Handle, (int)WindowMessages.WM_SYSMENU, 0, MakeLong((short)pos.X, (short)pos.Y)); + } + + protected override void WndProc(ref Message m) + { + if (DesignMode) + { + base.WndProc(ref m); + return; + } + + switch (m.Msg) + { + case (int)WindowMessages.WM_NCCALCSIZE: + { + // Provides new coordinates for the window client area. + WmNCCalcSize(ref m); + break; + } + case (int)WindowMessages.WM_NCPAINT: + { + // Here should all our painting occur, but... + WmNCPaint(ref m); + break; + } + case (int)WindowMessages.WM_NCACTIVATE: + { + // ... WM_NCACTIVATE does some painting directly + // without bothering with WM_NCPAINT ... + WmNCActivate(ref m); + break; + } + case (int)WindowMessages.WM_SETTEXT: + { + // ... and some painting is required in here as well + WmSetText(ref m); + break; + } + case (int)WindowMessages.WM_WINDOWPOSCHANGED: + { + WmWindowPosChanged(ref m); + break; + } + case 174: // ignore magic message number + { + break; + } + default: + { + base.WndProc(ref m); + break; + } + } + } + + private void SetWindowRegion(IntPtr hwnd, int left, int top, int right, int bottom) + { + var hrg = new HandleRef((object)this, NativeMethods.CreateRectRgn(0, 0, 0, 0)); + var r = NativeMethods.GetWindowRgn(hwnd, hrg.Handle); + RECT box; + NativeMethods.GetRgnBox(hrg.Handle, out box); + if (box.left != left || box.top != top || box.right != right || box.bottom != bottom) + { + var hr = new HandleRef((object)this, NativeMethods.CreateRectRgn(left, top, right, bottom)); + NativeMethods.SetWindowRgn(hwnd, hr.Handle, NativeMethods.IsWindowVisible(hwnd)); + } + } + + public FormWindowState MinMaxState + { + get + { + var s = NativeMethods.GetWindowLong(Handle, NativeConstants.GWL_STYLE); + var max = (s & (int)WindowStyle.WS_MAXIMIZE) > 0; + if (max) return FormWindowState.Maximized; + var min = (s & (int)WindowStyle.WS_MINIMIZE) > 0; + if (min) return FormWindowState.Minimized; + return FormWindowState.Normal; + } + } + + private void WmWindowPosChanged(ref Message m) + { + DefWndProc(ref m); + UpdateBounds(); + var pos = (WINDOWPOS)Marshal.PtrToStructure(m.LParam, typeof(WINDOWPOS)); + SetWindowRegion(m.HWnd, 0, 0, pos.cx, pos.cy); + m.Result = NativeConstants.TRUE; + } + + private void WmNCCalcSize(ref Message m) + { + // http://msdn.microsoft.com/library/default.asp?url=/library/en-us/winui/winui/windowsuserinterface/windowing/windows/windowreference/windowmessages/wm_nccalcsize.asp + // http://groups.google.pl/groups?selm=OnRNaGfDEHA.1600%40tk2msftngp13.phx.gbl + + var r = (RECT)Marshal.PtrToStructure(m.LParam, typeof(RECT)); + var max = MinMaxState == FormWindowState.Maximized; + + if (max) + { + var x = NativeMethods.GetSystemMetrics(NativeConstants.SM_CXSIZEFRAME); + var y = NativeMethods.GetSystemMetrics(NativeConstants.SM_CYSIZEFRAME); + var p = NativeMethods.GetSystemMetrics(NativeConstants.SM_CXPADDEDBORDER); + var w = x + p; + var h = y + p; + + r.left += w; + r.top += h; + r.right -= w; + r.bottom -= h; + + var appBarData = new APPBARDATA(); + appBarData.cbSize = Marshal.SizeOf(typeof(APPBARDATA)); + var autohide = (NativeMethods.SHAppBarMessage(NativeConstants.ABM_GETSTATE, ref appBarData) & NativeConstants.ABS_AUTOHIDE) != 0; + if (autohide) r.bottom -= 1; + + Marshal.StructureToPtr(r, m.LParam, true); + } + + m.Result = IntPtr.Zero; + } + + private void WmNCPaint(ref Message msg) + { + // http://msdn.microsoft.com/library/default.asp?url=/library/en-us/gdi/pantdraw_8gdw.asp + // example in q. 2.9 on http://www.syncfusion.com/FAQ/WindowsForms/FAQ_c41c.aspx#q1026q + + // The WParam contains handle to clipRegion or 1 if entire window should be repainted + //PaintNonClientArea(msg.HWnd, (IntPtr)msg.WParam); + + // we handled everything + msg.Result = NativeConstants.TRUE; + } + + private void WmSetText(ref Message msg) + { + // allow the system to receive the new window title + DefWndProc(ref msg); + + // repaint title bar + //PaintNonClientArea(msg.HWnd, (IntPtr)1); + } + + private void WmNCActivate(ref Message msg) + { + // http://msdn.microsoft.com/library/default.asp?url=/library/en-us/winui/winui/windowsuserinterface/windowing/windows/windowreference/windowmessages/wm_ncactivate.asp + + bool active = (msg.WParam == NativeConstants.TRUE); + + if (MinMaxState == FormWindowState.Minimized) + DefWndProc(ref msg); + else + { + // repaint title bar + //PaintNonClientArea(msg.HWnd, (IntPtr)1); + + // allow to deactivate window + msg.Result = NativeConstants.TRUE; + } + } + } + + public partial class FormBase : Form + { + public FormWindowState ToggleMaximize() + { + return WindowState = WindowState == FormWindowState.Maximized ? FormWindowState.Normal : FormWindowState.Maximized; + } + } + + public partial class FormBase : Form { + /** + * 模拟 操作系统的标准标题栏 拖动 + */ + + [System.Runtime.InteropServices.DllImportAttribute("user32.dll")] + public static extern int SendMessage(IntPtr hWnd, int Msg, int wParam, int lParam); + [System.Runtime.InteropServices.DllImportAttribute("user32.dll")] + public static extern bool ReleaseCapture(); + + public const int WM_NCLBUTTONDOWN = 0xA1; + public const int HT_CAPTION = 0x2; + + // 界面线程执行拖动操作 + public void SendHandleMessage() + { + if (InvokeRequired) { Invoke(new SendHandleMessageDelegate(SendHandleMessage), new object[] { }); return; } + + SendMessage(Handle, WM_NCLBUTTONDOWN, HT_CAPTION, 0); + } + public delegate void SendHandleMessageDelegate(); + } + + public partial class FormBase : Form + { + protected Form startingForm; + + /// + /// 设置程序启动画面 + /// + protected void SetIsStarting(bool isStarting) + { + if (this.InvokeRequired) { this.Invoke(new SetIsStartingDelegate(SetIsStarting), new object[] { isStarting }); return; } + + if (isStarting) + { + startingForm = new Form + { + Size = new Size(640, 400), + TopMost = true, + ControlBox = false, + ShowInTaskbar = false, + AutoSizeMode = AutoSizeMode.GrowAndShrink, + FormBorderStyle = FormBorderStyle.None, + StartPosition = FormStartPosition.CenterScreen, + BackgroundImageLayout = ImageLayout.Zoom, + BackgroundImage = Properties.Resources.StartingImg, + BackColor = ColorTranslator.FromHtml("#282c34") + }; + startingForm.Show(); + this.Opacity = 0; + } + else + { + startingForm.Hide(); + this.Opacity = 1; + } + + } + protected delegate void SetIsStartingDelegate(bool isStarting); + + protected void SplashScreen_MainForm_Load(object sender, EventArgs e) + { + // 程序启动画面 + SetIsStarting(true); + } + + protected void SplashScreen_Browser_FrameLoadEnd(object sender, FrameLoadEndEventArgs e) + { + // 关闭程序启动画面 + SetIsStarting(false); + } + } + + public partial class FormBase : Form + { + + } +} diff --git a/Nacollector/Util/Native.cs b/Nacollector/Util/Native.cs new file mode 100644 index 0000000..01ffd6f --- /dev/null +++ b/Nacollector/Util/Native.cs @@ -0,0 +1,396 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Runtime.InteropServices; +using System.Text; + +namespace Nacollector.Util +{ + public enum GetWindow_Cmd : uint + { + GW_HWNDFIRST = 0, + GW_HWNDLAST = 1, + GW_HWNDNEXT = 2, + GW_HWNDPREV = 3, + GW_OWNER = 4, + GW_CHILD = 5, + GW_ENABLEDPOPUP = 6 + } + + public enum HitTestValues + { + HTERROR = -2, + HTTRANSPARENT = -1, + HTNOWHERE = 0, + HTCLIENT = 1, + HTCAPTION = 2, + HTSYSMENU = 3, + HTGROWBOX = 4, + HTMENU = 5, + HTHSCROLL = 6, + HTVSCROLL = 7, + HTMINBUTTON = 8, + HTMAXBUTTON = 9, + HTLEFT = 10, + HTRIGHT = 11, + HTTOP = 12, + HTTOPLEFT = 13, + HTTOPRIGHT = 14, + HTBOTTOM = 15, + HTBOTTOMLEFT = 16, + HTBOTTOMRIGHT = 17, + HTBORDER = 18, + HTOBJECT = 19, + HTCLOSE = 20, + HTHELP = 21 + } + + public enum WindowMessages + { + WM_NULL = 0x0000, + WM_CREATE = 0x0001, + WM_DESTROY = 0x0002, + WM_MOVE = 0x0003, + WM_SIZE = 0x0005, + WM_ACTIVATE = 0x0006, + WM_SETFOCUS = 0x0007, + WM_KILLFOCUS = 0x0008, + WM_ENABLE = 0x000A, + WM_SETREDRAW = 0x000B, + WM_SETTEXT = 0x000C, + WM_GETTEXT = 0x000D, + WM_GETTEXTLENGTH = 0x000E, + WM_PAINT = 0x000F, + WM_CLOSE = 0x0010, + + WM_QUIT = 0x0012, + WM_ERASEBKGND = 0x0014, + WM_SYSCOLORCHANGE = 0x0015, + WM_SHOWWINDOW = 0x0018, + + WM_ACTIVATEAPP = 0x001C, + + WM_SETCURSOR = 0x0020, + WM_MOUSEACTIVATE = 0x0021, + WM_GETMINMAXINFO = 0x24, + WM_WINDOWPOSCHANGING = 0x0046, + WM_WINDOWPOSCHANGED = 0x0047, + + WM_CONTEXTMENU = 0x007B, + WM_STYLECHANGING = 0x007C, + WM_STYLECHANGED = 0x007D, + WM_DISPLAYCHANGE = 0x007E, + WM_GETICON = 0x007F, + WM_SETICON = 0x0080, + + // non client area + WM_NCCREATE = 0x0081, + WM_NCDESTROY = 0x0082, + WM_NCCALCSIZE = 0x0083, + WM_NCHITTEST = 0x84, + WM_NCPAINT = 0x0085, + WM_NCACTIVATE = 0x0086, + + WM_GETDLGCODE = 0x0087, + + WM_SYNCPAINT = 0x0088, + + // non client mouse + WM_NCMOUSEMOVE = 0x00A0, + WM_NCLBUTTONDOWN = 0x00A1, + WM_NCLBUTTONUP = 0x00A2, + WM_NCLBUTTONDBLCLK = 0x00A3, + WM_NCRBUTTONDOWN = 0x00A4, + WM_NCRBUTTONUP = 0x00A5, + WM_NCRBUTTONDBLCLK = 0x00A6, + WM_NCMBUTTONDOWN = 0x00A7, + WM_NCMBUTTONUP = 0x00A8, + WM_NCMBUTTONDBLCLK = 0x00A9, + + // keyboard + WM_KEYDOWN = 0x0100, + WM_KEYUP = 0x0101, + WM_CHAR = 0x0102, + + WM_SYSCOMMAND = 0x0112, + + // menu + WM_INITMENU = 0x0116, + WM_INITMENUPOPUP = 0x0117, + WM_MENUSELECT = 0x011F, + WM_MENUCHAR = 0x0120, + WM_ENTERIDLE = 0x0121, + WM_MENURBUTTONUP = 0x0122, + WM_MENUDRAG = 0x0123, + WM_MENUGETOBJECT = 0x0124, + WM_UNINITMENUPOPUP = 0x0125, + WM_MENUCOMMAND = 0x0126, + + WM_CHANGEUISTATE = 0x0127, + WM_UPDATEUISTATE = 0x0128, + WM_QUERYUISTATE = 0x0129, + + // mouse + WM_MOUSEFIRST = 0x0200, + WM_MOUSEMOVE = 0x0200, + WM_LBUTTONDOWN = 0x0201, + WM_LBUTTONUP = 0x0202, + WM_LBUTTONDBLCLK = 0x0203, + WM_RBUTTONDOWN = 0x0204, + WM_RBUTTONUP = 0x0205, + WM_RBUTTONDBLCLK = 0x0206, + WM_MBUTTONDOWN = 0x0207, + WM_MBUTTONUP = 0x0208, + WM_MBUTTONDBLCLK = 0x0209, + WM_MOUSEWHEEL = 0x020A, + WM_MOUSELAST = 0x020D, + + WM_PARENTNOTIFY = 0x0210, + WM_ENTERMENULOOP = 0x0211, + WM_EXITMENULOOP = 0x0212, + + WM_NEXTMENU = 0x0213, + WM_SIZING = 0x0214, + WM_CAPTURECHANGED = 0x0215, + WM_MOVING = 0x0216, + + WM_ENTERSIZEMOVE = 0x0231, + WM_EXITSIZEMOVE = 0x0232, + + WM_MOUSELEAVE = 0x02A3, + WM_MOUSEHOVER = 0x02A1, + WM_NCMOUSEHOVER = 0x02A0, + WM_NCMOUSELEAVE = 0x02A2, + + WM_MDIACTIVATE = 0x0222, + WM_HSCROLL = 0x0114, + WM_VSCROLL = 0x0115, + + WM_SYSMENU = 0x313, + + WM_PRINT = 0x0317, + WM_PRINTCLIENT = 0x0318, + } + + public enum SystemCommands + { + SC_SIZE = 0xF000, + SC_MOVE = 0xF010, + SC_MINIMIZE = 0xF020, + SC_MAXIMIZE = 0xF030, + SC_MAXIMIZE2 = 0xF032, // fired from double-click on caption + SC_NEXTWINDOW = 0xF040, + SC_PREVWINDOW = 0xF050, + SC_CLOSE = 0xF060, + SC_VSCROLL = 0xF070, + SC_HSCROLL = 0xF080, + SC_MOUSEMENU = 0xF090, + SC_KEYMENU = 0xF100, + SC_ARRANGE = 0xF110, + SC_RESTORE = 0xF120, + SC_RESTORE2 = 0xF122, // fired from double-click on caption + SC_TASKLIST = 0xF130, + SC_SCREENSAVE = 0xF140, + SC_HOTKEY = 0xF150, + + SC_DEFAULT = 0xF160, + SC_MONITORPOWER = 0xF170, + SC_CONTEXTHELP = 0xF180, + SC_SEPARATOR = 0xF00F + } + + [StructLayout(LayoutKind.Sequential)] + public struct RECT + { + public int left; + public int top; + public int right; + public int bottom; + + public RECT(int left, int top, int right, int bottom) + { + this.left = left; + this.top = top; + this.right = right; + this.bottom = bottom; + } + + public static RECT FromXYWH(int x, int y, int width, int height) + { + return new RECT(x, + y, + x + width, + y + height); + } + } + + [StructLayout(LayoutKind.Sequential)] + internal struct WINDOWPOS + { + internal IntPtr hwnd; + internal IntPtr hWndInsertAfter; + internal int x; + internal int y; + internal int cx; + internal int cy; + internal uint flags; + } + + [StructLayout(LayoutKind.Sequential)] + public struct POINTS + { + public short X; + public short Y; + } + + [Flags] + public enum WindowStyle + { + WS_OVERLAPPED = 0x00000000, + WS_POPUP = -2147483648, //0x80000000, + WS_CHILD = 0x40000000, + WS_MINIMIZE = 0x20000000, + WS_VISIBLE = 0x10000000, + WS_DISABLED = 0x08000000, + WS_CLIPSIBLINGS = 0x04000000, + WS_CLIPCHILDREN = 0x02000000, + WS_MAXIMIZE = 0x01000000, + WS_CAPTION = 0x00C00000, + WS_BORDER = 0x00800000, + WS_DLGFRAME = 0x00400000, + WS_VSCROLL = 0x00200000, + WS_HSCROLL = 0x00100000, + WS_SYSMENU = 0x00080000, + WS_THICKFRAME = 0x00040000, + WS_GROUP = 0x00020000, + WS_TABSTOP = 0x00010000, + WS_MINIMIZEBOX = 0x00020000, + WS_MAXIMIZEBOX = 0x00010000, + WS_TILED = WS_OVERLAPPED, + WS_ICONIC = WS_MINIMIZE, + WS_SIZEBOX = WS_THICKFRAME, + WS_TILEDWINDOW = WS_OVERLAPPEDWINDOW, + WS_OVERLAPPEDWINDOW = (WS_OVERLAPPED | WS_CAPTION | WS_SYSMENU | + WS_THICKFRAME | WS_MINIMIZEBOX | WS_MAXIMIZEBOX), + WS_POPUPWINDOW = (WS_POPUP | WS_BORDER | WS_SYSMENU), + WS_CHILDWINDOW = (WS_CHILD) + } + + [StructLayout(LayoutKind.Sequential)] + public struct NativeMessage + { + public IntPtr handle; + public uint msg; + public IntPtr wParam; + public IntPtr lParam; + public uint time; + public System.Drawing.Point p; + } + + [StructLayout(LayoutKind.Sequential)] + public struct APPBARDATA + { + public int cbSize; // initialize this field using: Marshal.SizeOf(typeof(APPBARDATA)); + public IntPtr hWnd; + public uint uCallbackMessage; + public uint uEdge; + public RECT rc; + public int lParam; + } + + public static class NativeMethods + { + [DllImport("user32.dll")] + public static extern bool ReleaseCapture(); + + [DllImport("user32.dll")] + public static extern IntPtr SetCapture(IntPtr hWnd); + + [DllImport("user32.dll")] + public static extern IntPtr GetCapture(); + + [DllImport("user32.dll")] + [return: MarshalAs(UnmanagedType.Bool)] + public static extern bool SetForegroundWindow(IntPtr hWnd); + + [DllImport("user32.dll", SetLastError = true)] + public static extern IntPtr SetActiveWindow(IntPtr hWnd); + + [DllImport("user32.dll")] + public static extern int SendMessage(IntPtr hwnd, int msg, int wparam, int lparam); + + [DllImport("user32.dll")] + public static extern int PostMessage(IntPtr hwnd, int msg, int wparam, int lparam); + + [DllImport("user32.dll")] + public static extern IntPtr GetSystemMenu(IntPtr hWnd, bool bRevert); + + [DllImport("user32.dll")] + public static extern int TrackPopupMenuEx(IntPtr hmenu, uint fuFlags, int x, int y, + IntPtr hwnd, IntPtr lptpm); + + [DllImport("user32.dll")] + public static extern int SendMessage(IntPtr hwnd, int wMsg, IntPtr wParam, IntPtr lParam); + + [DllImport("user32.dll")] + public static extern int SendMessage(IntPtr hwnd, int msg, int wparam, POINTS pos); + + [DllImport("user32.dll")] + public static extern int PostMessage(IntPtr hwnd, int wMsg, IntPtr wParam, IntPtr lParam); + + [DllImport("user32.dll")] + public static extern int PostMessage(IntPtr hwnd, int msg, int wparam, POINTS pos); + + [DllImport("user32.dll")] + public static extern int SetWindowRgn(IntPtr hWnd, IntPtr hRgn, bool bRedraw); + + [DllImport("user32.dll")] + [return: MarshalAs(UnmanagedType.Bool)] + public static extern bool IsWindowVisible(IntPtr hWnd); + + [DllImport("gdi32.dll")] + public static extern IntPtr CreateRectRgn(int nLeftRect, int nTopRect, int nRightRect, int nBottomRect); + + [DllImport("user32.dll")] + public static extern int GetWindowRgn(IntPtr hWnd, IntPtr hRgn); + + [DllImport("gdi32.dll")] + public static extern int GetRgnBox(IntPtr hrgn, out RECT lprc); + + [DllImport("user32.dll")] + public static extern Int32 GetWindowLong(IntPtr hWnd, Int32 Offset); + + [DllImport("user32.dll")] + public static extern int GetSystemMetrics(int smIndex); + + [DllImport("user32.dll", SetLastError = true)] + public static extern IntPtr FindWindowEx(IntPtr parentHandle, IntPtr childAfter, string className, string windowTitle); + + [DllImport("shell32.dll")] + public static extern int SHAppBarMessage(uint dwMessage, [In] ref APPBARDATA pData); + } + + public static class NativeConstants + { + public const int SM_CXSIZEFRAME = 32; + public const int SM_CYSIZEFRAME = 33; + public const int SM_CXPADDEDBORDER = 92; + + public const int GWL_ID = (-12); + public const int GWL_STYLE = (-16); + public const int GWL_EXSTYLE = (-20); + + public const int WM_NCLBUTTONDOWN = 0x00A1; + public const int WM_NCRBUTTONUP = 0x00A5; + + public const uint TPM_LEFTBUTTON = 0x0000; + public const uint TPM_RIGHTBUTTON = 0x0002; + public const uint TPM_RETURNCMD = 0x0100; + + public static readonly IntPtr TRUE = new IntPtr(1); + public static readonly IntPtr FALSE = new IntPtr(0); + + public const uint ABM_GETSTATE = 0x4; + public const int ABS_AUTOHIDE = 0x1; + } +} diff --git a/Nacollector/packages.config b/Nacollector/packages.config index ebd170b..f0a74c4 100644 --- a/Nacollector/packages.config +++ b/Nacollector/packages.config @@ -4,7 +4,6 @@ -