Skip to content

Commit cc1459e

Browse files
authored
Prevent SafeHandle from being re-generated in downstream assembly (#1514)
* Prevent SafeHandle from being re-generated
1 parent 06f31b6 commit cc1459e

3 files changed

Lines changed: 75 additions & 6 deletions

File tree

src/Microsoft.Windows.CsWin32/Generator.Handle.cs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,9 @@ public partial class Generator
7070
return safeHandleType;
7171
}
7272

73-
if (this.IsTypeAlreadyFullyDeclared($"{this.Namespace}.{safeHandleType}"))
73+
// Bail out early if someone already made the SafeHandle type
74+
string safeHandleFullyQualifiedName = $"{this.Namespace}.{safeHandleClassName}";
75+
if (this.IsTypeAlreadyFullyDeclared(safeHandleFullyQualifiedName))
7476
{
7577
return safeHandleType;
7678
}

test/CsWin32Generator.Tests/CsWin32GeneratorTests.cs

Lines changed: 71 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -283,15 +283,78 @@ public async Task DoNotEmitTypesFromInternalsVisibleToReferences(bool strongName
283283
.AddSyntaxTrees(
284284
CSharpSyntaxTree.ParseText(
285285
$@"
286+
#pragma warning disable CS1591,CS1573,CS0465,CS0649,CS8019,CS1570,CS1584,CS1658,CS0436,CS8981,SYSLIB1092
287+
using global::System;
288+
using global::System.Diagnostics;
289+
using global::System.Diagnostics.CodeAnalysis;
290+
using global::System.Runtime.CompilerServices;
291+
using global::System.Runtime.InteropServices;
292+
using global::System.Runtime.Versioning;
293+
286294
[assembly: System.Runtime.CompilerServices.InternalsVisibleTo(""{friendName}"")]
287295
288296
namespace Windows.Win32.Foundation
289297
{{
290-
internal struct PCWSTR
291-
{{
292-
// Field exists solely to validate that the containing type is considered non-struct-like.
293-
internal unsafe byte* Value;
294-
}}
298+
internal unsafe readonly partial struct PCWSTR
299+
: IEquatable<PCWSTR>
300+
{{
301+
internal readonly char* Value;
302+
303+
internal PCWSTR(char* value) => this.Value = value;
304+
305+
public static explicit operator char*(PCWSTR value) => value.Value;
306+
307+
public static implicit operator PCWSTR(char* value) => new PCWSTR(value);
308+
309+
public bool Equals(PCWSTR other) => this.Value == other.Value;
310+
311+
public override bool Equals(object obj) => obj is PCWSTR other && this.Equals(other);
312+
313+
public override int GetHashCode() => unchecked((int)this.Value);
314+
315+
internal int Length
316+
{{
317+
get
318+
{{
319+
char* p = this.Value;
320+
if (p is null)
321+
return 0;
322+
while (*p != '\0')
323+
p++;
324+
return checked((int)(p - this.Value));
325+
}}
326+
}}
327+
328+
329+
public override string ToString() => this.Value is null ? null : new string(this.Value);
330+
331+
internal ReadOnlySpan<char> AsSpan() => this.Value is null ? default(ReadOnlySpan<char>) : new ReadOnlySpan<char>(this.Value, this.Length);
332+
333+
private string DebuggerDisplay => this.ToString();
334+
}}
335+
}}
336+
337+
namespace Windows.Win32
338+
{{
339+
internal partial class SysFreeStringSafeHandle :SafeHandle {{
340+
private static readonly IntPtr INVALID_HANDLE_VALUE = new IntPtr(0L);
341+
342+
internal SysFreeStringSafeHandle():base(INVALID_HANDLE_VALUE, true)
343+
{{
344+
}}
345+
346+
internal SysFreeStringSafeHandle(IntPtr preexistingHandle, bool ownsHandle = true):base(INVALID_HANDLE_VALUE, ownsHandle)
347+
{{
348+
this.SetHandle(preexistingHandle);
349+
}}
350+
351+
public override bool IsInvalid => false;
352+
353+
protected override bool ReleaseHandle()
354+
{{
355+
return true;
356+
}}
357+
}}
295358
}}
296359
",
297360
this.parseOptions,
@@ -314,6 +377,8 @@ internal struct PCWSTR
314377

315378
this.nativeMethods.Add("PCWSTR");
316379
this.nativeMethods.Add("GetTickCount");
380+
this.nativeMethods.Add("StrToIntW"); // Method that uses PCWSTR
381+
this.nativeMethods.Add("IRestrictedErrorInfo"); // Generates BSTR out params and makes SysFreeStringSafeHandle
317382
this.additionalReferences.Add(referencedAssemblyPath);
318383
this.assemblyName = referencingAssemblyName;
319384
this.keyFile = strongNameSign ? strongNameKeyFilePath : null;
@@ -329,6 +394,7 @@ internal struct PCWSTR
329394
await this.InvokeGeneratorAndCompile(testCase: $"{nameof(this.DoNotEmitTypesFromInternalsVisibleToReferences)}_{strongNameSign}");
330395

331396
Assert.Empty(this.FindGeneratedType("PCWSTR"));
397+
Assert.Empty(this.FindGeneratedType("SysFreeStringSafeHandle"));
332398

333399
File.Delete(referencedAssemblyPath);
334400
}

test/CsWin32Generator.Tests/CsWin32GeneratorTestsBase.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,7 @@ protected async Task InvokeGeneratorAndCompileFromFact([CallerMemberName] string
5353

5454
protected async Task InvokeGeneratorAndCompile(string testCase, TestOptions options = TestOptions.None)
5555
{
56+
this.compilation = this.compilation.WithAssemblyName(this.assemblyName);
5657
this.compilation = this.compilation.AddReferences(this.additionalReferences.Select(x => MetadataReference.CreateFromFile(x)));
5758

5859
string outputPath = this.GetTestCaseOutputDirectory(testCase);

0 commit comments

Comments
 (0)