|
6 | 6 | using System.ComponentModel; |
7 | 7 | using System.Drawing; |
8 | 8 | using System.Runtime.CompilerServices; |
| 9 | +using System.Threading; |
9 | 10 | using System.Windows.Forms; |
10 | 11 | using CefSharp.Internals; |
11 | 12 | using CefSharp.WinForms.Internals; |
@@ -59,6 +60,25 @@ public class ChromiumWebBrowser : Control, IWebBrowserInternal, IWinFormsWebBrow |
59 | 60 | /// </summary> |
60 | 61 | private IRequestContext requestContext; |
61 | 62 |
|
| 63 | + /// <summary> |
| 64 | + /// The value for disposal, if it's 1 (one) then this instance is either disposed |
| 65 | + /// or in the process of getting disposed |
| 66 | + /// </summary> |
| 67 | + private int disposeSignaled; |
| 68 | + |
| 69 | + /// <summary> |
| 70 | + /// Gets a value indicating whether this instance is disposed. |
| 71 | + /// </summary> |
| 72 | + /// <value><see langword="true" /> if this instance is disposed; otherwise, <see langword="false" />.</value> |
| 73 | + [Browsable(false), EditorBrowsable(EditorBrowsableState.Never), DefaultValue(false)] |
| 74 | + public new bool IsDisposed |
| 75 | + { |
| 76 | + get |
| 77 | + { |
| 78 | + return Interlocked.CompareExchange(ref disposeSignaled, 1, 1) == 1; |
| 79 | + } |
| 80 | + } |
| 81 | + |
62 | 82 | /// <summary> |
63 | 83 | /// Set to true while handing an activating WM_ACTIVATE message. |
64 | 84 | /// MUST ONLY be cleared by DefaultFocusHandler. |
@@ -431,73 +451,71 @@ private void InitializeFieldsAndCefIfRequired() |
431 | 451 | } |
432 | 452 |
|
433 | 453 | /// <summary> |
434 | | - /// Releases the unmanaged resources used by the <see cref="T:System.Windows.Forms.Control" /> and its child controls and optionally releases the managed resources. |
| 454 | + /// If not in design mode; Releases unmanaged and - optionally - managed resources for the <see cref="ChromiumWebBrowser"/> |
435 | 455 | /// </summary> |
436 | | - /// <param name="disposing">true to release both managed and unmanaged resources; false to release only unmanaged resources.</param> |
| 456 | + /// <param name="disposing"><see langword="true" /> to release both managed and unmanaged resources; <see langword="false" /> to release only unmanaged resources.</param> |
437 | 457 | protected override void Dispose(bool disposing) |
438 | 458 | { |
439 | | - IsBrowserInitialized = false; |
440 | | - |
441 | | - if (!designMode) |
| 459 | + // Attempt to move the disposeSignaled state from 0 to 1. If successful, we can be assured that |
| 460 | + // this thread is the first thread to do so, and can safely dispose of the object. |
| 461 | + if (Interlocked.CompareExchange(ref disposeSignaled, 1, 0) != 0) |
442 | 462 | { |
443 | | - RemoveFromListOfCefBrowsers(); |
| 463 | + return; |
444 | 464 | } |
445 | 465 |
|
446 | | - //The unmanaged resources should never be created in design mode, so only dispose when |
447 | | - //at runtime |
448 | | - if (disposing && !designMode) |
| 466 | + if (!designMode) |
449 | 467 | { |
450 | | - FreeUnmanagedResources(); |
| 468 | + InternalDispose(disposing); |
451 | 469 | } |
452 | 470 |
|
453 | | - // Don't maintain a reference to event listeners anylonger: |
454 | | - AddressChanged = null; |
455 | | - ConsoleMessage = null; |
456 | | - FrameLoadEnd = null; |
457 | | - FrameLoadStart = null; |
458 | | - IsBrowserInitializedChanged = null; |
459 | | - LoadError = null; |
460 | | - LoadingStateChanged = null; |
461 | | - StatusMessage = null; |
462 | | - TitleChanged = null; |
463 | | - |
464 | | - // Release reference to handlers, make sure this is done after we dispose managedCefBrowserAdapter |
465 | | - // otherwise the ILifeSpanHandler.DoClose will not be invoked. |
466 | | - this.SetHandlersToNull(); |
467 | | - |
468 | 471 | base.Dispose(disposing); |
469 | 472 | } |
470 | 473 |
|
471 | 474 | /// <summary> |
472 | | - /// Required for designer support - this method cannot be inlined as the designer |
473 | | - /// will attempt to load libcef.dll and will subsiquently throw an exception. |
| 475 | + /// Releases unmanaged and - optionally - managed resources for the <see cref="ChromiumWebBrowser"/> |
474 | 476 | /// </summary> |
| 477 | + /// <param name="disposing"><see langword="true" /> to release both managed and unmanaged resources; <see langword="false" /> to release only unmanaged resources.</param> |
| 478 | + /// <remarks> |
| 479 | + /// This method cannot be inlined as the designer will attempt to load libcef.dll and will subsiquently throw an exception. |
| 480 | + /// </remarks> |
475 | 481 | [MethodImpl(MethodImplOptions.NoInlining)] |
476 | | - private void RemoveFromListOfCefBrowsers() |
| 482 | + private void InternalDispose(bool disposing) |
477 | 483 | { |
478 | | - Cef.RemoveDisposable(this); |
479 | | - } |
| 484 | + if (disposing) |
| 485 | + { |
| 486 | + IsBrowserInitialized = false; |
480 | 487 |
|
481 | | - /// <summary> |
482 | | - /// Required for designer support - this method cannot be inlined as the designer |
483 | | - /// will attempt to load libcef.dll and will subsiquently throw an exception. |
484 | | - /// </summary> |
485 | | - [MethodImpl(MethodImplOptions.NoInlining)] |
486 | | - private void FreeUnmanagedResources() |
487 | | - { |
488 | | - browser = null; |
| 488 | + browser = null; |
489 | 489 |
|
490 | | - if (parentFormMessageInterceptor != null) |
491 | | - { |
492 | | - parentFormMessageInterceptor.Dispose(); |
493 | | - parentFormMessageInterceptor = null; |
494 | | - } |
| 490 | + if (parentFormMessageInterceptor != null) |
| 491 | + { |
| 492 | + parentFormMessageInterceptor.Dispose(); |
| 493 | + parentFormMessageInterceptor = null; |
| 494 | + } |
| 495 | + |
| 496 | + if (managedCefBrowserAdapter != null) |
| 497 | + { |
| 498 | + managedCefBrowserAdapter.Dispose(); |
| 499 | + managedCefBrowserAdapter = null; |
| 500 | + } |
495 | 501 |
|
496 | | - if (managedCefBrowserAdapter != null) |
497 | | - { |
498 | | - managedCefBrowserAdapter.Dispose(); |
499 | | - managedCefBrowserAdapter = null; |
| 502 | + // Don't maintain a reference to event listeners anylonger: |
| 503 | + AddressChanged = null; |
| 504 | + ConsoleMessage = null; |
| 505 | + FrameLoadEnd = null; |
| 506 | + FrameLoadStart = null; |
| 507 | + IsBrowserInitializedChanged = null; |
| 508 | + LoadError = null; |
| 509 | + LoadingStateChanged = null; |
| 510 | + StatusMessage = null; |
| 511 | + TitleChanged = null; |
| 512 | + |
| 513 | + // Release reference to handlers, make sure this is done after we dispose managedCefBrowserAdapter |
| 514 | + // otherwise the ILifeSpanHandler.DoClose will not be invoked. |
| 515 | + this.SetHandlersToNull(); |
500 | 516 | } |
| 517 | + |
| 518 | + Cef.RemoveDisposable(this); |
501 | 519 | } |
502 | 520 |
|
503 | 521 | /// <summary> |
|
0 commit comments