1 /**
2  * コマンドに関連した処理
3  *
4  * Author: $(LINK2 https://twitter.com/dokutoku3, dokutoku)
5  * License: $(LINK2 https://creativecommons.org/publicdomain/zero/1.0/, CC0 1.0 Universal)
6  */
7 module remove_shortcut_arrow.command;
8 
9 
10 version (Windows) {
11 	private static import core.sys.windows.winbase;
12 	private static import core.sys.windows.windef;
13 	private static import core.sys.windows.winnt;
14 
15 	extern (Windows)
16 	nothrow @nogc
17 	core.sys.windows.windef.BOOL Wow64DisableWow64FsRedirection(core.sys.windows.winnt.PVOID*);
18 
19 	extern (Windows)
20 	nothrow @nogc
21 	core.sys.windows.windef.BOOL Wow64RevertWow64FsRedirection(core.sys.windows.winnt.PVOID);
22 }
23 
24 private static import remove_shortcut_arrow.path;
25 
26 /**
27  * PATHを除いたレジストリの追加コマンド。
28  */
29 public enum REG_PRE_ADD_COMMAND = "reg.exe add \"HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Explorer\\Shell Icons\" /f /v 29 /t REG_EXPAND_SZ /reg:64 /d \0"w;
30 
31 /**
32  * レジストリから削除するコマンド
33  */
34 public enum REG_DELETE_COMMAND = "reg.exe delete \"HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Explorer\\Shell Icons\" /f /v 29 /reg:64\0"w;
35 
36 version (Windows) {
37 	static assert(core.sys.windows.windef.MAX_PATH == 260);
38 }
39 
40 public enum REG_ADD_COMMAND_BUFFER_LENGTH = .REG_PRE_ADD_COMMAND.length + ("\"\"".length * 2) + 260 + remove_shortcut_arrow.path.PATH_LIMIT;
41 public enum REG_DELETE_COMMAND_BUFFER_LENGTH = .REG_DELETE_COMMAND.length + "\"\"".length + 260;
42 
43 /**
44  * 実行マシン上で有効な、ショートカット矢印を削除するためのコマンドをバッファにコピーする。
45  *
46  * Params:
47  *      command_buffer = コマンドのバッファ
48  *      buffer_size = コマンドのバッファサイズ
49  *      ico_path = icoファイルの絶対パス
50  */
51 version (Windows)
52 extern (C)
53 nothrow @nogc
54 public bool copy_remove_windows_shortcut_arrow_command(scope wchar* command_buffer, size_t buffer_size, const core.sys.windows.winnt.LPCWSTR ico_path)
55 
56 	do
57 	{
58 		if (command_buffer == null) {
59 			return false;
60 		}
61 
62 		if (buffer_size < .REG_ADD_COMMAND_BUFFER_LENGTH) {
63 			if (buffer_size != 0) {
64 				command_buffer[0] = '\0';
65 			}
66 
67 			return false;
68 		}
69 
70 		if (ico_path == null) {
71 			return false;
72 		}
73 
74 		if (core.sys.windows.winbase.lstrlenW(ico_path) >= remove_shortcut_arrow.path.PATH_LIMIT) {
75 			return false;
76 		}
77 
78 		wchar[core.sys.windows.windef.MAX_PATH] system_directory = void;
79 
80 		command_buffer[0] = '"';
81 		command_buffer[1] = '\0';
82 
83 		if (remove_shortcut_arrow.path.get_system32_path(system_directory) == 0) {
84 			return false;
85 		}
86 
87 		core.sys.windows.winbase.lstrcatW(command_buffer, &(system_directory[0]));
88 		core.sys.windows.winbase.lstrcatW(command_buffer, &("\\System32\0"w[0]));
89 
90 		version (none) {
91 			// reg:64オプションを削除しない場合(Vistaでは動作しない)
92 
93 			static assert(.REG_PRE_ADD_COMMAND[0 .. 8] == "reg.exe "w);
94 			static assert(.REG_PRE_ADD_COMMAND[($ - " /d \0"w.length) .. $] == " /d \0"w);
95 			enum TEMP = "\\reg.exe\" "w ~ .REG_PRE_ADD_COMMAND[8 .. ($ - " \0"w.length)] ~ " \"" ~ "\0";
96 		} else {
97 			// reg:64オプションを削除する場合
98 
99 			static assert(.REG_PRE_ADD_COMMAND[0 .. 8] == "reg.exe "w);
100 			static assert(.REG_PRE_ADD_COMMAND[($ - " /reg:64 /d \0"w.length) .. $] == " /reg:64 /d \0"w);
101 			enum TEMP = "\\reg.exe\" "w ~ .REG_PRE_ADD_COMMAND[8 .. ($ - " /reg:64 /d \0"w.length)] ~ " /d \"" ~ "\0";
102 		}
103 
104 		core.sys.windows.winbase.lstrcatW(command_buffer, &(TEMP[0]));
105 		core.sys.windows.winbase.lstrcatW(command_buffer, &(ico_path[0]));
106 		int length = core.sys.windows.winbase.lstrlenW(command_buffer);
107 		command_buffer[length] = '"';
108 		command_buffer[length + 1] = '\0';
109 
110 		return true;
111 	}
112 
113 /**
114  * 実行マシン上で有効な、削除したショートカット矢印を元に戻すためのコマンドをバッファにコピーする。
115  *
116  * Params:
117  *      command_buffer = コマンドのバッファ
118  *      buffer_size = コマンドのバッファサイズ
119  */
120 version (Windows)
121 extern (C)
122 nothrow @nogc
123 public bool copy_restore_windows_shortcut_arrow_command(scope wchar* command_buffer, size_t buffer_size)
124 
125 	do
126 	{
127 		if (command_buffer == null) {
128 			return false;
129 		}
130 
131 		if (buffer_size < .REG_DELETE_COMMAND_BUFFER_LENGTH) {
132 			if (buffer_size != 0) {
133 				command_buffer[0] = '\0';
134 			}
135 
136 			return false;
137 		}
138 
139 		wchar[core.sys.windows.windef.MAX_PATH] system_directory = void;
140 
141 		command_buffer[0] = '"';
142 		command_buffer[1] = '\0';
143 
144 		if (remove_shortcut_arrow.path.get_system32_path(system_directory) == 0) {
145 			return false;
146 		}
147 
148 		core.sys.windows.winbase.lstrcatW(command_buffer, &(system_directory[0]));
149 		core.sys.windows.winbase.lstrcatW(command_buffer, &("\\System32\0"w[0]));
150 
151 		version (none) {
152 			// reg:64オプションを削除しない場合(Vistaでは動作しない)
153 
154 			static assert(.REG_DELETE_COMMAND[0 .. 8] == "reg.exe "w);
155 			static assert(.REG_DELETE_COMMAND[$ - 1] == '\0');
156 			enum TEMP = "\\reg.exe\" "w ~ .REG_DELETE_COMMAND[8 .. $];
157 		} else {
158 			// reg:64オプションを削除する場合
159 
160 			static assert(.REG_DELETE_COMMAND[0 .. 8] == "reg.exe "w);
161 			static assert(.REG_DELETE_COMMAND[($ - " /reg:64\0"w.length) .. $] == " /reg:64\0"w);
162 			enum TEMP = "\\reg.exe\" "w ~ .REG_DELETE_COMMAND[8 .. ($ - " /reg:64\0"w.length)] ~ "\0";
163 		}
164 
165 		core.sys.windows.winbase.lstrcatW(command_buffer, &(TEMP[0]));
166 
167 		return true;
168 	}
169 
170 version (Windows)
171 nothrow @nogc
172 package bool exec_system32_command(core.sys.windows.winnt.LPWSTR command_buffer, bool disable_redirection = true)
173 
174 	do
175 	{
176 		core.sys.windows.winbase.STARTUPINFOW temp =
177 		{
178 			cb: core.sys.windows.winbase.STARTUPINFOW.sizeof,
179 		};
180 
181 		core.sys.windows.winbase.PROCESS_INFORMATION process_info;
182 		core.sys.windows.winnt.PVOID old_value = core.sys.windows.windef.NULL;
183 		core.sys.windows.windef.BOOL redirection_result = void;
184 
185 		if (disable_redirection) {
186 			redirection_result = .Wow64DisableWow64FsRedirection(&old_value);
187 		}
188 
189 		scope (exit) {
190 			if (disable_redirection) {
191 				if (redirection_result != core.sys.windows.windef.FALSE) {
192 					.Wow64RevertWow64FsRedirection(old_value);
193 				}
194 			}
195 		}
196 
197 		core.sys.windows.windef.BOOL result = core.sys.windows.winbase.CreateProcessW(null, command_buffer, null, null, core.sys.windows.windef.FALSE, core.sys.windows.winbase.CREATE_NO_WINDOW, null, null, &temp, &process_info);
198 
199 		if (result == core.sys.windows.windef.FALSE) {
200 			return false;
201 		}
202 
203 		scope (exit) {
204 			core.sys.windows.winbase.CloseHandle(process_info.hProcess);
205 			core.sys.windows.winbase.CloseHandle(process_info.hThread);
206 		}
207 
208 		core.sys.windows.windef.DWORD exit_code = 1;
209 
210 		return (core.sys.windows.winbase.WaitForSingleObject(process_info.hProcess, core.sys.windows.winbase.INFINITE) != core.sys.windows.winbase.WAIT_FAILED) && (core.sys.windows.winbase.GetExitCodeProcess(process_info.hProcess, &exit_code) != core.sys.windows.windef.FALSE) && (exit_code == 0);
211 	}
212 
213 /**
214  * アイコンキャッシュをie4uinit.exeを利用してクリアする?
215  *
216  * ie4uinit.exeがあるかどうかやそのPATHなどはOSによって違うのでうまくいかないかもしれない
217  */
218 version (Windows)
219 extern (C)
220 nothrow @nogc
221 public bool exec_clear_icon_cache()
222 
223 	do
224 	{
225 		wchar[core.sys.windows.windef.MAX_PATH] system_directory = void;
226 
227 		if (remove_shortcut_arrow.path.get_system32_path(system_directory) == 0) {
228 			return false;
229 		}
230 
231 		//Unicode版はバッファに入れなければならない
232 		wchar[core.sys.windows.windef.MAX_PATH + "\"\"".length + " -ClearIconCache".length + "\0".length] command_buffer = void;
233 		command_buffer[0] = '"';
234 		command_buffer[1] = '\0';
235 		core.sys.windows.winbase.lstrcatW(&(command_buffer[0]), &(system_directory[0]));
236 		core.sys.windows.winbase.lstrcatW(&(command_buffer[0]), &("\\System32\0"w[0]));
237 		core.sys.windows.winbase.lstrcatW(&(command_buffer[0]), &("\\ie4uinit.exe\" -ClearIconCache\0"w[0]));
238 
239 		int length = core.sys.windows.winbase.lstrlenW(&(command_buffer[0]));
240 		assert(length > 0);
241 
242 		core.sys.windows.winnt.PVOID old_value = core.sys.windows.windef.NULL;
243 		core.sys.windows.windef.BOOL redirection_result = void;
244 		redirection_result = .Wow64DisableWow64FsRedirection(&old_value);
245 
246 		scope (exit) {
247 			if (redirection_result != core.sys.windows.windef.FALSE) {
248 				.Wow64RevertWow64FsRedirection(old_value);
249 			}
250 		}
251 
252 		bool result1 = .exec_system32_command(&(command_buffer[0]), false);
253 		length -= "ClearIconCache".length;
254 
255 		command_buffer[length++] = 's';
256 		command_buffer[length++] = 'h';
257 		command_buffer[length++] = 'o';
258 		command_buffer[length++] = 'w';
259 		command_buffer[length] = '\0';
260 
261 		return (result1) && (.exec_system32_command(&(command_buffer[0]), false));
262 	}