Skip to content

Commit 4b297b0

Browse files
authored
Merge pull request #168 from end2endzone/feature-issue164 #164 #155
Merge branch feature-issue164.
2 parents 9e6558e + 5e8e791 commit 4b297b0

8 files changed

Lines changed: 238 additions & 14 deletions

File tree

CHANGES

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,13 @@
11
Changes for 0.10.0
22

3-
* Deprecated support for icons with negative resource id.
43
* Fixed issue #6 : (twice) Right-click on a directory with Windows Explorer in the left panel shows the menus twice.
54
* Fixed issue #31 : (twice) Error in logs for CContextMenu::GetCommandString()
65
* Fixed issue #109: Implement default and verbose logging.
76
* Fixed issue #150: ico icon (that do not specifically force index=0) are not working.
8-
* Fixed issue #155: Drop support for loading icons from a resource id (icons with a negative index smaller than -1).
97
* Fixed issue #157: Compilation fails on Github Action: `fatal error C1083: Cannot open include file: 'atlbase.h': No such file or directory`.
108
* Fixed issue #158: Compilation fails on Github Action: `CPack error : Problem running WiX.`.
119
* Fixed issue #159: Unit test fails on Github Actions: TestPlugins.testServices().
10+
* Fixed issue #64: Fails to identify icon for HTML files.
1211

1312

1413
Changes for 0.9.0

src/core/Icon.cpp

Lines changed: 38 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -50,25 +50,42 @@ namespace shellanything
5050
{
5151
}
5252

53-
const Icon& Icon::operator =(const Icon& icon)
53+
const Icon& Icon::operator =(const Icon& other)
5454
{
55-
if (this != &icon)
55+
if (this != &other)
5656
{
57-
mFileExtension = icon.mFileExtension;
58-
mPath = icon.mPath;
59-
mIndex = icon.mIndex;
57+
mFileExtension = other.mFileExtension;
58+
mPath = other.mPath;
59+
mIndex = other.mIndex;
6060
}
6161
return (*this);
6262
}
6363

64+
bool Icon::operator ==(const Icon& other) const
65+
{
66+
if (this == &other)
67+
return true;
68+
if ((mFileExtension == other.mFileExtension) &&
69+
(mPath == other.mPath) &&
70+
(mIndex == other.mIndex))
71+
return true;
72+
return false;
73+
}
74+
6475
bool Icon::IsValid() const
6576
{
6677
if (!mFileExtension.empty())
6778
return true;
68-
if (!mPath.empty() && mIndex >= 0) // not a resource id. See Issue #17, #150, #155
79+
80+
//See issue #17, 155, 164.
81+
//An icon with a negative index is valid from the registry.
82+
//Only the special case index = -1 should be considered invalid (Issue #17).
83+
//And ShellAnything accept positive (index) and negative index (resource id). (Issue #155, Issue #164).
84+
if (!mPath.empty() && mIndex != INVALID_ICON_INDEX)
6985
return true;
86+
7087
return false;
71-
}
88+
}
7289

7390
void Icon::ResolveFileExtensionIcon()
7491
{
@@ -89,7 +106,12 @@ namespace shellanything
89106

90107
//try to find the path to the icon module for the given file extension.
91108
Win32Registry::REGISTRY_ICON resolved_icon = Win32Registry::GetFileTypeIcon(file_extension.c_str());
92-
if (!resolved_icon.path.empty() && resolved_icon.index >= 0) // See Issue #17 #155. Do not accept icons which are resource ids.
109+
110+
//An icon with a negative index is valid from the registry.
111+
//Only the special case index = -1 should be considered invalid (Issue #17).
112+
//And ShellAnything accept positive (index) and negative index (resource id). (Issue #155, Issue #164).
113+
//See issue #17, 155, 164.
114+
if (Win32Registry::IsValid(resolved_icon))
93115
{
94116
//found the icon for the file extension
95117
//replace this menu's icon with the new information
@@ -150,6 +172,14 @@ namespace shellanything
150172
mIndex = index;
151173
}
152174

175+
Icon Icon::GetDefaultUnknownFileTypeIcon()
176+
{
177+
Icon tmp;
178+
tmp.SetFileExtension("this_file_extension_is_not_registered_on_system");
179+
tmp.ResolveFileExtensionIcon();
180+
return tmp;
181+
}
182+
153183
std::string Icon::ToShortString() const
154184
{
155185
std::string str;

src/core/Icon.h

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,20 @@ namespace shellanything
5353
/// <summary>
5454
/// Copy operator
5555
/// </summary>
56-
const Icon& operator =(const Icon& icon);
56+
const Icon& operator =(const Icon& other);
57+
58+
/// <summary>
59+
/// Equal operator
60+
/// </summary>
61+
bool operator ==(const Icon& other) const;
62+
63+
/// <summary>
64+
/// Not Equal operator
65+
/// </summary>
66+
inline bool operator !=(const Icon& other) const
67+
{
68+
return !((*this) == other);
69+
}
5770

5871
/// <summary>
5972
/// Returns true if the icon have a valid path and index.
@@ -96,6 +109,11 @@ namespace shellanything
96109
/// </summary>
97110
void SetIndex(const int& index);
98111

112+
/// <summary>
113+
/// Get the system's default visual icon for unknown file type.
114+
/// </summary>
115+
static Icon GetDefaultUnknownFileTypeIcon();
116+
99117
// IObject methods
100118
virtual std::string ToShortString() const;
101119
virtual void ToLongString(std::string& str, int indent) const;

src/shared/Win32Registry.cpp

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -684,8 +684,13 @@ namespace Win32Registry
684684
{
685685
if (icon.path.empty())
686686
return false;
687-
if (icon.index == INVALID_ICON_INDEX) //See issue #17
687+
688+
//See issue #17, 155, 164.
689+
//An icon with a negative index is valid from the registry.
690+
//Only the special case index = -1 should be considered invalid (Issue #17).
691+
if (icon.index == INVALID_ICON_INDEX)
688692
return false;
693+
689694
if (IsIconEquals(icon, NULL_ICON))
690695
return false;
691696
return true;

src/shared/Win32Utils.cpp

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1427,6 +1427,49 @@ namespace Win32Utils
14271427
return hReturn;
14281428
}
14291429

1430+
HBITMAP LoadIconFromFileAsBitmap32Bpp(const char* filename, int index)
1431+
{
1432+
HBITMAP hBitmap = INVALID_BITMAP_HANDLE;
1433+
HICON hIconSmall = NULL;
1434+
1435+
std::wstring icon_filename_wide = ra::unicode::Utf8ToUnicode(filename);
1436+
1437+
//check how many icons the file contains
1438+
UINT num_icon_in_file = ExtractIconExW(icon_filename_wide.c_str(), -1, NULL, NULL, 1);
1439+
if (num_icon_in_file == 0)
1440+
{
1441+
// ERROR: "File '" << filename << "' does not contains an icon.";
1442+
}
1443+
else
1444+
{
1445+
//the file contains 1 or more icons, try to load a small one
1446+
UINT num_icon_loaded = 0;
1447+
if (num_icon_in_file >= 1)
1448+
num_icon_loaded = ExtractIconExW(icon_filename_wide.c_str(), index, NULL, &hIconSmall, 1);
1449+
if (num_icon_in_file >= 1 && num_icon_loaded == 0)
1450+
{
1451+
// ERROR: "Failed to load icon index " << icon_index << " from file '" << icon_filename << "'.";
1452+
}
1453+
else
1454+
{
1455+
SIZE menu_icon_size = Win32Utils::GetIconSize(hIconSmall);
1456+
// ERROR: "Loaded icon " << icon_index << " from file '" << icon_filename << "' is " << menu_icon_size.cx << "x" << menu_icon_size.cy << ".";
1457+
1458+
//Convert the icon to a 32bpp bitmap with alpha channel (invisible background)
1459+
hBitmap = Win32Utils::CopyAsBitmap(hIconSmall);
1460+
if (hBitmap == INVALID_BITMAP_HANDLE)
1461+
{
1462+
// ERROR: "Icon " << icon_index << " from file '" << icon_filename << "' has failed to convert to bitmap.";
1463+
}
1464+
1465+
if (hIconSmall != NULL)
1466+
DestroyIcon(hIconSmall);
1467+
}
1468+
}
1469+
1470+
return hBitmap;
1471+
}
1472+
14301473
BOOL IsFullyTransparent(HBITMAP hBitmap)
14311474
{
14321475
SIZE bitmap_size = GetBitmapSize(hBitmap);

src/shared/Win32Utils.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,8 @@
3030

3131
namespace Win32Utils
3232
{
33+
static const HBITMAP INVALID_BITMAP_HANDLE = NULL;
34+
3335
void GetWindowsVersion(int& major, int& minor);
3436
std::string GetWindowsProductName();
3537
bool EnableMonitorDpiAwareness();
@@ -59,6 +61,7 @@ namespace Win32Utils
5961
bool SaveAs32BppBitmapV3File(const char* path, HBITMAP hBitmap);
6062
bool SaveBitmapFile(const char* path, HBITMAP hBitmap);
6163
HBITMAP LoadBitmapFromFile(const char* path);
64+
HBITMAP LoadIconFromFileAsBitmap32Bpp(const char* filename, int index);
6265
BOOL IsFullyTransparent(HBITMAP hBitmap);
6366
BOOL IsFullyTransparent(const std::string& buffer);
6467
std::string GetMenuItemDetails(HMENU hMenu, UINT pos);

src/tests/TestIcon.cpp

Lines changed: 65 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,39 @@ namespace shellanything
6565
{
6666
}
6767
//--------------------------------------------------------------------------------------------------
68+
TEST_F(TestIcon, testOperatorEquals)
69+
{
70+
Icon iconA;
71+
Icon iconB;
72+
73+
#define ASSERT_ICON_EQUALS(iconA, iconB) ASSERT_TRUE(iconA == iconB); ASSERT_FALSE(iconA != iconB);
74+
#define ASSERT_ICON_NOT_EQUALS(iconA, iconB) ASSERT_TRUE(iconA != iconB); ASSERT_FALSE(iconA == iconB);
75+
76+
// assert equals
77+
ASSERT_ICON_EQUALS(iconA, iconB);
78+
79+
iconA.SetFileExtension("txt");
80+
ASSERT_ICON_NOT_EQUALS(iconA, iconB);
81+
82+
iconA.SetFileExtension("exe");
83+
iconB.SetFileExtension("exe");
84+
ASSERT_ICON_EQUALS(iconA, iconB);
85+
86+
iconA.SetPath("foo.dll");
87+
ASSERT_ICON_NOT_EQUALS(iconA, iconB);
88+
89+
iconA.SetPath("bar.dll");
90+
iconB.SetPath("bar.dll");
91+
ASSERT_ICON_EQUALS(iconA, iconB);
92+
93+
iconA.SetIndex(11);
94+
ASSERT_ICON_NOT_EQUALS(iconA, iconB);
95+
96+
iconA.SetIndex(99);
97+
iconB.SetIndex(99);
98+
ASSERT_ICON_EQUALS(iconA, iconB);
99+
}
100+
//--------------------------------------------------------------------------------------------------
68101
TEST_F(TestIcon, testValidIcon)
69102
{
70103
// default ctor
@@ -100,11 +133,11 @@ namespace shellanything
100133
Icon icon;
101134
icon.SetPath("path/to/a/file.ico");
102135
icon.SetIndex(-99);
103-
ASSERT_FALSE(icon.IsValid());
136+
ASSERT_TRUE(icon.IsValid());
104137
}
105138
}
106139
//--------------------------------------------------------------------------------------------------
107-
TEST_F(TestIcon, testResolveFileExtensionIcon)
140+
TEST_F(TestIcon, testResolveFileExtensionIconTxt)
108141
{
109142
Icon icon;
110143
icon.SetFileExtension("txt");
@@ -115,6 +148,36 @@ namespace shellanything
115148
ASSERT_TRUE(icon.GetFileExtension().empty());
116149
ASSERT_FALSE(icon.GetPath().empty());
117150
ASSERT_NE(Icon::INVALID_ICON_INDEX, icon.GetIndex());
151+
152+
// assert we did not resolve to default unknown icon
153+
ASSERT_TRUE(icon != Icon::GetDefaultUnknownFileTypeIcon());
154+
}
155+
//--------------------------------------------------------------------------------------------------
156+
TEST_F(TestIcon, testResolveFileExtensionIconHtml)
157+
{
158+
Icon icon;
159+
icon.SetFileExtension("html");
160+
161+
//act
162+
icon.ResolveFileExtensionIcon();
163+
164+
ASSERT_TRUE(icon.GetFileExtension().empty());
165+
ASSERT_FALSE(icon.GetPath().empty());
166+
ASSERT_NE(Icon::INVALID_ICON_INDEX, icon.GetIndex());
167+
168+
// assert we did not resolve to default unknown icon
169+
ASSERT_TRUE(icon != Icon::GetDefaultUnknownFileTypeIcon());
170+
}
171+
//--------------------------------------------------------------------------------------------------
172+
TEST_F(TestIcon, testGetDefaultUnknownFileTypeIcon)
173+
{
174+
Icon icon = Icon::GetDefaultUnknownFileTypeIcon();
175+
176+
ASSERT_TRUE(icon.IsValid());
177+
ASSERT_TRUE(icon.GetFileExtension().empty());
178+
179+
ASSERT_FALSE(icon.GetPath().empty());
180+
ASSERT_NE(Icon::INVALID_ICON_INDEX, icon.GetIndex());
118181
}
119182
//--------------------------------------------------------------------------------------------------
120183
TEST_F(TestIcon, testResolveMultipleFileExtensionIcon) // issue #98

src/tests/TestWin32Utils.cpp

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828
#pragma warning( pop )
2929

3030
#include "TestWin32Utils.h"
31+
#include "Icon.h"
3132
#include "Win32Utils.h"
3233
#include "ArgumentsHandler.h"
3334

@@ -652,6 +653,68 @@ namespace shellanything
652653
}
653654
}
654655
//--------------------------------------------------------------------------------------------------
656+
TEST_F(TestWin32Utils, testNegativeIconIndexIssue155) // Issue #155
657+
{
658+
Icon icon;
659+
660+
// On my system, with current implementation,
661+
// Icon::ResolveFileExtensionIcon() resolves HTML file extension to the following:
662+
icon.SetPath("C:\\Program Files\\Internet Explorer\\iexplore.exe");
663+
icon.SetIndex(-17);
664+
665+
// act, load an icon with negative index
666+
HBITMAP hBitmap = Win32Utils::LoadIconFromFileAsBitmap32Bpp(icon.GetPath().c_str(), icon.GetIndex());
667+
ASSERT_NE(hBitmap, Win32Utils::INVALID_BITMAP_HANDLE);
668+
669+
// build output files
670+
std::string test_dir = ra::filesystem::GetTemporaryDirectory();
671+
ASSERT_TRUE(ra::filesystem::CreateDirectory(test_dir.c_str()));
672+
std::string bitmap_filename = ra::testing::GetTestQualifiedName() + ".bmp";
673+
std::string output_path = test_dir + "\\" + bitmap_filename;
674+
675+
// remove data from previous runs
676+
ra::filesystem::DeleteFile(output_path.c_str());
677+
678+
// Save as *.bmp
679+
bool saved = Win32Utils::SaveBitmapFile(output_path.c_str(), hBitmap);
680+
ASSERT_TRUE(saved) << "Failed to save bitmap to file: " << output_path;
681+
682+
// Cleanup
683+
DeleteObject(hBitmap);
684+
}
685+
//--------------------------------------------------------------------------------------------------
686+
TEST_F(TestWin32Utils, testNegativeIconIndexIssue164) // Issue #164
687+
{
688+
Icon icon;
689+
690+
// On my system, Icon::ResolveFileExtensionIcon() resolves 'txt' file extension to a negative icon index
691+
icon.SetFileExtension("txt");
692+
icon.ResolveFileExtensionIcon();
693+
694+
ASSERT_TRUE(icon.IsValid());
695+
ASSERT_TRUE(icon.GetIndex() < 1);
696+
697+
// act, load an icon with negative index
698+
HBITMAP hBitmap = Win32Utils::LoadIconFromFileAsBitmap32Bpp(icon.GetPath().c_str(), icon.GetIndex());
699+
ASSERT_NE(hBitmap, Win32Utils::INVALID_BITMAP_HANDLE);
700+
701+
// build output files
702+
std::string test_dir = ra::filesystem::GetTemporaryDirectory();
703+
ASSERT_TRUE(ra::filesystem::CreateDirectory(test_dir.c_str()));
704+
std::string bitmap_filename = ra::testing::GetTestQualifiedName() + ".bmp";
705+
std::string output_path = test_dir + "\\" + bitmap_filename;
706+
707+
// remove data from previous runs
708+
ra::filesystem::DeleteFile(output_path.c_str());
709+
710+
// Save as *.bmp
711+
bool saved = Win32Utils::SaveBitmapFile(output_path.c_str(), hBitmap);
712+
ASSERT_TRUE(saved) << "Failed to save bitmap to file: " << output_path;
713+
714+
// Cleanup
715+
DeleteObject(hBitmap);
716+
}
717+
//--------------------------------------------------------------------------------------------------
655718

656719
} //namespace test
657720
} //namespace shellanything

0 commit comments

Comments
 (0)