Skip to content

Commit 83543f4

Browse files
committed
Init
1 parent 80c9b43 commit 83543f4

File tree

9 files changed

+230
-149
lines changed

9 files changed

+230
-149
lines changed

src/Files.App.CsWin32/ComHeapPtr`1.cs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,15 @@ public ComHeapPtr(T* ptr)
2323
_ptr = ptr;
2424
}
2525

26+
[MethodImpl(MethodImplOptions.AggressiveInlining)]
27+
public void Attach(T* other)
28+
{
29+
if (_ptr is not null)
30+
((IUnknown*)_ptr)->Release();
31+
32+
_ptr = other;
33+
}
34+
2635
[MethodImpl(MethodImplOptions.AggressiveInlining)]
2736
public readonly T* Get()
2837
{

src/Files.App.CsWin32/ManualGuid.cs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,9 @@ public static Guid* IID_IStorageProviderStatusUISourceFactory
6262

6363
[GuidRVAGen.Guid("000214F4-0000-0000-C000-000000000046")]
6464
public static partial Guid* IID_IContextMenu2 { get; }
65+
66+
[GuidRVAGen.Guid("0000010E-0000-0000-C000-000000000046")]
67+
public static partial Guid* IID_IDataObject { get; }
6568
}
6669

6770
public static unsafe partial class CLSID
@@ -89,6 +92,9 @@ public static unsafe partial class CLSID
8992

9093
[GuidRVAGen.Guid("D969A300-E7FF-11d0-A93B-00A0C90F2719")]
9194
public static partial Guid* CLSID_NewMenu { get; }
95+
96+
[GuidRVAGen.Guid("09799AFB-AD67-11D1-ABCD-00C04FC30936")]
97+
public static partial Guid* CLSID_OpenWithMenu { get; }
9298
}
9399

94100
public static unsafe partial class BHID

src/Files.App.CsWin32/NativeMethods.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -236,3 +236,5 @@ GetMenuItemCount
236236
GetMenuItemInfo
237237
IsWow64Process2
238238
GetCurrentProcess
239+
ILFindLastID
240+
SHCreateDataObject

src/Files.App.Storage/Storables/WindowsStorage/ContextMenuItem.cs

Lines changed: 0 additions & 19 deletions
This file was deleted.
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
// Copyright (c) Files Community
2+
// Licensed under the MIT License.
3+
4+
namespace Files.App.Storage
5+
{
6+
/// <summary>
7+
/// Represents a Windows Shell ContextMenu item.
8+
/// </summary>
9+
public partial record WindowsContextMenuItem(uint Id = 0U, string? Name = null, byte[]? Icon = null, WindowsContextMenuType Type = WindowsContextMenuType.String, WindowsContextMenuState State = WindowsContextMenuState.Enabled);
10+
}

src/Files.App.Storage/Storables/WindowsStorage/ContextMenuType.cs renamed to src/Files.App.Storage/Storables/WindowsStorage/WindowsContextMenuState.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,9 @@
33

44
namespace Files.App.Storage
55
{
6-
public enum ContextMenuType
6+
public enum WindowsContextMenuState : uint
77
{
8-
Normal = 0x00000000,
8+
Enabled = 0x00000000,
99

1010
Disabled = 0x00000003,
1111

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
// Copyright (c) Files Community
2+
// Licensed under the MIT License.
3+
4+
namespace Files.App.Storage
5+
{
6+
[Flags]
7+
public enum WindowsContextMenuType : uint
8+
{
9+
Bitmap = 0x00000004,
10+
11+
MenuBarBreak = 0x00000020,
12+
13+
MenuBreak = 0x00000040,
14+
15+
OwnerDraw = 0x00000100,
16+
17+
RadioCheck = 0x00000200,
18+
19+
RightJustify = 0x00004000,
20+
21+
RightOrder = 0x00002000,
22+
23+
Separator = 0x00000800,
24+
25+
String = 0x00000000,
26+
}
27+
}

src/Files.App.Storage/Storables/WindowsStorage/WindowsStorableHelpers.Icon.cs

Lines changed: 96 additions & 118 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
// Licensed under the MIT License.
33

44
using System.Collections.Concurrent;
5+
using System.Runtime.CompilerServices;
56
using System.Runtime.InteropServices;
67
using Windows.Win32;
78
using Windows.Win32.Foundation;
@@ -44,168 +45,145 @@ public unsafe static HRESULT TryGetThumbnail(this IWindowsStorable storable, int
4445
{
4546
thumbnailData = null;
4647

47-
using ComPtr<IShellItemImageFactory> pShellItemImageFactory = default;
48-
storable.ThisPtr->QueryInterface(IID.IID_IShellItemImageFactory, (void**)pShellItemImageFactory.GetAddressOf());
49-
if (pShellItemImageFactory.IsNull)
50-
return HRESULT.E_NOINTERFACE;
51-
52-
// Get HBITMAP
5348
HBITMAP hBitmap = default;
54-
HRESULT hr = pShellItemImageFactory.Get()->GetImage(new(size, size), options, &hBitmap);
55-
if (hr.ThrowIfFailedOnDebug().Failed)
56-
{
57-
if (!hBitmap.IsNull) PInvoke.DeleteObject(hBitmap);
58-
return hr;
59-
}
49+
GpBitmap* gpBitmap = null;
6050

61-
// Retrieve BITMAP data
62-
BITMAP bmp = default;
63-
if (PInvoke.GetObject(hBitmap, sizeof(BITMAP), &bmp) is 0)
51+
try
6452
{
65-
if (!hBitmap.IsNull) PInvoke.DeleteObject(hBitmap);
66-
return HRESULT.E_FAIL;
67-
}
53+
using ComPtr<IShellItemImageFactory> pShellItemImageFactory = default;
54+
storable.ThisPtr->QueryInterface(IID.IID_IShellItemImageFactory, (void**)pShellItemImageFactory.GetAddressOf());
55+
if (pShellItemImageFactory.IsNull)
56+
return HRESULT.E_NOINTERFACE;
6857

69-
// Allocate buffer for flipped pixel data
70-
byte* flippedBits = (byte*)NativeMemory.AllocZeroed((nuint)(bmp.bmWidthBytes * bmp.bmHeight));
58+
HRESULT hr = pShellItemImageFactory.Get()->GetImage(new(size, size), options, &hBitmap);
59+
if (hr.ThrowIfFailedOnDebug().Failed) return hr;
7160

72-
// Flip the image manually row by row
73-
for (int y = 0; y < bmp.bmHeight; y++)
74-
{
75-
Buffer.MemoryCopy(
76-
(byte*)bmp.bmBits + y * bmp.bmWidthBytes,
77-
flippedBits + (bmp.bmHeight - y - 1) * bmp.bmWidthBytes,
78-
bmp.bmWidthBytes,
79-
bmp.bmWidthBytes
80-
);
81-
}
61+
gpBitmap = ConvertHBITMAPToGpBitmap(hBitmap);
62+
if (gpBitmap is null) return HRESULT.E_FAIL;
8263

83-
// Create GpBitmap from the flipped pixel data
84-
GpBitmap* gpBitmap = default;
85-
if (PInvoke.GdipCreateBitmapFromScan0(bmp.bmWidth, bmp.bmHeight, bmp.bmWidthBytes, PInvoke.PixelFormat32bppARGB, flippedBits, &gpBitmap) != Status.Ok)
86-
{
87-
if (flippedBits is not null) NativeMemory.Free(flippedBits);
88-
if (!hBitmap.IsNull) PInvoke.DeleteObject(hBitmap);
89-
return HRESULT.E_FAIL;
64+
return (thumbnailData = ConvertGpBitmapToByteArray(gpBitmap)) is null ? HRESULT.E_FAIL : HRESULT.S_OK;
9065
}
91-
92-
if (!TryConvertGpBitmapToByteArray(gpBitmap, out thumbnailData))
66+
finally
9367
{
68+
if (gpBitmap is not null) PInvoke.GdipDisposeImage((GpImage*)gpBitmap);
9469
if (!hBitmap.IsNull) PInvoke.DeleteObject(hBitmap);
95-
return HRESULT.E_FAIL;
9670
}
97-
98-
if (flippedBits is not null) NativeMemory.Free(flippedBits);
99-
if (!hBitmap.IsNull) PInvoke.DeleteObject(hBitmap);
100-
101-
return HRESULT.S_OK;
10271
}
10372

10473
public unsafe static HRESULT TryExtractImageFromDll(this IWindowsStorable storable, int size, int index, out byte[]? imageData)
10574
{
10675
DllIconCache ??= [];
10776
imageData = null;
77+
HICON hIcon = default;
78+
GpBitmap* gpBitmap = default;
10879

10980
if (storable.ToString() is not { } path)
11081
return HRESULT.E_INVALIDARG;
11182

112-
if (DllIconCache.TryGetValue((path, index, size), out var cachedImageData))
113-
{
114-
imageData = cachedImageData;
115-
return HRESULT.S_OK;
116-
}
117-
else
83+
try
11884
{
119-
HICON hIcon = default;
120-
HRESULT hr = default;
121-
122-
fixed (char* pszPath = path)
123-
hr = PInvoke.SHDefExtractIcon(pszPath, -1 * index, 0, &hIcon, null, (uint)size);
124-
125-
if (hr.ThrowIfFailedOnDebug().Failed)
85+
if (DllIconCache.TryGetValue((path, index, size), out var cachedImageData))
12686
{
127-
if (!hIcon.IsNull) PInvoke.DestroyIcon(hIcon);
128-
return hr;
87+
imageData = cachedImageData;
88+
return HRESULT.S_OK;
12989
}
130-
131-
// Convert to GpBitmap of GDI+
132-
GpBitmap* gpBitmap = default;
133-
if (PInvoke.GdipCreateBitmapFromHICON(hIcon, &gpBitmap) is not Status.Ok)
90+
else
13491
{
135-
if (!hIcon.IsNull) PInvoke.DestroyIcon(hIcon);
136-
return HRESULT.E_FAIL;
137-
}
92+
HRESULT hr = default;
13893

139-
if (!TryConvertGpBitmapToByteArray(gpBitmap, out imageData) || imageData is null)
140-
{
141-
if (!hIcon.IsNull) PInvoke.DestroyIcon(hIcon);
142-
return HRESULT.E_FAIL;
143-
}
94+
fixed (char* pszPath = path)
95+
hr = PInvoke.SHDefExtractIcon(pszPath, -1 * index, 0, &hIcon, null, (uint)size);
96+
if (hr.ThrowIfFailedOnDebug().Failed) return hr;
14497

145-
DllIconCache[(path, index, size)] = imageData;
146-
if (!hIcon.IsNull) PInvoke.DestroyIcon(hIcon);
98+
// Convert to GpBitmap of GDI+
99+
if (PInvoke.GdipCreateBitmapFromHICON(hIcon, &gpBitmap) is not Status.Ok) return HRESULT.E_FAIL;
100+
101+
imageData = ConvertGpBitmapToByteArray(gpBitmap);
102+
if (imageData is null) return HRESULT.E_FAIL;
147103

148-
return HRESULT.S_OK;
104+
DllIconCache[(path, index, size)] = imageData;
105+
106+
return HRESULT.S_OK;
107+
}
149108
}
109+
finally
110+
{
111+
if (!hIcon.IsNull) PInvoke.DestroyIcon(hIcon);
112+
}
113+
150114
}
151115

152-
public unsafe static bool TryConvertGpBitmapToByteArray(GpBitmap* gpBitmap, out byte[]? imageData)
116+
public unsafe static GpBitmap* ConvertHBITMAPToGpBitmap(HBITMAP hBitmap)
153117
{
154-
imageData = null;
118+
BITMAP bmp = default;
119+
byte* flippedBits = null;
120+
GpBitmap* gpBitmap = null;
155121

156-
// Get an encoder for PNG
157-
Guid format = Guid.Empty;
158-
if (PInvoke.GdipGetImageRawFormat((GpImage*)gpBitmap, &format) is not Status.Ok)
122+
try
159123
{
160-
if (gpBitmap is not null) PInvoke.GdipDisposeImage((GpImage*)gpBitmap);
161-
return false;
162-
}
124+
// Retrieve BITMAP data
125+
if (PInvoke.GetObject(hBitmap, sizeof(BITMAP), &bmp) is 0) return null;
163126

164-
Guid encoder = GetEncoderClsid(format);
165-
if (format == PInvoke.ImageFormatJPEG || encoder == Guid.Empty)
166-
{
167-
format = PInvoke.ImageFormatPNG;
168-
encoder = GetEncoderClsid(format);
169-
}
127+
// Flip the image manually row by row
128+
flippedBits = (byte*)NativeMemory.AllocZeroed((nuint)(bmp.bmWidthBytes * bmp.bmHeight));
129+
for (int y = 0; y < bmp.bmHeight; y++)
130+
Buffer.MemoryCopy((byte*)bmp.bmBits + y * bmp.bmWidthBytes, flippedBits + (bmp.bmHeight - y - 1) * bmp.bmWidthBytes, bmp.bmWidthBytes, bmp.bmWidthBytes);
170131

171-
using ComPtr<IStream> pStream = default;
172-
HRESULT hr = PInvoke.CreateStreamOnHGlobal(HGLOBAL.Null, true, pStream.GetAddressOf());
173-
if (hr.ThrowIfFailedOnDebug().Failed)
174-
{
175-
if (gpBitmap is not null) PInvoke.GdipDisposeImage((GpImage*)gpBitmap);
176-
return false;
177-
}
132+
// Create GpBitmap from the flipped pixel data
133+
Status status = PInvoke.GdipCreateBitmapFromScan0(bmp.bmWidth, bmp.bmHeight, bmp.bmWidthBytes, PInvoke.PixelFormat32bppARGB, flippedBits, &gpBitmap);
134+
if (status is not Status.Ok) return null;
178135

179-
if (PInvoke.GdipSaveImageToStream((GpImage*)gpBitmap, pStream.Get(), &encoder, (EncoderParameters*)null) is not Status.Ok)
180-
{
181-
if (gpBitmap is not null) PInvoke.GdipDisposeImage((GpImage*)gpBitmap);
182-
return false;
136+
return gpBitmap;
183137
}
184-
185-
STATSTG stat = default;
186-
hr = pStream.Get()->Stat(&stat, (uint)STATFLAG.STATFLAG_NONAME);
187-
if (hr.ThrowIfFailedOnDebug().Failed)
138+
finally
188139
{
189-
if (gpBitmap is not null) PInvoke.GdipDisposeImage((GpImage*)gpBitmap);
190-
return false;
140+
if (flippedBits is not null) NativeMemory.Free(flippedBits);
191141
}
142+
}
192143

193-
ulong statSize = stat.cbSize & 0xFFFFFFFF;
194-
byte* RawThumbnailData = (byte*)NativeMemory.Alloc((nuint)statSize);
144+
public unsafe static byte[]? ConvertGpBitmapToByteArray(GpBitmap* gpBitmap)
145+
{
146+
byte* pRawThumbnailData = null;
195147

196-
pStream.Get()->Seek(0L, (SystemIO.SeekOrigin)STREAM_SEEK.STREAM_SEEK_SET, null);
197-
hr = pStream.Get()->Read(RawThumbnailData, (uint)statSize);
198-
if (hr.ThrowIfFailedOnDebug().Failed)
148+
try
199149
{
200-
if (gpBitmap is not null) PInvoke.GdipDisposeImage((GpImage*)gpBitmap);
201-
if (RawThumbnailData is not null) NativeMemory.Free(RawThumbnailData);
202-
return false;
203-
}
150+
// Get an encoder for PNG
151+
Guid format = Guid.Empty;
152+
Status status = PInvoke.GdipGetImageRawFormat((GpImage*)gpBitmap, &format);
153+
if (status is not Status.Ok) return null;
204154

205-
imageData = new ReadOnlySpan<byte>(RawThumbnailData, (int)statSize / sizeof(byte)).ToArray();
206-
NativeMemory.Free(RawThumbnailData);
155+
Guid encoder = GetEncoderClsid(format);
156+
if (format == PInvoke.ImageFormatJPEG || encoder == Guid.Empty)
157+
{
158+
// Default to PNG
159+
format = PInvoke.ImageFormatPNG;
160+
encoder = GetEncoderClsid(format);
161+
}
207162

208-
return true;
163+
using ComPtr<IStream> pStream = default;
164+
HRESULT hr = PInvoke.CreateStreamOnHGlobal(HGLOBAL.Null, true, pStream.GetAddressOf());
165+
if (hr.ThrowIfFailedOnDebug().Failed) return null;
166+
167+
status = PInvoke.GdipSaveImageToStream((GpImage*)gpBitmap, pStream.Get(), &encoder, (EncoderParameters*)null);
168+
if (status is not Status.Ok) return null;
169+
170+
STATSTG stat = default;
171+
hr = pStream.Get()->Stat(&stat, (uint)STATFLAG.STATFLAG_NONAME);
172+
if (hr.ThrowIfFailedOnDebug().Failed) return null;
173+
174+
ulong statSize = stat.cbSize & 0xFFFFFFFF;
175+
pRawThumbnailData = (byte*)NativeMemory.Alloc((nuint)statSize);
176+
177+
pStream.Get()->Seek(0L, (SystemIO.SeekOrigin)STREAM_SEEK.STREAM_SEEK_SET, null);
178+
hr = pStream.Get()->Read(pRawThumbnailData, (uint)statSize);
179+
if (hr.ThrowIfFailedOnDebug().Failed) return null;
180+
181+
return new ReadOnlySpan<byte>(pRawThumbnailData, (int)statSize / sizeof(byte)).ToArray();
182+
}
183+
finally
184+
{
185+
if (pRawThumbnailData is not null) NativeMemory.Free(pRawThumbnailData);
186+
}
209187

210188
Guid GetEncoderClsid(Guid format)
211189
{

0 commit comments

Comments
 (0)