OSDN Git Service

Merge remote-tracking branch 'naminodarie/SortedRetweet'
[opentween/open-tween.git] / OpenTween / ApplicationEvents.cs
1 // OpenTween - Client of Twitter
2 // Copyright (c) 2007-2012 kiri_feather (@kiri_feather) <kiri.feather@gmail.com>
3 //           (c) 2008-2012 Moz (@syo68k)
4 //           (c) 2008-2012 takeshik (@takeshik) <http://www.takeshik.org/>
5 //           (c) 2010-2012 anis774 (@anis774) <http://d.hatena.ne.jp/anis774/>
6 //           (c) 2010-2012 fantasticswallow (@f_swallow) <http://twitter.com/f_swallow>
7 //           (c) 2012      Egtra (@egtra) <http://dev.activebasic.com/egtra/>
8 //           (c) 2012      kim_upsilon (@kim_upsilon) <https://upsilo.net/~upsilon/>
9 // All rights reserved.
10 // 
11 // This file is part of OpenTween.
12 // 
13 // This program is free software; you can redistribute it and/or modify it
14 // under the terms of the GNU General public License as published by the Free
15 // Software Foundation; either version 3 of the License, or (at your option)
16 // any later version.
17 // 
18 // This program is distributed in the hope that it will be useful, but
19 // WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
20 // or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General public License
21 // for more details.
22 // 
23 // You should have received a copy of the GNU General public License along
24 // with this program. If not, see <http://www.gnu.org/licenses/>, or write to
25 // the Free Software Foundation, Inc., 51 Franklin Street - Fifth Floor,
26 // Boston, MA 02110-1301, USA.
27
28 using System;
29 using System.Collections.Generic;
30 using System.IO;
31 using System.Linq;
32 using System.Diagnostics;
33 using System.Windows.Forms;
34 using System.Text.RegularExpressions;
35 using System.Threading;
36 using System.Threading.Tasks;
37 using System.Globalization;
38 using System.Reflection;
39 using Microsoft.Win32;
40
41 namespace OpenTween
42 {
43     internal class MyApplication
44     {
45         /// <summary>
46         /// 起動時に指定されたオプションを取得します
47         /// </summary>
48         public static IDictionary<string, string> StartupOptions { get; private set; }
49
50         /// <summary>
51         /// アプリケーションのメイン エントリ ポイントです。
52         /// </summary>
53         [STAThread]
54         static int Main(string[] args)
55         {
56             if (!CheckRuntimeVersion())
57             {
58                 var message = string.Format(Properties.Resources.CheckRuntimeVersion_Error, ".NET Framework 4.5.1");
59                 MessageBox.Show(message, Application.ProductName, MessageBoxButtons.OK, MessageBoxIcon.Error);
60                 return 1;
61             }
62
63             StartupOptions = ParseArguments(args);
64
65             if (!SetConfigDirectoryPath())
66                 return 1;
67
68             InitCulture();
69
70             // 同じ設定ファイルを使用する OpenTween プロセスの二重起動を防止する
71             string pt = MyCommon.settingPath.Replace("\\", "/") + "/" + Application.ProductName;
72             using (Mutex mt = new Mutex(false, pt))
73             {
74                 if (!mt.WaitOne(0, false))
75                 {
76                     var text = string.Format(MyCommon.ReplaceAppName(Properties.Resources.StartupText1), MyCommon.GetAssemblyName());
77                     MessageBox.Show(text, MyCommon.ReplaceAppName(Properties.Resources.StartupText2), MessageBoxButtons.OK, MessageBoxIcon.Information);
78
79                     TryActivatePreviousWindow();
80                     return 1;
81                 }
82
83                 TaskScheduler.UnobservedTaskException += (s, e) =>
84                 {
85                     e.SetObserved();
86                     OnUnhandledException(e.Exception.Flatten());
87                 };
88                 Application.ThreadException += (s, e) => OnUnhandledException(e.Exception);
89                 AppDomain.CurrentDomain.UnhandledException += (s, e) => OnUnhandledException((Exception)e.ExceptionObject);
90
91                 Application.EnableVisualStyles();
92                 Application.SetCompatibleTextRenderingDefault(false);
93                 Application.Run(new TweenMain());
94
95                 mt.ReleaseMutex();
96
97                 return 0;
98             }
99         }
100
101         /// <summary>
102         /// 動作中の .NET Framework のバージョンが適切かチェックします
103         /// </summary>
104         private static bool CheckRuntimeVersion()
105         {
106             // Mono 上で動作している場合はバージョンチェックを無視します
107             if (Type.GetType("Mono.Runtime", false) != null)
108                 return true;
109
110             // .NET Framework 4.5.1 以降で動作しているかチェックする
111             // 参照: http://msdn.microsoft.com/en-us/library/hh925568%28v=vs.110%29.aspx
112
113             using (var lmKey = RegistryKey.OpenBaseKey(RegistryHive.LocalMachine, RegistryView.Registry32))
114             using (var ndpKey = lmKey.OpenSubKey(@"SOFTWARE\Microsoft\NET Framework Setup\NDP\v4\Full\"))
115             {
116                 var releaseKey = (int)ndpKey.GetValue("Release");
117                 return releaseKey >= 378675;
118             }
119         }
120
121         /// <summary>
122         /// “/key:value”形式の起動オプションを解釈し IDictionary に変換する
123         /// </summary>
124         /// <remarks>
125         /// 不正な形式のオプションは除外されます。
126         /// また、重複したキーのオプションが入力された場合は末尾に書かれたオプションが採用されます。
127         /// </remarks>
128         internal static IDictionary<string, string> ParseArguments(IEnumerable<string> arguments)
129         {
130             var optionPattern = new Regex(@"^/(.+?)(?::(.*))?$");
131
132             return arguments.Select(x => optionPattern.Match(x))
133                 .Where(x => x.Success)
134                 .GroupBy(x => x.Groups[1].Value)
135                 .ToDictionary(x => x.Key, x => x.Last().Groups[2].Value);
136         }
137
138         private static void TryActivatePreviousWindow()
139         {
140             // 実行中の同じアプリケーションのウィンドウ・ハンドルの取得
141             var prevProcess = GetPreviousProcess();
142             if (prevProcess == null)
143             {
144                 return;
145             }
146
147             IntPtr windowHandle = NativeMethods.GetWindowHandle((uint)prevProcess.Id, Application.ProductName);
148             if (windowHandle != IntPtr.Zero)
149             {
150                 NativeMethods.SetActiveWindow(windowHandle);
151             }
152         }
153
154         private static Process GetPreviousProcess()
155         {
156             var currentProcess = Process.GetCurrentProcess();
157             try
158             {
159                 return Process.GetProcessesByName(currentProcess.ProcessName)
160                     .Where(p => p.Id != currentProcess.Id)
161                     .FirstOrDefault(p => p.MainModule.FileName.Equals(currentProcess.MainModule.FileName, StringComparison.OrdinalIgnoreCase));
162             }
163             catch
164             {
165                 return null;
166             }
167         }
168
169         private static void OnUnhandledException(Exception ex)
170         {
171             if (MyCommon.ExceptionOut(ex))
172             {
173                 Application.Exit();
174             }
175         }
176
177         private static bool IsEqualCurrentCulture(string CultureName)
178         {
179             return Thread.CurrentThread.CurrentUICulture.Name.StartsWith(CultureName, StringComparison.Ordinal);
180         }
181
182         public static string CultureCode
183         {
184             get
185             {
186                 if (MyCommon.cultureStr == null)
187                 {
188                     var cfgCommon = SettingCommon.Load();
189                     MyCommon.cultureStr = cfgCommon.Language;
190                     if (MyCommon.cultureStr == "OS")
191                     {
192                         if (!IsEqualCurrentCulture("ja") &&
193                            !IsEqualCurrentCulture("en") &&
194                            !IsEqualCurrentCulture("zh-CN"))
195                         {
196                             MyCommon.cultureStr = "en";
197                         }
198                     }
199                 }
200                 return MyCommon.cultureStr;
201             }
202         }
203
204         public static void InitCulture()
205         {
206             try
207             {
208                 var culture = CultureInfo.CurrentCulture;
209                 if (CultureCode != "OS")
210                     culture = new CultureInfo(CultureCode);
211
212                 CultureInfo.DefaultThreadCurrentUICulture = culture;
213                 Thread.CurrentThread.CurrentUICulture = culture;
214             }
215             catch (Exception)
216             {
217             }
218         }
219
220         private static bool SetConfigDirectoryPath()
221         {
222             string configDir;
223             if (StartupOptions.TryGetValue("configDir", out configDir) && !string.IsNullOrEmpty(configDir))
224             {
225                 // 起動オプション /configDir で設定ファイルの参照先を変更できます
226                 if (!Directory.Exists(configDir))
227                 {
228                     var text = string.Format(Properties.Resources.ConfigDirectoryNotExist, configDir);
229                     MessageBox.Show(text, Application.ProductName, MessageBoxButtons.OK, MessageBoxIcon.Error);
230                     return false;
231                 }
232
233                 MyCommon.settingPath = Path.GetFullPath(configDir);
234             }
235             else
236             {
237                 if (File.Exists(Path.Combine(Application.StartupPath, "roaming")))
238                 {
239                     MyCommon.settingPath = MySpecialPath.UserAppDataPath();
240                 }
241                 else
242                 {
243                     MyCommon.settingPath = Application.StartupPath;
244                 }
245             }
246
247             return true;
248         }
249     }
250 }