Reputation: 91
How to translate the following COM interface to F#? I cannot figure out how to annotate get
and set
of a property.
Plus, for COM interop, do I need to annotate both a property itself and its get
with DispId
?
[ComImport, TypeLibType((short)0x1040), Guid("F935DC23-1CF0-11D0-ADB9-00C04FD58A0B")]
private interface IWshShortcut
{
[DispId(0)]
string FullName { [return: MarshalAs(UnmanagedType.BStr)] [DispId(0)] get; }
[DispId(0x3e8)]
string Arguments { [return: MarshalAs(UnmanagedType.BStr)] [DispId(0x3e8)] get; [param: In, MarshalAs(UnmanagedType.BStr)] [DispId(0x3e8)] set; }
[DispId(0x3ec)]
string RelativePath { [param: In, MarshalAs(UnmanagedType.BStr)] [DispId(0x3ec)] set; }
[DispId(0x3ee)]
int WindowStyle { [DispId(0x3ee)] get; [param: In] [DispId(0x3ee)] set; }
[DispId(0x3ef)]
string WorkingDirectory { [return: MarshalAs(UnmanagedType.BStr)] [DispId(0x3ef)] get; [param: In, MarshalAs(UnmanagedType.BStr)] [DispId(0x3ef)] set; }
[TypeLibFunc((short)0x40), DispId(0x7d0)]
void Load([In, MarshalAs(UnmanagedType.BStr)] string PathLink);
[DispId(0x7d1)]
void Save();
}
Upvotes: 2
Views: 673
Reputation: 62975
Here's a correct, though not literal, translation:
[<ComImport; Guid("F935DC23-1CF0-11D0-ADB9-00C04FD58A0B"); TypeLibType(0x1040s)>]
type private IWshShortcut =
[<DispId(0)>]
abstract member FullName : [<MarshalAs(UnmanagedType.BStr)>] string with get
[<DispId(0x3e8)>]
abstract member Arguments : [<MarshalAs(UnmanagedType.BStr)>] string with get, set
[<DispId(0x3ec)>]
abstract member RelativePath : [<MarshalAs(UnmanagedType.BStr)>] string with set
[<DispId(0x3ee)>]
abstract member WindowStyle : int with get, set
[<DispId(0x3ef)>]
abstract member WorkingDirectory : [<MarshalAs(UnmanagedType.BStr)>] string with get, set
[<DispId(0x7d0); TypeLibFunc(0x40s)>]
abstract member Load : [<MarshalAs(UnmanagedType.BStr)>] PathLink:string -> unit
[<DispId(0x7d1)>]
abstract member Save : unit -> unit
As you found, you cannot add attributes to an abstract property's underlying getter/setter methods in F#, only to the property itself, but it doesn't matter for this particular interface:
get
and set
need the same MarshalAs
for both anyway.In
is the default directionality for string parameters, so specifying it would be redundant anyway.DispId
to the property getter as your C# code does is legal but pointless – while DispId
can be applied to both methods and properties, and property getters and setters technically happen to be methods, the attribute only has an effect for the property itself.N.b. because the CLR marshals string parameters for COM methods as BStrs by default, we can omit all the MarshalAs
directives as well and make this look a bit more trim (albeit less explicit):
[<ComImport; Guid("F935DC23-1CF0-11D0-ADB9-00C04FD58A0B"); TypeLibType(0x1040s)>]
type private IWshShortcut =
[<DispId(0)>] abstract member FullName:string with get
[<DispId(0x3e8)>] abstract member Arguments:string with get, set
[<DispId(0x3ec)>] abstract member RelativePath:string with set
[<DispId(0x3ee)>] abstract member WindowStyle:int with get, set
[<DispId(0x3ef)>] abstract member WorkingDirectory:string with get, set
[<DispId(0x7d0); TypeLibFunc(0x40s)>] abstract member Load : PathLink:string -> unit
[<DispId(0x7d1)>] abstract member Save : unit -> unit
Of course, all of this applies to the C# implementation as well, so it can be similarly simplified:
[ComImport, Guid("F935DC23-1CF0-11D0-ADB9-00C04FD58A0B"), TypeLibType((short)0x1040)]
private interface IWshShortcut
{
[DispId(0)] string FullName { get; }
[DispId(0x3e8)] string Arguments { get; set; }
[DispId(0x3ec)] string RelativePath { set; }
[DispId(0x3ee)] int WindowStyle { get; set; }
[DispId(0x3ef)] string WorkingDirectory { get; set; }
[DispId(0x7d0), TypeLibFunc((short)0x40)] void Load(string PathLink);
[DispId(0x7d1)] void Save();
}
Upvotes: 6