diff --git a/src/System.Windows.Forms.Primitives/tests/UnitTests/Interop/Mocks/MockCursor.cs b/src/System.Windows.Forms.Primitives/tests/UnitTests/Interop/Mocks/MockCursor.cs index 2e2e9dd50f1..618ef825740 100644 --- a/src/System.Windows.Forms.Primitives/tests/UnitTests/Interop/Mocks/MockCursor.cs +++ b/src/System.Windows.Forms.Primitives/tests/UnitTests/Interop/Mocks/MockCursor.cs @@ -2,7 +2,9 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. +using System.ComponentModel; using System.Drawing; +using System.Runtime.InteropServices; namespace System.Windows.Forms.Primitives.Tests.Interop.Mocks { @@ -18,37 +20,23 @@ internal MockCursor(PCWSTR nResourceId) _ownHandle = false; _resourceId = nResourceId; _handle = PInvoke.LoadCursor(HINSTANCE.Null, nResourceId); + if (_handle.IsNull) + { + throw new Win32Exception(Marshal.GetLastWin32Error()); + } } public void Dispose() { - if (!_handle.IsNull) + if (!_handle.IsNull && _ownHandle) { - if (_ownHandle) - { - PInvoke.DestroyCursor(_handle); - } - + PInvoke.DestroyCursor(_handle); _handle = HCURSOR.Null; } } - internal HCURSOR Handle - { - get - { - if (_handle.IsNull) - { - throw new ObjectDisposedException(nameof(MockCursor)); - } - - return _handle; - } - } + internal HCURSOR Handle => _handle.IsNull ? throw new ObjectDisposedException(nameof(MockCursor)) : _handle; - public Size Size - { - get => SystemInformation.CursorSize; - } + public Size Size => SystemInformation.CursorSize; } } diff --git a/src/System.Windows.Forms/src/Resources/SR.resx b/src/System.Windows.Forms/src/Resources/SR.resx index ef0874fa370..716535574f5 100644 --- a/src/System.Windows.Forms/src/Resources/SR.resx +++ b/src/System.Windows.Forms/src/Resources/SR.resx @@ -6961,4 +6961,7 @@ Stack trace where the illegal operation occurred was: Argument '{0}' must be of type '{1}'. + + Failed to load cursor. Error '{1}'. + \ No newline at end of file diff --git a/src/System.Windows.Forms/src/Resources/xlf/SR.cs.xlf b/src/System.Windows.Forms/src/Resources/xlf/SR.cs.xlf index 05180451eef..bb2a0ad2415 100644 --- a/src/System.Windows.Forms/src/Resources/xlf/SR.cs.xlf +++ b/src/System.Windows.Forms/src/Resources/xlf/SR.cs.xlf @@ -5021,6 +5021,11 @@ Pokud kliknete na Pokračovat, aplikace bude tuto chybu ignorovat a pokusí se p Určuje, zda dialogové okno zajišťuje, že názvy souborů neobsahují neplatné znaky nebo posloupnosti. + + Failed to load cursor. Error '{1}'. + Failed to load cursor. Error '{1}'. + + Controls whether to add the file being opened or saved to the recent list. Určuje, jestli se má soubor, který se otevírá nebo ukládá, přidat do seznamu posledních souborů. diff --git a/src/System.Windows.Forms/src/Resources/xlf/SR.de.xlf b/src/System.Windows.Forms/src/Resources/xlf/SR.de.xlf index a2eaf7b8fba..34459e9fbe0 100644 --- a/src/System.Windows.Forms/src/Resources/xlf/SR.de.xlf +++ b/src/System.Windows.Forms/src/Resources/xlf/SR.de.xlf @@ -5021,6 +5021,11 @@ Wenn Sie auf "Weiter" klicken, ignoriert die Anwendung den Fehler und setzt den Steuert, ob das Dialogfeld sicherstellt, dass Dateinamen keine ungültigen Zeichen oder Sequenzen enthalten. + + Failed to load cursor. Error '{1}'. + Failed to load cursor. Error '{1}'. + + Controls whether to add the file being opened or saved to the recent list. Steuert, ob die Datei, die geöffnet oder gespeichert wird, zur Liste zuletzt verwendeten Elemente hinzugefügt wird. diff --git a/src/System.Windows.Forms/src/Resources/xlf/SR.es.xlf b/src/System.Windows.Forms/src/Resources/xlf/SR.es.xlf index e972a42b1d8..41be70b3be7 100644 --- a/src/System.Windows.Forms/src/Resources/xlf/SR.es.xlf +++ b/src/System.Windows.Forms/src/Resources/xlf/SR.es.xlf @@ -5021,6 +5021,11 @@ Si hace clic en Continuar, la aplicación pasará por alto este error e intentar Controla si el cuadro de diálogo comprueba que los nombres de archivo no contienen caracteres o secuencias no válidas. + + Failed to load cursor. Error '{1}'. + Failed to load cursor. Error '{1}'. + + Controls whether to add the file being opened or saved to the recent list. Controla si se va a agregar el archivo que se va a abrir o guardar en la lista reciente. diff --git a/src/System.Windows.Forms/src/Resources/xlf/SR.fr.xlf b/src/System.Windows.Forms/src/Resources/xlf/SR.fr.xlf index ab9bc0ee5ac..2de87108bdf 100644 --- a/src/System.Windows.Forms/src/Resources/xlf/SR.fr.xlf +++ b/src/System.Windows.Forms/src/Resources/xlf/SR.fr.xlf @@ -5021,6 +5021,11 @@ Si vous cliquez sur Continuer, l'application va ignorer cette erreur et tenter d Contrôle si la boîte de dialogue s'assure que les noms de fichiers ne contiennent pas de caractères ou de séquences non valides. + + Failed to load cursor. Error '{1}'. + Failed to load cursor. Error '{1}'. + + Controls whether to add the file being opened or saved to the recent list. Contrôle s'il faut ajouter le fichier en cours d'ouverture ou d'enregistrement à la liste récente. diff --git a/src/System.Windows.Forms/src/Resources/xlf/SR.it.xlf b/src/System.Windows.Forms/src/Resources/xlf/SR.it.xlf index 071f0e3da7d..8e0e20377ad 100644 --- a/src/System.Windows.Forms/src/Resources/xlf/SR.it.xlf +++ b/src/System.Windows.Forms/src/Resources/xlf/SR.it.xlf @@ -5021,6 +5021,11 @@ Se si sceglie Continua, l'errore verrà ignorato e l'applicazione proverà a con Determina se la finestra di dialogo ha il compito di verificare che i nomi di file non contengano caratteri o sequenze non valide. + + Failed to load cursor. Error '{1}'. + Failed to load cursor. Error '{1}'. + + Controls whether to add the file being opened or saved to the recent list. Controlla se aggiungere il file aperto o salvato nell'elenco degli elementi recenti. diff --git a/src/System.Windows.Forms/src/Resources/xlf/SR.ja.xlf b/src/System.Windows.Forms/src/Resources/xlf/SR.ja.xlf index 9255b6946f9..a63d9ed61b3 100644 --- a/src/System.Windows.Forms/src/Resources/xlf/SR.ja.xlf +++ b/src/System.Windows.Forms/src/Resources/xlf/SR.ja.xlf @@ -5021,6 +5021,11 @@ If you click Continue, the application will ignore this error and attempt to con 無効な文字やシーケンスがファイル名に含まれていないことをダイアログ ボックスが確認するかどうかを設定します。 + + Failed to load cursor. Error '{1}'. + Failed to load cursor. Error '{1}'. + + Controls whether to add the file being opened or saved to the recent list. 開いているファイルを追加するか、最近使用したリストに保存するかを制御します。 diff --git a/src/System.Windows.Forms/src/Resources/xlf/SR.ko.xlf b/src/System.Windows.Forms/src/Resources/xlf/SR.ko.xlf index c79cc8b3db4..4990a37135d 100644 --- a/src/System.Windows.Forms/src/Resources/xlf/SR.ko.xlf +++ b/src/System.Windows.Forms/src/Resources/xlf/SR.ko.xlf @@ -5021,6 +5021,11 @@ If you click Continue, the application will ignore this error and attempt to con 대화 상자에서 파일 이름에 잘못된 문자나 시퀀스가 없는지 확인할지 여부를 제어합니다. + + Failed to load cursor. Error '{1}'. + Failed to load cursor. Error '{1}'. + + Controls whether to add the file being opened or saved to the recent list. 연 파일 또는 저장한 파일을 최근 목록에 추가할지를 제어합니다. diff --git a/src/System.Windows.Forms/src/Resources/xlf/SR.pl.xlf b/src/System.Windows.Forms/src/Resources/xlf/SR.pl.xlf index 23158017a94..1eb7e8b2db3 100644 --- a/src/System.Windows.Forms/src/Resources/xlf/SR.pl.xlf +++ b/src/System.Windows.Forms/src/Resources/xlf/SR.pl.xlf @@ -5021,6 +5021,11 @@ Jeśli klikniesz pozycję Kontynuuj, aplikacja zignoruje ten błąd i będzie pr Kontroluje, czy w oknie dialogowym nazwy plików nie zawierają nieprawidłowych znaków ani sekwencji. + + Failed to load cursor. Error '{1}'. + Failed to load cursor. Error '{1}'. + + Controls whether to add the file being opened or saved to the recent list. Określa, czy dodać otwierany lub zapisywany plik do listy ostatnio używanych. diff --git a/src/System.Windows.Forms/src/Resources/xlf/SR.pt-BR.xlf b/src/System.Windows.Forms/src/Resources/xlf/SR.pt-BR.xlf index 027be9a27c6..5dcc95af440 100644 --- a/src/System.Windows.Forms/src/Resources/xlf/SR.pt-BR.xlf +++ b/src/System.Windows.Forms/src/Resources/xlf/SR.pt-BR.xlf @@ -5021,6 +5021,11 @@ Se você clicar em Continuar, o aplicativo ignorará esse erro e tentará contin Controla se a caixa de diálogo assegura que os nomes de arquivos não contém cadeias ou caracteres inválidos. + + Failed to load cursor. Error '{1}'. + Failed to load cursor. Error '{1}'. + + Controls whether to add the file being opened or saved to the recent list. Controla se o arquivo que está sendo aberto ou salvo na lista recente deve ser adicionado. diff --git a/src/System.Windows.Forms/src/Resources/xlf/SR.ru.xlf b/src/System.Windows.Forms/src/Resources/xlf/SR.ru.xlf index dd972040721..da9ff7d64ff 100644 --- a/src/System.Windows.Forms/src/Resources/xlf/SR.ru.xlf +++ b/src/System.Windows.Forms/src/Resources/xlf/SR.ru.xlf @@ -5022,6 +5022,11 @@ If you click Continue, the application will ignore this error and attempt to con Определяет, проверяет ли диалоговое окно имена файлов на наличие недопустимых символов или последовательностей. + + Failed to load cursor. Error '{1}'. + Failed to load cursor. Error '{1}'. + + Controls whether to add the file being opened or saved to the recent list. Определяет, следует ли добавлять открываемый или сохраненный файл в список "Последние". diff --git a/src/System.Windows.Forms/src/Resources/xlf/SR.tr.xlf b/src/System.Windows.Forms/src/Resources/xlf/SR.tr.xlf index 153f31728b6..88eb2cebcaa 100644 --- a/src/System.Windows.Forms/src/Resources/xlf/SR.tr.xlf +++ b/src/System.Windows.Forms/src/Resources/xlf/SR.tr.xlf @@ -5021,6 +5021,11 @@ Devam'a tıkladığınızda uygulama bu hatayı yoksayar ve işlemi sürdürmeye İletişim kutusunun dosya adlarında geçersiz karakterler ve sıralar içermediğinden emin olup olmayacağını denetler. + + Failed to load cursor. Error '{1}'. + Failed to load cursor. Error '{1}'. + + Controls whether to add the file being opened or saved to the recent list. Açılmakta olan veya kaydedilen dosyanın son listeye eklenip eklenmeyeceğini kontrol eder. diff --git a/src/System.Windows.Forms/src/Resources/xlf/SR.zh-Hans.xlf b/src/System.Windows.Forms/src/Resources/xlf/SR.zh-Hans.xlf index e9bd42be763..66b160073f3 100644 --- a/src/System.Windows.Forms/src/Resources/xlf/SR.zh-Hans.xlf +++ b/src/System.Windows.Forms/src/Resources/xlf/SR.zh-Hans.xlf @@ -5021,6 +5021,11 @@ If you click Continue, the application will ignore this error and attempt to con 控制对话框是否确保文件名中不包含无效的字符或序列。 + + Failed to load cursor. Error '{1}'. + Failed to load cursor. Error '{1}'. + + Controls whether to add the file being opened or saved to the recent list. 控制是否将打开或保存的文件添加到最近使用项列表中。 diff --git a/src/System.Windows.Forms/src/Resources/xlf/SR.zh-Hant.xlf b/src/System.Windows.Forms/src/Resources/xlf/SR.zh-Hant.xlf index ee07d32baa2..42918d30856 100644 --- a/src/System.Windows.Forms/src/Resources/xlf/SR.zh-Hant.xlf +++ b/src/System.Windows.Forms/src/Resources/xlf/SR.zh-Hant.xlf @@ -5021,6 +5021,11 @@ If you click Continue, the application will ignore this error and attempt to con 控制對話方塊是否要確認檔名並未包含無效的字元或逸出字元。 + + Failed to load cursor. Error '{1}'. + Failed to load cursor. Error '{1}'. + + Controls whether to add the file being opened or saved to the recent list. 控制是否新增開啟或儲存至最近使用之清單的檔案。 diff --git a/src/System.Windows.Forms/src/System/Windows/Forms/Cursor.cs b/src/System.Windows.Forms/src/System/Windows/Forms/Cursor.cs index 4cd0b7a81bf..bed16cdd109 100644 --- a/src/System.Windows.Forms/src/System/Windows/Forms/Cursor.cs +++ b/src/System.Windows.Forms/src/System/Windows/Forms/Cursor.cs @@ -39,6 +39,10 @@ internal unsafe Cursor(PCWSTR nResourceId) _ownHandle = false; _resourceId = nResourceId; _handle = PInvoke.LoadCursor((HINSTANCE)0, nResourceId); + if (_handle.IsNull) + { + throw new Win32Exception(string.Format(SR.FailedToLoadCursor, Marshal.GetLastWin32Error())); + } } /// @@ -222,13 +226,9 @@ public void Dispose() private void Dispose(bool disposing) { - if (!_handle.IsNull) + if (!_handle.IsNull && _ownHandle) { - if (_ownHandle) - { - PInvoke.DestroyCursor(_handle); - } - + PInvoke.DestroyCursor(_handle); _handle = HCURSOR.Null; } } @@ -430,6 +430,11 @@ private unsafe void LoadPicture(IStream.Interface stream, string paramName) picSize.Height, IMAGE_FLAGS.LR_DEFAULTCOLOR).Value; + if (_handle.IsNull) + { + throw new Win32Exception(string.Format(SR.FailedToLoadCursor, Marshal.GetLastWin32Error())); + } + _ownHandle = true; } else @@ -461,6 +466,8 @@ internal unsafe byte[] GetData() return (byte[])_cursorData.Clone(); } + internal bool IsValid() => _handle != IntPtr.Zero; + /// /// Displays the cursor. For every call to Cursor.show() there must have been /// a previous call to Cursor.hide(). diff --git a/src/System.Windows.Forms/src/System/Windows/Forms/Cursors.cs b/src/System.Windows.Forms/src/System/Windows/Forms/Cursors.cs index b12b52770de..28412c34048 100644 --- a/src/System.Windows.Forms/src/System/Windows/Forms/Cursors.cs +++ b/src/System.Windows.Forms/src/System/Windows/Forms/Cursors.cs @@ -65,33 +65,37 @@ public static class Cursors public static Cursor WaitCursor => s_wait ??= new Cursor(PInvoke.IDC_WAIT); public static Cursor Help => s_help ??= new Cursor(PInvoke.IDC_HELP); + public static Cursor Hand => s_hand ??= new Cursor(PInvoke.IDC_HAND); - public static Cursor HSplit => s_hSplit ??= new Cursor(typeof(Cursor), "hsplit.cur"); + public static Cursor HSplit => GetCursor(ref s_hSplit, "hsplit.cur"); - public static Cursor VSplit => s_vSplit ??= new Cursor(typeof(Cursor), "vsplit.cur"); + public static Cursor VSplit => GetCursor(ref s_vSplit, "vsplit.cur"); - public static Cursor NoMove2D => s_noMove2D ??= new Cursor(typeof(Cursor), "nomove2d.cur"); + public static Cursor NoMove2D => GetCursor(ref s_noMove2D, "nomove2d.cur"); - public static Cursor NoMoveHoriz => s_noMoveHoriz ??= new Cursor(typeof(Cursor), "nomoveh.cur"); + public static Cursor NoMoveHoriz => GetCursor(ref s_noMoveHoriz, "nomoveh.cur"); - public static Cursor NoMoveVert => s_noMoveVert ??= new Cursor(typeof(Cursor), "nomovev.cur"); + public static Cursor NoMoveVert => GetCursor(ref s_noMoveVert, "nomovev.cur"); - public static Cursor PanEast => s_panEast ??= new Cursor(typeof(Cursor), "east.cur"); + public static Cursor PanEast => GetCursor(ref s_panEast, "east.cur"); - public static Cursor PanNE => s_panNE ??= new Cursor(typeof(Cursor), "ne.cur"); + public static Cursor PanNE => GetCursor(ref s_panNE, "ne.cur"); - public static Cursor PanNorth => s_panNorth ??= new Cursor(typeof(Cursor), "north.cur"); + public static Cursor PanNorth => GetCursor(ref s_panNorth, "north.cur"); - public static Cursor PanNW => s_panNW ??= new Cursor(typeof(Cursor), "nw.cur"); + public static Cursor PanNW => GetCursor(ref s_panNW, "nw.cur"); - public static Cursor PanSE => s_panSE ??= new Cursor(typeof(Cursor), "se.cur"); + public static Cursor PanSE => GetCursor(ref s_panSE, "se.cur"); - public static Cursor PanSouth => s_panSouth ??= new Cursor(typeof(Cursor), "south.cur"); + public static Cursor PanSouth => GetCursor(ref s_panSouth, "south.cur"); - public static Cursor PanSW => s_panSW ??= new Cursor(typeof(Cursor), "sw.cur"); + public static Cursor PanSW => GetCursor(ref s_panSW, "sw.cur"); - public static Cursor PanWest => s_panWest ??= new Cursor(typeof(Cursor), "west.cur"); + public static Cursor PanWest => GetCursor(ref s_panWest, "west.cur"); - public static Cursor Hand => s_hand ??= new Cursor(PInvoke.IDC_HAND); + private static Cursor GetCursor(ref Cursor? cursor, string resource) + => cursor is not null && cursor.IsValid() + ? cursor + : cursor = new Cursor(typeof(Cursor), resource); } } diff --git a/src/System.Windows.Forms/tests/UnitTests/System/Windows/Forms/CursorTests.cs b/src/System.Windows.Forms/tests/UnitTests/System/Windows/Forms/CursorTests.cs index 9539573623c..f34b0c2e582 100644 --- a/src/System.Windows.Forms/tests/UnitTests/System/Windows/Forms/CursorTests.cs +++ b/src/System.Windows.Forms/tests/UnitTests/System/Windows/Forms/CursorTests.cs @@ -358,12 +358,8 @@ public void Cursor_Dispose_InvokeNotOwned_Success() var cursor = new Cursor(2); cursor.Dispose(); - Assert.Throws(() => cursor.Handle); - Assert.Throws(() => cursor.HotSpot); - - cursor.Dispose(); - Assert.Throws(() => cursor.Handle); - Assert.Throws(() => cursor.HotSpot); + // Cursors not owned should not be disposed. + Assert.NotEqual(IntPtr.Zero, cursor.Handle); } public static IEnumerable Draw_TestData() diff --git a/src/System.Windows.Forms/tests/UnitTests/System/Windows/Forms/CursorsTests.cs b/src/System.Windows.Forms/tests/UnitTests/System/Windows/Forms/CursorsTests.cs index 77d14fbcd5b..fa19838f543 100644 --- a/src/System.Windows.Forms/tests/UnitTests/System/Windows/Forms/CursorsTests.cs +++ b/src/System.Windows.Forms/tests/UnitTests/System/Windows/Forms/CursorsTests.cs @@ -22,7 +22,7 @@ public static IEnumerable Cursors_TestData() yield return new object[] { I(() => Cursors.Default) }; yield return new object[] { I(() => Cursors.Hand) }; yield return new object[] { I(() => Cursors.Help) }; - // yield return new object[] { I(() => Cursors.HSplit) }; Tracking issue: https://github.com/dotnet/winforms/issues/8810. + yield return new object[] { I(() => Cursors.HSplit) }; yield return new object[] { I(() => Cursors.IBeam) }; yield return new object[] { I(() => Cursors.No) }; yield return new object[] { I(() => Cursors.NoMove2D) }; @@ -42,7 +42,7 @@ public static IEnumerable Cursors_TestData() yield return new object[] { I(() => Cursors.SizeNWSE) }; yield return new object[] { I(() => Cursors.SizeWE) }; yield return new object[] { I(() => Cursors.UpArrow) }; - // yield return new object[] { I(() => Cursors.VSplit) }; Tracking issue: https://github.com/dotnet/winforms/issues/8810. + yield return new object[] { I(() => Cursors.VSplit) }; yield return new object[] { I(() => Cursors.WaitCursor) }; }