1 /*
2 * COPYRIGHT: See COPYING in the top level directory
3 * PROJECT: ReactOS Registry namespace extension
4 * FILE: dll/win32/shell32/extracticon.c
5 * PURPOSE: Icon extraction
6 *
7 * PROGRAMMERS: Herv� Poussineau (hpoussin@reactos.org)
8 */
9
10 #include "precomp.h"
11
12 WINE_DEFAULT_DEBUG_CHANNEL(shell);
13
14 struct IconLocation
15 {
16 LPWSTR file;
17 UINT index;
18 };
19
20 class CExtractIcon :
21 public CComObjectRootEx<CComMultiThreadModelNoCS>,
22 public IDefaultExtractIconInit,
23 public IExtractIconW,
24 public IExtractIconA,
25 public IPersistFile
26 {
27 private:
28 UINT flags;
29 struct IconLocation defaultIcon;
30 struct IconLocation normalIcon;
31 struct IconLocation openIcon;
32 struct IconLocation shortcutIcon;
33 public:
34 CExtractIcon();
35 ~CExtractIcon();
36
37 // IDefaultExtractIconInit
38 STDMETHOD(SetDefaultIcon)(LPCWSTR pszFile, int iIcon) override;
39 STDMETHOD(SetFlags)(UINT uFlags) override;
40 STDMETHOD(SetKey)(HKEY hkey) override;
41 STDMETHOD(SetNormalIcon)(LPCWSTR pszFile, int iIcon) override;
42 STDMETHOD(SetOpenIcon)(LPCWSTR pszFile, int iIcon) override;
43 STDMETHOD(SetShortcutIcon)(LPCWSTR pszFile, int iIcon) override;
44
45 // IExtractIconW
46 STDMETHOD(GetIconLocation)(UINT uFlags, LPWSTR szIconFile, UINT cchMax, int *piIndex, UINT *pwFlags) override;
47 STDMETHOD(Extract)(LPCWSTR pszFile, UINT nIconIndex, HICON *phiconLarge, HICON *phiconSmall, UINT nIconSize) override;
48
49 // IExtractIconA
50 STDMETHOD(GetIconLocation)(UINT uFlags, LPSTR szIconFile, UINT cchMax, int *piIndex, UINT *pwFlags) override;
51 STDMETHOD(Extract)(LPCSTR pszFile, UINT nIconIndex, HICON *phiconLarge, HICON *phiconSmall, UINT nIconSize) override;
52
53 // IPersist
54 STDMETHOD(GetClassID)(CLSID *pClassID) override;
55 STDMETHOD(IsDirty)() override;
56
57 // IPersistFile
58 STDMETHOD(Load)(LPCOLESTR pszFileName, DWORD dwMode) override;
59 STDMETHOD(Save)(LPCOLESTR pszFileName, BOOL fRemember) override;
60 STDMETHOD(SaveCompleted)(LPCOLESTR pszFileName) override;
61 STDMETHOD(GetCurFile)(LPOLESTR *ppszFileName) override;
62
63 BEGIN_COM_MAP(CExtractIcon)
64 COM_INTERFACE_ENTRY_IID(IID_IDefaultExtractIconInit, IDefaultExtractIconInit)
65 COM_INTERFACE_ENTRY_IID(IID_IExtractIconW, IExtractIconW)
66 COM_INTERFACE_ENTRY_IID(IID_IExtractIconA, IExtractIconA)
67 COM_INTERFACE_ENTRY_IID(IID_IPersist, IPersist)
68 COM_INTERFACE_ENTRY_IID(IID_IPersistFile, IPersistFile)
69 END_COM_MAP()
70 };
71
DuplicateString(LPCWSTR Source,LPWSTR * Destination)72 VOID DuplicateString(
73 LPCWSTR Source,
74 LPWSTR *Destination)
75 {
76 SIZE_T cb;
77
78 if (*Destination)
79 CoTaskMemFree(*Destination);
80
81 cb = (wcslen(Source) + 1) * sizeof(WCHAR);
82 *Destination = (LPWSTR)CoTaskMemAlloc(cb);
83 if (!*Destination)
84 return;
85 CopyMemory(*Destination, Source, cb);
86 }
87
CExtractIcon()88 CExtractIcon::CExtractIcon()
89 {
90 flags = 0;
91 memset(&defaultIcon, 0, sizeof(defaultIcon));
92 memset(&normalIcon, 0, sizeof(normalIcon));
93 memset(&openIcon, 0, sizeof(openIcon));
94 memset(&shortcutIcon, 0, sizeof(shortcutIcon));
95 }
96
~CExtractIcon()97 CExtractIcon::~CExtractIcon()
98 {
99 if (defaultIcon.file) CoTaskMemFree(defaultIcon.file);
100 if (normalIcon.file) CoTaskMemFree(normalIcon.file);
101 if (openIcon.file) CoTaskMemFree(openIcon.file);
102 if (shortcutIcon.file) CoTaskMemFree(shortcutIcon.file);
103 }
104
SetDefaultIcon(LPCWSTR pszFile,int iIcon)105 HRESULT STDMETHODCALLTYPE CExtractIcon::SetDefaultIcon(
106 LPCWSTR pszFile,
107 int iIcon)
108 {
109 TRACE("(%p, %s, %d)\n", this, debugstr_w(pszFile), iIcon);
110
111 DuplicateString(pszFile, &defaultIcon.file);
112 if (!defaultIcon.file)
113 return E_OUTOFMEMORY;
114 defaultIcon.index = iIcon;
115 return S_OK;
116 }
117
SetFlags(UINT uFlags)118 HRESULT STDMETHODCALLTYPE CExtractIcon::SetFlags(
119 UINT uFlags)
120 {
121 TRACE("(%p, 0x%x)\n", this, uFlags);
122
123 flags = uFlags;
124 return S_OK;
125 }
126
SetKey(HKEY hkey)127 HRESULT STDMETHODCALLTYPE CExtractIcon::SetKey(
128 HKEY hkey)
129 {
130 FIXME("(%p, %p)\n", this, hkey);
131 UNIMPLEMENTED;
132 return E_NOTIMPL;
133 }
134
SetNormalIcon(LPCWSTR pszFile,int iIcon)135 HRESULT STDMETHODCALLTYPE CExtractIcon::SetNormalIcon(
136 LPCWSTR pszFile,
137 int iIcon)
138 {
139 TRACE("(%p, %s, %d)\n", this, debugstr_w(pszFile), iIcon);
140
141 DuplicateString(pszFile, &normalIcon.file);
142 if (!normalIcon.file)
143 return E_OUTOFMEMORY;
144 normalIcon.index = iIcon;
145 return S_OK;
146 }
147
SetOpenIcon(LPCWSTR pszFile,int iIcon)148 HRESULT STDMETHODCALLTYPE CExtractIcon::SetOpenIcon(
149 LPCWSTR pszFile,
150 int iIcon)
151 {
152 TRACE("(%p, %s, %d)\n", this, debugstr_w(pszFile), iIcon);
153
154 DuplicateString(pszFile, &openIcon.file);
155 if (!openIcon.file)
156 return E_OUTOFMEMORY;
157 openIcon.index = iIcon;
158 return S_OK;
159 }
160
SetShortcutIcon(LPCWSTR pszFile,int iIcon)161 HRESULT STDMETHODCALLTYPE CExtractIcon::SetShortcutIcon(
162 LPCWSTR pszFile,
163 int iIcon)
164 {
165 TRACE("(%p, %s, %d)\n", this, debugstr_w(pszFile), iIcon);
166
167 DuplicateString(pszFile, &shortcutIcon.file);
168 if (!shortcutIcon.file)
169 return E_OUTOFMEMORY;
170 shortcutIcon.index = iIcon;
171 return S_OK;
172 }
173
GetIconLocation(UINT uFlags,LPWSTR szIconFile,UINT cchMax,int * piIndex,UINT * pwFlags)174 HRESULT STDMETHODCALLTYPE CExtractIcon::GetIconLocation(
175 UINT uFlags,
176 LPWSTR szIconFile,
177 UINT cchMax,
178 int *piIndex,
179 UINT *pwFlags)
180 {
181 const struct IconLocation *icon = NULL;
182 SIZE_T cb;
183
184 TRACE("(%p, 0x%x, %s, 0x%x, %p, %p)\n", this, uFlags, debugstr_w(szIconFile), cchMax, piIndex, pwFlags);
185
186 if (!piIndex || !pwFlags)
187 return E_POINTER;
188
189 if (uFlags & GIL_DEFAULTICON)
190 icon = defaultIcon.file ? &defaultIcon : &normalIcon;
191 else if (uFlags & GIL_FORSHORTCUT)
192 icon = shortcutIcon.file ? &shortcutIcon : &normalIcon;
193 else if (uFlags & GIL_OPENICON)
194 icon = openIcon.file ? &openIcon : &normalIcon;
195 else
196 icon = &normalIcon;
197
198 if (!icon->file)
199 return E_FAIL;
200
201 cb = wcslen(icon->file) + 1;
202 if (cchMax < (UINT)cb)
203 return E_FAIL;
204 CopyMemory(szIconFile, icon->file, cb * sizeof(WCHAR));
205 *piIndex = icon->index;
206 *pwFlags = flags;
207 return S_OK;
208 }
209
Extract(LPCWSTR pszFile,UINT nIconIndex,HICON * phiconLarge,HICON * phiconSmall,UINT nIconSize)210 HRESULT STDMETHODCALLTYPE CExtractIcon::Extract(
211 LPCWSTR pszFile,
212 UINT nIconIndex,
213 HICON *phiconLarge,
214 HICON *phiconSmall,
215 UINT nIconSize)
216 {
217 TRACE("(%p, %s, %u, %p, %p, %u)\n", this, debugstr_w(pszFile), nIconIndex, phiconLarge, phiconSmall, nIconSize);
218
219 /* Nothing to do, ExtractIconW::GetIconLocation should be enough */
220 return S_FALSE;
221 }
222
GetIconLocation(UINT uFlags,LPSTR szIconFile,UINT cchMax,int * piIndex,UINT * pwFlags)223 HRESULT STDMETHODCALLTYPE CExtractIcon::GetIconLocation(
224 UINT uFlags,
225 LPSTR szIconFile,
226 UINT cchMax,
227 int *piIndex,
228 UINT *pwFlags)
229 {
230 LPWSTR szIconFileW = NULL;
231 HRESULT hr;
232
233 if (cchMax > 0)
234 {
235 szIconFileW = (LPWSTR)CoTaskMemAlloc(cchMax * sizeof(WCHAR));
236 if (!szIconFileW)
237 return E_OUTOFMEMORY;
238 }
239
240 hr = GetIconLocation(uFlags, szIconFileW, cchMax, piIndex, pwFlags);
241 if (SUCCEEDED(hr) && cchMax > 0)
242 if (0 == WideCharToMultiByte(CP_ACP, 0, szIconFileW, cchMax, szIconFile, cchMax, NULL, NULL))
243 hr = E_FAIL;
244
245 if (szIconFileW)
246 CoTaskMemFree(szIconFileW);
247 return hr;
248 }
249
Extract(LPCSTR pszFile,UINT nIconIndex,HICON * phiconLarge,HICON * phiconSmall,UINT nIconSize)250 HRESULT STDMETHODCALLTYPE CExtractIcon::Extract(
251 LPCSTR pszFile,
252 UINT nIconIndex,
253 HICON *phiconLarge,
254 HICON *phiconSmall,
255 UINT nIconSize)
256 {
257 LPWSTR pszFileW = NULL;
258 HRESULT hr;
259
260 if (pszFile)
261 {
262 int nLength;
263
264 nLength = MultiByteToWideChar(CP_ACP, 0, pszFile, -1, NULL, 0);
265 if (nLength == 0)
266 return E_FAIL;
267 pszFileW = (LPWSTR)CoTaskMemAlloc(nLength * sizeof(WCHAR));
268 if (!pszFileW)
269 return E_OUTOFMEMORY;
270 if (!MultiByteToWideChar(CP_ACP, 0, pszFile, nLength, pszFileW, nLength))
271 {
272 CoTaskMemFree(pszFileW);
273 return E_FAIL;
274 }
275 }
276
277 hr = Extract(pszFileW, nIconIndex, phiconLarge, phiconSmall, nIconSize);
278
279 if (pszFileW)
280 CoTaskMemFree(pszFileW);
281 return hr;
282 }
283
GetClassID(CLSID * pClassID)284 HRESULT STDMETHODCALLTYPE CExtractIcon::GetClassID(
285 CLSID *pClassID)
286 {
287 TRACE("(%p, %p)\n", this, pClassID);
288
289 if (!pClassID)
290 return E_POINTER;
291
292 *pClassID = GUID_NULL;
293 return S_OK;
294 }
295
IsDirty()296 HRESULT STDMETHODCALLTYPE CExtractIcon::IsDirty()
297 {
298 FIXME("(%p)\n", this);
299 UNIMPLEMENTED;
300 return E_NOTIMPL;
301 }
302
Load(LPCOLESTR pszFileName,DWORD dwMode)303 HRESULT STDMETHODCALLTYPE CExtractIcon::Load(
304 LPCOLESTR pszFileName,
305 DWORD dwMode)
306 {
307 FIXME("(%p, %s, %u)\n", this, debugstr_w(pszFileName), dwMode);
308 UNIMPLEMENTED;
309 return E_NOTIMPL;
310 }
311
Save(LPCOLESTR pszFileName,BOOL fRemember)312 HRESULT STDMETHODCALLTYPE CExtractIcon::Save(
313 LPCOLESTR pszFileName,
314 BOOL fRemember)
315 {
316 FIXME("(%p, %s, %d)\n", this, debugstr_w(pszFileName), fRemember);
317 UNIMPLEMENTED;
318 return E_NOTIMPL;
319 }
320
SaveCompleted(LPCOLESTR pszFileName)321 HRESULT STDMETHODCALLTYPE CExtractIcon::SaveCompleted(
322 LPCOLESTR pszFileName)
323 {
324 FIXME("(%p, %s)\n", this, debugstr_w(pszFileName));
325 UNIMPLEMENTED;
326 return E_NOTIMPL;
327 }
328
GetCurFile(LPOLESTR * ppszFileName)329 HRESULT STDMETHODCALLTYPE CExtractIcon::GetCurFile(
330 LPOLESTR *ppszFileName)
331 {
332 FIXME("(%p, %p)\n", this, ppszFileName);
333 UNIMPLEMENTED;
334 return E_NOTIMPL;
335 }
336
SHCreateDefaultExtractIcon(REFIID riid,void ** ppv)337 HRESULT WINAPI SHCreateDefaultExtractIcon(REFIID riid, void **ppv)
338 {
339 return ShellObjectCreator<CExtractIcon>(riid, ppv);
340 }
341
342 /*
343 * Partially implemented
344 * See apitests\shell32\SHCreateFileExtractIconW.cpp for details
345 * Currently (march 2018) our shell does not handle IExtractIconW with an invalid path,
346 * so this (wrong) implementation actually works better for us.
347 */
348 EXTERN_C
349 HRESULT
350 WINAPI
SHCreateFileExtractIconW(_In_ LPCWSTR pszFile,_In_ DWORD dwFileAttributes,_In_ REFIID riid,_Outptr_ void ** ppv)351 SHCreateFileExtractIconW(
352 _In_ LPCWSTR pszFile,
353 _In_ DWORD dwFileAttributes,
354 _In_ REFIID riid,
355 _Outptr_ void **ppv)
356 {
357 SHFILEINFOW shfi;
358 ULONG_PTR firet = SHGetFileInfoW(pszFile, dwFileAttributes, &shfi, sizeof(shfi), SHGFI_USEFILEATTRIBUTES | SHGFI_ICONLOCATION);
359 HRESULT hr = E_FAIL;
360 if (firet)
361 {
362 CComPtr<IDefaultExtractIconInit> iconInit;
363 hr = SHCreateDefaultExtractIcon(IID_PPV_ARG(IDefaultExtractIconInit, &iconInit));
364 if (FAILED_UNEXPECTEDLY(hr))
365 return hr;
366
367 hr = iconInit->SetNormalIcon(shfi.szDisplayName, shfi.iIcon);
368 if (FAILED_UNEXPECTEDLY(hr))
369 return hr;
370
371 return iconInit->QueryInterface(riid, ppv);
372 }
373 if (FAILED_UNEXPECTEDLY(hr))
374 return hr;
375
376 return hr;
377 }
378