xref: /reactos/dll/win32/shell32/CExtractIcon.cpp (revision 49b2b1da)
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