OSDN Git Service

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