OSDN Git Service

C#7.0で追加されたout変数宣言の構文を使用する
[opentween/open-tween.git] / OpenTween / NativeMethods.cs
1 // OpenTween - Client of Twitter
2 // Copyright (c) 2007-2011 kiri_feather (@kiri_feather) <kiri.feather@gmail.com>
3 //           (c) 2008-2011 Moz (@syo68k)
4 //           (c) 2008-2011 takeshik (@takeshik) <http://www.takeshik.org/>
5 //           (c) 2010-2011 anis774 (@anis774) <http://d.hatena.ne.jp/anis774/>
6 //           (c) 2010-2011 fantasticswallow (@f_swallow) <http://twitter.com/f_swallow>
7 //           (c) 2011      Egtra (@egtra) <http://dev.activebasic.com/egtra/>
8 //           (c) 2014      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.ComponentModel;
30 using System.Diagnostics;
31 using System.Linq;
32 using System.Net;
33 using System.Runtime.InteropServices;
34 using System.Threading;
35 using System.Windows.Forms;
36 using System.Text;
37 using OpenTween.Connection;
38
39 namespace OpenTween
40 {
41     internal static class NativeMethods
42     {
43         // 指定されたウィンドウへ、指定されたメッセージを送信します
44         [DllImport("user32.dll")]
45         private extern static IntPtr SendMessage(
46             IntPtr hwnd,
47             SendMessageType wMsg,
48             IntPtr wParam,
49             IntPtr lParam);
50
51         [DllImport("user32.dll")]
52         private extern static IntPtr SendMessage(
53             IntPtr hwnd,
54             SendMessageType wMsg,
55             IntPtr wParam,
56             ref LVITEM lParam);
57
58         // SendMessageで送信するメッセージ
59         private enum SendMessageType : uint
60         {
61             WM_SETREDRAW = 0x000B,               //再描画を許可するかを設定
62             WM_USER = 0x400,                     //ユーザー定義メッセージ
63
64             TCM_FIRST = 0x1300,                  //タブコントロールメッセージ
65             TCM_SETMINTABWIDTH = TCM_FIRST + 49, //タブアイテムの最小幅を設定
66
67             LVM_FIRST = 0x1000,                    //リストビューメッセージ
68             LVM_SETITEMSTATE = LVM_FIRST + 43,     //アイテムの状態を設定
69             LVM_GETSELECTIONMARK = LVM_FIRST + 66, //複数選択時の起点になるアイテムの位置を取得
70             LVM_SETSELECTIONMARK = LVM_FIRST + 67, //複数選択時の起点になるアイテムを設定
71         }
72
73         /// <summary>
74         /// コントロールの再描画を許可するかを設定します
75         /// </summary>
76         /// <param name="control">対象となるコントロール</param>
77         /// <param name="redraw">再描画を許可する場合は true、抑制する場合は false</param>
78         /// <returns>このメッセージを処理する場合、アプリケーションは 0 を返します</returns>
79         public static int SetRedrawState(Control control, bool redraw)
80         {
81             var state = redraw ? new IntPtr(1) : IntPtr.Zero;
82             return (int)SendMessage(control.Handle, SendMessageType.WM_SETREDRAW, state, IntPtr.Zero);
83         }
84
85         /// <summary>
86         /// タブコントロールのアイテムの最小幅を設定します
87         /// </summary>
88         /// <param name="tabControl">対象となるタブコントロール</param>
89         /// <param name="width">アイテムの最小幅。-1 を指定するとデフォルトの幅が使用されます</param>
90         /// <returns>設定前の最小幅</returns>
91         public static int SetMinTabWidth(TabControl tabControl, int width)
92         {
93             return (int)SendMessage(tabControl.Handle, SendMessageType.TCM_SETMINTABWIDTH, IntPtr.Zero, (IntPtr)width);
94         }
95
96         // 参照: LVITEM structure (Windows)
97         // http://msdn.microsoft.com/en-us/library/windows/desktop/bb774760%28v=vs.85%29.aspx
98         [StructLayout(LayoutKind.Sequential)]
99         [BestFitMapping(false, ThrowOnUnmappableChar = true)]
100         private struct LVITEM
101         {
102             public uint mask;
103             public int iItem;
104             public int iSubItem;
105             public LVIS state;
106             public LVIS stateMask;
107             public string pszText;
108             public int cchTextMax;
109             public int iImage;
110             public IntPtr lParam;
111             public int iIndent;
112             public int iGroupId;
113             public uint cColumns;
114             public uint puColumns;
115             public int piColFmt;
116             public int iGroup;
117         }
118
119         // 参照: List-View Item States (Windows)
120         // http://msdn.microsoft.com/en-us/library/windows/desktop/bb774733%28v=vs.85%29.aspx
121         [Flags]
122         private enum LVIS : uint
123         {
124             SELECTED = 0x02,
125         }
126
127         /// <summary>
128         /// ListView のアイテムを選択された状態にします
129         /// </summary>
130         /// <param name="listView">対象となる ListView</param>
131         /// <param name="index">選択するアイテムのインデックス</param>
132         /// <returns>成功した場合は true、それ以外の場合は false</returns>
133         public static bool SelectItem(ListView listView, int index)
134         {
135             // LVM_SETITEMSTATE では stateMask, state 以外のメンバーは無視される
136             var lvitem = new LVITEM
137             {
138                 stateMask = LVIS.SELECTED,
139                 state = LVIS.SELECTED,
140             };
141
142             var ret = (int)SendMessage(listView.Handle, SendMessageType.LVM_SETITEMSTATE, (IntPtr)index, ref lvitem);
143             return ret != 0;
144         }
145
146         /// <summary>
147         /// ListView の全アイテムを選択された状態にします
148         /// </summary>
149         /// <param name="listView">対象となる ListView</param>
150         /// <returns>成功した場合は true、それ以外の場合は false</returns>
151         public static bool SelectAllItems(ListView listView)
152         {
153             return SelectItem(listView, -1 /* all items */);
154         }
155
156         #region "画面ブリンク用"
157         public static bool FlashMyWindow(IntPtr hwnd,
158             FlashSpecification flashType,
159             int flashCount)
160         {
161             var fInfo = new FLASHWINFO();
162             fInfo.cbSize = Convert.ToInt32(Marshal.SizeOf(fInfo));
163             fInfo.hwnd = hwnd;
164             fInfo.dwFlags = (int)FlashSpecification.FlashAll;
165             fInfo.uCount = flashCount;
166             fInfo.dwTimeout = 0;
167
168             return FlashWindowEx(ref fInfo);
169         }
170
171         public enum FlashSpecification : uint
172         {
173             FlashStop = FLASHW_STOP,
174             FlashCaption = FLASHW_CAPTION,
175             FlashTray = FLASHW_TRAY,
176             FlashAll = FLASHW_ALL,
177             FlashTimer = FLASHW_TIMER,
178             FlashTimerNoForeground = FLASHW_TIMERNOFG,
179         }
180         /// http://www.atmarkit.co.jp/fdotnet/dotnettips/723flashwindow/flashwindow.html
181         [DllImport("user32.dll")]
182         [return: MarshalAs(UnmanagedType.Bool)]
183         private static extern bool FlashWindowEx(
184             ref FLASHWINFO FWInfo);
185
186
187         private struct FLASHWINFO
188         {
189             public Int32 cbSize;    // FLASHWINFO構造体のサイズ
190             public IntPtr hwnd;     // 点滅対象のウィンドウ・ハンドル
191             public Int32 dwFlags;   // 以下の「FLASHW_XXX」のいずれか
192             public Int32 uCount;    // 点滅する回数
193             public Int32 dwTimeout; // 点滅する間隔(ミリ秒単位)
194         }
195
196         // 点滅を止める
197         private const Int32 FLASHW_STOP = 0;
198         // タイトルバーを点滅させる
199         private const Int32 FLASHW_CAPTION = 0x1;
200         // タスクバー・ボタンを点滅させる
201         private const Int32 FLASHW_TRAY = 0x2;
202         // タスクバー・ボタンとタイトルバーを点滅させる
203         private const Int32 FLASHW_ALL = 0x3;
204         // FLASHW_STOPが指定されるまでずっと点滅させる
205         private const Int32 FLASHW_TIMER = 0x4;
206         // ウィンドウが最前面に来るまでずっと点滅させる
207         private const Int32 FLASHW_TIMERNOFG = 0xC;
208         #endregion
209
210         [DllImport("user32.dll")]
211         [return: MarshalAs(UnmanagedType.Bool)]
212         public static extern bool ValidateRect(
213             IntPtr hwnd,
214             IntPtr rect);
215
216         #region "selection mark"
217         // 複数選択時の起点になるアイテム (selection mark) の位置を取得する
218         public static int ListView_GetSelectionMark(IntPtr hwndLV)
219         {
220             return SendMessage(hwndLV, SendMessageType.LVM_GETSELECTIONMARK, IntPtr.Zero, IntPtr.Zero).ToInt32();
221         }
222
223         // 複数選択時の起点になるアイテム (selection mark) を設定する
224         public static void ListView_SetSelectionMark(IntPtr hwndLV, int itemIndex)
225         {
226             SendMessage(hwndLV, SendMessageType.LVM_SETSELECTIONMARK, IntPtr.Zero, (IntPtr)itemIndex);
227         }
228         #endregion
229
230         #region "スクリーンセーバー起動中か判定"
231         [DllImport("user32", CharSet = CharSet.Auto)]
232         [return: MarshalAs(UnmanagedType.Bool)]
233         private static extern bool SystemParametersInfo(
234                     int intAction,
235                     int intParam,
236                     [MarshalAs(UnmanagedType.Bool)] ref bool bParam,
237                     int intWinIniFlag);
238         // returns non-zero value if function succeeds
239
240         //スクリーンセーバーが起動中かを取得する定数
241         private const int SPI_GETSCREENSAVERRUNNING = 0x0072;
242
243         public static bool IsScreenSaverRunning()
244         {
245             var isRunning = false;
246             SystemParametersInfo(SPI_GETSCREENSAVERRUNNING, 0, ref isRunning, 0);
247             return isRunning;
248         }
249         #endregion
250
251         #region "グローバルフック"
252         [DllImport("user32")]
253         private static extern int RegisterHotKey(IntPtr hwnd, int id,
254             int fsModifiers, int vk);
255         [DllImport("user32")]
256         private static extern int UnregisterHotKey(IntPtr hwnd, int id);
257         [DllImport("kernel32", CharSet = CharSet.Auto, BestFitMapping = false, ThrowOnUnmappableChar = true)]
258         private static extern ushort GlobalAddAtom([MarshalAs(UnmanagedType.LPTStr)] string lpString);
259         [DllImport("kernel32")]
260         private static extern ushort GlobalDeleteAtom(ushort nAtom);
261
262         private static int registerCount = 0;
263         // register a global hot key
264         public static int RegisterGlobalHotKey(int hotkeyValue, int modifiers, Form targetForm)
265         {
266             ushort hotkeyID = 0;
267             try
268             {
269                 // use the GlobalAddAtom API to get a unique ID (as suggested by MSDN docs)
270                 registerCount++;
271                 var atomName = Thread.CurrentThread.ManagedThreadId.ToString("X8") + targetForm.Name + registerCount;
272                 hotkeyID = GlobalAddAtom(atomName);
273                 if (hotkeyID == 0)
274                 {
275                     throw new Win32Exception();
276                 }
277
278                 // register the hotkey, throw if any error
279                 if (RegisterHotKey(targetForm.Handle, hotkeyID, modifiers, hotkeyValue) == 0)
280                 {
281                     throw new Win32Exception();
282                 }
283                 return hotkeyID;
284             }
285             catch (Exception)
286             {
287                 // clean up if hotkey registration failed
288                 UnregisterGlobalHotKey(hotkeyID, targetForm);
289                 return 0;
290             }
291         }
292
293         // unregister a global hotkey
294         public static void UnregisterGlobalHotKey(ushort hotkeyID, Form targetForm)
295         {
296             if (hotkeyID != 0)
297             {
298                 UnregisterHotKey(targetForm.Handle, hotkeyID);
299                 // clean up the atom list
300                 GlobalDeleteAtom(hotkeyID);
301                 hotkeyID = 0;
302             }
303         }
304         #endregion
305
306         #region "プロセスのProxy設定"
307
308         [DllImport("wininet.dll", SetLastError = true)]
309         [return: MarshalAs(UnmanagedType.Bool)]
310         private static extern bool InternetSetOption(IntPtr hInternet,
311             InternetOption dwOption,
312             [In] ref InternetProxyInfo lpBuffer,
313             int lpdwBufferLength);
314
315         private enum InternetOption
316         {
317             PROXY = 38,
318             PROXY_USERNAME = 43,
319             PROXY_PASSWORD = 44,
320         }
321
322         [StructLayout(LayoutKind.Sequential)]
323         [BestFitMapping(false, ThrowOnUnmappableChar = true)]
324         private struct InternetProxyInfo
325         {
326             public InternetOpenType dwAccessType;
327             public string proxy;
328             public string proxyBypass;
329         }
330
331         private enum InternetOpenType
332         {
333             //PRECONFIG = 0, // IE setting
334             DIRECT = 1, // Direct
335             PROXY = 3, // Custom
336         }
337
338         private static void RefreshProxySettings(string strProxy)
339         {
340             InternetProxyInfo ipi;
341
342             // Filling in structure
343             if (!string.IsNullOrEmpty(strProxy))
344             {
345                 ipi = new InternetProxyInfo
346                 {
347                     dwAccessType = InternetOpenType.PROXY,
348                     proxy = strProxy,
349                     proxyBypass = "local",
350                 };
351             }
352             else if (strProxy == null)
353             {
354                 //IE Default
355                 var p = WebRequest.GetSystemWebProxy();
356                 if (p.IsBypassed(new Uri("http://www.google.com/")))
357                 {
358                     ipi = new InternetProxyInfo
359                     {
360                         dwAccessType = InternetOpenType.DIRECT,
361                         proxy = null,
362                         proxyBypass = null,
363                     };
364                 }
365                 else
366                 {
367                     ipi = new InternetProxyInfo
368                     {
369                         dwAccessType = InternetOpenType.PROXY,
370                         proxy = p.GetProxy(new Uri("http://www.google.com/")).Authority,
371                         proxyBypass = "local",
372                     };
373                 }
374             }
375             else
376             {
377                 ipi = new InternetProxyInfo
378                 {
379                     dwAccessType = InternetOpenType.DIRECT,
380                     proxy = null,
381                     proxyBypass = null,
382                 };
383             }
384
385             if (!InternetSetOption(IntPtr.Zero, InternetOption.PROXY, ref ipi, Marshal.SizeOf(ipi)))
386                 throw new Win32Exception();
387         }
388
389         public static void SetProxy(ProxyType pType, string host, int port, string username, string password)
390         {
391             string proxy = null;
392             switch (pType)
393             {
394             case ProxyType.IE:
395                 proxy = null;
396                 break;
397             case ProxyType.None:
398                 proxy = "";
399                 break;
400             case ProxyType.Specified:
401                 proxy = host + (port > 0 ? ":" + port : "");
402                 break;
403             }
404             RefreshProxySettings(proxy);
405         }
406 #endregion
407
408         [StructLayout(LayoutKind.Sequential)]
409         private struct SCROLLINFO
410         {
411             public int cbSize;
412             public ScrollInfoMask fMask;
413             public int nMin;
414             public int nMax;
415             public int nPage;
416             public int nPos;
417             public int nTrackPos;
418         }
419
420         public enum ScrollBarDirection
421         {
422             SB_HORZ = 0,
423             SB_VERT = 1,
424             SB_CTL = 2,
425             SB_BOTH = 3,
426         }
427
428         private enum ScrollInfoMask
429         {
430             SIF_RANGE = 0x1,
431             SIF_PAGE = 0x2,
432             SIF_POS = 0x4,
433             SIF_DISABLENOSCROLL = 0x8,
434             SIF_TRACKPOS = 0x10,
435             SIF_ALL = (SIF_RANGE | SIF_PAGE | SIF_POS | SIF_TRACKPOS),
436         }
437
438         [DllImport("user32.dll")]
439         private static extern int GetScrollInfo(IntPtr hWnd, ScrollBarDirection fnBar, ref SCROLLINFO lpsi);
440
441         public static int GetScrollPosition(Control control, ScrollBarDirection direction)
442         {
443             var si = new SCROLLINFO
444             {
445                 cbSize = Marshal.SizeOf<SCROLLINFO>(),
446                 fMask = ScrollInfoMask.SIF_POS,
447             };
448
449             if (NativeMethods.GetScrollInfo(control.Handle, direction, ref si) == 0)
450                 throw new Win32Exception();
451
452             return si.nPos;
453         }
454
455         #region "ウィンドウの検索"
456
457         [DllImport("user32.dll")]
458         private static extern uint GetWindowThreadProcessId(IntPtr hWnd, out uint procId);
459         
460         [return: MarshalAs(UnmanagedType.Bool)]
461         private delegate bool EnumWindowCallback(IntPtr hWnd, int lParam);
462
463         [DllImport("user32")]
464         [return: MarshalAs(UnmanagedType.Bool)]
465         private static extern bool EnumWindows(EnumWindowCallback lpEnumFunc, IntPtr lParam);
466
467         [DllImport("user32.dll", CharSet = CharSet.Unicode)]
468         private static extern int GetWindowText(IntPtr hWnd, StringBuilder lpString, int nMaxCount);
469
470         [DllImport("user32.dll", CharSet = CharSet.Unicode)]
471         private static extern int GetWindowTextLength(IntPtr hWnd);
472
473         /// <summary>
474         /// 指定したPIDとタイトルを持つウィンドウのウィンドウハンドルを取得します
475         /// </summary>
476         /// <param name="pid">対象ウィンドウのPID</param>
477         /// <param name="searchWindowTitle">対象ウィンドウのタイトル</param>
478         /// <returns>ウィンドウハンドル。検索に失敗した場合は<see cref="IntPtr.Zero"/></returns>
479         public static IntPtr GetWindowHandle(uint pid, string searchWindowTitle)
480         {
481             var foundHwnd = IntPtr.Zero;
482
483             EnumWindows((hWnd, lParam) =>
484             {
485                 GetWindowThreadProcessId(hWnd, out var procId);
486
487                 if (procId == pid)
488                 {
489                     int windowTitleLen = GetWindowTextLength(hWnd);
490
491                     if (windowTitleLen > 0)
492                     {
493                         StringBuilder windowTitle = new StringBuilder(windowTitleLen + 1);
494                         GetWindowText(hWnd, windowTitle, windowTitle.Capacity);
495
496                         if (windowTitle.ToString().Contains(searchWindowTitle))
497                         {
498                             foundHwnd = hWnd;
499                             return false;
500                         }
501                     }
502                 }
503
504                 return true;
505             }, IntPtr.Zero);
506
507             return foundHwnd;
508         }
509
510         #endregion
511
512         #region "ウィンドウのアクティブ化"
513
514         private enum ShowWindowCommands : int
515         {
516             /// <summary>最小化・最大化されたウィンドウを元に戻して表示</summary>
517             SW_RESTORE = 9,
518         }
519
520         [DllImport("user32.dll")]
521         [return: MarshalAs(UnmanagedType.Bool)]
522         private static extern bool ShowWindow(IntPtr hWnd, ShowWindowCommands nCmdShow);
523
524         [DllImport("user32.dll")]
525         [return: MarshalAs(UnmanagedType.Bool)]
526         private static extern bool SetForegroundWindow(IntPtr hWnd);
527
528         /// <summary>
529         /// 指定したウィンドウをアクティブにします
530         /// </summary>
531         /// <param name="hWnd">アクティブにするウィンドウのウィンドウハンドル</param>
532         public static void SetActiveWindow(IntPtr hWnd)
533         {
534             ShowWindow(hWnd, ShowWindowCommands.SW_RESTORE);
535             SetForegroundWindow(hWnd);
536         }
537
538         #endregion
539     }
540 }