OSDN Git Service

設定ファイルの参照先が異なる複数の OpenTween.exe の起動を許可する
[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
40 namespace OpenTween
41 {
42     internal class MyApplication
43     {
44         /// <summary>
45         /// 起動時に指定されたオプションを取得します
46         /// </summary>
47         public static IDictionary<string, string> StartupOptions { get; private set; }
48
49         /// <summary>
50         /// アプリケーションのメイン エントリ ポイントです。
51         /// </summary>
52         [STAThread]
53         static int Main(string[] args)
54         {
55             StartupOptions = ParseArguments(args);
56
57             if (!SetConfigDirectoryPath())
58                 return 1;
59
60             InitCulture();
61
62             // 同じ設定ファイルを使用する OpenTween プロセスの二重起動を防止する
63             string pt = MyCommon.settingPath.Replace("\\", "/") + "/" + Application.ProductName;
64             using (Mutex mt = new Mutex(false, pt))
65             {
66                 if (!mt.WaitOne(0, false))
67                 {
68                     var text = string.Format(MyCommon.ReplaceAppName(Properties.Resources.StartupText1), MyCommon.GetAssemblyName());
69                     MessageBox.Show(text, MyCommon.ReplaceAppName(Properties.Resources.StartupText2), MessageBoxButtons.OK, MessageBoxIcon.Information);
70
71                     ShowPreviousWindow();
72                     return 1;
73                 }
74
75                 TaskScheduler.UnobservedTaskException += (s, e) =>
76                 {
77                     e.SetObserved();
78                     OnUnhandledException(e.Exception.Flatten());
79                 };
80                 Application.ThreadException += (s, e) => OnUnhandledException(e.Exception);
81                 AppDomain.CurrentDomain.UnhandledException += (s, e) => OnUnhandledException((Exception)e.ExceptionObject);
82
83                 Application.EnableVisualStyles();
84                 Application.SetCompatibleTextRenderingDefault(false);
85                 Application.Run(new TweenMain());
86
87                 mt.ReleaseMutex();
88
89                 return 0;
90             }
91         }
92
93         /// <summary>
94         /// “/key:value”形式の起動オプションを解釈し IDictionary に変換する
95         /// </summary>
96         /// <remarks>
97         /// 不正な形式のオプションは除外されます。
98         /// また、重複したキーのオプションが入力された場合は末尾に書かれたオプションが採用されます。
99         /// </remarks>
100         internal static IDictionary<string, string> ParseArguments(IEnumerable<string> arguments)
101         {
102             var optionPattern = new Regex(@"^/(.+?)(?::(.*))?$");
103
104             return arguments.Select(x => optionPattern.Match(x))
105                 .Where(x => x.Success)
106                 .GroupBy(x => x.Groups[1].Value)
107                 .ToDictionary(x => x.Key, x => x.Last().Groups[2].Value);
108         }
109
110         private static void ShowPreviousWindow()
111         {
112             // 実行中の同じアプリケーションのウィンドウ・ハンドルの取得
113             var prevProcess = Win32Api.GetPreviousProcess();
114             if (prevProcess == null)
115                 return;
116
117             if (prevProcess.MainWindowHandle != IntPtr.Zero)
118             {
119                 // 起動中のアプリケーションを最前面に表示
120                 Win32Api.WakeupWindow(prevProcess.MainWindowHandle);
121             }
122             else
123             {
124                 //プロセス特定は出来たが、ウィンドウハンドルが取得できなかった(アイコン化されている)
125                 //タスクトレイアイコンのクリックをエミュレート
126                 //注:アイコン特定はTooltipの文字列で行うため、多重起動時は先に見つけた物がアクティブになる
127                 Win32Api.ClickTasktrayIcon(Application.ProductName);
128             }
129         }
130
131         private static void OnUnhandledException(Exception ex)
132         {
133             if (MyCommon.ExceptionOut(ex))
134             {
135                 Application.Exit();
136             }
137         }
138
139         private static bool IsEqualCurrentCulture(string CultureName)
140         {
141             return Thread.CurrentThread.CurrentUICulture.Name.StartsWith(CultureName);
142         }
143
144         public static string CultureCode
145         {
146             get
147             {
148                 if (MyCommon.cultureStr == null)
149                 {
150                     var cfgCommon = SettingCommon.Load();
151                     MyCommon.cultureStr = cfgCommon.Language;
152                     if (MyCommon.cultureStr == "OS")
153                     {
154                         if (!IsEqualCurrentCulture("ja") &&
155                            !IsEqualCurrentCulture("en") &&
156                            !IsEqualCurrentCulture("zh-CN"))
157                         {
158                             MyCommon.cultureStr = "en";
159                         }
160                     }
161                 }
162                 return MyCommon.cultureStr;
163             }
164         }
165
166         public static void InitCulture(string code)
167         {
168             try
169             {
170                 Thread.CurrentThread.CurrentUICulture = new CultureInfo(code);
171             }
172             catch (Exception)
173             {
174             }
175         }
176         public static void InitCulture()
177         {
178             try
179             {
180                 if (CultureCode != "OS") Thread.CurrentThread.CurrentUICulture = new CultureInfo(CultureCode);
181             }
182             catch (Exception)
183             {
184             }
185         }
186
187         private static bool SetConfigDirectoryPath()
188         {
189             string configDir;
190             if (StartupOptions.TryGetValue("configDir", out configDir) && !string.IsNullOrEmpty(configDir))
191             {
192                 // 起動オプション /configDir で設定ファイルの参照先を変更できます
193                 if (!Directory.Exists(configDir))
194                 {
195                     var text = string.Format(Properties.Resources.ConfigDirectoryNotExist, configDir);
196                     MessageBox.Show(text, Application.ProductName, MessageBoxButtons.OK, MessageBoxIcon.Error);
197                     return false;
198                 }
199
200                 MyCommon.settingPath = Path.GetFullPath(configDir);
201             }
202             else
203             {
204                 if (File.Exists(Path.Combine(Application.StartupPath, "roaming")))
205                 {
206                     MyCommon.settingPath = MySpecialPath.UserAppDataPath();
207                 }
208                 else
209                 {
210                     MyCommon.settingPath = Application.StartupPath;
211                 }
212             }
213
214             return true;
215         }
216     }
217 }