Skip to content

Instantly share code, notes, and snippets.

@vbfox
Last active August 22, 2018 07:45
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save vbfox/5c8aad940132ad7a762c1d7024ffb0ec to your computer and use it in GitHub Desktop.
Save vbfox/5c8aad940132ad7a762c1d7024ffb0ec to your computer and use it in GitHub Desktop.
F# vswhere
// Microsoft.VisualStudio.Setup.Configuration.Interop.dll
module VsInstances =
open System.Runtime.InteropServices
[<Guid("B41463C3-8866-43B5-BC33-2B0676F7F42E")>]
[<InterfaceType(ComInterfaceType.InterfaceIsIUnknown)>]
[<ComImport>]
[<AllowNullLiteral>]
type private ISetupInstance =
abstract member GetInstanceId: unit -> [<MarshalAs(UnmanagedType.BStr)>] string
abstract member GetInstallDate: unit -> [<MarshalAs(UnmanagedType.Struct)>] System.Runtime.InteropServices.ComTypes.FILETIME
abstract member GetInstallationName : unit -> [<MarshalAs(UnmanagedType.BStr)>] string
abstract member GetInstallationPath : unit -> [<MarshalAs(UnmanagedType.BStr)>] string
abstract member GetInstallationVersion : unit -> [<MarshalAs(UnmanagedType.BStr)>] string
abstract member GetDisplayName : [<MarshalAs(UnmanagedType.U4)>][<In>] lcid: int -> [<MarshalAs(UnmanagedType.BStr)>] string
abstract member GetDescription : [<MarshalAs(UnmanagedType.U4)>][<In>] lcid: int -> [<MarshalAs(UnmanagedType.BStr)>] string
abstract member ResolvePath : [<MarshalAs(UnmanagedType.LPWStr)>][<In>] pwszRelativePath: string -> [<MarshalAs(UnmanagedType.BStr)>] string
[<Flags>]
type InstanceState =
| None = 0u
| Local = 1u
| Registered = 2u
| NoRebootRequired = 4u
| NoErrors = 8u
| Complete = 0xFFFFFFFFu
[<Guid("DA8D8A16-B2B6-4487-A2F1-594CCCCD6BF5")>]
[<InterfaceType(ComInterfaceType.InterfaceIsIUnknown)>]
[<ComImport>]
[<AllowNullLiteral>]
type private ISetupPackageReference =
abstract member GetId : unit -> [<MarshalAs(UnmanagedType.BStr)>] string
abstract member GetVersion : unit -> [<MarshalAs(UnmanagedType.BStr)>] string
abstract member GetChip : unit -> [<MarshalAs(UnmanagedType.BStr)>] string
abstract member GetLanguage : unit -> [<MarshalAs(UnmanagedType.BStr)>] string
abstract member GetBranch : unit -> [<MarshalAs(UnmanagedType.BStr)>] string
abstract member GetType : unit -> [<MarshalAs(UnmanagedType.BStr)>] string
abstract member GetUniqueId : unit -> [<MarshalAs(UnmanagedType.BStr)>] string
abstract member GetIsExtension : unit -> [<MarshalAs(UnmanagedType.VariantBool)>] bool
[<Guid("E73559CD-7003-4022-B134-27DC650B280F")>]
[<InterfaceType(ComInterfaceType.InterfaceIsIUnknown)>]
[<ComImport>]
[<AllowNullLiteral>]
type private ISetupFailedPackageReference =
inherit ISetupPackageReference
abstract member GetId : unit -> [<MarshalAs(UnmanagedType.BStr)>] string
abstract member GetVersion : unit -> [<MarshalAs(UnmanagedType.BStr)>] string
abstract member GetChip : unit -> [<MarshalAs(UnmanagedType.BStr)>] string
abstract member GetLanguage : unit -> [<MarshalAs(UnmanagedType.BStr)>] string
abstract member GetBranch : unit -> [<MarshalAs(UnmanagedType.BStr)>] string
abstract member GetType : unit -> [<MarshalAs(UnmanagedType.BStr)>] string
abstract member GetUniqueId : unit -> [<MarshalAs(UnmanagedType.BStr)>] string
abstract member GetIsExtension : unit -> [<MarshalAs(UnmanagedType.VariantBool)>] bool
[<Guid("E73559CD-7003-4022-B134-27DC650B280F")>]
[<InterfaceType(ComInterfaceType.InterfaceIsIUnknown)>]
[<ComImport>]
[<AllowNullLiteral>]
type private ISetupFailedPackageReference2 =
inherit ISetupFailedPackageReference
abstract member GetId : unit -> [<MarshalAs(UnmanagedType.BStr)>] string
abstract member GetVersion : unit -> [<MarshalAs(UnmanagedType.BStr)>] string
abstract member GetChip : unit -> [<MarshalAs(UnmanagedType.BStr)>] string
abstract member GetLanguage : unit -> [<MarshalAs(UnmanagedType.BStr)>] string
abstract member GetBranch : unit -> [<MarshalAs(UnmanagedType.BStr)>] string
abstract member GetType : unit -> [<MarshalAs(UnmanagedType.BStr)>] string
abstract member GetUniqueId : unit -> [<MarshalAs(UnmanagedType.BStr)>] string
abstract member GetIsExtension : unit -> [<MarshalAs(UnmanagedType.VariantBool)>] bool
abstract member GetLogFilePath : unit -> [<MarshalAs(UnmanagedType.BStr)>] string
abstract member GetDescription : unit -> [<MarshalAs(UnmanagedType.BStr)>] string
abstract member GetSignature : unit -> [<MarshalAs(UnmanagedType.BStr)>] string
abstract member GetDetails: unit -> [<MarshalAs(UnmanagedType.SafeArray, SafeArraySubType = VarEnum.VT_BSTR)>] string[]
abstract member GetAffectedPackages: unit -> [<MarshalAs(UnmanagedType.SafeArray, SafeArraySubType = VarEnum.VT_UNKNOWN)>] ISetupPackageReference[]
[<Guid("2A2F3292-958E-4905-B36E-013BE84E27AB")>]
[<InterfaceType(ComInterfaceType.InterfaceIsIUnknown)>]
[<ComImport>]
[<AllowNullLiteral>]
type private ISetupErrorInfo =
abstract member GetErrorHResult: unit -> int
abstract member GetErrorClassName: unit -> [<MarshalAs(UnmanagedType.BStr)>] string
abstract member GetErrorMessage: unit -> [<MarshalAs(UnmanagedType.BStr)>] string
[<Guid("46DCCD94-A287-476A-851E-DFBC2FFDBC20")>]
[<InterfaceType(ComInterfaceType.InterfaceIsIUnknown)>]
[<ComImport>]
[<AllowNullLiteral>]
type private ISetupErrorState =
abstract member GetFailedPackages: unit -> [<MarshalAs(UnmanagedType.SafeArray, SafeArraySubType = VarEnum.VT_UNKNOWN)>] ISetupFailedPackageReference[]
abstract member GetSkippedPackages: unit -> [<MarshalAs(UnmanagedType.SafeArray, SafeArraySubType = VarEnum.VT_UNKNOWN)>] ISetupPackageReference[]
[<Guid("9871385B-CA69-48F2-BC1F-7A37CBF0B1EF")>]
[<InterfaceType(ComInterfaceType.InterfaceIsIUnknown)>]
[<ComImport>]
[<AllowNullLiteral>]
type private ISetupErrorState2 =
inherit ISetupErrorState
abstract member GetFailedPackages: unit -> [<MarshalAs(UnmanagedType.SafeArray, SafeArraySubType = VarEnum.VT_UNKNOWN)>] ISetupFailedPackageReference[]
abstract member GetSkippedPackages: unit -> [<MarshalAs(UnmanagedType.SafeArray, SafeArraySubType = VarEnum.VT_UNKNOWN)>] ISetupPackageReference[]
abstract member GetErrorLogFilePath : unit -> [<MarshalAs(UnmanagedType.BStr)>] string
abstract member GetLogFilePath : unit -> [<MarshalAs(UnmanagedType.BStr)>] string
[<Guid("290019AD-28E2-46D5-9DE5-DA4B6BCF8057")>]
[<InterfaceType(ComInterfaceType.InterfaceIsIUnknown)>]
[<ComImport>]
[<AllowNullLiteral>]
type private ISetupErrorState3 =
inherit ISetupErrorState2
abstract member GetFailedPackages: unit -> [<MarshalAs(UnmanagedType.SafeArray, SafeArraySubType = VarEnum.VT_UNKNOWN)>] ISetupFailedPackageReference[]
abstract member GetSkippedPackages: unit -> [<MarshalAs(UnmanagedType.SafeArray, SafeArraySubType = VarEnum.VT_UNKNOWN)>] ISetupPackageReference[]
abstract member GetErrorLogFilePath : unit -> [<MarshalAs(UnmanagedType.BStr)>] string
abstract member GetLogFilePath : unit -> [<MarshalAs(UnmanagedType.BStr)>] string
abstract member GetRuntimeError : unit -> ISetupErrorInfo
[<Guid("c601c175-a3be-44bc-91f6-4568d230fc83")>]
[<InterfaceType(ComInterfaceType.InterfaceIsIUnknown)>]
[<ComImport>]
[<AllowNullLiteral>]
type private ISetupPropertyStore =
abstract member GetNames : unit -> [<MarshalAs(UnmanagedType.SafeArray, SafeArraySubType = VarEnum.VT_BSTR)>] string[]
abstract member GetValue: [<MarshalAs(UnmanagedType.LPWStr)>][<In>] pwszName: string -> obj
[<Guid("B41463C3-8866-43B5-BC33-2B0676F7F42E")>]
[<InterfaceType(ComInterfaceType.InterfaceIsIUnknown)>]
[<ComImport>]
[<AllowNullLiteral>]
type private ISetupInstance2 =
inherit ISetupInstance
abstract member GetInstanceId: unit -> [<MarshalAs(UnmanagedType.BStr)>] string
abstract member GetInstallDate: unit -> [<MarshalAs(UnmanagedType.Struct)>] System.Runtime.InteropServices.ComTypes.FILETIME
abstract member GetInstallationName : unit -> [<MarshalAs(UnmanagedType.BStr)>] string
abstract member GetInstallationPath : unit -> [<MarshalAs(UnmanagedType.BStr)>] string
abstract member GetInstallationVersion : unit -> [<MarshalAs(UnmanagedType.BStr)>] string
abstract member GetDisplayName : [<MarshalAs(UnmanagedType.U4)>][<In>] lcid: int -> [<MarshalAs(UnmanagedType.BStr)>] string
abstract member GetDescription : [<MarshalAs(UnmanagedType.U4)>][<In>] lcid: int -> [<MarshalAs(UnmanagedType.BStr)>] string
abstract member ResolvePath : [<MarshalAs(UnmanagedType.LPWStr)>][<In>] pwszRelativePath: string -> [<MarshalAs(UnmanagedType.BStr)>] string
abstract member GetState: unit -> [<MarshalAs(UnmanagedType.U4)>] InstanceState
abstract member GetPackages: unit -> [<MarshalAs(UnmanagedType.SafeArray, SafeArraySubType = VarEnum.VT_UNKNOWN)>] ISetupPackageReference[]
abstract member GetProduct: unit -> ISetupPackageReference
abstract member GetProductPath: unit -> [<MarshalAs(UnmanagedType.BStr)>] string
abstract member GetErrors: unit -> ISetupErrorState
abstract member IsLaunchable: unit -> [<MarshalAs(UnmanagedType.VariantBool)>] bool
abstract member IsComplete: unit -> [<MarshalAs(UnmanagedType.VariantBool)>] bool
abstract member GetProperties: unit -> ISetupPropertyStore
abstract member GetEnginePath: unit -> [<MarshalAs(UnmanagedType.BStr)>] string
[<Guid("6380BCFF-41D3-4B2E-8B2E-BF8A6810C848")>]
[<InterfaceType(ComInterfaceType.InterfaceIsIUnknown)>]
[<ComImport>]
[<AllowNullLiteral>]
type private IEnumSetupInstances =
abstract member Next:
[<MarshalAs(UnmanagedType.U4)>][<In>] celt: int
* [<MarshalAs(UnmanagedType.LPArray, ArraySubType = UnmanagedType.Interface)>][<Out>] rgelt: ISetupInstance[]
* [<MarshalAs(UnmanagedType.U4)>][<Out>] pceltFetched: byref<int>
-> unit
abstract member Skip: [<MarshalAs(UnmanagedType.U4)>][<In>] celt: int -> unit
abstract member Reset: unit -> unit
abstract member Clone: unit -> [<MarshalAs(UnmanagedType.Interface)>] IEnumSetupInstances
[<Guid("42843719-DB4C-46C2-8E7C-64F1816EFD5B")>]
[<InterfaceType(ComInterfaceType.InterfaceIsIUnknown)>]
[<ComImport>]
[<AllowNullLiteral>]
type private ISetupConfiguration =
abstract member EnumInstances: unit -> [<MarshalAs(UnmanagedType.Interface)>] IEnumSetupInstances
abstract member GetInstanceForCurrentProcess: unit -> [<MarshalAs(UnmanagedType.Interface)>] ISetupInstance
abstract member GetInstanceForPath: [<MarshalAs(UnmanagedType.LPWStr)>][<In>] path: string -> [<MarshalAs(UnmanagedType.Interface)>] ISetupInstance
[<Guid("26AAB78C-4A60-49D6-AF3B-3C35BC93365D")>]
[<InterfaceType(ComInterfaceType.InterfaceIsIUnknown)>]
[<ComImport>]
[<AllowNullLiteral>]
type private ISetupConfiguration2 =
inherit ISetupConfiguration
abstract member EnumInstances: unit -> [<MarshalAs(UnmanagedType.Interface)>] IEnumSetupInstances
abstract member GetInstanceForCurrentProcess: unit -> [<MarshalAs(UnmanagedType.Interface)>] ISetupInstance
abstract member GetInstanceForPath: [<MarshalAs(UnmanagedType.LPWStr)>][<In>] path: string -> [<MarshalAs(UnmanagedType.Interface)>] ISetupInstance
abstract member EnumAllInstances: unit -> [<MarshalAs(UnmanagedType.Interface)>] IEnumSetupInstances
[<Guid("9AD8E40F-39A2-40F1-BF64-0A6C50DD9EEB")>]
[<InterfaceType(ComInterfaceType.InterfaceIsIUnknown)>]
[<ComImport>]
type private ISetupInstanceCatalog =
abstract member GetCatalogInfo: unit -> ISetupPropertyStore
abstract member IsPrerelease: unit -> [<MarshalAs(UnmanagedType.VariantBool)>] bool
let private setupConfiguration = lazy(
let configType = System.Type.GetTypeFromCLSID (System.Guid "177F0C4A-1CD3-4DE7-A32C-71DBBB9FA36D")
Activator.CreateInstance configType :?> ISetupConfiguration
)
let private enumAllInstances () =
let instancesEnumerator =
let v1 = setupConfiguration.Value
match v1 with
| :? ISetupConfiguration2 as v2 -> v2.EnumAllInstances()
| _ -> v1.EnumInstances()
let instances = Array.zeroCreate<ISetupInstance> 1
let fetched = ref 1
seq {
while !fetched = 1 do
instancesEnumerator.Next(1, instances, fetched)
if !fetched = 1 then
yield instances.[0]
}
type VsSetupPackage =
{ Id: string
Version: string
Chip: string
Language: string
Branch: string
Type: string
UniqueId: string
IsExtension: bool }
type VsSetupErrorInfo =
{ HResult: int
ErrorClassName: string
ErrorMessage: string }
type VsSetupErrorState =
{ FailedPackages: VsSetupPackage list
SkippedPackages: VsSetupPackage list
ErrorLogFilePath: string option
LogFilePath: string option
RuntimeError: VsSetupErrorInfo option }
type VsSetupInstance =
{
InstanceId: string
InstallDate: DateTimeOffset
InstallationName: string
InstallationPath: string
InstallationVersion: string
DisplayName: string
Description: string
State: InstanceState option
Packages: VsSetupPackage list
Product: VsSetupPackage option
ProductPath: string option
Errors: VsSetupErrorState option
IsLaunchable: bool option
IsComplete: bool option
Properties: Map<string, obj>
EnginePath: string option
IsPrerelease: bool option
CatalogInfo: Map<string, obj>
}
let private parseErrorInfo (error: ISetupErrorInfo) =
{
HResult = error.GetErrorHResult()
ErrorClassName = error.GetErrorClassName()
ErrorMessage = error.GetErrorMessage()
}
let private parsePackageReference (instance: ISetupPackageReference) =
{
Id = instance.GetId()
Version = instance.GetId()
Chip = instance.GetChip()
Language = instance.GetLanguage()
Branch = instance.GetBranch()
Type = instance.GetType()
UniqueId = instance.GetUniqueId()
IsExtension = instance.GetIsExtension()
}
let private parseErrorState (state: ISetupErrorState) =
let result =
{
FailedPackages = state.GetFailedPackages() |> Seq.map parsePackageReference |> List.ofSeq
SkippedPackages = state.GetSkippedPackages() |> Seq.map parsePackageReference |> List.ofSeq
ErrorLogFilePath = None
LogFilePath = None
RuntimeError = None
}
match state with
| :? ISetupErrorState2 as state2 ->
let result2 =
{ result with
ErrorLogFilePath = state2.GetErrorLogFilePath() |> Some
LogFilePath = state2.GetLogFilePath() |> Some
}
match state2 with
| :? ISetupErrorState3 as state3 ->
{ result2 with
RuntimeError = state3.GetRuntimeError() |> parseErrorInfo |> Some
}
| _-> result2
| _ -> result
let private parseDate (date: System.Runtime.InteropServices.ComTypes.FILETIME) =
let high = uint64 (uint32 date.dwHighDateTime)
let low = uint64 (uint32 date.dwLowDateTime)
let composed = (high <<< 32) ||| low
DateTimeOffset.FromFileTime(int64 composed)
let private parseProperties (store: ISetupPropertyStore) =
store.GetNames()
|> Seq.map(fun name ->
let value = store.GetValue(name)
name, value)
|> Map.ofSeq
let private parseInstance (instance: ISetupInstance) =
let mutable result =
{ InstanceId = instance.GetInstanceId()
InstallDate = parseDate (instance.GetInstallDate())
InstallationName = instance.GetInstallationName()
InstallationPath = instance.GetInstallationPath()
InstallationVersion = instance.GetInstallationVersion()
DisplayName = instance.GetDisplayName(0)
Description = instance.GetDescription(0)
State = None
Packages = []
Product = None
ProductPath = None
Errors = None
IsLaunchable = None
IsComplete = None
Properties = Map.empty
EnginePath = None
IsPrerelease = None
CatalogInfo = Map.empty }
match instance with
| :? ISetupInstanceCatalog as catalog ->
result <- { result with
IsPrerelease = catalog.IsPrerelease() |> Some
CatalogInfo = catalog.GetCatalogInfo() |> parseProperties }
| _ -> ()
match instance with
| :? ISetupInstance2 as v2 ->
{ result with
State = v2.GetState() |> Some
Packages = v2.GetPackages() |> Seq.map parsePackageReference |> List.ofSeq
Product = parsePackageReference (v2.GetProduct()) |> Some
ProductPath = v2.GetProductPath() |> Some
Errors = v2.GetErrors() |> Option.ofObj |> Option.map parseErrorState
IsLaunchable = v2.IsLaunchable() |> Some
IsComplete = v2.IsComplete() |> Some
Properties = parseProperties (v2.GetProperties())
EnginePath = v2.GetEnginePath() |> Some }
| _ -> result
let getAll () =
try
enumAllInstances ()
|> Seq.map parseInstance
|> List.ofSeq
with
| :? COMException ->
[]
VsInstances.getAll () |> Dump
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment