Skip to content

Commit

Permalink
Merge pull request #19 from Techsola/updates
Browse files Browse the repository at this point in the history
Update dependencies
  • Loading branch information
jnm2 authored Oct 9, 2024
2 parents 9e1e49e + 8e42b54 commit d25df1f
Show file tree
Hide file tree
Showing 17 changed files with 160 additions and 149 deletions.
6 changes: 3 additions & 3 deletions .github/workflows/CI.yml
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ jobs:
runs-on: windows-latest

steps:
- uses: actions/checkout@v2
- uses: actions/checkout@v4
with:
fetch-depth: 0 # Needed in order for tags to be available so prereleases autoincrement the version

Expand All @@ -24,14 +24,14 @@ jobs:

- name: Upload packages artifact
if: always()
uses: actions/upload-artifact@v2
uses: actions/upload-artifact@v4
with:
name: Packages
path: artifacts/Packages

- name: Upload logs artifact
if: always()
uses: actions/upload-artifact@v2
uses: actions/upload-artifact@v4
with:
name: Logs
path: artifacts/Logs
11 changes: 3 additions & 8 deletions src/Directory.Build.props
Original file line number Diff line number Diff line change
@@ -1,21 +1,16 @@
<Project>

<PropertyGroup>
<LangVersion>9.0</LangVersion>
<LangVersion>12</LangVersion>
<Nullable>enable</Nullable>
<AnalysisLevel>5</AnalysisLevel>
<AnalysisLevel>8</AnalysisLevel>
<TreatWarningsAsErrors Condition="'$(Configuration)' == 'Release'">true</TreatWarningsAsErrors>
<NoWarn>RA1000</NoWarn>
</PropertyGroup>

<ItemGroup>
<PackageReference Include="TunnelVisionLabs.ReferenceAssemblyAnnotator" Version="1.0.0-alpha.160" PrivateAssets="all" />
<PackageDownload Include="Microsoft.NETCore.App.Ref" Version="[5.0.0]" />
</ItemGroup>

<ItemGroup>
<!-- Needed for net3.5 support until .NET 6 SDK -->
<PackageReference Include="Microsoft.NETFramework.ReferenceAssemblies" Version="1.0.2" PrivateAssets="all" />
<PackageDownload Include="Microsoft.NETCore.App.Ref" Version="[6.0.0]" />
</ItemGroup>

</Project>
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<TargetFrameworks>net35;net48;net5.0-windows</TargetFrameworks>
<TargetFrameworks>net35;net48;net8.0-windows</TargetFrameworks>
</PropertyGroup>

<ItemGroup>
<PackageReference Include="NUnit" Version="3.13.1" />
<PackageReference Include="NUnit.Analyzers" Version="3.0.0" />
<PackageReference Include="NUnit3TestAdapter" Version="3.17.0" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.9.1" />
<PackageReference Include="NUnit" Version="3.14.0" />
<PackageReference Include="NUnit.Analyzers" Version="4.3.0" PrivateAssets="all" />
<PackageReference Include="NUnit3TestAdapter" Version="4.6.0" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.11.1" />
<PackageReference Include="Shouldly" Version="2.8.3" />
</ItemGroup>

Expand Down
47 changes: 28 additions & 19 deletions src/Techsola.InstantReplay/AnimatedCursorRenderer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -42,28 +42,37 @@ public void Render(DeleteDCSafeHandle deviceContext, HCURSOR cursorHandle, int c
if (!cursorAnimationStepByHandle.TryGetValue(cursorHandle, out var cursorAnimationStep))
cursorAnimationStep = (Current: 0, Max: uint.MaxValue);

while (!PInvoke.DrawIconEx(
deviceContext,
cursorX - (int)cursorInfo.Hotspot.X,
cursorY - (int)cursorInfo.Hotspot.Y,
/* Workaround for https://github.com/microsoft/CsWin32/issues/256
↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓ */
new UnownedHandle(cursorHandle),
cxWidth: 0,
cyWidth: 0,
cursorAnimationStep.Current,
hbrFlickerFreeDraw: null,
DI_FLAGS.DI_NORMAL))
var deviceContextNeedsRelease = false;
deviceContext.DangerousAddRef(ref deviceContextNeedsRelease);
try
{
var lastError = Marshal.GetLastWin32Error();

if ((ERROR)lastError == ERROR.INVALID_PARAMETER && cursorAnimationStep.Current > 0)
while (!PInvoke.DrawIconEx(
(HDC)deviceContext.DangerousGetHandle(),
cursorX - (int)cursorInfo.Hotspot.X,
cursorY - (int)cursorInfo.Hotspot.Y,
/* Workaround for https://github.com/microsoft/CsWin32/issues/256
↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓ */
new UnownedHandle(cursorHandle),
cxWidth: 0,
cyWidth: 0,
cursorAnimationStep.Current,
hbrFlickerFreeDraw: null,
DI_FLAGS.DI_NORMAL))
{
cursorAnimationStep = (Current: 0, Max: cursorAnimationStep.Current - 1);
continue;
}
var lastError = Marshal.GetLastWin32Error();

if ((ERROR)lastError == ERROR.INVALID_PARAMETER && cursorAnimationStep.Current > 0)
{
cursorAnimationStep = (Current: 0, Max: cursorAnimationStep.Current - 1);
continue;
}

throw new Win32Exception(lastError);
throw new Win32Exception(lastError);
}
}
finally
{
if (deviceContextNeedsRelease) deviceContext.DangerousRelease();
}

cursorAnimationStep.Current = cursorAnimationStep.Current == cursorAnimationStep.Max ? 0 : cursorAnimationStep.Current + 1;
Expand Down
65 changes: 43 additions & 22 deletions src/Techsola.InstantReplay/Composition.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
using System.ComponentModel;
using System.Runtime.InteropServices;
using Techsola.InstantReplay.Native;
using Windows.Win32;
using Windows.Win32.Graphics.Gdi;

Expand Down Expand Up @@ -34,27 +35,37 @@ public Composition(uint width, uint height, ushort bitsPerPixel)
BytesPerPixel = (byte)(bitsPerPixel >> 3);
Stride = (((width * BytesPerPixel) + 3) / 4) * 4;

DeviceContext = PInvoke.CreateCompatibleDC(null).ThrowWithoutLastErrorAvailableIfInvalid(nameof(PInvoke.CreateCompatibleDC));
unsafe
DeviceContext = new DeleteDCSafeHandle(PInvoke.CreateCompatibleDC(default)).ThrowWithoutLastErrorAvailableIfInvalid(nameof(PInvoke.CreateCompatibleDC));
var deviceContextNeedsRelease = false;
DeviceContext.DangerousAddRef(ref deviceContextNeedsRelease);
try
{
bitmap = PInvoke.CreateDIBSection(DeviceContext, new()
unsafe
{
bmiHeader =
var bitmapInfo = new BITMAPINFO
{
biSize = (uint)Marshal.SizeOf(typeof(BITMAPINFOHEADER)),
biWidth = (int)width,
biHeight = -(int)height,
biPlanes = 1,
biBitCount = bitsPerPixel,
},
}, DIB_USAGE.DIB_RGB_COLORS, out var pointer, hSection: null, offset: 0).ThrowLastErrorIfInvalid();
bmiHeader =
{
biSize = (uint)Marshal.SizeOf(typeof(BITMAPINFOHEADER)),
biWidth = (int)width,
biHeight = -(int)height,
biPlanes = 1,
biBitCount = bitsPerPixel,
},
};

PixelDataPointer = (byte*)pointer;
}
bitmap = PInvoke.CreateDIBSection((HDC)DeviceContext.DangerousGetHandle(), &bitmapInfo, DIB_USAGE.DIB_RGB_COLORS, out var pointer, hSection: null, offset: 0).ThrowLastErrorIfInvalid();

PixelDataPointer = (byte*)pointer;
}

// Workaround for https://github.com/microsoft/CsWin32/issues/199
if (PInvoke.SelectObject(DeviceContext, (HGDIOBJ)bitmap.DangerousGetHandle()).IsNull)
throw new Win32Exception("SelectObject failed.");
if (PInvoke.SelectObject((HDC)DeviceContext.DangerousGetHandle(), (HGDIOBJ)bitmap.DangerousGetHandle()).IsNull)
throw new Win32Exception("SelectObject failed.");
}
finally
{
if (deviceContextNeedsRelease) DeviceContext.DangerousRelease();
}
}

public void Dispose()
Expand All @@ -67,16 +78,26 @@ public void Clear(int x, int y, int width, int height, ref bool needsGdiFlush)
{
if (width <= 0 || height <= 0) return;

if (!PInvoke.BitBlt(DeviceContext, x, y, width, height, null, 0, 0, ROP_CODE.BLACKNESS))
var deviceContextNeedsRelease = false;
DeviceContext.DangerousAddRef(ref deviceContextNeedsRelease);
try
{
var lastError = Marshal.GetLastWin32Error();
if (lastError != 0) throw new Win32Exception(lastError);
needsGdiFlush = true;
if (!PInvoke.BitBlt((HDC)DeviceContext.DangerousGetHandle(), x, y, width, height, hdcSrc: default, 0, 0, ROP_CODE.BLACKNESS))
{
var lastError = Marshal.GetLastWin32Error();
if (lastError != 0) throw new Win32Exception(lastError);
needsGdiFlush = true;
}
else
{
needsGdiFlush = false;
}
}
else
finally
{
needsGdiFlush = false;
if (deviceContextNeedsRelease) DeviceContext.DangerousRelease();
}
}
}
}

41 changes: 26 additions & 15 deletions src/Techsola.InstantReplay/Frame.cs
Original file line number Diff line number Diff line change
Expand Up @@ -52,29 +52,40 @@ public void Overwrite(
bitmap.Dispose();
}

unsafe
var bitmapDCNeedsRelease = false;
bitmapDC.DangerousAddRef(ref bitmapDCNeedsRelease);
try
{
bitmap = PInvoke.CreateDIBSection(bitmapDC, new()
unsafe
{
bmiHeader =
var bitmapInfo = new BITMAPINFO
{
biSize = (uint)Marshal.SizeOf(typeof(BITMAPINFOHEADER)),
biWidth = bitmapWidth,
biHeight = -bitmapHeight,
biPlanes = 1,
biBitCount = BitsPerPixel,
},
}, DIB_USAGE.DIB_RGB_COLORS, ppvBits: out _, hSection: null, offset: 0).ThrowLastErrorIfInvalid();
bmiHeader =
{
biSize = (uint)Marshal.SizeOf(typeof(BITMAPINFOHEADER)),
biWidth = bitmapWidth,
biHeight = -bitmapHeight,
biPlanes = 1,
biBitCount = BitsPerPixel,
},
};

bitmap = PInvoke.CreateDIBSection((HDC)bitmapDC.DangerousGetHandle(), &bitmapInfo, DIB_USAGE.DIB_RGB_COLORS, ppvBits: out _, hSection: null, offset: 0).ThrowLastErrorIfInvalid();
}
}
finally
{
if (bitmapDCNeedsRelease) bitmapDC.DangerousRelease();
}
}

// Workaround for https://github.com/microsoft/CsWin32/issues/199
if (PInvoke.SelectObject(bitmapDC, (HGDIOBJ)bitmap.DangerousGetHandle()).IsNull)
if (PInvoke.SelectObject((HDC)bitmapDC.DangerousGetHandle(), (HGDIOBJ)bitmap.DangerousGetHandle()).IsNull)
throw new Win32Exception("SelectObject failed.");

retryBitBlt:
PInvoke.SetLastError(0); // BitBlt doesn't set the last error if it returns false to indicate that the operation has been batched
if (!PInvoke.BitBlt(bitmapDC, 0, 0, windowMetrics.ClientWidth, windowMetrics.ClientHeight, windowDC, 0, 0, ROP_CODE.SRCCOPY))
if (!PInvoke.BitBlt((HDC)bitmapDC.DangerousGetHandle(), 0, 0, windowMetrics.ClientWidth, windowMetrics.ClientHeight, (HDC)windowDC.DangerousGetHandle(), 0, 0, ROP_CODE.SRCCOPY))
{
var lastError = Marshal.GetLastWin32Error();
if ((ERROR)lastError is ERROR.INVALID_WINDOW_HANDLE or ERROR.DC_NOT_FOUND)
Expand Down Expand Up @@ -125,7 +136,7 @@ public void Compose(
}

// Workaround for https://github.com/microsoft/CsWin32/issues/199
if (PInvoke.SelectObject(bitmapDC, (HGDIOBJ)bitmap.DangerousGetHandle()).IsNull)
if (PInvoke.SelectObject((HDC)bitmapDC.DangerousGetHandle(), (HGDIOBJ)bitmap.DangerousGetHandle()).IsNull)
throw new Win32Exception("SelectObject failed.");

changedArea = new(
Expand All @@ -136,12 +147,12 @@ public void Compose(

PInvoke.SetLastError(0); // BitBlt doesn't set the last error if it returns false to indicate that the operation has been batched
if (!PInvoke.BitBlt(
compositionDC,
(HDC)compositionDC.DangerousGetHandle(),
changedArea.Left,
changedArea.Top,
changedArea.Width,
changedArea.Height,
bitmapDC,
(HDC)bitmapDC.DangerousGetHandle(),
0,
0,
ROP_CODE.SRCCOPY))
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
using System.Collections.Generic;
using Windows.Win32;
using Techsola.InstantReplay.Native;
using Windows.Win32.UI.WindowsAndMessaging;

namespace Techsola.InstantReplay
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
using System;
using Techsola.InstantReplay.Native;
using Windows.Win32;

namespace Techsola.InstantReplay
{
Expand Down
9 changes: 5 additions & 4 deletions src/Techsola.InstantReplay/InstantReplayCamera.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
using System.Collections.Generic;
using System.ComponentModel;
using System.Diagnostics;
using System.Drawing;
using System.Linq;
using System.Runtime.InteropServices;
using System.Threading;
Expand Down Expand Up @@ -115,14 +116,14 @@ private static void AddFrames(object? state)

var currentWindows = (windowEnumerator ??= new()).GetCurrentWindowHandlesInZOrder();

bitmapDC ??= PInvoke.CreateCompatibleDC(null).ThrowWithoutLastErrorAvailableIfInvalid(nameof(PInvoke.CreateCompatibleDC));
bitmapDC ??= new DeleteDCSafeHandle(PInvoke.CreateCompatibleDC(default)).ThrowWithoutLastErrorAvailableIfInvalid(nameof(PInvoke.CreateCompatibleDC));

lock (InfoByWindowHandle)
{
Frames.Add((
Timestamp: now,
Cursor: (cursorInfo.flags & (CURSORINFO_FLAGS.CURSOR_SHOWING | CURSORINFO_FLAGS.CURSOR_SUPPRESSED)) == CURSORINFO_FLAGS.CURSOR_SHOWING
? (cursorInfo.ptScreenPos.x, cursorInfo.ptScreenPos.y, cursorInfo.hCursor)
? (cursorInfo.ptScreenPos.X, cursorInfo.ptScreenPos.Y, cursorInfo.hCursor)
: null));

var zOrder = 0u;
Expand Down Expand Up @@ -215,7 +216,7 @@ private static void AddFrames(object? state)

private static WindowMetrics? GetWindowMetricsIfExists(HWND window)
{
var clientTopLeft = default(POINT);
var clientTopLeft = default(Point);
if (!PInvoke.ClientToScreen(window, ref clientTopLeft))
return null; // This is what happens when the window handle becomes invalid.

Expand All @@ -226,7 +227,7 @@ private static void AddFrames(object? state)
throw new Win32Exception(lastError);
}

return new(clientTopLeft.x, clientTopLeft.y, clientRect.right, clientRect.bottom);
return new(clientTopLeft.X, clientTopLeft.Y, clientRect.right, clientRect.bottom);
}

#if !NET35
Expand Down
22 changes: 22 additions & 0 deletions src/Techsola.InstantReplay/Native/DeleteDCSafeHandle.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
using System;
using System.Runtime.InteropServices;
using Windows.Win32;
using Windows.Win32.Graphics.Gdi;

namespace Techsola.InstantReplay.Native;

// Workaround for https://github.com/microsoft/CsWin32/issues/209
internal sealed class DeleteDCSafeHandle : SafeHandle
{
public DeleteDCSafeHandle(IntPtr handle) : base(invalidHandleValue: IntPtr.Zero, ownsHandle: true)
{
SetHandle(handle);
}

public override bool IsInvalid => handle == IntPtr.Zero;

protected override bool ReleaseHandle()
{
return (bool)PInvoke.DeleteDC((HDC)handle);
}
}
Loading

0 comments on commit d25df1f

Please sign in to comment.