Skip to content

Commit a2f94d5

Browse files
committed
Implemented WindowsIconResolutionService class as main implementation for IIconResolutionService. #167
1 parent e20dc58 commit a2f94d5

6 files changed

Lines changed: 304 additions & 5 deletions

File tree

src/shellextension/dllmain.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@
3939
#include "WindowsClipboardService.h"
4040
#include "WindowsKeyboardService.h"
4141
#include "PcgRandomService.h"
42-
#include "LegacyIconResolutionService.h"
42+
#include "WindowsIconResolutionService.h"
4343

4444
#include "shellanything/version.h"
4545
#include "shellanything/config.h"
@@ -201,7 +201,7 @@ extern "C" int APIENTRY DllMain(HINSTANCE hInstance, DWORD dwReason, LPVOID lpRe
201201
app.SetRandomService(random_service);
202202

203203
// Setup an active icon resolution service in ShellAnything's core.
204-
icon_resolution_service = new shellanything::LegacyIconResolutionService();
204+
icon_resolution_service = new shellanything::WindowsIconResolutionService();
205205
app.SetIconResolutionService(icon_resolution_service);
206206

207207
// Setup and starting application

src/tests/TestIcon.cpp

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -263,7 +263,15 @@ namespace shellanything
263263
ASSERT_TRUE(read) << "Failed to read log file: " << path;
264264

265265
int count = CountString(content, UNKNOWN_FILE_EXTENSION);
266-
static const int MAX_COUNT = 2; // Issue 167. A maximum of 2 reference is allowed. One from LegacyIconResolutionService class and another from Icon::ResolveFileExtensionIcon().
266+
267+
// Define how many reference we should see in log file.
268+
// With LegacyIconResolutionService class as active service, a maximum of 2 reference is allowed:
269+
// One from LegacyIconResolutionService class and another from Icon::ResolveFileExtensionIcon().
270+
//
271+
// With WindowsIconResolutionService class as active service, a maximum of 3 reference is allowed:
272+
// Two from WindowsIconResolutionService class and another from Icon::ResolveFileExtensionIcon().
273+
static const int MAX_COUNT = 3; // Issue #18. Issue #167.
274+
267275
ASSERT_LE(count, MAX_COUNT) << "Log file '" << path << "' contains " << count << " references to '" << UNKNOWN_FILE_EXTENSION << "'.";
268276
}
269277
}

src/tests/main.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@
5050
#include "WindowsClipboardService.h"
5151
#include "TestKeyboardService.h"
5252
#include "PcgRandomService.h"
53-
#include "LegacyIconResolutionService.h"
53+
#include "WindowsIconResolutionService.h"
5454
#include "ConfigManager.h"
5555

5656
using namespace ra;
@@ -149,7 +149,7 @@ int main(int argc, char** argv)
149149
app.SetRandomService(random_service);
150150

151151
// Setup an active icon resolution service in ShellAnything's core.
152-
shellanything::IIconResolutionService* icon_resolution_service = new shellanything::LegacyIconResolutionService();
152+
shellanything::IIconResolutionService* icon_resolution_service = new shellanything::WindowsIconResolutionService();
153153
app.SetIconResolutionService(icon_resolution_service);
154154

155155
//Issue #60 - Unit tests cannot execute from installation directory.

src/windows/CMakeLists.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@ add_library(sa.windows SHARED
66
Win32Clipboard.h
77
WindowsClipboardService.cpp
88
WindowsClipboardService.h
9+
WindowsIconResolutionService.cpp
10+
WindowsIconResolutionService.h
911
WindowsRegistryService.cpp
1012
WindowsRegistryService.h
1113
WindowsKeyboardService.cpp
Lines changed: 191 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,191 @@
1+
/**********************************************************************************
2+
* MIT License
3+
*
4+
* Copyright (c) 2018 Antoine Beauchamp
5+
*
6+
* Permission is hereby granted, free of charge, to any person obtaining a copy
7+
* of this software and associated documentation files (the "Software"), to deal
8+
* in the Software without restriction, including without limitation the rights
9+
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10+
* copies of the Software, and to permit persons to whom the Software is
11+
* furnished to do so, subject to the following conditions:
12+
*
13+
* The above copyright notice and this permission notice shall be included in all
14+
* copies or substantial portions of the Software.
15+
*
16+
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17+
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18+
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19+
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20+
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21+
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22+
* SOFTWARE.
23+
*********************************************************************************/
24+
25+
#include "WindowsIconResolutionService.h"
26+
#include "PropertyManager.h"
27+
#include "SelectionContext.h"
28+
#include "LoggerHelper.h"
29+
#include "App.h"
30+
#include "SaUtils.h"
31+
#include "Win32Registry.h"
32+
33+
#include "rapidassist/strings.h"
34+
#include "rapidassist/environment.h"
35+
#include "rapidassist/filesystem_utf8.h"
36+
#include "rapidassist/unicode.h"
37+
38+
#include <Windows.h>
39+
40+
namespace shellanything
41+
{
42+
WindowsIconResolutionService::WindowsIconResolutionService()
43+
{
44+
}
45+
46+
WindowsIconResolutionService::~WindowsIconResolutionService()
47+
{
48+
}
49+
50+
bool WindowsIconResolutionService::ResolveFileExtensionIcon(Icon& icon)
51+
{
52+
// Is this icon have a file extension ?
53+
std::string original_file_extension = icon.GetFileExtension();
54+
if (original_file_extension.empty())
55+
return false; //nothing to resolve
56+
57+
shellanything::PropertyManager& pmgr = shellanything::PropertyManager::GetInstance();
58+
std::string file_extension = pmgr.Expand(original_file_extension);
59+
if (file_extension.empty())
60+
return false; //nothing to resolve
61+
62+
// Check for multiple values. Keep the first value, forget about other selected file extensions.
63+
const std::string separator = pmgr.GetProperty(SelectionContext::MULTI_SELECTION_SEPARATOR_PROPERTY_NAME);
64+
if (file_extension.find(separator) != std::string::npos)
65+
{
66+
//multiple values detected.
67+
ra::strings::StringVector extension_list = ra::strings::Split(file_extension, separator.c_str());
68+
if (!extension_list.empty())
69+
file_extension = extension_list[0];
70+
}
71+
72+
// Search within previous resolutions
73+
IconResolutionInfoMap::const_iterator mapIt = mResolutions.find(file_extension);
74+
bool found = (mapIt != mResolutions.end());
75+
if (found)
76+
{
77+
const ICON_RESOLUTION_INFO& resolution = mapIt->second;
78+
icon.SetPath(resolution.path);
79+
icon.SetIndex(resolution.index);
80+
icon.SetFileExtension("");
81+
return true;
82+
}
83+
84+
// Generate an empty file with the required file extension
85+
IRandomService* random_service = App::GetInstance().GetRandomService();
86+
if (random_service == NULL)
87+
{
88+
SA_LOG(ERROR) << "No Random service configured for resolving file extensions to icon.";
89+
return false;
90+
}
91+
92+
SA_LOG(INFO) << "Resolving icon for file extension '" << file_extension << "'.";
93+
94+
// Generate a random filename in %TEMP% directory
95+
std::string temp_file_path;
96+
temp_file_path += ra::filesystem::GetTemporaryDirectoryUtf8();
97+
temp_file_path += ra::filesystem::GetPathSeparatorStr();
98+
temp_file_path += "icon_resolution"; // file prefix
99+
temp_file_path += ra::strings::ToString(random_service->GetRandomValue(10000,99999));
100+
temp_file_path += "."; // file postfix
101+
temp_file_path += file_extension;
102+
103+
// Create a blank file
104+
bool file_created = ra::filesystem::WriteFileUtf8(temp_file_path, file_extension);
105+
if (!file_created)
106+
{
107+
SA_LOG(ERROR) << "Failed to create sample file '" << temp_file_path << "' to resolve icon for file extension '" << file_extension << "'.";
108+
mFailures.insert(file_extension);
109+
return false;
110+
}
111+
112+
std::wstring temp_file_path_wide = ra::unicode::Utf8ToUnicode(temp_file_path);
113+
114+
// Try to get an actual icon
115+
HINSTANCE hInstance = GetModuleHandle(NULL);
116+
wchar_t buffer[2 * MAX_PATH];
117+
wsprintfW(buffer, L"%s", temp_file_path_wide.c_str());
118+
WORD official_index = 0;
119+
WORD official_id = 0;
120+
HICON hIcon = ExtractAssociatedIconExW(hInstance, buffer, &official_index, &official_id);
121+
std::wstring official_file_path_wide = buffer;
122+
123+
// Clean up
124+
ra::filesystem::DeleteFileUtf8(temp_file_path.c_str());
125+
if (hIcon)
126+
DestroyIcon(hIcon);
127+
128+
// Did we failed resolution?
129+
if (hIcon == NULL || official_file_path_wide == temp_file_path_wide)
130+
{
131+
DWORD dwLastError = GetLastError();
132+
Win32Registry::REGISTRY_ICON unknown_file_icon = Win32Registry::GetUnknownFileTypeIcon();
133+
SA_LOG(ERROR) <<
134+
"Failed to find icon for file extension '" << file_extension << "' with ExtractAssociatedIconExW(). GetLastError(): " << ToHexString(dwLastError) << ". "
135+
"Resolving icon with default icon for unknown file type '" << unknown_file_icon.path << "' with index '" << unknown_file_icon.index << "'";
136+
137+
// Remember this result
138+
ICON_RESOLUTION_INFO info;
139+
info.path = unknown_file_icon.path;
140+
info.index = unknown_file_icon.index;
141+
info.id = -1;
142+
ResolveWith(icon, file_extension, info);
143+
144+
// But also remember that its not a true resolution
145+
mFailures.insert(file_extension);
146+
147+
return true;
148+
}
149+
150+
// Remember this result
151+
ICON_RESOLUTION_INFO info;
152+
info.path = ra::unicode::UnicodeToUtf8(official_file_path_wide);
153+
info.index = official_index;
154+
info.id = official_id;
155+
ResolveWith(icon, file_extension, info);
156+
157+
SA_LOG(INFO) << "Resolved icon for file extension '" << file_extension << "' to file '" << info.path << "' with index '" << info.index << "'";
158+
159+
return true;
160+
}
161+
162+
bool WindowsIconResolutionService::HaveResolvedBefore(const std::string& file_extension) const
163+
{
164+
IconResolutionInfoMap::const_iterator mapIt = mResolutions.find(file_extension);
165+
bool found = (mapIt != mResolutions.end());
166+
if (found)
167+
return true;
168+
169+
// or did we resolved as a failure ?
170+
found = HaveFailedBefore(file_extension);
171+
return found;
172+
}
173+
174+
bool WindowsIconResolutionService::HaveFailedBefore(const std::string& file_extension) const
175+
{
176+
bool have_failed_before = mFailures.find(file_extension) != mFailures.end();
177+
return have_failed_before;
178+
}
179+
180+
void WindowsIconResolutionService::ResolveWith(Icon& icon, const std::string& file_extension, const ICON_RESOLUTION_INFO& info)
181+
{
182+
icon.SetPath(info.path);
183+
icon.SetIndex(info.index);
184+
icon.SetFileExtension("");
185+
186+
// Remember this result
187+
mResolutions[file_extension] = info;
188+
}
189+
190+
191+
} //namespace shellanything
Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,98 @@
1+
/**********************************************************************************
2+
* MIT License
3+
*
4+
* Copyright (c) 2018 Antoine Beauchamp
5+
*
6+
* Permission is hereby granted, free of charge, to any person obtaining a copy
7+
* of this software and associated documentation files (the "Software"), to deal
8+
* in the Software without restriction, including without limitation the rights
9+
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10+
* copies of the Software, and to permit persons to whom the Software is
11+
* furnished to do so, subject to the following conditions:
12+
*
13+
* The above copyright notice and this permission notice shall be included in all
14+
* copies or substantial portions of the Software.
15+
*
16+
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17+
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18+
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19+
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20+
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21+
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22+
* SOFTWARE.
23+
*********************************************************************************/
24+
25+
#ifndef SA_WINDOWS_ICON_RESOLUTION_SERVICE_H
26+
#define SA_WINDOWS_ICON_RESOLUTION_SERVICE_H
27+
28+
#include "sa_windows_export.h"
29+
#include "IIconResolutionService.h"
30+
31+
#include <map>
32+
33+
namespace shellanything
34+
{
35+
/// <summary>
36+
/// Windows implementation class of IIconResolutionService that resolves icon based on ExtractAssociatedIconEx().
37+
/// </summary>
38+
class SA_WINDOWS_EXPORT WindowsIconResolutionService : public virtual IIconResolutionService
39+
{
40+
public:
41+
WindowsIconResolutionService();
42+
virtual ~WindowsIconResolutionService();
43+
44+
private:
45+
// Disable and copy constructor, dtor and copy operator
46+
WindowsIconResolutionService(const WindowsIconResolutionService&);
47+
WindowsIconResolutionService& operator=(const WindowsIconResolutionService&);
48+
public:
49+
50+
/// <summary>
51+
/// Resolve an icon's file extension to the system icon for this file extension.
52+
/// </summary>
53+
/// <param name="icon">The icon that have its fileextension's attribute set to resolve.</param>
54+
/// <returns>Returns true if the operation is successful. Returns false otherwise.</returns>
55+
virtual bool ResolveFileExtensionIcon(Icon& icon);
56+
57+
/// <summary>
58+
/// Check if the given file extension have resolved before.
59+
/// </summary>
60+
/// <param name="file_entension">The file extension to check.</param>
61+
/// <returns>Returns true if the file extension has previously resolve to a valid icon. Returns false otherwise.</returns>
62+
virtual bool HaveResolvedBefore(const std::string& file_extension) const;
63+
64+
/// <summary>
65+
/// Check if the given file extension have previously failed to resolve.
66+
/// </summary>
67+
/// <param name="file_extension">The file extension to check.</param>
68+
/// <returns>Returns true if the file extension has previously failed to resolve. Returns false otherwise.</returns>
69+
virtual bool HaveFailedBefore(const std::string& file_extension) const;
70+
71+
private:
72+
//------------------------
73+
// Typedef
74+
//------------------------
75+
struct ICON_RESOLUTION_INFO
76+
{
77+
std::string path;
78+
int index;
79+
int id;
80+
};
81+
82+
/// <summary>
83+
/// Resolve an icon's with the given ICON_RESOLUTION_INFO.
84+
/// </summary>
85+
/// <param name="icon">The icon that have its fileextension's attribute set to resolve.</param>
86+
/// <param name="file_extension">The file extension specified by the icon.</param>
87+
/// <param name="info">The infomation to resolve the icon with.</param>
88+
void ResolveWith(Icon& icon, const std::string & file_extension, const ICON_RESOLUTION_INFO & info);
89+
90+
typedef std::map<std::string /*file extension*/, ICON_RESOLUTION_INFO /*info*/> IconResolutionInfoMap;
91+
typedef std::set<std::string /*file extension*/> FileExtensionSet;
92+
IconResolutionInfoMap mResolutions;
93+
FileExtensionSet mFailures;
94+
};
95+
96+
} //namespace shellanything
97+
98+
#endif //SA_WINDOWS_ICON_RESOLUTION_SERVICE_H

0 commit comments

Comments
 (0)