/* * Greenshot - a free and open source screenshot tool * Copyright (C) 2007-2012 Thomas Braun, Jens Klingen, Robin Krom * * For more information see: http://getgreenshot.org/ * The Greenshot project is hosted on Sourceforge: http://sourceforge.net/projects/greenshot/ * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 1 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ using System; using System.Collections.Generic; using System.Diagnostics; using System.Drawing; using System.IO; using System.Reflection; using System.Security.AccessControl; using System.Security.Principal; using System.Text; using System.Threading; using System.Windows.Forms; using Greenshot.Configuration; using Greenshot.Experimental; using Greenshot.Forms; using Greenshot.Help; using Greenshot.Helpers; using Greenshot.Plugin; using GreenshotPlugin.UnmanagedHelpers; using GreenshotPlugin.Controls; using GreenshotPlugin.Core; using Greenshot.IniFile; using Greenshot.Destinations; namespace Greenshot { /// /// Description of MainForm. /// public partial class MainForm : BaseForm { private static log4net.ILog LOG = null; private static Mutex applicationMutex = null; private static CoreConfiguration conf; public static string LogFileLocation = null; public static void Start(string[] args) { bool isAlreadyRunning = false; List filesToOpen = new List(); // Set the Thread name, is better than "1" Thread.CurrentThread.Name = Application.ProductName; // Init Log4NET LogFileLocation = LogHelper.InitializeLog4NET(); // Get logger LOG = log4net.LogManager.GetLogger(typeof(MainForm)); Application.ThreadException += new System.Threading.ThreadExceptionEventHandler(Application_ThreadException); AppDomain.CurrentDomain.UnhandledException += new UnhandledExceptionEventHandler(CurrentDomain_UnhandledException); // Initialize the IniConfig IniConfig.Init(); // Log the startup LOG.Info("Starting: " + EnvironmentInfo.EnvironmentToString(false)); // Upgrade if needed AppConfig.UpgradeToIni(); // Read configuration conf = IniConfig.GetIniSection(); try { // Fix for Bug 2495900, Multi-user Environment // check whether there's an local instance running already try { // Added Mutex Security, hopefully this prevents the UnauthorizedAccessException more gracefully // See an example in Bug #3131534 SecurityIdentifier sid = new SecurityIdentifier(WellKnownSidType.WorldSid, null); MutexSecurity mutexsecurity = new MutexSecurity(); mutexsecurity.AddAccessRule(new MutexAccessRule(sid, MutexRights.FullControl, AccessControlType.Allow)); mutexsecurity.AddAccessRule(new MutexAccessRule(sid, MutexRights.ChangePermissions, AccessControlType.Deny)); mutexsecurity.AddAccessRule(new MutexAccessRule(sid, MutexRights.Delete, AccessControlType.Deny)); bool created = false; // 1) Create Mutex applicationMutex = new Mutex(false, @"Local\F48E86D3-E34C-4DB7-8F8F-9A0EA55F0D08", out created, mutexsecurity); // 2) Get the right to it, this returns false if it's already locked if (!applicationMutex.WaitOne(0, false)) { LOG.Debug("Greenshot seems already to be running!"); isAlreadyRunning = true; // Clean up applicationMutex.Close(); applicationMutex = null; } } catch (AbandonedMutexException e) { // Another Greenshot instance didn't cleanup correctly! // we can ignore the exception, it happend on the "waitone" but still the mutex belongs to us LOG.Warn("Greenshot didn't cleanup correctly!", e); } catch (UnauthorizedAccessException e) { LOG.Warn("Greenshot is most likely already running for a different user in the same session, can't create mutex due to error: ", e); isAlreadyRunning = true; } catch (Exception e) { LOG.Warn("Problem obtaining the Mutex, assuming it was already taken!", e); isAlreadyRunning = true; } if (args.Length > 0 && LOG.IsDebugEnabled) { StringBuilder argumentString = new StringBuilder(); for(int argumentNr = 0; argumentNr < args.Length; argumentNr++) { argumentString.Append("[").Append(args[argumentNr]).Append("] "); } LOG.Debug("Greenshot arguments: " + argumentString.ToString()); } for(int argumentNr = 0; argumentNr < args.Length; argumentNr++) { string argument = args[argumentNr]; // Help if (argument.ToLower().Equals("/help") || argument.ToLower().Equals("/h") || argument.ToLower().Equals("/?")) { // Try to attach to the console bool attachedToConsole = Kernel32.AttachConsole(Kernel32.ATTACHCONSOLE_ATTACHPARENTPROCESS); // If attach didn't work, open a console if (!attachedToConsole) { Kernel32.AllocConsole(); } StringBuilder helpOutput = new StringBuilder(); helpOutput.AppendLine(); helpOutput.AppendLine("Greenshot commandline options:"); helpOutput.AppendLine(); helpOutput.AppendLine(); helpOutput.AppendLine("\t/help"); helpOutput.AppendLine("\t\tThis help."); helpOutput.AppendLine(); helpOutput.AppendLine(); helpOutput.AppendLine("\t/exit"); helpOutput.AppendLine("\t\tTries to close all running instances."); helpOutput.AppendLine(); helpOutput.AppendLine(); helpOutput.AppendLine("\t/reload"); helpOutput.AppendLine("\t\tReload the configuration of Greenshot."); helpOutput.AppendLine(); helpOutput.AppendLine(); helpOutput.AppendLine("\t/language [language code]"); helpOutput.AppendLine("\t\tSet the language of Greenshot, e.g. greenshot /language en-US."); helpOutput.AppendLine(); helpOutput.AppendLine(); helpOutput.AppendLine("\t[filename]"); helpOutput.AppendLine("\t\tOpen the bitmap files in the running Greenshot instance or start a new instance"); Console.WriteLine(helpOutput.ToString()); // If attach didn't work, wait for key otherwise the console will close to quickly if (!attachedToConsole) { Console.ReadKey(); } FreeMutex(); return; } if (argument.ToLower().Equals("/exit")) { // unregister application on uninstall (allow uninstall) try { LOG.Info("Sending all instances the exit command."); // Pass Exit to running instance, if any SendData(new CopyDataTransport(CommandEnum.Exit)); } catch (Exception e) { LOG.Warn("Exception by exit.", e); } FreeMutex(); return; } // Reload the configuration if (argument.ToLower().Equals("/reload")) { // Modify configuration LOG.Info("Reloading configuration!"); // Update running instances SendData(new CopyDataTransport(CommandEnum.ReloadConfig)); FreeMutex(); return; } // Stop running if (argument.ToLower().Equals("/norun")) { // Make an exit possible FreeMutex(); return; } // Language if (argument.ToLower().Equals("/language")) { conf.Language = args[++argumentNr]; IniConfig.Save(); continue; } // Files to open filesToOpen.Add(argument); } // Finished parsing the command line arguments, see if we need to do anything CopyDataTransport transport = new CopyDataTransport(); if (filesToOpen.Count > 0) { foreach(string fileToOpen in filesToOpen) { transport.AddCommand(CommandEnum.OpenFile, fileToOpen); } } if (isAlreadyRunning) { // We didn't initialize the language yet, do it here just for the message box if (filesToOpen.Count > 0) { SendData(transport); } else { StringBuilder instanceInfo = new StringBuilder(); bool matchedThisProcess = false; int index = 1; foreach (Process greenshotProcess in Process.GetProcessesByName("greenshot")) { try { instanceInfo.Append(index++ + ": ").AppendLine(Kernel32.GetProcessPath(new IntPtr(greenshotProcess.Id))); if (Process.GetCurrentProcess().Id == greenshotProcess.Id) { matchedThisProcess = true; } } catch (Exception ex) { LOG.Debug(ex); } } if (!matchedThisProcess) { instanceInfo.Append(index++ + ": ").AppendLine(Kernel32.GetProcessPath(new IntPtr(Process.GetCurrentProcess().Id))); } MessageBox.Show(Language.GetString(LangKey.error_multipleinstances) + "\r\n" + instanceInfo.ToString(), Language.GetString(LangKey.error)); } FreeMutex(); Application.Exit(); return; } // From here on we continue starting Greenshot Application.EnableVisualStyles(); Application.SetCompatibleTextRenderingDefault(false); // if language is not set, show language dialog if(string.IsNullOrEmpty(conf.Language)) { LanguageDialog languageDialog = LanguageDialog.GetInstance(); languageDialog.ShowDialog(); conf.Language = languageDialog.SelectedLanguage; IniConfig.Save(); } // Check if it's the first time launch? if(conf.IsFirstLaunch) { conf.IsFirstLaunch = false; IniConfig.Save(); transport.AddCommand(CommandEnum.FirstLaunch); } MainForm mainForm = new MainForm(transport); Application.Run(); } catch(Exception ex) { LOG.Error("Exception in startup.", ex); Application_ThreadException(MainForm.ActiveForm, new ThreadExceptionEventArgs(ex)); } } /// /// Send DataTransport Object via Window-messages /// /// DataTransport with data for a running instance private static void SendData(CopyDataTransport dataTransport) { string appName = Application.ProductName; CopyData copyData = new CopyData(); copyData.Channels.Add(appName); copyData.Channels[appName].Send(dataTransport); } private static void FreeMutex() { // Remove the application mutex if (applicationMutex != null) { try { applicationMutex.ReleaseMutex(); applicationMutex = null; } catch (Exception ex) { LOG.Error("Error releasing Mutex!", ex); } } } private static MainForm instance = null; public static MainForm Instance { get { return instance; } } private ToolTip tooltip; private CopyData copyData = null; // Thumbnail preview private ThumbnailForm thumbnailForm = null; private IntPtr thumbnailHandle = IntPtr.Zero; private Rectangle parentMenuBounds = Rectangle.Empty; // Make sure we have only one settings form private SettingsForm settingsForm = null; // Make sure we have only one about form private AboutForm aboutForm = null; public NotifyIcon NotifyIcon { get { return notifyIcon; } } public MainForm(CopyDataTransport dataTransport) { instance = this; // // The InitializeComponent() call is required for Windows Forms designer support. // InitializeComponent(); this.notifyIcon.Icon = GreenshotPlugin.Core.GreenshotResources.getGreenshotIcon(); this.Icon = GreenshotPlugin.Core.GreenshotResources.getGreenshotIcon(); // Disable access to the settings, for feature #3521446 contextmenu_settings.Visible = !conf.DisableSettings; IniConfig.IniChanged += new FileSystemEventHandler(ReloadConfiguration); // Make sure all hotkeys pass this window! HotkeyControl.RegisterHotkeyHWND(this.Handle); RegisterHotkeys(); tooltip = new ToolTip(); UpdateUI(); // This forces the registration of all destinations inside Greenshot itself. DestinationHelper.GetAllDestinations(); // This forces the registration of all processors inside Greenshot itself. ProcessorHelper.GetAllProcessors(); // Load all the plugins PluginHelper.Instance.LoadPlugins(); // Check destinations, remove all that don't exist foreach(string destination in conf.OutputDestinations.ToArray()) { if (DestinationHelper.GetDestination(destination) == null) { conf.OutputDestinations.Remove(destination); } } // we should have at least one! if (conf.OutputDestinations.Count == 0) { conf.OutputDestinations.Add(Destinations.EditorDestination.DESIGNATION); } if (conf.DisableQuickSettings) { contextmenu_quicksettings.Visible = false; } else { // Do after all plugins & finding the destination, otherwise they are missing! InitializeQuickSettingsMenu(); } SoundHelper.Initialize(); // Set the Greenshot icon visibility depending on the configuration. (Added for feature #3521446) // Setting it to true this late prevents Problems with the context menu notifyIcon.Visible = !conf.HideTrayicon; // Make sure we never capture the mainform WindowDetails.RegisterIgnoreHandle(this.Handle); // Create a new instance of the class: copyData = new CopyData(); copyData = new CopyData(); // Assign the handle: copyData.AssignHandle(this.Handle); // Create the channel to send on: copyData.Channels.Add("Greenshot"); // Hook up received event: copyData.CopyDataReceived += new CopyDataReceivedEventHandler(CopyDataDataReceived); if (dataTransport != null) { HandleDataTransport(dataTransport); } } /// /// DataReceivedEventHandler /// /// /// private void CopyDataDataReceived(object sender, CopyDataReceivedEventArgs copyDataReceivedEventArgs) { // Cast the data to the type of object we sent: CopyDataTransport dataTransport = (CopyDataTransport)copyDataReceivedEventArgs.Data; HandleDataTransport(dataTransport); } private void HandleDataTransport(CopyDataTransport dataTransport) { foreach(KeyValuePair command in dataTransport.Commands) { LOG.Debug("Data received, Command = " + command.Key + ", Data: " + command.Value); switch(command.Key) { case CommandEnum.Exit: LOG.Info("Exit requested"); Exit(); break; case CommandEnum.FirstLaunch: LOG.Info("FirstLaunch: Created new configuration, showing balloon."); try { EventHandler balloonTipClickedHandler = null; EventHandler balloonTipClosedHandler = null; balloonTipClosedHandler = delegate(object sender, EventArgs e) { notifyIcon.BalloonTipClicked -= balloonTipClickedHandler; notifyIcon.BalloonTipClosed -= balloonTipClosedHandler; }; balloonTipClickedHandler = delegate(object sender, EventArgs e) { ShowSetting(); notifyIcon.BalloonTipClicked -= balloonTipClickedHandler; notifyIcon.BalloonTipClosed -= balloonTipClosedHandler; }; notifyIcon.BalloonTipClicked += balloonTipClickedHandler; notifyIcon.BalloonTipClosed += balloonTipClosedHandler; notifyIcon.ShowBalloonTip(2000, "Greenshot", Language.GetFormattedString(LangKey.tooltip_firststart, HotkeyControl.GetLocalizedHotkeyStringFromString(conf.RegionHotkey)), ToolTipIcon.Info); } catch {} break; case CommandEnum.ReloadConfig: LOG.Info("Reload requested"); try { IniConfig.Reload(); ReloadConfiguration(null, null); } catch {} break; case CommandEnum.OpenFile: string filename = command.Value; LOG.InfoFormat("Open file requested: {0}", filename); if (File.Exists(filename)) { BeginInvoke((MethodInvoker)delegate { CaptureHelper.CaptureFile(filename); }); } else { LOG.Warn("No such file: " + filename); } break; default: LOG.Error("Unknown command!"); break; } } } /// /// This is called when the ini-file changes /// /// /// private void ReloadConfiguration(object source, FileSystemEventArgs e) { Language.CurrentLanguage = null; // Reload this.Invoke((MethodInvoker) delegate { // Even update language when needed UpdateUI(); // Update the hotkey // Make sure the current hotkeys are disabled HotkeyControl.UnregisterHotkeys(); RegisterHotkeys(); }); } public ContextMenuStrip MainMenu { get {return contextMenu;} } #region hotkeys protected override void WndProc(ref Message m) { if (HotkeyControl.HandleMessages(ref m)) { return; } base.WndProc(ref m); } /// /// Helper method to cleanly register a hotkey /// /// /// /// /// /// private static bool RegisterHotkey(StringBuilder failedKeys, string functionName, string hotkeyString, HotKeyHandler handler) { Keys modifierKeyCode = HotkeyControl.HotkeyModifiersFromString(hotkeyString); Keys virtualKeyCode = HotkeyControl.HotkeyFromString(hotkeyString); if (!Keys.None.Equals(virtualKeyCode)) { if (HotkeyControl.RegisterHotKey(modifierKeyCode, virtualKeyCode, handler) < 0) { LOG.DebugFormat("Failed to register {0} to hotkey: {1}", functionName, hotkeyString); if (failedKeys.Length > 0) { failedKeys.Append(", "); } failedKeys.Append(hotkeyString); return false; } else { LOG.DebugFormat("Registered {0} to hotkey: {1}", functionName, hotkeyString); } } else { LOG.InfoFormat("Skipping hotkey registration for {0}, no hotkey set!", functionName); } return true; } private static bool RegisterWrapper(StringBuilder failedKeys, string functionName, string configurationKey, HotKeyHandler handler) { IniValue hotkeyValue = conf.Values[configurationKey]; try { return RegisterHotkey(failedKeys, functionName, hotkeyValue.Value.ToString(), handler); } catch (Exception ex) { LOG.Warn(ex); LOG.WarnFormat("Repairing the hotkey for {0}, stored under {1} from '{2}' to '{3}'", functionName, configurationKey, hotkeyValue.Value, hotkeyValue.Attributes.DefaultValue); // when getting an exception the key wasn't found: reset the hotkey value hotkeyValue.UseValueOrDefault(null); hotkeyValue.ContainingIniSection.IsDirty = true; return RegisterHotkey(failedKeys, functionName, hotkeyValue.Value.ToString(), handler); } } public static void RegisterHotkeys() { if (instance == null) { return; } bool success = true; StringBuilder failedKeys = new StringBuilder(); if (!RegisterWrapper(failedKeys, "CaptureRegion", "RegionHotkey", new HotKeyHandler(instance.CaptureRegion))) { success = false; } if (!RegisterWrapper(failedKeys, "CaptureWindow", "WindowHotkey", new HotKeyHandler(instance.CaptureWindow))) { success = false; } if (!RegisterWrapper(failedKeys, "CaptureFullScreen", "FullscreenHotkey", new HotKeyHandler(instance.CaptureFullScreen))) { success = false; } if (!RegisterWrapper(failedKeys, "CaptureLastRegion", "LastregionHotkey", new HotKeyHandler(instance.CaptureLastRegion))) { success = false; } if (conf.IECapture) { if (!RegisterWrapper(failedKeys, "CaptureIE", "IEHotkey", new HotKeyHandler(instance.CaptureIE))) { success = false; } } if (!success) { MessageBox.Show(Language.GetFormattedString(LangKey.warning_hotkeys, failedKeys.ToString()),Language.GetString(LangKey.warning)); } } #endregion public void UpdateUI() { // As the form is never loaded, call ApplyLanguage ourselves ApplyLanguage(); // Show hotkeys in Contextmenu this.contextmenu_capturearea.ShortcutKeyDisplayString = HotkeyControl.GetLocalizedHotkeyStringFromString(conf.RegionHotkey); this.contextmenu_capturelastregion.ShortcutKeyDisplayString = HotkeyControl.GetLocalizedHotkeyStringFromString(conf.LastregionHotkey); this.contextmenu_capturewindow.ShortcutKeyDisplayString = HotkeyControl.GetLocalizedHotkeyStringFromString(conf.WindowHotkey); this.contextmenu_capturefullscreen.ShortcutKeyDisplayString = HotkeyControl.GetLocalizedHotkeyStringFromString(conf.FullscreenHotkey); this.contextmenu_captureie.ShortcutKeyDisplayString = HotkeyControl.GetLocalizedHotkeyStringFromString(conf.IEHotkey); } #region mainform events void MainFormFormClosing(object sender, FormClosingEventArgs e) { LOG.DebugFormat("Mainform closing, reason: {0}", e.CloseReason); instance = null; Exit(); } void MainFormActivated(object sender, EventArgs e) { Hide(); ShowInTaskbar = false; } #endregion #region key handlers void CaptureRegion() { CaptureHelper.CaptureRegion(true); } void CaptureClipboard() { CaptureHelper.CaptureClipboard(); } void CaptureFile() { OpenFileDialog openFileDialog = new OpenFileDialog(); openFileDialog.Filter = "Image files (*.greenshot, *.png, *.jpg, *.gif, *.bmp, *.ico, *.tiff, *.wmf)|*.greenshot; *.png; *.jpg; *.jpeg; *.gif; *.bmp; *.ico; *.tiff; *.tif; *.wmf"; if (openFileDialog.ShowDialog() == DialogResult.OK) { if (File.Exists(openFileDialog.FileName)) { CaptureHelper.CaptureFile(openFileDialog.FileName); } } } void CaptureFullScreen() { CaptureHelper.CaptureFullscreen(true, conf.ScreenCaptureMode); } void CaptureLastRegion() { CaptureHelper.CaptureLastRegion(true); } void CaptureIE() { if (conf.IECapture) { CaptureHelper.CaptureIE(true, null); } } void CaptureWindow() { if (conf.CaptureWindowsInteractive) { CaptureHelper.CaptureWindowInteractive(true); } else { CaptureHelper.CaptureWindow(true); } } #endregion #region contextmenu void ContextMenuOpening(object sender, System.ComponentModel.CancelEventArgs e) { contextmenu_captureclipboard.Enabled = ClipboardHelper.ContainsImage(); contextmenu_capturelastregion.Enabled = RuntimeConfig.LastCapturedRegion != Rectangle.Empty; // IE context menu code try { if (conf.IECapture && IECaptureHelper.IsIERunning()) { this.contextmenu_captureie.Enabled = true; this.contextmenu_captureiefromlist.Enabled = true; } else { this.contextmenu_captureie.Enabled = false; this.contextmenu_captureiefromlist.Enabled = false; } } catch (Exception ex) { LOG.WarnFormat("Problem accessing IE information: {0}", ex.Message); } // Multi-Screen captures this.contextmenu_capturefullscreen.Click -= new System.EventHandler(this.CaptureFullScreenToolStripMenuItemClick); this.contextmenu_capturefullscreen.DropDownOpening -= new System.EventHandler(MultiScreenDropDownOpening); this.contextmenu_capturefullscreen.DropDownClosed -= new System.EventHandler(MultiScreenDropDownClosing); if (Screen.AllScreens.Length > 1) { this.contextmenu_capturefullscreen.DropDownOpening += new System.EventHandler(MultiScreenDropDownOpening); this.contextmenu_capturefullscreen.DropDownClosed += new System.EventHandler(MultiScreenDropDownClosing); } else { this.contextmenu_capturefullscreen.Click += new System.EventHandler(this.CaptureFullScreenToolStripMenuItemClick); } } void ContextMenuClosing(object sender, EventArgs e) { this.contextmenu_captureiefromlist.DropDownItems.Clear(); this.contextmenu_capturewindowfromlist.DropDownItems.Clear(); cleanupThumbnail(); } /// /// Build a selectable list of IE tabs when we enter the menu item /// void CaptureIEMenuDropDownOpening(object sender, EventArgs e) { if (!conf.IECapture) { return; } try { List> tabs = IECaptureHelper.GetBrowserTabs(); this.contextmenu_captureiefromlist.DropDownItems.Clear(); if (tabs.Count > 0) { this.contextmenu_captureie.Enabled = true; this.contextmenu_captureiefromlist.Enabled = true; Dictionary counter = new Dictionary(); foreach(KeyValuePair tabData in tabs) { ToolStripMenuItem captureIETabItem = new ToolStripMenuItem(tabData.Value); int index; if (counter.ContainsKey(tabData.Key)) { index = counter[tabData.Key]; } else { index = 0; } captureIETabItem.Tag = new KeyValuePair(tabData.Key, index++); captureIETabItem.Click += new System.EventHandler(Contextmenu_captureiefromlist_Click); this.contextmenu_captureiefromlist.DropDownItems.Add(captureIETabItem); if (counter.ContainsKey(tabData.Key)) { counter[tabData.Key] = index; } else { counter.Add(tabData.Key, index); } } } else { this.contextmenu_captureie.Enabled = false; this.contextmenu_captureiefromlist.Enabled = false; } } catch (Exception ex) { LOG.WarnFormat("Problem accessing IE information: {0}", ex.Message); } } /// /// MultiScreenDropDownOpening is called when mouse hovers over the Capture-Screen context menu /// /// /// private void MultiScreenDropDownOpening(object sender, EventArgs e) { ToolStripMenuItem captureScreenMenuItem = (ToolStripMenuItem)sender; captureScreenMenuItem.DropDownItems.Clear(); if (Screen.AllScreens.Length > 1) { ToolStripMenuItem captureScreenItem; Rectangle allScreensBounds = WindowCapture.GetScreenBounds(); string allDeviceName = ""; foreach (Screen screen in Screen.AllScreens) { string deviceName = screen.DeviceName; if (allDeviceName.Length > 0) { allDeviceName += " + "; } allDeviceName += deviceName.Substring(deviceName.Length - 1); } captureScreenItem = new ToolStripMenuItem(Language.GetString(LangKey.contextmenu_capturefullscreen_all) + " (" + allDeviceName + ")"); captureScreenItem.Click += delegate { BeginInvoke((MethodInvoker)delegate { CaptureHelper.CaptureFullscreen(false, ScreenCaptureMode.FullScreen); }); }; captureScreenMenuItem.DropDownItems.Add(captureScreenItem); foreach (Screen screen in Screen.AllScreens) { Screen screenToCapture = screen; string deviceName = screenToCapture.DeviceName; string deviceAlignment = ""; deviceName = deviceName.Substring(deviceName.Length - 1); if(screen.Bounds.Top == allScreensBounds.Top && screen.Bounds.Bottom != allScreensBounds.Bottom) { deviceAlignment += " " + Language.GetString(LangKey.contextmenu_capturefullscreen_top); } else if(screen.Bounds.Top != allScreensBounds.Top && screen.Bounds.Bottom == allScreensBounds.Bottom) { deviceAlignment += " " + Language.GetString(LangKey.contextmenu_capturefullscreen_bottom); } if(screen.Bounds.Left == allScreensBounds.Left && screen.Bounds.Right != allScreensBounds.Right) { deviceAlignment += " " + Language.GetString(LangKey.contextmenu_capturefullscreen_left); } else if(screen.Bounds.Left != allScreensBounds.Left && screen.Bounds.Right == allScreensBounds.Right) { deviceAlignment += " " + Language.GetString(LangKey.contextmenu_capturefullscreen_right); } deviceName = deviceAlignment + " ("+ deviceName +")"; captureScreenItem = new ToolStripMenuItem(deviceName); captureScreenItem.Click += delegate { BeginInvoke((MethodInvoker)delegate { CaptureHelper.CaptureRegion(false, screenToCapture.Bounds); }); }; captureScreenMenuItem.DropDownItems.Add(captureScreenItem); } } } /// /// MultiScreenDropDownOpening is called when mouse leaves the context menu /// /// /// private void MultiScreenDropDownClosing(object sender, EventArgs e) { ToolStripMenuItem captureScreenMenuItem = (ToolStripMenuItem)sender; captureScreenMenuItem.DropDownItems.Clear(); } /// /// Build a selectable list of windows when we enter the menu item /// private void CaptureWindowFromListMenuDropDownOpening(object sender, EventArgs e) { // The Capture window context menu item used to go to the following code: // captureForm.MakeCapture(CaptureMode.Window, false); // Now we check which windows are there to capture ToolStripMenuItem captureWindowFromListMenuItem = (ToolStripMenuItem)sender; AddCaptureWindowMenuItems(captureWindowFromListMenuItem, Contextmenu_capturewindowfromlist_Click); } private void CaptureWindowFromListMenuDropDownClosed(object sender, EventArgs e) { cleanupThumbnail(); } private void ShowThumbnailOnEnter(object sender, EventArgs e) { ToolStripMenuItem captureWindowItem = sender as ToolStripMenuItem; WindowDetails window = captureWindowItem.Tag as WindowDetails; if (thumbnailForm == null) { thumbnailForm = new ThumbnailForm(); } thumbnailForm.ShowThumbnail(window, captureWindowItem.GetCurrentParent().TopLevelControl); } private void HideThumbnailOnLeave(object sender, EventArgs e) { thumbnailForm.Hide(); } private void cleanupThumbnail() { if (thumbnailForm != null) { thumbnailForm.Close(); thumbnailForm = null; } } public void AddCaptureWindowMenuItems(ToolStripMenuItem menuItem, EventHandler eventHandler) { menuItem.DropDownItems.Clear(); // check if thumbnailPreview is enabled and DWM is enabled bool thumbnailPreview = conf.ThumnailPreview && DWM.isDWMEnabled(); List windows = WindowDetails.GetTopLevelWindows(); foreach(WindowDetails window in windows) { ToolStripMenuItem captureWindowItem = new ToolStripMenuItem(window.Text); captureWindowItem.Tag = window; captureWindowItem.Image = window.DisplayIcon; captureWindowItem.Click += new System.EventHandler(eventHandler); // Only show preview when enabled if (thumbnailPreview) { captureWindowItem.MouseEnter += new System.EventHandler(ShowThumbnailOnEnter); captureWindowItem.MouseLeave += new System.EventHandler(HideThumbnailOnLeave); } menuItem.DropDownItems.Add(captureWindowItem); } } void CaptureAreaToolStripMenuItemClick(object sender, EventArgs e) { BeginInvoke((MethodInvoker)delegate { CaptureHelper.CaptureRegion(false); }); } void CaptureClipboardToolStripMenuItemClick(object sender, EventArgs e) { BeginInvoke((MethodInvoker)delegate { CaptureHelper.CaptureClipboard(); }); } void OpenFileToolStripMenuItemClick(object sender, EventArgs e) { BeginInvoke((MethodInvoker)delegate { CaptureFile(); }); } void CaptureFullScreenToolStripMenuItemClick(object sender, EventArgs e) { BeginInvoke((MethodInvoker)delegate { CaptureHelper.CaptureFullscreen(false, conf.ScreenCaptureMode); }); } void Contextmenu_capturelastregionClick(object sender, EventArgs e) { BeginInvoke((MethodInvoker)delegate { CaptureHelper.CaptureLastRegion(false); }); } void Contextmenu_capturewindow_Click(object sender,EventArgs e) { BeginInvoke((MethodInvoker)delegate { CaptureHelper.CaptureWindowInteractive(false); }); } void Contextmenu_capturewindowfromlist_Click(object sender,EventArgs e) { ToolStripMenuItem clickedItem = (ToolStripMenuItem)sender; BeginInvoke((MethodInvoker)delegate { try { WindowDetails windowToCapture = (WindowDetails)clickedItem.Tag; CaptureHelper.CaptureWindow(windowToCapture); } catch (Exception exception) { LOG.Error(exception); } }); } void Contextmenu_captureie_Click(object sender, EventArgs e) { CaptureIE(); } void Contextmenu_captureiefromlist_Click(object sender, EventArgs e) { if (!conf.IECapture) { LOG.InfoFormat("IE Capture is disabled."); return; } ToolStripMenuItem clickedItem = (ToolStripMenuItem)sender; KeyValuePair tabData = (KeyValuePair)clickedItem.Tag; BeginInvoke((MethodInvoker)delegate { WindowDetails ieWindowToCapture = tabData.Key; if (ieWindowToCapture != null && (!ieWindowToCapture.Visible || ieWindowToCapture.Iconic)) { ieWindowToCapture.Restore(); } try { IECaptureHelper.ActivateIETab(ieWindowToCapture, tabData.Value); } catch (Exception exception) { LOG.Error(exception); } try { CaptureHelper.CaptureIE(false, ieWindowToCapture); } catch (Exception exception) { LOG.Error(exception); } }); } /// /// Context menu entry "Support Greenshot" /// /// /// void Contextmenu_donateClick(object sender, EventArgs e) { BeginInvoke((MethodInvoker)delegate { Process.Start("http://getgreenshot.org/support/"); }); } /// /// Context menu entry "Preferences" /// /// /// void Contextmenu_settingsClick(object sender, EventArgs e) { BeginInvoke((MethodInvoker)delegate { ShowSetting(); }); } /// /// This is called indirectly from the context menu "Preferences" /// public void ShowSetting() { if (settingsForm != null) { WindowDetails.ToForeground(settingsForm.Handle); } else { try { using (settingsForm = new SettingsForm()) { if (settingsForm.ShowDialog() == DialogResult.OK) { InitializeQuickSettingsMenu(); } } } finally { settingsForm = null; } } } /// /// The "About Greenshot" entry is clicked /// /// /// void Contextmenu_aboutClick(object sender, EventArgs e) { if (aboutForm != null) { WindowDetails.ToForeground(aboutForm.Handle); } else { try { using (aboutForm = new AboutForm()) { aboutForm.ShowDialog(this); } } finally { aboutForm = null; } } } /// /// The "Help" entry is clicked /// /// /// void Contextmenu_helpClick(object sender, EventArgs e) { HelpFileLoader.LoadHelp(); } /// /// The "Exit" entry is clicked /// /// /// void Contextmenu_exitClick(object sender, EventArgs e) { Exit(); } /// /// This needs to be called to initialize the quick settings menu entries /// private void InitializeQuickSettingsMenu() { this.contextmenu_quicksettings.DropDownItems.Clear(); if (conf.DisableQuickSettings) { return; } // Only add if the value is not fixed if (!conf.Values["CaptureMousepointer"].IsFixed) { // For the capture mousecursor option ToolStripMenuSelectListItem captureMouseItem = new ToolStripMenuSelectListItem(); captureMouseItem.Text = Language.GetString("settings_capture_mousepointer"); captureMouseItem.Checked = conf.CaptureMousepointer; captureMouseItem.CheckOnClick = true; captureMouseItem.CheckStateChanged += delegate { conf.CaptureMousepointer = captureMouseItem.Checked; }; this.contextmenu_quicksettings.DropDownItems.Add(captureMouseItem); } ToolStripMenuSelectList selectList = null; if (!conf.Values["Destinations"].IsFixed) { // screenshot destination selectList = new ToolStripMenuSelectList("destinations", true); selectList.Text = Language.GetString(LangKey.settings_destination); // Working with IDestination: foreach (IDestination destination in DestinationHelper.GetAllDestinations()) { selectList.AddItem(destination.Description, destination, conf.OutputDestinations.Contains(destination.Designation)); } selectList.CheckedChanged += new EventHandler(this.QuickSettingDestinationChanged); this.contextmenu_quicksettings.DropDownItems.Add(selectList); } if (!conf.Values["WindowCaptureMode"].IsFixed) { // Capture Modes selectList = new ToolStripMenuSelectList("capturemodes", false); selectList.Text = Language.GetString(LangKey.settings_window_capture_mode); string enumTypeName = typeof(WindowCaptureMode).Name; foreach (WindowCaptureMode captureMode in Enum.GetValues(typeof(WindowCaptureMode))) { selectList.AddItem(Language.GetString(enumTypeName + "." + captureMode.ToString()), captureMode, conf.WindowCaptureMode == captureMode); } selectList.CheckedChanged += new EventHandler(this.QuickSettingCaptureModeChanged); this.contextmenu_quicksettings.DropDownItems.Add(selectList); } // print options selectList = new ToolStripMenuSelectList("printoptions",true); selectList.Text = Language.GetString(LangKey.settings_printoptions); IniValue iniValue; foreach(string propertyName in conf.Values.Keys) { if (propertyName.StartsWith("OutputPrint")) { iniValue = conf.Values[propertyName]; if (iniValue.Attributes.LanguageKey != null && !iniValue.IsFixed) { selectList.AddItem(Language.GetString(iniValue.Attributes.LanguageKey), iniValue, (bool)iniValue.Value); } } } if (selectList.DropDownItems.Count > 0) { selectList.CheckedChanged += new EventHandler(this.QuickSettingBoolItemChanged); this.contextmenu_quicksettings.DropDownItems.Add(selectList); } // effects selectList = new ToolStripMenuSelectList("effects",true); selectList.Text = Language.GetString(LangKey.settings_visualization); iniValue = conf.Values["PlayCameraSound"]; if (!iniValue.IsFixed) { selectList.AddItem(Language.GetString(iniValue.Attributes.LanguageKey), iniValue, (bool)iniValue.Value); } iniValue = conf.Values["ShowTrayNotification"]; if (!iniValue.IsFixed) { selectList.AddItem(Language.GetString(iniValue.Attributes.LanguageKey), iniValue, (bool)iniValue.Value); } if (selectList.DropDownItems.Count > 0) { selectList.CheckedChanged += new EventHandler(this.QuickSettingBoolItemChanged); this.contextmenu_quicksettings.DropDownItems.Add(selectList); } } void QuickSettingCaptureModeChanged(object sender, EventArgs e) { ToolStripMenuSelectListItem item = ((ItemCheckedChangedEventArgs)e).Item; WindowCaptureMode windowsCaptureMode = (WindowCaptureMode)item.Data; if (item.Checked) { conf.WindowCaptureMode = windowsCaptureMode; } } void QuickSettingBoolItemChanged(object sender, EventArgs e) { ToolStripMenuSelectListItem item = ((ItemCheckedChangedEventArgs)e).Item; IniValue iniValue = item.Data as IniValue; if (iniValue != null) { iniValue.Value = item.Checked; IniConfig.Save(); } } void QuickSettingDestinationChanged(object sender, EventArgs e) { ToolStripMenuSelectListItem item = ((ItemCheckedChangedEventArgs)e).Item; IDestination selectedDestination = (IDestination)item.Data; if (item.Checked) { if (selectedDestination.Designation.Equals(PickerDestination.DESIGNATION)) { // If the item is the destination picker, remove all others conf.OutputDestinations.Clear(); } else { // If the item is not the destination picker, remove the picker conf.OutputDestinations.Remove(PickerDestination.DESIGNATION); } // Checked an item, add if the destination is not yet selected if (!conf.OutputDestinations.Contains(selectedDestination.Designation)) { conf.OutputDestinations.Add(selectedDestination.Designation); } } else { // deselected a destination, only remove if it was selected if (conf.OutputDestinations.Contains(selectedDestination.Designation)) { conf.OutputDestinations.Remove(selectedDestination.Designation); } } // Check if something was selected, if not make the picker the default if (conf.OutputDestinations == null || conf.OutputDestinations.Count == 0) { conf.OutputDestinations.Add(PickerDestination.DESIGNATION); } IniConfig.Save(); // Rebuild the quick settings menu with the new settings. InitializeQuickSettingsMenu(); } #endregion private static void CurrentDomain_UnhandledException(object sender, UnhandledExceptionEventArgs e) { Exception exceptionToLog = e.ExceptionObject as Exception; string exceptionText = EnvironmentInfo.BuildReport(exceptionToLog); LOG.Error(EnvironmentInfo.ExceptionToString(exceptionToLog)); new BugReportForm(exceptionText).ShowDialog(); } private static void Application_ThreadException(object sender, ThreadExceptionEventArgs e) { Exception exceptionToLog = e.Exception; string exceptionText = EnvironmentInfo.BuildReport(exceptionToLog); LOG.Error(EnvironmentInfo.ExceptionToString(exceptionToLog)); new BugReportForm(exceptionText).ShowDialog(); } /// /// Handle the notify icon click /// /// /// private void NotifyIconClick(object sender, MouseEventArgs e) { // The right button will automatically be handled with the context menu, here we only check the left. if (e.Button == MouseButtons.Left) { switch (conf.LeftClickAction) { case LeftClickActions.OPEN_LAST_IN_EXPLORER: string path = null; string configPath = FilenameHelper.FillVariables(conf.OutputFilePath, false); string lastFilePath = Path.GetDirectoryName(conf.OutputFileAsFullpath); if (Directory.Exists(lastFilePath)) { path = lastFilePath; } else if (Directory.Exists(configPath)) { path = configPath; } try { System.Diagnostics.Process.Start(path); } catch (Exception ex) { // Make sure we show what we tried to open in the exception ex.Data.Add("path", path); throw ex; } break; case LeftClickActions.OPEN_LAST_IN_EDITOR: if (File.Exists(conf.OutputFileAsFullpath)) { CaptureHelper.CaptureFile(conf.OutputFileAsFullpath, DestinationHelper.GetDestination(EditorDestination.DESIGNATION)); } break; case LeftClickActions.CONTEXT_MENU: MethodInfo oMethodInfo = typeof(NotifyIcon).GetMethod("ShowContextMenu", BindingFlags.Instance | BindingFlags.NonPublic); oMethodInfo.Invoke(notifyIcon, null); break; default: // Do nothing break; } } } /// /// The Contextmenu_OpenRecent currently opens the last know save location /// private void Contextmenu_OpenRecent(object sender, EventArgs eventArgs) { string path; string configPath = FilenameHelper.FillVariables(conf.OutputFilePath, false); string lastFilePath = Path.GetDirectoryName(conf.OutputFileAsFullpath); if (Directory.Exists(lastFilePath)) { path = lastFilePath; } else if (Directory.Exists(configPath)) { path = configPath; } else { // What do I open when nothing can be found? Right, nothing... return; } LOG.Debug("DoubleClick was called! Starting: " + path); try { System.Diagnostics.Process.Start(path); } catch (Exception e) { // Make sure we show what we tried to open in the exception e.Data.Add("path", path); throw e; } } /// /// Shutdown / cleanup /// public void Exit() { LOG.Info("Exit: " + EnvironmentInfo.EnvironmentToString(false)); // Close all open forms (except this), use a separate List to make sure we don't get a "InvalidOperationException: Collection was modified" List
formsToClose = new List(); foreach(Form form in Application.OpenForms) { if (form.Handle != this.Handle && !form.GetType().Equals(typeof(Greenshot.ImageEditorForm))) { formsToClose.Add(form); } } foreach(Form form in formsToClose) { try { LOG.InfoFormat("Closing form: {0}", form.Name); this.Invoke((MethodInvoker) delegate { form.Close(); }); } catch (Exception e) { LOG.Error("Error closing form!", e); } } // Make sure hotkeys are disabled try { HotkeyControl.UnregisterHotkeys(); } catch (Exception e) { LOG.Error("Error unregistering hotkeys!", e); } // Now the sound isn't needed anymore try { SoundHelper.Deinitialize(); } catch (Exception e) { LOG.Error("Error deinitializing sound!", e); } // Inform all registed plugins try { PluginHelper.Instance.Shutdown(); } catch (Exception e) { LOG.Error("Error shutting down plugins!", e); } // Gracefull shutdown try { Application.DoEvents(); Application.Exit(); } catch (Exception e) { LOG.Error("Error closing application!", e); } ImageOutput.RemoveTmpFiles(); // Store any open configuration changes try { IniConfig.Save(); } catch (Exception e) { LOG.Error("Error storing configuration!", e); } // Remove the application mutex FreeMutex(); // make the icon invisible otherwise it stays even after exit!! if (notifyIcon != null) { notifyIcon.Visible = false; notifyIcon.Dispose(); notifyIcon = null; } } /// /// Do work in the background /// /// /// private void BackgroundWorkerTimerTick(object sender, EventArgs e) { if (conf.MinimizeWorkingSetSize) { LOG.Info("Calling EmptyWorkingSet"); PsAPI.EmptyWorkingSet(Process.GetCurrentProcess().Handle); } if (UpdateHelper.IsUpdateCheckNeeded()) { LOG.Debug("BackgroundWorkerTimerTick checking for update"); // Start update check in the background Thread backgroundTask = new Thread (new ThreadStart(UpdateHelper.CheckAndAskForUpdate)); backgroundTask.Name = "Update check"; backgroundTask.IsBackground = true; backgroundTask.Start(); } } } }