/*
* Greenshot - a free and open source screenshot tool
* Copyright (C) 2007-2020 Thomas Braun, Jens Klingen, Robin Krom
*
* For more information see: http://getgreenshot.org/
* The Greenshot project is hosted on GitHub https://github.com/greenshot/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.ComponentModel;
using System.Diagnostics;
using System.Drawing;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading;
using System.Windows.Forms;
using System.Windows.Forms.Integration;
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;
using Greenshot.Drawing;
using log4net;
using Timer = System.Timers.Timer;
using System.Threading.Tasks;
namespace Greenshot {
///
/// Description of MainForm.
///
public partial class MainForm : BaseForm {
private static ILog LOG;
private static ResourceMutex _applicationMutex;
private static CoreConfiguration _conf;
public static string LogFileLocation;
public static void Start(string[] arguments) {
var filesToOpen = new List();
// Set the Thread name, is better than "1"
Thread.CurrentThread.Name = Application.ProductName;
// Init Log4NET
LogFileLocation = LogHelper.InitializeLog4Net();
// Get logger
LOG = LogManager.GetLogger(typeof(MainForm));
Application.ThreadException += Application_ThreadException;
AppDomain.CurrentDomain.UnhandledException += CurrentDomain_UnhandledException;
TaskScheduler.UnobservedTaskException += Task_UnhandledException;
// Initialize the IniConfig
IniConfig.Init();
// Log the startup
LOG.Info("Starting: " + EnvironmentInfo.EnvironmentToString(false));
// Read configuration
_conf = IniConfig.GetIniSection();
try {
// Fix for Bug 2495900, Multi-user Environment
// check whether there's an local instance running already
_applicationMutex = ResourceMutex.Create("F48E86D3-E34C-4DB7-8F8F-9A0EA55F0D08", "Greenshot", false);
var isAlreadyRunning = !_applicationMutex.IsLocked;
if (arguments.Length > 0 && LOG.IsDebugEnabled) {
StringBuilder argumentString = new StringBuilder();
foreach (string argument in arguments)
{
argumentString.Append("[").Append(argument).Append("] ");
}
LOG.Debug("Greenshot arguments: " + argumentString);
}
for(int argumentNr = 0; argumentNr < arguments.Length; argumentNr++) {
string argument = arguments[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();
}
var 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/inidirectory [directory]");
helpOutput.AppendLine("\t\tSet the directory where the greenshot.ini should be stored & read.");
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 = arguments[++argumentNr];
IniConfig.Save();
continue;
}
// Setting the INI-directory
if (argument.ToLower().Equals("/inidirectory")) {
IniConfig.IniDirectory = arguments[++argumentNr];
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;
int currentProcessId;
using (Process currentProcess = Process.GetCurrentProcess()) {
currentProcessId = currentProcess.Id;
}
foreach (Process greenshotProcess in Process.GetProcessesByName("greenshot")) {
try {
instanceInfo.Append(index++ + ": ").AppendLine(Kernel32.GetProcessPath(greenshotProcess.Id));
if (currentProcessId == greenshotProcess.Id) {
matchedThisProcess = true;
}
} catch (Exception ex) {
LOG.Debug(ex);
}
greenshotProcess.Dispose();
}
if (!matchedThisProcess)
{
using Process currentProcess = Process.GetCurrentProcess();
instanceInfo.Append(index + ": ").AppendLine(Kernel32.GetProcessPath(currentProcess.Id));
}
// A dirty fix to make sure the message box is visible as a Greenshot window on the taskbar
using Form dummyForm = new Form
{
Icon = GreenshotResources.getGreenshotIcon(),
ShowInTaskbar = true,
FormBorderStyle = FormBorderStyle.None,
Location = new Point(int.MinValue, int.MinValue)
};
dummyForm.Load += delegate { dummyForm.Size = Size.Empty; };
dummyForm.Show();
MessageBox.Show(dummyForm, Language.GetString(LangKey.error_multipleinstances) + "\r\n" + instanceInfo, Language.GetString(LangKey.error), MessageBoxButtons.OK, MessageBoxIcon.Exclamation);
}
FreeMutex();
Application.Exit();
return;
}
// Make sure we can use forms
WindowsFormsHost.EnableWindowsFormsInterop();
// BUG-1809: Add message filter, to filter out all the InputLangChanged messages which go to a target control with a handle > 32 bit.
Application.AddMessageFilter(new WmInputLangChangeRequestFilter());
// 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);
}
// Should fix BUG-1633
Application.DoEvents();
_instance = new MainForm(transport);
Application.Run();
} catch(Exception ex) {
LOG.Error("Exception in startup.", ex);
Application_ThreadException(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.Dispose();
_applicationMutex = null;
} catch (Exception ex) {
LOG.Error("Error releasing Mutex!", ex);
}
}
}
private static MainForm _instance;
public static MainForm Instance => _instance;
private readonly CopyData _copyData;
// Thumbnail preview
private ThumbnailForm _thumbnailForm;
// Make sure we have only one settings form
private SettingsForm _settingsForm;
// Make sure we have only one about form
private AboutForm _aboutForm;
// Timer for the double click test
private readonly Timer _doubleClickTimer = new Timer();
public NotifyIcon NotifyIcon => notifyIcon;
public MainForm(CopyDataTransport dataTransport) {
_instance = this;
// Factory for surface objects
ImageHelper.SurfaceFactory = () => new Surface();
//
// The InitializeComponent() call is required for Windows Forms designer support.
//
try {
InitializeComponent();
} catch (ArgumentException ex) {
// Added for Bug #1420, this doesn't solve the issue but maybe the user can do something with it.
ex.Data.Add("more information here", "http://support.microsoft.com/kb/943140");
throw;
}
notifyIcon.Icon = GreenshotResources.getGreenshotIcon();
// Disable access to the settings, for feature #3521446
contextmenu_settings.Visible = !_conf.DisableSettings;
// Make sure all hotkeys pass this window!
HotkeyControl.RegisterHotkeyHwnd(Handle);
RegisterHotkeys();
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(EditorDestination.DESIGNATION);
}
if (_conf.DisableQuickSettings) {
contextmenu_quicksettings.Visible = false;
} else {
// Do after all plugins & finding the destination, otherwise they are missing!
InitializeQuickSettingsMenu();
}
SoundHelper.Initialize();
coreConfiguration.PropertyChanged += OnIconSizeChanged;
OnIconSizeChanged(this, new PropertyChangedEventArgs("IconSize"));
// 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(Handle);
// Create a new instance of the class: copyData = new CopyData();
_copyData = new CopyData();
// Assign the handle:
_copyData.AssignHandle(Handle);
// Create the channel to send on:
_copyData.Channels.Add("Greenshot");
// Hook up received event:
_copyData.CopyDataReceived += CopyDataDataReceived;
if (dataTransport != null) {
HandleDataTransport(dataTransport);
}
// Make Greenshot use less memory after startup
if (_conf.MinimizeWorkingSetSize) {
PsAPI.EmptyWorkingSet();
}
}
///
/// DataReceivedEventHandler
///
///
///
private void CopyDataDataReceived(object sender, CopyDataReceivedEventArgs copyDataReceivedEventArgs) {
// Cast the data to the type of object we sent:
var dataTransport = (CopyDataTransport)copyDataReceivedEventArgs.Data;
HandleDataTransport(dataTransport);
}
private void BalloonTipClicked(object sender, EventArgs e) {
try {
ShowSetting();
} finally {
BalloonTipClosed(sender, e);
}
}
private void BalloonTipClosed(object sender, EventArgs e) {
notifyIcon.BalloonTipClicked -= BalloonTipClicked;
notifyIcon.BalloonTipClosed -= BalloonTipClosed;
}
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 {
notifyIcon.BalloonTipClicked += BalloonTipClicked;
notifyIcon.BalloonTipClosed += BalloonTipClosed;
notifyIcon.ShowBalloonTip(2000, "Greenshot", Language.GetFormattedString(LangKey.tooltip_firststart, HotkeyControl.GetLocalizedHotkeyStringFromString(_conf.RegionHotkey)), ToolTipIcon.Info);
} catch (Exception ex) {
LOG.Warn("Exception while showing first launch: ", ex);
}
break;
case CommandEnum.ReloadConfig:
LOG.Info("Reload requested");
try {
IniConfig.Reload();
Invoke((MethodInvoker) delegate {
// Even update language when needed
UpdateUi();
// Update the hotkey
// Make sure the current hotkeys are disabled
HotkeyControl.UnregisterHotkeys();
RegisterHotkeys();
});
} catch (Exception ex) {
LOG.Warn("Exception while reloading configuration: ", ex);
}
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;
}
}
}
///
/// Main context menu
///
public ContextMenuStrip MainMenu => contextMenu;
protected override void WndProc(ref Message m) {
if (HotkeyControl.HandleMessages(ref m)) {
return;
}
// BUG-1809 prevention, filter the InputLangChange messages
if (WmInputLangChangeRequestFilter.PreFilterMessageExternal(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;
}
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, bool ignoreFailedRegistration) {
IniValue hotkeyValue = _conf.Values[configurationKey];
try {
bool success = RegisterHotkey(failedKeys, functionName, hotkeyValue.Value.ToString(), handler);
if (!success && ignoreFailedRegistration) {
LOG.DebugFormat("Ignoring failed hotkey registration for {0}, with value '{1}', resetting to 'None'.", functionName, hotkeyValue);
_conf.Values[configurationKey].Value = Keys.None.ToString();
_conf.IsDirty = true;
}
return success;
} catch (Exception ex) {
LOG.Warn(ex);
LOG.WarnFormat("Restoring default 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);
}
}
///
/// Fix icon reference
///
///
///
private void OnIconSizeChanged(object sender, PropertyChangedEventArgs e) {
if (e.PropertyName == "IconSize") {
contextMenu.ImageScalingSize = coreConfiguration.ScaledIconSize;
string ieExePath = PluginUtils.GetExePath("iexplore.exe");
if (!string.IsNullOrEmpty(ieExePath)) {
contextmenu_captureie.Image = PluginUtils.GetCachedExeIcon(ieExePath, 0);
}
}
}
///
/// Registers all hotkeys as configured, displaying a dialog in case of hotkey conflicts with other tools.
///
/// Whether the hotkeys could be registered to the users content. This also applies if conflicts arise and the user decides to ignore these (i.e. not to register the conflicting hotkey).
public static bool RegisterHotkeys() {
return RegisterHotkeys(false);
}
///
/// Registers all hotkeys as configured, displaying a dialog in case of hotkey conflicts with other tools.
///
/// if true, a failed hotkey registration will not be reported to the user - the hotkey will simply not be registered
/// Whether the hotkeys could be registered to the users content. This also applies if conflicts arise and the user decides to ignore these (i.e. not to register the conflicting hotkey).
private static bool RegisterHotkeys(bool ignoreFailedRegistration) {
if (_instance == null) {
return false;
}
bool success = true;
StringBuilder failedKeys = new StringBuilder();
if (!RegisterWrapper(failedKeys, "CaptureRegion", "RegionHotkey", _instance.CaptureRegion, ignoreFailedRegistration)) {
success = false;
}
if (!RegisterWrapper(failedKeys, "CaptureWindow", "WindowHotkey", _instance.CaptureWindow, ignoreFailedRegistration)) {
success = false;
}
if (!RegisterWrapper(failedKeys, "CaptureFullScreen", "FullscreenHotkey", _instance.CaptureFullScreen, ignoreFailedRegistration)) {
success = false;
}
if (!RegisterWrapper(failedKeys, "CaptureLastRegion", "LastregionHotkey", _instance.CaptureLastRegion, ignoreFailedRegistration)) {
success = false;
}
if (_conf.IECapture) {
if (!RegisterWrapper(failedKeys, "CaptureIE", "IEHotkey", _instance.CaptureIE, ignoreFailedRegistration)) {
success = false;
}
}
if (!success) {
if (!ignoreFailedRegistration) {
success = HandleFailedHotkeyRegistration(failedKeys.ToString());
} else {
// if failures have been ignored, the config has probably been updated
if (_conf.IsDirty) {
IniConfig.Save();
}
}
}
return success || ignoreFailedRegistration;
}
///
/// Check if OneDrive is blocking hotkeys
///
/// true if onedrive has hotkeys turned on
private static bool IsOneDriveBlockingHotkey()
{
if (!WindowsVersion.IsWindows10OrLater)
{
return false;
}
var localAppData = Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData);
var oneDriveSettingsPath = Path.Combine(localAppData, @"Microsoft\OneDrive\settings\Personal");
if (!Directory.Exists(oneDriveSettingsPath))
{
return false;
}
var oneDriveSettingsFile = Directory.GetFiles(oneDriveSettingsPath, "*_screenshot.dat").FirstOrDefault();
if (!File.Exists(oneDriveSettingsFile))
{
return false;
}
var screenshotSetting = File.ReadAllLines(oneDriveSettingsFile).Skip(1).Take(1).First();
return "2".Equals(screenshotSetting);
}
///
/// Displays a dialog for the user to choose how to handle hotkey registration failures:
/// retry (allowing to shut down the conflicting application before),
/// ignore (not registering the conflicting hotkey and resetting the respective config to "None", i.e. not trying to register it again on next startup)
/// abort (do nothing about it)
///
/// comma separated list of the hotkeys that could not be registered, for display in dialog text
///
private static bool HandleFailedHotkeyRegistration(string failedKeys) {
bool success = false;
var warningTitle = Language.GetString(LangKey.warning);
var message = string.Format(Language.GetString(LangKey.warning_hotkeys), failedKeys, IsOneDriveBlockingHotkey() ? " (OneDrive)": "");
DialogResult dr = MessageBox.Show(Instance, message, warningTitle, MessageBoxButtons.AbortRetryIgnore, MessageBoxIcon.Exclamation);
if (dr == DialogResult.Retry) {
LOG.DebugFormat("Re-trying to register hotkeys");
HotkeyControl.UnregisterHotkeys();
success = RegisterHotkeys(false);
} else if (dr == DialogResult.Ignore) {
LOG.DebugFormat("Ignoring failed hotkey registration");
HotkeyControl.UnregisterHotkeys();
success = RegisterHotkeys(true);
}
return success;
}
public void UpdateUi() {
// As the form is never loaded, call ApplyLanguage ourselves
ApplyLanguage();
// Show hotkeys in Contextmenu
contextmenu_capturearea.ShortcutKeyDisplayString = HotkeyControl.GetLocalizedHotkeyStringFromString(_conf.RegionHotkey);
contextmenu_capturelastregion.ShortcutKeyDisplayString = HotkeyControl.GetLocalizedHotkeyStringFromString(_conf.LastregionHotkey);
contextmenu_capturewindow.ShortcutKeyDisplayString = HotkeyControl.GetLocalizedHotkeyStringFromString(_conf.WindowHotkey);
contextmenu_capturefullscreen.ShortcutKeyDisplayString = HotkeyControl.GetLocalizedHotkeyStringFromString(_conf.FullscreenHotkey);
contextmenu_captureie.ShortcutKeyDisplayString = HotkeyControl.GetLocalizedHotkeyStringFromString(_conf.IEHotkey);
}
private void MainFormFormClosing(object sender, FormClosingEventArgs e) {
LOG.DebugFormat("Mainform closing, reason: {0}", e.CloseReason);
_instance = null;
Exit();
}
private void MainFormActivated(object sender, EventArgs e) {
Hide();
ShowInTaskbar = false;
}
private void CaptureRegion() {
CaptureHelper.CaptureRegion(true);
}
private void CaptureFile() {
var openFileDialog = new 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);
}
}
}
private void CaptureFullScreen() {
CaptureHelper.CaptureFullscreen(true, _conf.ScreenCaptureMode);
}
private void CaptureLastRegion() {
CaptureHelper.CaptureLastRegion(true);
}
private void CaptureIE() {
if (_conf.IECapture) {
CaptureHelper.CaptureIe(true, null);
}
}
private void CaptureWindow() {
if (_conf.CaptureWindowsInteractive) {
CaptureHelper.CaptureWindowInteractive(true);
} else {
CaptureHelper.CaptureWindow(true);
}
}
private void ContextMenuOpening(object sender, CancelEventArgs e) {
contextmenu_captureclipboard.Enabled = ClipboardHelper.ContainsImage();
contextmenu_capturelastregion.Enabled = coreConfiguration.LastCapturedRegion != Rectangle.Empty;
// IE context menu code
try {
if (_conf.IECapture && IeCaptureHelper.IsIeRunning()) {
contextmenu_captureie.Enabled = true;
contextmenu_captureiefromlist.Enabled = true;
} else {
contextmenu_captureie.Enabled = false;
contextmenu_captureiefromlist.Enabled = false;
}
} catch (Exception ex) {
LOG.WarnFormat("Problem accessing IE information: {0}", ex.Message);
}
// Multi-Screen captures
contextmenu_capturefullscreen.Click -= CaptureFullScreenToolStripMenuItemClick;
contextmenu_capturefullscreen.DropDownOpening -= MultiScreenDropDownOpening;
contextmenu_capturefullscreen.DropDownClosed -= MultiScreenDropDownClosing;
if (Screen.AllScreens.Length > 1) {
contextmenu_capturefullscreen.DropDownOpening += MultiScreenDropDownOpening;
contextmenu_capturefullscreen.DropDownClosed += MultiScreenDropDownClosing;
} else {
contextmenu_capturefullscreen.Click += CaptureFullScreenToolStripMenuItemClick;
}
var now = DateTime.Now;
if ((now.Month == 12 && now.Day > 19 && now.Day < 27) || // christmas
(now.Month == 3 && now.Day > 13 && now.Day < 21)) { // birthday
var resources = new ComponentResourceManager(typeof(MainForm));
contextmenu_donate.Image = (Image)resources.GetObject("contextmenu_present.Image");
}
}
private void ContextMenuClosing(object sender, EventArgs e) {
contextmenu_captureiefromlist.DropDownItems.Clear();
contextmenu_capturewindowfromlist.DropDownItems.Clear();
CleanupThumbnail();
}
///
/// Build a selectable list of IE tabs when we enter the menu item
///
private void CaptureIeMenuDropDownOpening(object sender, EventArgs e) {
if (!_conf.IECapture) {
return;
}
try {
List> tabs = IeCaptureHelper.GetBrowserTabs();
contextmenu_captureiefromlist.DropDownItems.Clear();
if (tabs.Count > 0) {
contextmenu_captureie.Enabled = true;
contextmenu_captureiefromlist.Enabled = true;
Dictionary counter = new Dictionary();
foreach(KeyValuePair tabData in tabs) {
string title = tabData.Value;
if (title == null) {
continue;
}
if (title.Length > _conf.MaxMenuItemLength) {
title = title.Substring(0, Math.Min(title.Length, _conf.MaxMenuItemLength));
}
var captureIeTabItem = contextmenu_captureiefromlist.DropDownItems.Add(title);
int index = counter.ContainsKey(tabData.Key) ? counter[tabData.Key] : 0;
captureIeTabItem.Image = tabData.Key.DisplayIcon;
captureIeTabItem.Tag = new KeyValuePair(tabData.Key, index++);
captureIeTabItem.Click += Contextmenu_captureiefromlist_Click;
contextmenu_captureiefromlist.DropDownItems.Add(captureIeTabItem);
if (counter.ContainsKey(tabData.Key)) {
counter[tabData.Key] = index;
} else {
counter.Add(tabData.Key, index);
}
}
} else {
contextmenu_captureie.Enabled = false;
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) {
Rectangle allScreensBounds = WindowCapture.GetScreenBounds();
var captureScreenItem = new ToolStripMenuItem(Language.GetString(LangKey.contextmenu_capturefullscreen_all));
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 deviceAlignment = "";
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);
}
captureScreenItem = new ToolStripMenuItem(deviceAlignment);
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) {
if (!(sender is ToolStripMenuItem captureWindowItem)) return;
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)
{
return;
}
_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();
foreach(WindowDetails window in WindowDetails.GetTopLevelWindows()) {
string title = window.Text;
if (title != null) {
if (title.Length > _conf.MaxMenuItemLength) {
title = title.Substring(0, Math.Min(title.Length, _conf.MaxMenuItemLength));
}
ToolStripItem captureWindowItem = menuItem.DropDownItems.Add(title);
captureWindowItem.Tag = window;
captureWindowItem.Image = window.DisplayIcon;
captureWindowItem.Click += eventHandler;
// Only show preview when enabled
if (thumbnailPreview) {
captureWindowItem.MouseEnter += ShowThumbnailOnEnter;
captureWindowItem.MouseLeave += HideThumbnailOnLeave;
}
}
}
}
private void CaptureAreaToolStripMenuItemClick(object sender, EventArgs e) {
BeginInvoke((MethodInvoker)delegate {
CaptureHelper.CaptureRegion(false);
});
}
private void CaptureClipboardToolStripMenuItemClick(object sender, EventArgs e) {
BeginInvoke((MethodInvoker)CaptureHelper.CaptureClipboard);
}
private void OpenFileToolStripMenuItemClick(object sender, EventArgs e) {
BeginInvoke((MethodInvoker)CaptureFile);
}
private void CaptureFullScreenToolStripMenuItemClick(object sender, EventArgs e) {
BeginInvoke((MethodInvoker)delegate {
CaptureHelper.CaptureFullscreen(false, _conf.ScreenCaptureMode);
});
}
private void Contextmenu_capturelastregionClick(object sender, EventArgs e) {
BeginInvoke((MethodInvoker)delegate {
CaptureHelper.CaptureLastRegion(false);
});
}
private void Contextmenu_capturewindow_Click(object sender,EventArgs e) {
BeginInvoke((MethodInvoker)delegate {
CaptureHelper.CaptureWindowInteractive(false);
});
}
private 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);
}
});
}
private void Contextmenu_captureie_Click(object sender, EventArgs e) {
CaptureIE();
}
private 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"
///
///
///
private void Contextmenu_donateClick(object sender, EventArgs e) {
BeginInvoke((MethodInvoker)delegate {
Process.Start("http://getgreenshot.org/support/?version=" + Assembly.GetEntryAssembly().GetName().Version);
});
}
///
/// Context menu entry "Preferences"
///
///
///
private void Contextmenu_settingsClick(object sender, EventArgs e) {
BeginInvoke((MethodInvoker)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
///
///
///
private void Contextmenu_aboutClick(object sender, EventArgs e) {
ShowAbout();
}
public void ShowAbout() {
if (_aboutForm != null) {
WindowDetails.ToForeground(_aboutForm.Handle);
} else {
try {
using (_aboutForm = new AboutForm()) {
_aboutForm.ShowDialog(this);
}
} finally {
_aboutForm = null;
}
}
}
///
/// The "Help" entry is clicked
///
///
///
private void Contextmenu_helpClick(object sender, EventArgs e) {
HelpFileLoader.LoadHelp();
}
///
/// The "Exit" entry is clicked
///
///
///
private void Contextmenu_exitClick(object sender, EventArgs e) {
Exit();
}
private void CheckStateChangedHandler(object sender, EventArgs e) {
if (sender is ToolStripMenuSelectListItem captureMouseItem) {
_conf.CaptureMousepointer = captureMouseItem.Checked;
}
}
///
/// This needs to be called to initialize the quick settings menu entries
///
private void InitializeQuickSettingsMenu() {
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
{
Text = Language.GetString("settings_capture_mousepointer"),
Checked = _conf.CaptureMousepointer,
CheckOnClick = true
};
captureMouseItem.CheckStateChanged += CheckStateChangedHandler;
contextmenu_quicksettings.DropDownItems.Add(captureMouseItem);
}
ToolStripMenuSelectList selectList;
if (!_conf.Values["Destinations"].IsFixed) {
// screenshot destination
selectList = new ToolStripMenuSelectList("destinations", true)
{
Text = Language.GetString(LangKey.settings_destination)
};
// Working with IDestination:
foreach (var destination in DestinationHelper.GetAllDestinations()) {
selectList.AddItem(destination.Description, destination, _conf.OutputDestinations.Contains(destination.Designation));
}
selectList.CheckedChanged += QuickSettingDestinationChanged;
contextmenu_quicksettings.DropDownItems.Add(selectList);
}
if (!_conf.Values["WindowCaptureMode"].IsFixed) {
// Capture Modes
selectList = new ToolStripMenuSelectList("capturemodes", false)
{
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), captureMode, _conf.WindowCaptureMode == captureMode);
}
selectList.CheckedChanged += QuickSettingCaptureModeChanged;
contextmenu_quicksettings.DropDownItems.Add(selectList);
}
// print options
selectList = new ToolStripMenuSelectList("printoptions", true)
{
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 += QuickSettingBoolItemChanged;
contextmenu_quicksettings.DropDownItems.Add(selectList);
}
// effects
selectList = new ToolStripMenuSelectList("effects", true)
{
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 += QuickSettingBoolItemChanged;
contextmenu_quicksettings.DropDownItems.Add(selectList);
}
}
private void QuickSettingCaptureModeChanged(object sender, EventArgs e) {
ToolStripMenuSelectListItem item = ((ItemCheckedChangedEventArgs)e).Item;
WindowCaptureMode windowsCaptureMode = (WindowCaptureMode)item.Data;
if (item.Checked) {
_conf.WindowCaptureMode = windowsCaptureMode;
}
}
private void QuickSettingBoolItemChanged(object sender, EventArgs e) {
ToolStripMenuSelectListItem item = ((ItemCheckedChangedEventArgs)e).Item;
if (item.Data is IniValue iniValue) {
iniValue.Value = item.Checked;
IniConfig.Save();
}
}
private 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();
}
private static void Task_UnhandledException(object sender, UnobservedTaskExceptionEventArgs args)
{
try {
Exception exceptionToLog = args.Exception;
string exceptionText = EnvironmentInfo.BuildReport(exceptionToLog);
LOG.Error("Exception caught in the UnobservedTaskException handler.");
LOG.Error(exceptionText);
new BugReportForm(exceptionText).ShowDialog();
}
finally {
args.SetObserved();
}
}
private static void CurrentDomain_UnhandledException(object sender, UnhandledExceptionEventArgs e) {
Exception exceptionToLog = e.ExceptionObject as Exception;
string exceptionText = EnvironmentInfo.BuildReport(exceptionToLog);
LOG.Error("Exception caught in the UnhandledException handler.");
LOG.Error(exceptionText);
if (exceptionText != null && exceptionText.Contains("InputLanguageChangedEventArgs"))
{
// Ignore for BUG-1809
return;
}
new BugReportForm(exceptionText).ShowDialog();
}
private static void Application_ThreadException(object sender, ThreadExceptionEventArgs e) {
Exception exceptionToLog = e.Exception;
string exceptionText = EnvironmentInfo.BuildReport(exceptionToLog);
LOG.Error("Exception caught in the ThreadException handler.");
LOG.Error(exceptionText);
if (exceptionText != null && exceptionText.Contains("InputLanguageChangedEventArgs"))
{
// Ignore for BUG-1809
return;
}
new BugReportForm(exceptionText).ShowDialog();
}
///
/// Handle the notify icon click
///
///
///
private void NotifyIconClickTest(object sender, MouseEventArgs e) {
if (e.Button != MouseButtons.Left) {
return;
}
// The right button will automatically be handled with the context menu, here we only check the left.
if (_conf.DoubleClickAction == ClickActions.DO_NOTHING) {
// As there isn't a double-click we can start the Left click
NotifyIconClick(_conf.LeftClickAction);
// ready with the test
return;
}
// If the timer is enabled we are waiting for a double click...
if (_doubleClickTimer.Enabled) {
// User clicked a second time before the timer tick: Double-click!
_doubleClickTimer.Elapsed -= NotifyIconSingleClickTest;
_doubleClickTimer.Stop();
NotifyIconClick(_conf.DoubleClickAction);
} else {
// User clicked without a timer, set the timer and if it ticks it was a single click
// Create timer, if it ticks before the NotifyIconClickTest is called again we have a single click
_doubleClickTimer.Elapsed += NotifyIconSingleClickTest;
_doubleClickTimer.Interval = SystemInformation.DoubleClickTime;
_doubleClickTimer.Start();
}
}
///
/// Called by the doubleClickTimer, this means a single click was used on the tray icon
///
///
///
private void NotifyIconSingleClickTest(object sender, EventArgs e) {
_doubleClickTimer.Elapsed -= NotifyIconSingleClickTest;
_doubleClickTimer.Stop();
BeginInvoke((MethodInvoker)delegate {
NotifyIconClick(_conf.LeftClickAction);
});
}
///
/// Handle the notify icon click
///
private void NotifyIconClick(ClickActions clickAction) {
switch (clickAction) {
case ClickActions.OPEN_LAST_IN_EXPLORER:
Contextmenu_OpenRecent(this, null);
break;
case ClickActions.OPEN_LAST_IN_EDITOR:
_conf.ValidateAndCorrectOutputFileAsFullpath();
if (File.Exists(_conf.OutputFileAsFullpath)) {
CaptureHelper.CaptureFile(_conf.OutputFileAsFullpath, DestinationHelper.GetDestination(EditorDestination.DESIGNATION));
}
break;
case ClickActions.OPEN_SETTINGS:
ShowSetting();
break;
case ClickActions.SHOW_CONTEXT_MENU:
MethodInfo oMethodInfo = typeof(NotifyIcon).GetMethod("ShowContextMenu", BindingFlags.Instance | BindingFlags.NonPublic);
oMethodInfo.Invoke(notifyIcon, null);
break;
}
}
///
/// The Contextmenu_OpenRecent currently opens the last know save location
///
private void Contextmenu_OpenRecent(object sender, EventArgs eventArgs)
{
_conf.ValidateAndCorrectOutputFilePath();
_conf.ValidateAndCorrectOutputFileAsFullpath();
string path = _conf.OutputFileAsFullpath;
if (!File.Exists(path))
{
path = FilenameHelper.FillVariables(_conf.OutputFilePath, false);
// Fix for #1470, problems with a drive which is no longer available
try
{
string lastFilePath = Path.GetDirectoryName(_conf.OutputFileAsFullpath);
if (lastFilePath != null && Directory.Exists(lastFilePath))
{
path = lastFilePath;
}
else if (!Directory.Exists(path))
{
// What do I open when nothing can be found? Right, nothing...
return;
}
}
catch (Exception ex)
{
LOG.Warn("Couldn't open the path to the last exported file, taking default.", ex);
}
}
try
{
ExplorerHelper.OpenInExplorer(path);
}
catch (Exception ex)
{
// Make sure we show what we tried to open in the exception
ex.Data.Add("path", path);
LOG.Warn("Couldn't open the path to the last exported file", ex);
// No reason to create a bug-form, we just display the error.
MessageBox.Show(this, ex.Message, "Opening " + path, MessageBoxButtons.OK, MessageBoxIcon.Error);
}
}
///
/// 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