When interop-ing with C++ code that exposes changeable values as VARIANT pointers, it's much easier to work with objects on the managed side. Here's a helper class that wraps such a variant pointer into a managed class and exposes the variant value as object.
This class can be constructed with an IntPtr pointing to a VARIANT struct in memory. It then marshals the contained object to the _object member. If disposed, it writes the value of the _object member back to the wrapped VARIANT which can then be used in the unmanaged code again.
using System.Runtime.InteropServices;
[StructLayout(LayoutKind.Explicit, Size = 16)]
struct Variant {
[FieldOffset(0)]
public ushort vt;
[FieldOffset(2)]
public ushort wReserved1;
[FieldOffset(4)]
public ushort wReserved2;
[FieldOffset(6)]
public ushort wReserved3;
}
public class NativeVariantWrapper: IDisposable {
Variant varStruct;
object _object;
IntPtr _variant;
public NativeVariantWrapper(IntPtr variant) {
_variant = variant;
if (variant != IntPtr.Zero) {
unsafe {
void * variantPointer = variant.ToPointer();
varStruct = * (Variant * ) variantPointer;
}
switch ((VarEnum) varStruct.vt) {
case VarEnum.VT_EMPTY:
_object = String.Empty;
break;
case VarEnum.VT_NULL:
_object = DBNull.Value;
break;
default:
_object = Marshal.GetObjectForNativeVariant(variant);
break;
}
}
}
public object Object {
get {
return _object;
}
set {
_object = value;
}
}
public void SaveValue() {
if (_variant != IntPtr.Zero) Marshal.GetNativeVariantForObject(_object, _variant);
}
public void Dispose() {
Dispose(true);
GC.SuppressFinalize(this);
}
~NativeVariantWrapper() {
Dispose(false);
}
private bool disposed;
protected virtual void Dispose(bool disposing) {
if (!disposed) {
if (disposing) {
SaveValue();
}
disposed = true;
}
}
}