diff --git a/src/Lithnet.CredentialProvider.UnitTests.x64/BitmapControlTests.cs b/src/Lithnet.CredentialProvider.UnitTests.x64/BitmapControlTests.cs
new file mode 100644
index 0000000..d543897
--- /dev/null
+++ b/src/Lithnet.CredentialProvider.UnitTests.x64/BitmapControlTests.cs
@@ -0,0 +1,97 @@
+using System;
+using System.Drawing;
+using System.Drawing.Imaging;
+using System.IO;
+using System.Runtime.InteropServices;
+using NUnit.Framework;
+
+namespace Lithnet.CredentialProvider.UnitTests
+{
+ public class BitmapControlTests
+ {
+ [Test]
+ public void TransparentBufferPreservesAlphaChannel()
+ {
+ using (Bitmap source = new Bitmap(2, 1, PixelFormat.Format32bppArgb))
+ {
+ source.SetPixel(0, 0, Color.FromArgb(0, 10, 20, 30));
+ source.SetPixel(1, 0, Color.FromArgb(128, 40, 50, 60));
+
+ var control = new UserTileControl("image", "Image", source);
+
+ byte[] bytes = GetBitmapBuffer(control);
+
+ Assert.That(bytes, Has.Length.GreaterThan(8));
+ Assert.That(bytes[0], Is.EqualTo(0x89));
+ Assert.That(bytes[1], Is.EqualTo(0x50));
+ Assert.That(bytes[2], Is.EqualTo(0x4e));
+ Assert.That(bytes[3], Is.EqualTo(0x47));
+
+ using (MemoryStream stream = new MemoryStream(bytes))
+ using (Bitmap decoded = new Bitmap(stream))
+ {
+ Assert.That(decoded.GetPixel(0, 0).A, Is.EqualTo(0));
+ Assert.That(decoded.GetPixel(1, 0).A, Is.EqualTo(128));
+ Assert.That(decoded.GetPixel(1, 0).R, Is.EqualTo(40));
+ Assert.That(decoded.GetPixel(1, 0).G, Is.EqualTo(50));
+ Assert.That(decoded.GetPixel(1, 0).B, Is.EqualTo(60));
+ }
+ }
+ }
+
+ [Test]
+ public void BitmapBufferDoesNotApplyConfiguredBackgroundColor()
+ {
+ using (Bitmap source = new Bitmap(1, 1, PixelFormat.Format32bppArgb))
+ {
+ source.SetPixel(0, 0, Color.Transparent);
+
+ var control = new UserTileControl("image", "Image", source)
+ {
+ BackgroundColor = Color.FromArgb(12, 34, 56)
+ };
+
+ byte[] bytes = GetBitmapBuffer(control);
+
+ using (MemoryStream stream = new MemoryStream(bytes))
+ using (Bitmap decoded = new Bitmap(stream))
+ {
+ Assert.That(decoded.GetPixel(0, 0).A, Is.EqualTo(0));
+ }
+ }
+ }
+
+ [Test]
+ public void CloneCopiesBitmapAndBackgroundColor()
+ {
+ using (Bitmap source = new Bitmap(1, 1))
+ {
+ var control = new UserTileControl("image", "Image", source)
+ {
+ BackgroundColor = Color.CornflowerBlue
+ };
+
+ var clone = (UserTileControl)control.Clone();
+
+ Assert.That(clone.Bitmap, Is.SameAs(source));
+ Assert.That(clone.BackgroundColor, Is.EqualTo(Color.CornflowerBlue));
+ }
+ }
+
+ private static byte[] GetBitmapBuffer(BitmapControl control)
+ {
+ IntPtr buffer = control.GetBitmapBuffer(out uint size);
+
+ try
+ {
+ byte[] bytes = new byte[size];
+ Marshal.Copy(buffer, bytes, 0, checked((int)size));
+ return bytes;
+ }
+ finally
+ {
+ Marshal.FreeCoTaskMem(buffer);
+ }
+ }
+ }
+}
diff --git a/src/Lithnet.CredentialProvider.UnitTests.x86/Lithnet.CredentialProvider.UnitTests.x86.csproj b/src/Lithnet.CredentialProvider.UnitTests.x86/Lithnet.CredentialProvider.UnitTests.x86.csproj
index 31f57a7..70b55c6 100644
--- a/src/Lithnet.CredentialProvider.UnitTests.x86/Lithnet.CredentialProvider.UnitTests.x86.csproj
+++ b/src/Lithnet.CredentialProvider.UnitTests.x86/Lithnet.CredentialProvider.UnitTests.x86.csproj
@@ -14,6 +14,7 @@
+
diff --git a/src/Lithnet.CredentialProvider/Controls/BitmapControl.cs b/src/Lithnet.CredentialProvider/Controls/BitmapControl.cs
index 15fa7ef..35c71b5 100644
--- a/src/Lithnet.CredentialProvider/Controls/BitmapControl.cs
+++ b/src/Lithnet.CredentialProvider/Controls/BitmapControl.cs
@@ -22,11 +22,16 @@ namespace Lithnet.CredentialProvider
this.backgroundColor = Color.FromArgb(70, 70, 70);
}
- protected BitmapControl(BitmapControl source) : base(source) { }
+ protected BitmapControl(BitmapControl source) : base(source)
+ {
+ this.bitmap = source.bitmap;
+ this.backgroundColor = source.backgroundColor;
+ }
///
- /// Specifies the background color that should replace any transparent elements of the image. This defaults to #707070
+ /// Specifies the background color used to replace transparent pixels for and . This defaults to #707070.
///
+ /// This property does not apply to .
public Color BackgroundColor
{
get { return this.backgroundColor; }
@@ -53,10 +58,7 @@ namespace Lithnet.CredentialProvider
{
this.bitmap = value;
- if (this.Events is ICredentialProviderCredentialEvents2 e)
- {
- e.SetFieldBitmap(this.Credential, this.Id, this.GetHBitmap());
- }
+ this.UpdateBitmap();
this.RaisePropertyChanged();
}
@@ -76,26 +78,48 @@ namespace Lithnet.CredentialProvider
internal IntPtr GetBitmapBuffer(out uint size)
{
size = 0;
- var hbitmap = this.GetHBitmap();
- if (hbitmap == IntPtr.Zero)
+ if (this.bitmap == null)
{
return IntPtr.Zero;
}
- var image = Bitmap.FromHbitmap(hbitmap);
-
- IntPtr buffer = IntPtr.Zero;
using (MemoryStream ms = new MemoryStream())
{
- image.Save(ms, ImageFormat.Bmp);
+ this.bitmap.Save(ms, ImageFormat.Png);
var bitmapBytes = ms.ToArray();
- size = (uint)bitmapBytes.Length;
- buffer = Marshal.AllocCoTaskMem(bitmapBytes.Length);
+ size = checked((uint)bitmapBytes.Length);
+ IntPtr buffer = Marshal.AllocCoTaskMem(bitmapBytes.Length);
Marshal.Copy(bitmapBytes, 0, buffer, bitmapBytes.Length);
+ return buffer;
+ }
+ }
+
+ private void UpdateBitmap()
+ {
+ if (this.Credential is ICredentialProviderCredential3 && this.Events is ICredentialProviderCredentialEvents3 events3)
+ {
+ IntPtr buffer = this.GetBitmapBuffer(out uint size);
+
+ try
+ {
+ events3.SetFieldBitmapBuffer(this.Credential, this.Id, size, buffer);
+ }
+ finally
+ {
+ if (buffer != IntPtr.Zero)
+ {
+ Marshal.FreeCoTaskMem(buffer);
+ }
+ }
+
+ return;
}
- return buffer;
+ if (this.Events is ICredentialProviderCredentialEvents2 events2)
+ {
+ events2.SetFieldBitmap(this.Credential, this.Id, this.GetHBitmap());
+ }
}
}
-}
\ No newline at end of file
+}
diff --git a/src/Lithnet.CredentialProvider/Controls/CredentialProviderLogoControl.cs b/src/Lithnet.CredentialProvider/Controls/CredentialProviderLogoControl.cs
index 0f93a27..f8b11b2 100644
--- a/src/Lithnet.CredentialProvider/Controls/CredentialProviderLogoControl.cs
+++ b/src/Lithnet.CredentialProvider/Controls/CredentialProviderLogoControl.cs
@@ -35,10 +35,7 @@ namespace Lithnet.CredentialProvider
internal override ControlBase Clone()
{
- var clone = new CredentialProviderLogoControl(this);
- clone.Bitmap = this.Bitmap;
- clone.BackgroundColor = this.BackgroundColor;
- return clone;
+ return new CredentialProviderLogoControl(this);
}
}
}
\ No newline at end of file
diff --git a/src/Lithnet.CredentialProvider/Controls/UserTileControl.cs b/src/Lithnet.CredentialProvider/Controls/UserTileControl.cs
index 92eb0cf..f37deb3 100644
--- a/src/Lithnet.CredentialProvider/Controls/UserTileControl.cs
+++ b/src/Lithnet.CredentialProvider/Controls/UserTileControl.cs
@@ -32,11 +32,7 @@ namespace Lithnet.CredentialProvider
internal override ControlBase Clone()
{
- var clone = new UserTileControl(this);
- clone.Bitmap = this.Bitmap;
- clone.BackgroundColor = this.BackgroundColor;
-
- return clone;
+ return new UserTileControl(this);
}
}
}
\ No newline at end of file
diff --git a/src/Lithnet.CredentialProvider/CredentialTile3.ICredentialProviderCredential3.cs b/src/Lithnet.CredentialProvider/CredentialTile3.ICredentialProviderCredential3.cs
index 088b9e5..892d37f 100644
--- a/src/Lithnet.CredentialProvider/CredentialTile3.ICredentialProviderCredential3.cs
+++ b/src/Lithnet.CredentialProvider/CredentialTile3.ICredentialProviderCredential3.cs
@@ -3,7 +3,7 @@ using Lithnet.CredentialProvider.Interop;
namespace Lithnet.CredentialProvider
{
- internal abstract partial class CredentialTile3 : ICredentialProviderCredential3
+ public abstract partial class CredentialTile3 : ICredentialProviderCredential3
{
int ICredentialProviderCredential3.GetBitmapBufferValue(uint dwFieldID, out uint pImageBufferSize, out IntPtr ppImageBuffer)
{
@@ -18,7 +18,7 @@ namespace Lithnet.CredentialProvider
if (this.Controls.TryGetControl(dwFieldID, FieldType.TileImage, out var instance))
{
- var hbitmap = instance.GetBitmapBuffer(out pImageBufferSize);
+ ppImageBuffer = instance.GetBitmapBuffer(out pImageBufferSize);
return HRESULT.S_OK;
}
diff --git a/src/Lithnet.CredentialProvider/CredentialTile3.cs b/src/Lithnet.CredentialProvider/CredentialTile3.cs
index 20207b1..fa0f365 100644
--- a/src/Lithnet.CredentialProvider/CredentialTile3.cs
+++ b/src/Lithnet.CredentialProvider/CredentialTile3.cs
@@ -7,7 +7,7 @@ namespace Lithnet.CredentialProvider
/// Represents a user credential tile that implements the functionality of and , but includes support for dynamically updating bitmap images.
///
/// This interface is public, but undocumented by Microsoft. It is recommended to use tiles unless this specific functionality is needed
- internal abstract partial class CredentialTile3 : CredentialTile2
+ public abstract partial class CredentialTile3 : CredentialTile2
{
protected CredentialTile3(CredentialProviderBase credentialProvider) : this(credentialProvider, null) { }