/*
* 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.Forms;
using Greenshot.Help;
using Greenshot.Helpers;
using GreenshotPlugin.UnmanagedHelpers;
using GreenshotPlugin.Controls;
using GreenshotPlugin.Core;
using Greenshot.Destinations;
using Greenshot.Drawing;
using log4net;
using Timer = System.Timers.Timer;
using System.Threading.Tasks;
using GreenshotPlugin.IniFile;
using GreenshotPlugin.Interfaces;
using GreenshotPlugin.Interfaces.Plugin;
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;
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 MainForm(CopyDataTransport dataTransport) {
var uiContext = TaskScheduler.FromCurrentSynchronizationContext();
SimpleServiceProvider.Current.AddService(uiContext);
DpiChanged += (e,o) => ApplyDpiScaling();
// The most important form is this
SimpleServiceProvider.Current.AddService