Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
99 changes: 82 additions & 17 deletions Ink Canvas/Helpers/PPTManager.cs
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,7 @@ public bool IsConnected
private readonly object _lockObject = new object();
private bool _disposed;
private static bool IsPptBusyHResult(uint hr) => hr == 0x80010001 || hr == 0x8001010A;
private static bool IsNoActivePresentationOrWindowHResult(uint hr) => hr == 0x80048240;
#endregion

#region Constructor & Initialization
Expand Down Expand Up @@ -537,7 +538,7 @@ private void SafeReleaseComObject(object comObject, string objectName)
}
}

private void UpdateCurrentPresentationInfo()
private void UpdateCurrentPresentationInfo(bool preferSlideShowWindow = false, SlideShowWindow knownSlideShowWindow = null)
{
object activePresentation = null;
object slideShowWindows = null;
Expand All @@ -553,7 +554,7 @@ private void UpdateCurrentPresentationInfo()
{
try
{
activePresentation = PPTApplication.ActivePresentation;
activePresentation = TryGetActivePresentation(knownSlideShowWindow);
if (activePresentation != null)
{
SafeReleaseComObject(CurrentPresentation, "CurrentPresentation");
Expand Down Expand Up @@ -583,21 +584,30 @@ private void UpdateCurrentPresentationInfo()
try
{
slideShowWindows = PPTApplication.SlideShowWindows;
if (IsInSlideShow && slideShowWindows != null)
var shouldUseSlideShowWindow = preferSlideShowWindow || IsInSlideShow;
if (shouldUseSlideShowWindow && slideShowWindows != null)
{
dynamic ssw = slideShowWindows;
if (ssw.Count > 0)
if (knownSlideShowWindow != null)
{
slideShowWindow = ssw[1];
if (slideShowWindow != null)
slideShowWindow = knownSlideShowWindow;
}
Comment on lines +590 to +593

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Avoid releasing the slideshow event window instance

When knownSlideShowWindow is provided, this branch stores the caller-owned event argument in slideShowWindow, and UpdateCurrentPresentationInfo later calls SafeReleaseComObject(slideShowWindow) in finally. In OnSlideShowBegin/OnSlideShowNextSlide, that means wn can be released before SlideShowBegin?.Invoke(wn) or SlideShowNextSlide?.Invoke(wn), so handlers that access COM members like wn.View may hit InvalidComObjectException during normal slideshow events.

Useful? React with 👍 / 👎.

else
{
dynamic ssw = slideShowWindows;
if (ssw.Count > 0)
{
dynamic sswObj = slideShowWindow;
view = sswObj.View;
if (view != null)
{
dynamic viewObj = view;
slideShowWindow = ssw[1];
}
}

if (slideShowWindow != null)
{
dynamic sswObj = slideShowWindow;
view = sswObj.View;
if (view != null)
{
dynamic viewObj = view;
CurrentSlide = viewObj.Slide as Slide;
}
}
}
}
Expand Down Expand Up @@ -633,7 +643,7 @@ private void UpdateCurrentPresentationInfo()
catch (COMException comEx)
{
var hr = (uint)comEx.HResult;
if (hr != 0x8001010E && hr != 0x80004005)
if (hr != 0x8001010E && hr != 0x80004005 && !IsNoActivePresentationOrWindowHResult(hr))
{
LogHelper.WriteLogToFile($"获取当前幻灯片失败: {comEx.Message}", LogHelper.LogType.Warning);
}
Expand All @@ -655,7 +665,7 @@ private void UpdateCurrentPresentationInfo()
catch (COMException comEx)
{
var hr = (uint)comEx.HResult;
if (hr == 0x8001010E || hr == 0x80004005)
if (hr == 0x8001010E || hr == 0x80004005 || IsNoActivePresentationOrWindowHResult(hr))
{
CurrentPresentation = null;
CurrentSlides = null;
Expand Down Expand Up @@ -698,6 +708,61 @@ private void UpdateCurrentPresentationInfo()
}
}
}

private object TryGetActivePresentation(SlideShowWindow knownSlideShowWindow)
{
try
{
return PPTApplication.ActivePresentation;
}
catch (COMException comEx)
{
var hr = (uint)comEx.HResult;
if (!IsNoActivePresentationOrWindowHResult(hr))
{
throw;
}
}

try
{
if (knownSlideShowWindow != null)
{
return knownSlideShowWindow.Presentation;
}

var slideShowWindows = PPTApplication.SlideShowWindows;
if (slideShowWindows == null)
{
return null;
}

dynamic ssw = slideShowWindows;
if (ssw.Count == 0)
{
return null;
}

var slideShowWindow = ssw[1];
if (slideShowWindow == null)
{
return null;
}

dynamic sswObj = slideShowWindow;
return sswObj.Presentation;
Comment on lines +752 to +753

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Release COM references before returning presentation fallback

The fallback path in TryGetActivePresentation returns sswObj.Presentation directly without any finally cleanup for the temporary SlideShowWindows/SlideShowWindow COM objects created earlier in the method. Because this helper is now called on each presentation-state refresh, those unreleased COM references can accumulate and keep PowerPoint objects alive longer than expected, degrading stability over time.

Useful? React with 👍 / 👎.

}
catch (COMException comEx)
{
var hr = (uint)comEx.HResult;
if (!IsNoActivePresentationOrWindowHResult(hr))
{
throw;
}
}

return null;
}
#endregion

#region Event Handlers
Expand Down Expand Up @@ -734,7 +799,7 @@ private void OnSlideShowBegin(SlideShowWindow wn)
_cachedIsInSlideShow = true;
try
{
UpdateCurrentPresentationInfo();
UpdateCurrentPresentationInfo(preferSlideShowWindow: true, knownSlideShowWindow: wn);
SlideShowBegin?.Invoke(wn);
}
catch (Exception ex)
Expand All @@ -747,7 +812,7 @@ private void OnSlideShowNextSlide(SlideShowWindow wn)
{
try
{
UpdateCurrentPresentationInfo();
UpdateCurrentPresentationInfo(preferSlideShowWindow: true, knownSlideShowWindow: wn);
SlideShowNextSlide?.Invoke(wn);
}
catch (Exception ex)
Expand Down
Loading