Skip to content

Commit 51ee33b

Browse files
author
dahall
committed
Documented problems with trying to create a marshaler for VARIANT values.
1 parent f635262 commit 51ee33b

File tree

3 files changed

+94
-88
lines changed

3 files changed

+94
-88
lines changed

PInvoke/ActiveDS/IADs.Interface4.cs

Lines changed: 0 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -2791,31 +2791,6 @@ public class SafeADS_SEARCH_HANDLE : SafeHANDLE
27912791
protected override bool InternalReleaseHandle() { try { ids?.CloseSearchHandle(handle); return true; } catch { return false; } }
27922792
}
27932793

2794-
/*internal class VariantMarshaler<T> : ICustomMarshaler
2795-
{
2796-
public static ICustomMarshaler GetInstance(string pstrCookie) => new VariantMarshaler<T>();
2797-
void ICustomMarshaler.CleanUpManagedData(object ManagedObj) { }
2798-
void ICustomMarshaler.CleanUpNativeData(IntPtr pNativeData) => Marshal.FreeCoTaskMem(pNativeData);
2799-
int ICustomMarshaler.GetNativeDataSize() => IntPtr.Size;
2800-
IntPtr ICustomMarshaler.MarshalManagedToNative(object ManagedObj)
2801-
{
2802-
if (ManagedObj is null) return IntPtr.Zero;
2803-
if (ManagedObj.GetType() != typeof(T)) throw new ArgumentException("Only specified type parameter type can be marshaled.");
2804-
SafeCoTaskMemStruct<VARIANT> pv = new(new VARIANT(ManagedObj));
2805-
return pv.TakeOwnership();
2806-
}
2807-
object ICustomMarshaler.MarshalNativeToManaged(IntPtr pNativeData)
2808-
{
2809-
if (pNativeData == IntPtr.Zero) return new string?[0];
2810-
unsafe
2811-
{
2812-
VARIANT* v = (VARIANT*)&pNativeData;
2813-
if (v->vt.GetCorrespondingType() != typeof(T)) throw new ArgumentException("Only specified type parameter type can be marshaled.");
2814-
return v->ToObject();
2815-
}
2816-
}
2817-
}*/
2818-
28192794
/// <summary>CLSID_AccessControlEntry</summary>
28202795
[ComImport, Guid("B75AC000-9BDD-11D0-852C-00C04FD8D503"), ClassInterface(ClassInterfaceType.None)]
28212796
public class AccessControlEntry

PInvoke/Ole/OleAut32/OAIdl.VARIANT.cs

Lines changed: 37 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2506,7 +2506,7 @@ public enum VtBits
25062506
// const VARIANTARG *pvargSrc );
25072507
[DllImport(Lib.OleAut32, SetLastError = false, ExactSpelling = true)]
25082508
[PInvokeData("oleauto.h", MSDNShortId = "5d9be6cd-92e5-485c-ba0d-8630d3e414b8")]
2509-
public static extern HRESULT VariantCopyInd(out VARIANT pvarDest, [MarshalAs(UnmanagedType.Struct)] in object pvargSrc);
2509+
public static extern HRESULT VariantCopyInd(out VARIANT pvarDest, [MarshalAs(UnmanagedType.Struct)] in object? pvargSrc);
25102510

25112511
/// <summary>
25122512
/// Frees the destination variant and makes a copy of the source variant, performing the necessary indirection if the source is
@@ -2559,7 +2559,7 @@ public enum VtBits
25592559
// const VARIANTARG *pvargSrc );
25602560
[DllImport(Lib.OleAut32, SetLastError = false, ExactSpelling = true)]
25612561
[PInvokeData("oleauto.h", MSDNShortId = "5d9be6cd-92e5-485c-ba0d-8630d3e414b8")]
2562-
public static extern HRESULT VariantCopyInd([MarshalAs(UnmanagedType.Struct)] out object pvarDest, in VARIANT pvargSrc);
2562+
public static extern HRESULT VariantCopyInd([MarshalAs(UnmanagedType.Struct)] out object? pvarDest, in VARIANT pvargSrc);
25632563

25642564
/// <summary>
25652565
/// Frees the destination variant and makes a copy of the source variant, performing the necessary indirection if the source is
@@ -2612,7 +2612,7 @@ public enum VtBits
26122612
// const VARIANTARG *pvargSrc );
26132613
[DllImport(Lib.OleAut32, SetLastError = false, ExactSpelling = true)]
26142614
[PInvokeData("oleauto.h", MSDNShortId = "5d9be6cd-92e5-485c-ba0d-8630d3e414b8")]
2615-
public static extern HRESULT VariantCopyInd([MarshalAs(UnmanagedType.Struct)] out object pvarDest, [In, MarshalAs(UnmanagedType.Struct)] in object pvargSrc);
2615+
public static extern HRESULT VariantCopyInd([MarshalAs(UnmanagedType.Struct)] out object? pvarDest, [In, MarshalAs(UnmanagedType.Struct)] in object? pvargSrc);
26162616

26172617
/// <summary>Initializes a variant.</summary>
26182618
/// <param name="pvarg">The variant to initialize.</param>
@@ -3321,23 +3321,54 @@ private readonly struct Record
33213321

33223322
/// <summary>Initializes a new instance of the <see cref="VARIANT"/> struct.</summary>
33233323
/// <param name="o">An object value.</param>
3324-
public VARIANT(object o)
3324+
public VARIANT(object? o)
33253325
{
33263326
VariantCopyInd(out VARIANT v, o).ThrowIfFailed();
33273327
this = v;
33283328
}
33293329

33303330
/// <summary>Performs a conversion from <see cref="VARIANT"/> to <see cref="System.Object"/>.</summary>
33313331
/// <returns>The result of the conversion.</returns>
3332-
public readonly object ToObject()
3332+
public readonly object? ToObject()
33333333
{
33343334
if (vt.IsFlagSet(VARTYPE.VT_ARRAY))
33353335
{
33363336
SafeSAFEARRAY a = new(byref, false);
33373337
return a.ToArray();
33383338
}
3339-
VariantCopyInd(out object o, this).ThrowIfFailed();
3339+
VariantCopyInd(out object? o, this).ThrowIfFailed();
33403340
return o;
33413341
}
33423342
}
3343+
3344+
//*** This was a fun experiment, but it turns out that custom marshalers cannot be used for values or nullables ***
3345+
///// <summary>Provides a custom marshaler for marshaling a specific type as a VARIANT.</summary>
3346+
//public class VariantMarshaler<T> : ICustomMarshaler
3347+
//{
3348+
// /// <summary>Gets an instance of the <see cref="VariantMarshaler{T}"/> class.</summary>
3349+
// public static ICustomMarshaler GetInstance(string pstrCookie) => new VariantMarshaler<T>();
3350+
3351+
// void ICustomMarshaler.CleanUpManagedData(object ManagedObj) { }
3352+
// void ICustomMarshaler.CleanUpNativeData(IntPtr pNativeData) => Marshal.FreeCoTaskMem(pNativeData);
3353+
// int ICustomMarshaler.GetNativeDataSize() => IntPtr.Size;
3354+
3355+
// IntPtr ICustomMarshaler.MarshalManagedToNative(object ManagedObj)
3356+
// {
3357+
// if (ManagedObj is not null && ManagedObj.GetType() != typeof(T)) throw new ArgumentException("Only specified type parameter type can be marshaled.");
3358+
// SafeCoTaskMemStruct<VARIANT> pv = new(ManagedObj is null ? new VARIANT { vt = VARTYPE.VT_EMPTY } : new VARIANT(ManagedObj));
3359+
// return pv.ReleaseOwnership();
3360+
// }
3361+
3362+
// object ICustomMarshaler.MarshalNativeToManaged(IntPtr pNativeData)
3363+
// {
3364+
// if (pNativeData == IntPtr.Zero) return null!;
3365+
// unsafe
3366+
// {
3367+
// VARIANT* v = (VARIANT*)&pNativeData;
3368+
// if (v->vt == VARTYPE.VT_EMPTY) return null!;
3369+
// if (v->vt.GetCorrespondingType() != typeof(T)) throw new ArgumentException("Only specified type parameter type can be marshaled.");
3370+
// return v->ToObject()!;
3371+
// }
3372+
// }
3373+
//}
33433374
}

UnitTests/CodeGen/CodeGenTests.cs

Lines changed: 57 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -39,72 +39,72 @@ namespace Vanara.PInvoke.Tests;
3939
[TestFixture]
4040
public class CodeGenTests
4141
{
42-
[Test]
43-
public void AddAsMemberTest()
44-
{
45-
const string src = /* lang=c#-test */ """
46-
using System;
47-
using System.Runtime.InteropServices;
48-
namespace Vanara.PInvoke
49-
{
50-
[AttributeUsage(AttributeTargets.Parameter, Inherited = false, AllowMultiple = false)]
51-
public sealed class AddAsMemberAttribute : Attribute { }
42+
//[Test]
43+
//public void AddAsMemberTest()
44+
//{
45+
// const string src = /* lang=c#-test */ """
46+
// using System;
47+
// using System.Runtime.InteropServices;
48+
// namespace Vanara.PInvoke
49+
// {
50+
// [AttributeUsage(AttributeTargets.Parameter, Inherited = false, AllowMultiple = false)]
51+
// public sealed class AddAsMemberAttribute : Attribute { }
5252

53-
[AttributeUsage(AttributeTargets.Parameter | AttributeTargets.ReturnValue, Inherited = false, AllowMultiple = false)]
54-
public sealed class AddAsCtorAttribute : Attribute { }
53+
// [AttributeUsage(AttributeTargets.Parameter | AttributeTargets.ReturnValue, Inherited = false, AllowMultiple = false)]
54+
// public sealed class AddAsCtorAttribute : Attribute { }
5555

56-
[AttributeUsage(AttributeTargets.Field | AttributeTargets.Parameter, Inherited = false, AllowMultiple = false)]
57-
public sealed class IgnoreAttribute : Attribute { }
56+
// [AttributeUsage(AttributeTargets.Field | AttributeTargets.Parameter, Inherited = false, AllowMultiple = false)]
57+
// public sealed class IgnoreAttribute : Attribute { }
5858

59-
public static partial class Test32
60-
{
61-
public partial struct HTEST { }
59+
// public static partial class Test32
60+
// {
61+
// public partial struct HTEST { }
6262

63-
public partial class SafeHTEST { }
63+
// public partial class SafeHTEST { }
6464

65-
[DllImport("test32.dll", SetLastError = true)]
66-
public static extern bool IsExcluded([AddAsMember] HTEST2 hTest);
65+
// [DllImport("test32.dll", SetLastError = true)]
66+
// public static extern bool IsExcluded([AddAsMember] HTEST2 hTest);
6767

68-
/// <summary>Determines if a test is active.</summary>
69-
/// <param name="hTest">The test handle.</param>
70-
/// <param name="flag">The flag.</param>
71-
/// <returns><see langword="true"/> on success, <see langword="false"/> on failure.</returns>
72-
[DllImport("test32.dll", SetLastError = true)]
73-
public static extern bool IsActive<T>([AddAsMember] HTEST hTest, T t, [Optional, Ignore] uint flag) where T : struct;
68+
// /// <summary>Determines if a test is active.</summary>
69+
// /// <param name="hTest">The test handle.</param>
70+
// /// <param name="flag">The flag.</param>
71+
// /// <returns><see langword="true"/> on success, <see langword="false"/> on failure.</returns>
72+
// [DllImport("test32.dll", SetLastError = true)]
73+
// public static extern bool IsActive<T>([AddAsMember] HTEST hTest, T t, [Optional, Ignore] uint flag) where T : struct;
7474

75-
/// <summary>Gets a test name.</summary>
76-
/// <param name="hTest">The test handle.</param>
77-
/// <param name="ptr">The pointer to the name buffer.</param>
78-
/// <param name="sz">The size of the name buffer.</param>
79-
/// <param name="name">The name.</param>
80-
/// <returns><see langword="true"/> on success, <see langword="false"/> on failure.</returns>
81-
[DllImport("test32.dll", SetLastError = true)]
82-
public static extern bool GetName([AddAsMember] HTEST hTest, [In, Optional, SizeDef(nameof(sz))] IntPtr ptr, uint sz, out string? name);
75+
// /// <summary>Gets a test name.</summary>
76+
// /// <param name="hTest">The test handle.</param>
77+
// /// <param name="ptr">The pointer to the name buffer.</param>
78+
// /// <param name="sz">The size of the name buffer.</param>
79+
// /// <param name="name">The name.</param>
80+
// /// <returns><see langword="true"/> on success, <see langword="false"/> on failure.</returns>
81+
// [DllImport("test32.dll", SetLastError = true)]
82+
// public static extern bool GetName([AddAsMember] HTEST hTest, [In, Optional, SizeDef(nameof(sz))] IntPtr ptr, uint sz, out string? name);
8383

84-
/// <summary>Sets a test name.</summary>
85-
/// <param name="hTest">The test handle.</param>
86-
/// <param name="name">The name.</param>
87-
/// <param name="code">The code.</param>
88-
/// <returns><see langword="true"/> on success, <see langword="false"/> on failure.</returns>
89-
[DllImport("test32.dll", SetLastError = true)]
90-
public static extern bool SetName([AddAsMember] HTEST hTest, string? name, [Ignore] uint code = 0);
84+
// /// <summary>Sets a test name.</summary>
85+
// /// <param name="hTest">The test handle.</param>
86+
// /// <param name="name">The name.</param>
87+
// /// <param name="code">The code.</param>
88+
// /// <returns><see langword="true"/> on success, <see langword="false"/> on failure.</returns>
89+
// [DllImport("test32.dll", SetLastError = true)]
90+
// public static extern bool SetName([AddAsMember] HTEST hTest, string? name, [Ignore] uint code = 0);
9191

92-
/// <summary>Creates a test.</summary>
93-
/// <param name="name">The name.</param>
94-
/// <param name="code">The code.</param>
95-
/// <param name="test">The test handle.</param>
96-
/// <returns><see langword="true"/> on success, <see langword="false"/> on failure.</returns>
97-
[DllImport("test32.dll", SetLastError = true)]
98-
public static extern bool CreateTest(string? name, [Optional][Ignore] uint code, [AddAsCtor] out SafeHTEST test);
99-
}
100-
}
101-
""";
102-
var compilation = GetCompilation(src);
103-
CreateGeneratorDriverAndRun(compilation, new AddAsMemberGenerator(), null, out var output, out var diag);
104-
Assert.That(output.SyntaxTrees, Has.Exactly(2).Items);
105-
Assert.That(diag.Where(d => d.Severity == DiagnosticSeverity.Error), Has.Exactly(1).Items);
106-
WriteTrees(TestContext.Out, output.SyntaxTrees, false);
107-
}
92+
// /// <summary>Creates a test.</summary>
93+
// /// <param name="name">The name.</param>
94+
// /// <param name="code">The code.</param>
95+
// /// <param name="test">The test handle.</param>
96+
// /// <returns><see langword="true"/> on success, <see langword="false"/> on failure.</returns>
97+
// [DllImport("test32.dll", SetLastError = true)]
98+
// public static extern bool CreateTest(string? name, [Optional][Ignore] uint code, [AddAsCtor] out SafeHTEST test);
99+
// }
100+
// }
101+
// """;
102+
// var compilation = GetCompilation(src);
103+
// CreateGeneratorDriverAndRun(compilation, new AddAsMemberGenerator(), null, out var output, out var diag);
104+
// Assert.That(output.SyntaxTrees, Has.Exactly(2).Items);
105+
// Assert.That(diag.Where(d => d.Severity == DiagnosticSeverity.Error), Has.Exactly(1).Items);
106+
// WriteTrees(TestContext.Out, output.SyntaxTrees, false);
107+
//}
108108

109109
[Test]
110110
public void AutoHandleTest()

0 commit comments

Comments
 (0)