Centralise lifetime management of native ImageList

Move calls to Create, Duplicate and Destroy into NativeImageList to
reduce a chance of interleaved calls to Win32 API.
This commit is contained in:
Igor Velikorossov 2020-06-30 20:48:23 +10:00
Родитель 33ac99ea6f
Коммит 9647c6b0b3
18 изменённых файлов: 148 добавлений и 34 удалений

Просмотреть файл

@ -14,13 +14,6 @@ internal partial class Interop
{
[DllImport(Libraries.Comctl32, ExactSpelling = true, EntryPoint = "ImageList_Duplicate")]
public static extern IntPtr Duplicate(IntPtr himl);
public static IntPtr Duplicate(IHandle himl)
{
IntPtr result = Duplicate(himl.Handle);
GC.KeepAlive(himl);
return result;
}
}
}
}

Просмотреть файл

@ -3344,6 +3344,9 @@ Stack trace where the illegal operation occurred was:
<data name="ImageListCreateFailed" xml:space="preserve">
<value>Creation of the ImageList handle did not succeed.</value>
</data>
<data name="ImageListDuplicateFailed" xml:space="preserve">
<value>Duplication of the ImageList handle did not succeed.</value>
</data>
<data name="ImageListEntryType" xml:space="preserve">
<value>Image added to an ImageList must either derive from Image or be an Icon.</value>
</data>

5
src/System.Windows.Forms/src/Resources/xlf/SR.cs.xlf сгенерированный
Просмотреть файл

@ -5556,6 +5556,11 @@ Trasování zásobníku, kde došlo k neplatné operaci:
<target state="translated">Vytvoření popisovače ImageList se nezdařilo.</target>
<note />
</trans-unit>
<trans-unit id="ImageListDuplicateFailed">
<source>Duplication of the ImageList handle did not succeed.</source>
<target state="new">Duplication of the ImageList handle did not succeed.</target>
<note />
</trans-unit>
<trans-unit id="ImageListEntryType">
<source>Image added to an ImageList must either derive from Image or be an Icon.</source>
<target state="translated">Obrázek přidaný do seznamu ImageList musí být buď odvozen od objektu Image, nebo musí být objektem Icon.</target>

5
src/System.Windows.Forms/src/Resources/xlf/SR.de.xlf сгенерированный
Просмотреть файл

@ -5556,6 +5556,11 @@ Stapelüberwachung, in der der unzulässige Vorgang auftrat:
<target state="translated">Fehler beim Erstellen des ImageList-Handles.</target>
<note />
</trans-unit>
<trans-unit id="ImageListDuplicateFailed">
<source>Duplication of the ImageList handle did not succeed.</source>
<target state="new">Duplication of the ImageList handle did not succeed.</target>
<note />
</trans-unit>
<trans-unit id="ImageListEntryType">
<source>Image added to an ImageList must either derive from Image or be an Icon.</source>
<target state="translated">Das zu einer ImageList hinzugefügte Bild muss von "Image" abgeleitet oder ein Symbol sein.</target>

5
src/System.Windows.Forms/src/Resources/xlf/SR.es.xlf сгенерированный
Просмотреть файл

@ -5556,6 +5556,11 @@ El seguimiento de la pila donde tuvo lugar la operación no válida fue:
<target state="translated">Error al crear el identificador de ImageList.</target>
<note />
</trans-unit>
<trans-unit id="ImageListDuplicateFailed">
<source>Duplication of the ImageList handle did not succeed.</source>
<target state="new">Duplication of the ImageList handle did not succeed.</target>
<note />
</trans-unit>
<trans-unit id="ImageListEntryType">
<source>Image added to an ImageList must either derive from Image or be an Icon.</source>
<target state="translated">Las imágenes que se agreguen a ImageList deben derivar de una imagen o ser un icono.</target>

5
src/System.Windows.Forms/src/Resources/xlf/SR.fr.xlf сгенерированный
Просмотреть файл

@ -5556,6 +5556,11 @@ Cette opération non conforme s'est produite sur la trace de la pile :
<target state="translated">Échec de la création du handle ImageList.</target>
<note />
</trans-unit>
<trans-unit id="ImageListDuplicateFailed">
<source>Duplication of the ImageList handle did not succeed.</source>
<target state="new">Duplication of the ImageList handle did not succeed.</target>
<note />
</trans-unit>
<trans-unit id="ImageListEntryType">
<source>Image added to an ImageList must either derive from Image or be an Icon.</source>
<target state="translated">Une image ajoutée à ImageList doit dériver de Image ou être un Icon.</target>

5
src/System.Windows.Forms/src/Resources/xlf/SR.it.xlf сгенерированный
Просмотреть файл

@ -5556,6 +5556,11 @@ Traccia dello stack da cui si è verificata l'operazione non valida:
<target state="translated">Creazione dell'handle di ImageList non riuscita.</target>
<note />
</trans-unit>
<trans-unit id="ImageListDuplicateFailed">
<source>Duplication of the ImageList handle did not succeed.</source>
<target state="new">Duplication of the ImageList handle did not succeed.</target>
<note />
</trans-unit>
<trans-unit id="ImageListEntryType">
<source>Image added to an ImageList must either derive from Image or be an Icon.</source>
<target state="translated">Un'immagine aggiunta a ImageList deve essere un'icona o derivare da Image.</target>

5
src/System.Windows.Forms/src/Resources/xlf/SR.ja.xlf сгенерированный
Просмотреть файл

@ -5556,6 +5556,11 @@ Stack trace where the illegal operation occurred was:
<target state="translated">ImageList ハンドルを作成できませんでした。</target>
<note />
</trans-unit>
<trans-unit id="ImageListDuplicateFailed">
<source>Duplication of the ImageList handle did not succeed.</source>
<target state="new">Duplication of the ImageList handle did not succeed.</target>
<note />
</trans-unit>
<trans-unit id="ImageListEntryType">
<source>Image added to an ImageList must either derive from Image or be an Icon.</source>
<target state="translated">ImageList に追加されるイメージは Image から派生しているか、またはアイコンでなければなりません。</target>

5
src/System.Windows.Forms/src/Resources/xlf/SR.ko.xlf сгенерированный
Просмотреть файл

@ -5556,6 +5556,11 @@ Stack trace where the illegal operation occurred was:
<target state="translated">ImageList 핸들을 만들지 못했습니다.</target>
<note />
</trans-unit>
<trans-unit id="ImageListDuplicateFailed">
<source>Duplication of the ImageList handle did not succeed.</source>
<target state="new">Duplication of the ImageList handle did not succeed.</target>
<note />
</trans-unit>
<trans-unit id="ImageListEntryType">
<source>Image added to an ImageList must either derive from Image or be an Icon.</source>
<target state="translated">ImageList에 추가하는 이미지는 이미지에서 파생되거나 아이콘이어야 합니다.</target>

5
src/System.Windows.Forms/src/Resources/xlf/SR.pl.xlf сгенерированный
Просмотреть файл

@ -5556,6 +5556,11 @@ Stos śledzenia, w którym wystąpiła zabroniona operacja:
<target state="translated">Nie można utworzyć uchwytu elementu ImageList.</target>
<note />
</trans-unit>
<trans-unit id="ImageListDuplicateFailed">
<source>Duplication of the ImageList handle did not succeed.</source>
<target state="new">Duplication of the ImageList handle did not succeed.</target>
<note />
</trans-unit>
<trans-unit id="ImageListEntryType">
<source>Image added to an ImageList must either derive from Image or be an Icon.</source>
<target state="translated">Obraz dodany do listy ImageList musi dziedziczyć po elemencie Image lub musi być elementem Icon.</target>

Просмотреть файл

@ -5556,6 +5556,11 @@ Rastreamento de pilha em que a operação ilegal ocorreu:
<target state="translated">A criação do identificador de ImageList não teve êxito.</target>
<note />
</trans-unit>
<trans-unit id="ImageListDuplicateFailed">
<source>Duplication of the ImageList handle did not succeed.</source>
<target state="new">Duplication of the ImageList handle did not succeed.</target>
<note />
</trans-unit>
<trans-unit id="ImageListEntryType">
<source>Image added to an ImageList must either derive from Image or be an Icon.</source>
<target state="translated">A imagem adicionada a ImageList deve derivar de Image ou ser um Ícone.</target>

5
src/System.Windows.Forms/src/Resources/xlf/SR.ru.xlf сгенерированный
Просмотреть файл

@ -5557,6 +5557,11 @@ Stack trace where the illegal operation occurred was:
<target state="translated">Не удалось создать дескриптор ImageList.</target>
<note />
</trans-unit>
<trans-unit id="ImageListDuplicateFailed">
<source>Duplication of the ImageList handle did not succeed.</source>
<target state="new">Duplication of the ImageList handle did not succeed.</target>
<note />
</trans-unit>
<trans-unit id="ImageListEntryType">
<source>Image added to an ImageList must either derive from Image or be an Icon.</source>
<target state="translated">Изображение, добавляемое к ImageList, должно иметь тип, производный от Image, либо тип Icon.</target>

5
src/System.Windows.Forms/src/Resources/xlf/SR.tr.xlf сгенерированный
Просмотреть файл

@ -5556,6 +5556,11 @@ Geçersiz işlemin gerçekleştiği yığın izi:
<target state="translated">ImageList işleyicisi oluşturulamadı.</target>
<note />
</trans-unit>
<trans-unit id="ImageListDuplicateFailed">
<source>Duplication of the ImageList handle did not succeed.</source>
<target state="new">Duplication of the ImageList handle did not succeed.</target>
<note />
</trans-unit>
<trans-unit id="ImageListEntryType">
<source>Image added to an ImageList must either derive from Image or be an Icon.</source>
<target state="translated">ImageList'e eklenen resim Image'den türetilmeli veya Icon olmalıdır.</target>

Просмотреть файл

@ -5556,6 +5556,11 @@ Stack trace where the illegal operation occurred was:
<target state="translated">创建 ImageList 句柄失败。</target>
<note />
</trans-unit>
<trans-unit id="ImageListDuplicateFailed">
<source>Duplication of the ImageList handle did not succeed.</source>
<target state="new">Duplication of the ImageList handle did not succeed.</target>
<note />
</trans-unit>
<trans-unit id="ImageListEntryType">
<source>Image added to an ImageList must either derive from Image or be an Icon.</source>
<target state="translated">添加到 ImageList 的图像必须从 Image 派生或者为 Icon。</target>

Просмотреть файл

@ -5556,6 +5556,11 @@ Stack trace where the illegal operation occurred was:
<target state="translated">建立 ImageList 控制代碼失敗。</target>
<note />
</trans-unit>
<trans-unit id="ImageListDuplicateFailed">
<source>Duplication of the ImageList handle did not succeed.</source>
<target state="new">Duplication of the ImageList handle did not succeed.</target>
<note />
</trans-unit>
<trans-unit id="ImageListEntryType">
<source>Image added to an ImageList must either derive from Image or be an Icon.</source>
<target state="translated">只有衍生自 Image 或本身為 Icon 的影像才可以加入至 ImageList 中。</target>

Просмотреть файл

@ -3,7 +3,9 @@
// See the LICENSE file in the project root for more information.
using System.Diagnostics;
using System.Drawing;
using static Interop;
using static Interop.ComCtl32;
namespace System.Windows.Forms
{
@ -11,30 +13,61 @@ namespace System.Windows.Forms
{
internal class NativeImageList : IDisposable, IHandle
{
#if DEBUG
private readonly string _callStack;
#endif
private const int GrowBy = 4;
private const int InitialCapacity = 4;
internal NativeImageList(IntPtr himl)
private static readonly object s_syncLock = new object();
public NativeImageList(Ole32.IStream pstm)
{
IntPtr himl;
lock (s_syncLock)
{
himl = ComCtl32.ImageList.Read(pstm);
Init(himl);
}
}
public NativeImageList(Size imageSize, ILC flags)
{
IntPtr himl;
lock (s_syncLock)
{
himl = ComCtl32.ImageList.Create(imageSize.Width, imageSize.Height, flags, InitialCapacity, GrowBy);
Init(himl);
}
}
private NativeImageList(IntPtr himl)
{
Handle = himl;
}
private void Init(IntPtr himl)
{
if (himl != IntPtr.Zero)
{
Handle = himl;
return;
}
#if DEBUG
_callStack = Environment.StackTrace;
Debug.Fail($"{nameof(NativeImageList)} could not be created. Originating stack:\n{_callStack}");
#endif
throw new InvalidOperationException(SR.ImageListCreateFailed);
}
public IntPtr Handle { get; private set; }
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
lock (s_syncLock)
{
if (Handle == IntPtr.Zero)
{
return;
}
public void Dispose(bool disposing)
{
if (Handle != IntPtr.Zero)
{
ComCtl32.ImageList.Destroy(Handle);
Handle = IntPtr.Zero;
}
@ -44,13 +77,35 @@ namespace System.Windows.Forms
#endif
}
#if DEBUG
private readonly string _callStack = new StackTrace().ToString();
~NativeImageList()
{
#if DEBUG
Debug.Fail($"{nameof(NativeImageList)} was not disposed properly. Originating stack:\n{_callStack}");
// We can't do anything with the fields when we're on the finalizer as they're all classes. If any of
// them become structs they'll be a part of this instance and possible to clean up. Ideally we fix
// the leaks and never come in on the finalizer.
return;
}
#endif
Dispose(false);
internal NativeImageList Duplicate()
{
lock (s_syncLock)
{
IntPtr himl = ComCtl32.ImageList.Duplicate(Handle);
if (himl != IntPtr.Zero)
{
return new NativeImageList(himl);
}
}
#if DEBUG
Debug.Fail($"{nameof(NativeImageList)} could not be duplicated. Originating stack:\n{_callStack}");
#endif
throw new InvalidOperationException(SR.ImageListDuplicateFailed);
}
}
}

Просмотреть файл

@ -33,9 +33,6 @@ namespace System.Windows.Forms
private static readonly Color s_fakeTransparencyColor = Color.FromArgb(0x0d, 0x0b, 0x0c);
private static readonly Size s_defaultImageSize = new Size(16, 16);
private const int InitialCapacity = 4;
private const int GrowBy = 4;
private const int MaxDimension = 256;
private static int s_maxImageWidth = MaxDimension;
private static int s_maxImageHeight = MaxDimension;
@ -235,7 +232,7 @@ namespace System.Windows.Forms
bool recreatingHandle = HandleCreated; // We only need to fire RecreateHandle if there was a previous handle
DestroyHandle();
_originals = null;
_nativeImageList = new NativeImageList(ComCtl32.ImageList.Duplicate(himl));
_nativeImageList = himl.Duplicate();
if (ComCtl32.ImageList.GetIconSize(new HandleRef(this, _nativeImageList.Handle), out int x, out int y).IsTrue())
{
_imageSize = new Size(x, y);
@ -460,18 +457,20 @@ namespace System.Windows.Forms
try
{
ComCtl32.InitCommonControls();
_nativeImageList = new NativeImageList(ComCtl32.ImageList.Create(_imageSize.Width, _imageSize.Height, flags, InitialCapacity, GrowBy));
if (_nativeImageList != null)
{
_nativeImageList.Dispose();
_nativeImageList = null;
}
_nativeImageList = new NativeImageList(_imageSize, flags);
}
finally
{
ThemingScope.Deactivate(userCookie);
}
if (Handle == IntPtr.Zero)
{
throw new InvalidOperationException(SR.ImageListCreateFailed);
}
ComCtl32.ImageList.SetBkColor(this, ComCtl32.CLR.NONE);
Debug.Assert(_originals != null, "Handle not yet created, yet original images are gone");

Просмотреть файл

@ -54,12 +54,11 @@ namespace System.Windows.Forms
try
{
MemoryStream ms = new MemoryStream(Decompress(dat));
using MemoryStream ms = new MemoryStream(Decompress(dat));
lock (internalSyncObject)
{
ComCtl32.InitCommonControls();
nativeImageList = new ImageList.NativeImageList(ComCtl32.ImageList.Read(new Ole32.GPStream(ms)));
nativeImageList = new ImageList.NativeImageList(new Ole32.GPStream(ms));
}
}
finally