diff --git a/VisualStudioMock/ComProxy.cs b/VisualStudioMock/ComProxy.cs new file mode 100644 index 0000000..720eeaa --- /dev/null +++ b/VisualStudioMock/ComProxy.cs @@ -0,0 +1,101 @@ +using System; +using System.Runtime.InteropServices; + +namespace VisualStudioMock +{ + delegate int GetInterfaceDelegate(IntPtr @this, [MarshalAs(UnmanagedType.LPStruct)] Guid iid, out IntPtr ppv); + delegate int AddRefDelegate(IntPtr @this); + delegate int ReleaseDelegate(IntPtr @this); + + sealed class ComProxy + { + private const int S_OK = 0; + private const int E_NOINTERFACE = -2147467262; + + private static readonly Guid IID_IUnknown = new Guid("00000000-0000-0000-c000-000000000046"); + private static readonly Guid IID_IManagedObject = new Guid("C3FCC19E-A970-11D2-8B5A-00A0C9B7C9C4"); + + private readonly object _realInstance; + private readonly IntPtr _realInstanceN; + private readonly IntPtr _proxyInstanceN; + private readonly GetInterfaceDelegate _getInterface; + private readonly IntPtr _getInterfaceN; + private readonly AddRefDelegate _addRef; + private readonly ReleaseDelegate _release; + + internal ComProxy(object instance) + { + this._realInstance = instance; + this._realInstanceN = Marshal.GetIUnknownForObject(instance); + this._proxyInstanceN = Marshal.AllocHGlobal(IntPtr.Size * 4); + + IntPtr vtbl = _proxyInstanceN + IntPtr.Size; + Marshal.WriteIntPtr(_proxyInstanceN, vtbl); + Marshal.WriteIntPtr(vtbl, this._getInterfaceN = Marshal.GetFunctionPointerForDelegate((Delegate)(this._getInterface = new GetInterfaceDelegate(GetInterface)))); + Marshal.WriteIntPtr(vtbl, IntPtr.Size * 1, Marshal.GetFunctionPointerForDelegate((Delegate)(this._addRef = new AddRefDelegate(AddRef)))); + Marshal.WriteIntPtr(vtbl, IntPtr.Size * 2, Marshal.GetFunctionPointerForDelegate((Delegate)(this._release = new ReleaseDelegate(Release)))); + } + + internal object GetIUnknown() + { + return Marshal.GetObjectForIUnknown(_proxyInstanceN); + } + + private int GetInterface(IntPtr @this, [MarshalAs(UnmanagedType.LPStruct)] Guid iid, out IntPtr ppv) + { + if (iid == IID_IUnknown) + { + ppv = _proxyInstanceN; + Ext.LogCall(_realInstance.GetType().Name, nameof(GetInterface), "IUnknown OK"); + return S_OK; + } + + if (iid == IID_IManagedObject) + { + ppv = IntPtr.Zero; + Ext.LogCall(_realInstance.GetType().Name, nameof(GetInterface), "IManagedObject Fail"); + return E_NOINTERFACE; + } + + int result = Marshal.QueryInterface(_realInstanceN, ref iid, out ppv); + if (result == S_OK) + { + // Patch the vtable with our own thunks + IntPtr origVtbl = Marshal.ReadIntPtr(ppv); + + Type iType = null; + foreach (Type i in _realInstance.GetType().GetInterfaces()) + if (i.GUID == iid) + { + iType = i; + break; + } + if (iType != null) + { + // TODO generate proper thunks in machine code + int slotCount = Marshal.GetEndComSlot(iType) + 1; + int slotsSizeBytes = IntPtr.Size * slotCount; + IntPtr newVtbl = Marshal.AllocHGlobal(slotsSizeBytes); + ... + } + + Ext.LogCall(_realInstance.GetType().Name, nameof(GetInterface), (iType == null ? iid.ToString() : iType.Name) + " OK"); + } + else + { + Ext.LogCall(_realInstance.GetType().Name, nameof(GetInterface), iid.ToString() + " Fail"); + } + return result; + } + + private int AddRef(IntPtr @this) + { + return 1; + } + + private int Release(IntPtr @this) + { + return 1; + } + } +} diff --git a/VisualStudioMock/Ext.cs b/VisualStudioMock/Ext.cs new file mode 100644 index 0000000..18e3208 --- /dev/null +++ b/VisualStudioMock/Ext.cs @@ -0,0 +1,31 @@ +using System; + +namespace VisualStudioMock +{ + static class Ext + { + public static void Log(string message) + { + Console.WriteLine("Mock: " + message); + } + + public static void LogCall(string @class, string method, string message = null) + { + Console.WriteLine("Mock call: " + @class + "!" + method + " " + message); + } + + public static void LogEnter(string @class, string method) + { + Console.WriteLine("Mock enter: " + @class + "!" + method); + } + + public static void LogExit(string @class, string method) + { + Console.WriteLine("Mock exit: " + @class + "!" + method); + } + + public static U ComBarrier(this T instance) where T : U { + return (U)(new ComProxy(instance).GetIUnknown()); + } + } +} \ No newline at end of file diff --git a/VisualStudioMock/IEnumSetupInstances.cs b/VisualStudioMock/IEnumSetupInstances.cs new file mode 100644 index 0000000..2a56976 --- /dev/null +++ b/VisualStudioMock/IEnumSetupInstances.cs @@ -0,0 +1,41 @@ +using System; +using System.Runtime.InteropServices; + +namespace VisualStudioMock +{ + /// + /// An enumerator of installed objects. + /// + [ComImport] + [ComVisible(true)] + [Guid("6380BCFF-41D3-4B2E-8B2E-BF8A6810C848")] + [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] + public interface IEnumSetupInstances + { + /// + /// Retrieves the next set of product instances in the enumeration sequence. + /// + /// The number of product instances to retrieve. + /// A pointer to an array of . + /// A pointer to the number of product instances retrieved. If is 1 this parameter may be NULL. + void Next([In] [MarshalAs(UnmanagedType.U4)] int celt, [Out] [MarshalAs(UnmanagedType.LPArray, ArraySubType = UnmanagedType.Interface)] ISetupInstance[] rgelt, [MarshalAs(UnmanagedType.U4)] out int pceltFetched); + + /// + /// Skips the next set of product instances in the enumeration sequence. + /// + /// The number of product instances to skip. + void Skip([In] [MarshalAs(UnmanagedType.U4)] int celt); + + /// + /// Resets the enumeration sequence to the beginning. + /// + void Reset(); + + /// + /// Creates a new enumeration object in the same state as the current enumeration object: the new object points to the same place in the enumeration sequence. + /// + /// A pointer to a pointer to a new interface. If the method fails, this parameter is undefined. + [return: MarshalAs(UnmanagedType.Interface)] + IEnumSetupInstances Clone(); + } +} \ No newline at end of file diff --git a/VisualStudioMock/ISetupConfiguration.cs b/VisualStudioMock/ISetupConfiguration.cs new file mode 100644 index 0000000..86d336b --- /dev/null +++ b/VisualStudioMock/ISetupConfiguration.cs @@ -0,0 +1,42 @@ +using System; +using System.Runtime.InteropServices; + +namespace VisualStudioMock +{ + /// + /// Gets information about product instances installed on the machine. + /// + [ComImport] + [Guid("42843719-DB4C-46C2-8E7C-64F1816EFD5B")] + [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] + public interface ISetupConfiguration + { + /// + /// Enumerates all launchable product instances installed. + /// + /// An enumeration of installed product instances. + [return: MarshalAs(UnmanagedType.Interface)] + IEnumSetupInstances EnumInstances(); + + /// + /// Gets the instance for the current process path. + /// + /// The instance for the current process path. + /// + /// The returned instance may not be launchable. + /// + [return: MarshalAs(UnmanagedType.Interface)] + ISetupInstance GetInstanceForCurrentProcess(); + + /// + /// Gets the instance for the given path. + /// + /// Path used to determine instance + /// The instance for the given path. + /// + /// The returned instance may not be launchable. + /// + [return: MarshalAs(UnmanagedType.Interface)] + ISetupInstance GetInstanceForPath([In] [MarshalAs(UnmanagedType.LPWStr)] string path); + } +} \ No newline at end of file diff --git a/VisualStudioMock/ISetupConfiguration2.cs b/VisualStudioMock/ISetupConfiguration2.cs new file mode 100644 index 0000000..a492116 --- /dev/null +++ b/VisualStudioMock/ISetupConfiguration2.cs @@ -0,0 +1,49 @@ +using System; +using System.Runtime.InteropServices; + +namespace VisualStudioMock +{ + /// + /// Gets information about product instances set up on the machine. + /// + [ComImport] + [Guid("26AAB78C-4A60-49D6-AF3B-3C35BC93365D")] + [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] + public interface ISetupConfiguration2 : ISetupConfiguration + { + /// + /// Enumerates all launchable product instances installed. + /// + /// An enumeration of installed product instances. + [return: MarshalAs(UnmanagedType.Interface)] + new IEnumSetupInstances EnumInstances(); + + /// + /// Gets the instance for the current process path. + /// + /// The instance for the current process path. + /// + /// The returned instance may not be launchable. + /// + [return: MarshalAs(UnmanagedType.Interface)] + new ISetupInstance GetInstanceForCurrentProcess(); + + /// + /// Gets the instance for the given path. + /// + /// Path used to determine instance + /// The instance for the given path. + /// + /// The returned instance may not be launchable. + /// + [return: MarshalAs(UnmanagedType.Interface)] + new ISetupInstance GetInstanceForPath([In] [MarshalAs(UnmanagedType.LPWStr)] string path); + + /// + /// Enumerates all product instances. + /// + /// An enumeration of all product instances. + [return: MarshalAs(UnmanagedType.Interface)] + IEnumSetupInstances EnumAllInstances(); + } +} \ No newline at end of file diff --git a/VisualStudioMock/ISetupErrorState.cs b/VisualStudioMock/ISetupErrorState.cs new file mode 100644 index 0000000..ac3ecac --- /dev/null +++ b/VisualStudioMock/ISetupErrorState.cs @@ -0,0 +1,30 @@ +using System; +using System.Runtime.InteropServices; + +namespace VisualStudioMock +{ + /// + /// Information about the error state of an instance. + /// + [ComImport] + [Guid("46DCCD94-A287-476A-851E-DFBC2FFDBC20")] + [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] + public interface ISetupErrorState + { + /* + /// + /// Gets an array of failed package references. + /// + /// An array of failed package references. + [return: MarshalAs(UnmanagedType.SafeArray, SafeArraySubType = VarEnum.VT_UNKNOWN)] + ISetupFailedPackageReference[] GetFailedPackages(); + + /// + /// Gets an array of skipped package references. + /// + /// An array of skipped package references. + [return: MarshalAs(UnmanagedType.SafeArray, SafeArraySubType = VarEnum.VT_UNKNOWN)] + ISetupPackageReference[] GetSkippedPackages(); + */ + } +} \ No newline at end of file diff --git a/VisualStudioMock/ISetupInstance.cs b/VisualStudioMock/ISetupInstance.cs new file mode 100644 index 0000000..b319c73 --- /dev/null +++ b/VisualStudioMock/ISetupInstance.cs @@ -0,0 +1,77 @@ +using System; +using System.Runtime.InteropServices; +using FILETIME = System.Runtime.InteropServices.ComTypes.FILETIME; + +namespace VisualStudioMock +{ + /// + /// Information about an instance of a product. + /// + /// + /// You can enumerate all properties of basic types by casting to an . + /// + [ComImport] + [Guid("B41463C3-8866-43B5-BC33-2B0676F7F42E")] + [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] + public interface ISetupInstance + { + /// + /// Gets the instance identifier (should match the name of the parent instance directory). + /// + /// The instance identifier. + [return: MarshalAs(UnmanagedType.BStr)] + string GetInstanceId(); + + /// + /// Gets the local date and time when the installation was originally installed. + /// + /// The local date and time when the installation was originally installed. + [return: MarshalAs(UnmanagedType.Struct)] + FILETIME GetInstallDate(); + + /// + /// Gets the unique name of the installation, often indicating the branch and other information used for telemetry. + /// + /// The unique name of the installation, often indicating the branch and other information used for telemetry. + [return: MarshalAs(UnmanagedType.BStr)] + string GetInstallationName(); + + /// + /// Gets the path to the installation root of the product. + /// + /// The path to the installation root of the product. + [return: MarshalAs(UnmanagedType.BStr)] + string GetInstallationPath(); + + /// + /// Gets the version of the product installed in this instance. + /// + /// The version of the product installed in this instance. + [return: MarshalAs(UnmanagedType.BStr)] + string GetInstallationVersion(); + + /// + /// Gets the display name (title) of the product installed in this instance. + /// + /// The LCID for the display name. + /// The display name (title) of the product installed in this instance. + [return: MarshalAs(UnmanagedType.BStr)] + string GetDisplayName([In] [MarshalAs(UnmanagedType.U4)] int lcid = 0); + + /// + /// Gets the description of the product installed in this instance. + /// + /// The LCID for the description. + /// The description of the product installed in this instance. + [return: MarshalAs(UnmanagedType.BStr)] + string GetDescription([In] [MarshalAs(UnmanagedType.U4)] int lcid = 0); + + /// + /// Resolves the optional relative path to the root path of the instance. + /// + /// A relative path within the instance to resolve, or NULL to get the root path. + /// The full path to the optional relative path within the instance. If the relative path is NULL, the root path will always terminate in a backslash. + [return: MarshalAs(UnmanagedType.BStr)] + string ResolvePath([In] [MarshalAs(UnmanagedType.LPWStr)] string pwszRelativePath = null); + } +} \ No newline at end of file diff --git a/VisualStudioMock/ISetupInstance2.cs b/VisualStudioMock/ISetupInstance2.cs new file mode 100644 index 0000000..2d6c3eb --- /dev/null +++ b/VisualStudioMock/ISetupInstance2.cs @@ -0,0 +1,83 @@ +using System; +using System.Runtime.InteropServices; + +namespace VisualStudioMock +{ + /// + /// Information about an instance of a product. + /// + /// + /// You can enumerate all properties of basic types by casting to an . + /// + [ComImport] + [Guid("89143C9A-05AF-49B0-B717-72E218A2185C")] + [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] + public interface ISetupInstance2 : ISetupInstance + { + /// + /// Gets the state of the instance. + /// + /// The state of the instance. + [return: MarshalAs(UnmanagedType.U4)] + InstanceState GetState(); + + /// + /// Gets an array of package references registered to the instance. + /// + /// An array of package references registered to the instance. + [return: MarshalAs(UnmanagedType.SafeArray, SafeArraySubType = VarEnum.VT_UNKNOWN)] + ISetupPackageReference[] GetPackages(); + + /// + /// Gets a package reference to the product registered to the instance + /// + /// A package reference to the product registered to the instance. This may be null if does not return . + ISetupPackageReference GetProduct(); + + /// + /// Gets the relative path to the product application, if available. + /// + /// The relative path to the product application, if available. + [return: MarshalAs(UnmanagedType.BStr)] + string GetProductPath(); + + /// + /// Gets the error state of the instance, if available. + /// + /// The error state of the instance, if available. + ISetupErrorState GetErrors(); + + /// + /// Gets a value indicating whether the instance can be launched. + /// + /// Whether the instance can be launched. + /// + /// An instance could have had errors during install but still be launched. Some features may not work correctly, but others will. + /// + [return: MarshalAs(UnmanagedType.VariantBool)] + bool IsLaunchable(); + + /// + /// Gets a value indicating whether the instance is complete. + /// + /// Whether the instance is complete. + /// + /// An instance is complete if it had no errors during install, resume, or repair. + /// + [return: MarshalAs(UnmanagedType.VariantBool)] + bool IsComplete(); + + /// + /// Gets product-specific properties. + /// + /// An of product-specific properties, or null if no properties are defined. + ISetupPropertyStore GetProperties(); + + /// + /// Gets the directory path to the setup engine that installed the instance. + /// + /// The directory path to the setup engine that installed the instance. + [return: MarshalAs(UnmanagedType.BStr)] + string GetEnginePath(); + } +} \ No newline at end of file diff --git a/VisualStudioMock/ISetupPackageReference.cs b/VisualStudioMock/ISetupPackageReference.cs new file mode 100644 index 0000000..e06fe0d --- /dev/null +++ b/VisualStudioMock/ISetupPackageReference.cs @@ -0,0 +1,73 @@ +using System; +using System.Runtime.InteropServices; + +namespace VisualStudioMock +{ + /// + /// A reference to a package. + /// + /// + /// You can enumerate all properties of basic types by casting to an . + /// + [ComImport] + [Guid("DA8D8A16-B2B6-4487-A2F1-594CCCCD6BF5")] + [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] + public interface ISetupPackageReference + { + /// + /// Gets the general package identifier. + /// + /// The general package identifier. + [return: MarshalAs(UnmanagedType.BStr)] + string GetId(); + + /// + /// Gets the version of the package. + /// + /// The version of the package. + [return: MarshalAs(UnmanagedType.BStr)] + string GetVersion(); + + /// + /// Gets the target process architecture of the package. + /// + /// The target process architecture of the package. + [return: MarshalAs(UnmanagedType.BStr)] + string GetChip(); + + /// + /// Gets the language and optional region identifier. + /// + /// The language and optional region identifier. + [return: MarshalAs(UnmanagedType.BStr)] + string GetLanguage(); + + /// + /// Gets the build branch of the package. + /// + /// The build branch of the package. + [return: MarshalAs(UnmanagedType.BStr)] + string GetBranch(); + + /// + /// Gets the type of the package. + /// + /// The type of the package. + [return: MarshalAs(UnmanagedType.BStr)] + new string GetType(); + + /// + /// Gets the unique identifier consisting of all defined tokens. + /// + /// The unique identifier consisting of all defined tokens. + [return: MarshalAs(UnmanagedType.BStr)] + string GetUniqueId(); + + /// + /// Gets a value indicating whether the package refers to an external extension. + /// + /// A value indicating whether the package refers to an external extension. + [return: MarshalAs(UnmanagedType.VariantBool)] + bool GetIsExtension(); + } +} \ No newline at end of file diff --git a/VisualStudioMock/ISetupPropertyStore.cs b/VisualStudioMock/ISetupPropertyStore.cs new file mode 100644 index 0000000..8b3b3a0 --- /dev/null +++ b/VisualStudioMock/ISetupPropertyStore.cs @@ -0,0 +1,31 @@ +using System; +using System.Runtime.InteropServices; + +namespace VisualStudioMock +{ + /// + /// Provides named properties. + /// + /// + /// You can get this from an , , or derivative. + /// + [ComImport] + [Guid("c601c175-a3be-44bc-91f6-4568d230fc83")] + [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] + public interface ISetupPropertyStore + { + /// + /// Gets an array of property names in this property store. + /// + /// An array of property names in this property store. + [return: MarshalAs(UnmanagedType.SafeArray, SafeArraySubType = VarEnum.VT_BSTR)] + string[] GetNames(); + + /// + /// Gets the value of a named property in this property store. + /// + /// The name of the property to get. + /// The value of the property. + object GetValue([In] [MarshalAs(UnmanagedType.LPWStr)] string pwszName); + } +} \ No newline at end of file diff --git a/VisualStudioMock/InstanceState.cs b/VisualStudioMock/InstanceState.cs new file mode 100644 index 0000000..f34124d --- /dev/null +++ b/VisualStudioMock/InstanceState.cs @@ -0,0 +1,36 @@ +using System; + +namespace VisualStudioMock +{ + /// + /// The state of an . + /// + [Flags] + public enum InstanceState : uint + { + /// + /// The instance state has not been determined. + /// + None = 0x0, + /// + /// The instance installation path exists. + /// + Local = 0x1, + /// + /// A product is registered to the instance. + /// + Registered = 0x2, + /// + /// No reboot is required for the instance. + /// + NoRebootRequired = 0x4, + /// + /// No errors were reported for the instance. + /// + NoErrors = 0x8, + /// + /// The instance represents a complete install. + /// + Complete = uint.MaxValue + } +} \ No newline at end of file diff --git a/VisualStudioMock/Program.cs b/VisualStudioMock/Program.cs new file mode 100644 index 0000000..f100f33 --- /dev/null +++ b/VisualStudioMock/Program.cs @@ -0,0 +1,50 @@ +using System; +using System.Reflection; +using Microsoft.Win32; + +[assembly: AssemblyCompany("Ivasoft s.r.o.")] +[assembly: AssemblyProduct("VisualStudioMock")] +[assembly: AssemblyTitle("Visual Studio Mock for EAZfuscator")] +[assembly: AssemblyVersion("1.0.0.0")] + +namespace VisualStudioMock +{ + public class Program + { + [STAThread] + public static void Main(string[] args) + { + AppDomain.CurrentDomain.UnhandledException += (object sender, UnhandledExceptionEventArgs e) => Console.WriteLine("Exception: " + e.ExceptionObject); + + if (args.Length == 1) + switch (args[0]) { + case "/reg": + using (RegistryKey regBase = RegistryKey.OpenBaseKey(RegistryHive.ClassesRoot, RegistryView.Registry64)) + using (RegistryKey regCoClass = regBase.CreateSubKey("CLSID\\{177F0C4A-1CD3-4DE7-A32C-71DBBB9FA36D}\\InprocServer32")) + { + regCoClass.SetValue("", "mscoree.dll"); + regCoClass.SetValue("Class", "VisualStudioMock.SetupConfiguration"); + regCoClass.SetValue("Assembly", "VisualStudioMock, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null"); + regCoClass.SetValue("RuntimeVersion", "v4.0.30319"); + regCoClass.SetValue("ThreadingModel", "Apartment"); + regCoClass.SetValue("CodeBase", Assembly.GetExecutingAssembly().Location); + } + Console.WriteLine("Visual Studio Mock registered."); + return; + + case "/unreg": + using (RegistryKey regBase = RegistryKey.OpenBaseKey(RegistryHive.ClassesRoot, RegistryView.Registry64)) + { + regBase.DeleteSubKey("CLSID\\{177F0C4A-1CD3-4DE7-A32C-71DBBB9FA36D}\\InprocServer32", throwOnMissingSubKey: false); + regBase.DeleteSubKey("CLSID\\{177F0C4A-1CD3-4DE7-A32C-71DBBB9FA36D}\\LocalServer32", throwOnMissingSubKey: false); + regBase.DeleteSubKey("CLSID\\{177F0C4A-1CD3-4DE7-A32C-71DBBB9FA36D}", throwOnMissingSubKey: false); + } + Console.WriteLine("Visual Studio Mock unregistered."); + return; + } + + Console.WriteLine("Usage: VisualStudioMock.exe [ /reg | /unreg ]"); + return; + } + } +} \ No newline at end of file diff --git a/VisualStudioMock/SetupConfiguration.cs b/VisualStudioMock/SetupConfiguration.cs new file mode 100644 index 0000000..076cfc4 --- /dev/null +++ b/VisualStudioMock/SetupConfiguration.cs @@ -0,0 +1,140 @@ +using System; +using System.Runtime.InteropServices; + +namespace VisualStudioMock +{ + [ComVisible(true)] + [Guid("177F0C4A-1CD3-4DE7-A32C-71DBBB9FA36D")] + [ClassInterface(ClassInterfaceType.None)] + public sealed class SetupConfiguration : ISetupConfiguration, ISetupConfiguration2 + { + private readonly SetupInstance _singleInstance; + + public SetupConfiguration() + { + Ext.LogCall(nameof(SetupConfiguration), ".ctor"); + + ComProxy proxy = new ComProxy(new SetupConfiguration(false)); + object proxyCcw = proxy.GetIUnknown(); + + // Copy managed object + GCHandle hThis = GCHandle.Alloc(this, GCHandleType.WeakTrackResurrection); + IntPtr pThis = Marshal.ReadIntPtr(GCHandle.ToIntPtr(hThis)); + GCHandle hProxy = GCHandle.Alloc(proxyCcw, GCHandleType.WeakTrackResurrection); + IntPtr pProxy = Marshal.ReadIntPtr(GCHandle.ToIntPtr(hProxy)); + byte[] buffer = new byte[IntPtr.Size * 3]; + Marshal.Copy(pProxy - IntPtr.Size, buffer, 0, buffer.Length); + Marshal.Copy(buffer, 0, pThis - IntPtr.Size, buffer.Length); + + /* + Ext.Log("Casting to ISetupConfiguration"); + proxyCcw = this; + ISetupConfigurationTest wrapper = (ISetupConfigurationTest)proxyCcw; + GC.KeepAlive(wrapper.GetInstanceForCurrentProcess()); + */ + + Ext.LogExit(nameof(SetupConfiguration), ".ctor"); + } + + private SetupConfiguration(bool any) + { + this._singleInstance = new SetupInstance(); + } + + #region ISetupConfiguration Members + + public IEnumSetupInstances EnumInstances() + { + Ext.LogCall(nameof(SetupConfiguration), nameof(EnumInstances)); + return new Enum(this).ComBarrier(); + } + + public ISetupInstance GetInstanceForCurrentProcess() + { + Ext.LogCall(nameof(SetupConfiguration), nameof(GetInstanceForCurrentProcess)); + return _singleInstance.ComBarrier(); + } + + public ISetupInstance GetInstanceForPath([In, MarshalAs(UnmanagedType.LPWStr)] string path) + { + Ext.LogCall(nameof(SetupConfiguration), nameof(GetInstanceForPath)); + throw new NotImplementedException(); + } + + #endregion + + #region ISetupConfiguration2 Members + + public IEnumSetupInstances EnumAllInstances() + { + Ext.LogEnter(nameof(SetupConfiguration), nameof(EnumAllInstances)); + IEnumSetupInstances result = new Enum(this).ComBarrier(); + Ext.LogExit(nameof(SetupConfiguration), nameof(EnumAllInstances)); + return result; + } + + #endregion + + sealed class Enum : IEnumSetupInstances + { + private readonly SetupConfiguration _owner; + private int _idx; + + internal Enum(SetupConfiguration owner) + { + this._owner = owner; + } + + public IEnumSetupInstances Clone() + { + Ext.LogCall(nameof(Enum), nameof(Clone)); + return ((Enum)MemberwiseClone()).ComBarrier(); + } + + public void Next([In, MarshalAs(UnmanagedType.U4)] int celt, [MarshalAs(UnmanagedType.LPArray, ArraySubType = UnmanagedType.Interface), Out] ISetupInstance[] rgelt, [MarshalAs(UnmanagedType.U4)] out int pceltFetched) + { + Ext.LogEnter(nameof(Enum), nameof(Next)); + if (_idx == 0 && celt > 0) + { + rgelt[0] = _owner._singleInstance.ComBarrier(); + pceltFetched = 1; + } + else + { + pceltFetched = 0; + } + Ext.LogExit(nameof(Enum), nameof(Next)); + } + + public void Reset() + { + Ext.LogCall(nameof(Enum), nameof(Reset)); + _idx = 0; + } + + public void Skip([In, MarshalAs(UnmanagedType.U4)] int celt) + { + Ext.LogEnter(nameof(Enum), nameof(Skip)); + if (celt < 0) + throw new ArgumentOutOfRangeException(); + _idx += celt; + Ext.LogExit(nameof(Enum), nameof(Skip)); + } + } + + /* + [ComImport] + [Guid("42843719-DB4C-46C2-8E7C-64F1816EFD5B")] + [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] + public interface ISetupConfigurationTest + { + [return: MarshalAs(UnmanagedType.Interface)] + IEnumSetupInstances EnumInstances(); + [return: MarshalAs(UnmanagedType.Interface)] + ISetupInstance GetInstanceForCurrentProcess(); + [return: MarshalAs(UnmanagedType.Interface)] + ISetupInstance GetInstanceForPath([In] [MarshalAs(UnmanagedType.LPWStr)] string path); + } + */ + } +} \ No newline at end of file diff --git a/VisualStudioMock/SetupInstance.cs b/VisualStudioMock/SetupInstance.cs new file mode 100644 index 0000000..bbf036a --- /dev/null +++ b/VisualStudioMock/SetupInstance.cs @@ -0,0 +1,135 @@ +using System; +using System.Runtime.InteropServices; +using FILETIME = System.Runtime.InteropServices.ComTypes.FILETIME; + +namespace VisualStudioMock +{ + public class SetupInstance : ISetupInstance, ISetupInstance2, ISetupPropertyStore + { + #region ISetupInstance Members + + public string GetDescription([In, MarshalAs(UnmanagedType.U4)] int lcid = 0) + { + Ext.LogCall(nameof(SetupInstance), nameof(GetDescription)); + return "description"; + } + + public string GetDisplayName([In, MarshalAs(UnmanagedType.U4)] int lcid = 0) + { + Ext.LogCall(nameof(SetupInstance), nameof(GetDisplayName)); + return "display name"; + } + + public string GetInstallationName() + { + Ext.LogCall(nameof(SetupInstance), nameof(GetInstallationName)); + return "installation name"; + } + + public string GetInstallationPath() + { + Ext.LogCall(nameof(SetupInstance), nameof(GetInstallationPath)); + throw new NotImplementedException(); + } + + public string GetInstallationVersion() + { + Ext.LogCall(nameof(SetupInstance), nameof(GetInstallationVersion)); + throw new NotImplementedException(); + } + + public FILETIME GetInstallDate() + { + Ext.LogCall(nameof(SetupInstance), nameof(GetInstallDate)); + throw new NotImplementedException(); + } + + public string GetInstanceId() + { + Ext.LogCall(nameof(SetupInstance), nameof(GetInstanceId)); + return "id1"; + } + + public string ResolvePath([In, MarshalAs(UnmanagedType.LPWStr)] string pwszRelativePath = null) + { + Ext.LogCall(nameof(SetupInstance), nameof(ResolvePath)); + throw new NotImplementedException(); + } + + #endregion + + #region ISetupInstance2 Members + + public string GetEnginePath() + { + Ext.LogCall(nameof(SetupInstance), nameof(GetEnginePath)); + throw new NotImplementedException(); + } + + public ISetupErrorState GetErrors() + { + Ext.LogCall(nameof(SetupInstance), nameof(GetEnginePath)); + throw new NotImplementedException(); + } + + public ISetupPackageReference[] GetPackages() + { + Ext.LogCall(nameof(SetupInstance), nameof(GetEnginePath)); + throw new NotImplementedException(); + } + + public ISetupPackageReference GetProduct() + { + Ext.LogCall(nameof(SetupInstance), nameof(GetEnginePath)); + throw new NotImplementedException(); + } + + public string GetProductPath() + { + Ext.LogCall(nameof(SetupInstance), nameof(GetEnginePath)); + throw new NotImplementedException(); + } + + public ISetupPropertyStore GetProperties() + { + Ext.LogCall(nameof(SetupInstance), nameof(GetProperties)); + return this; + } + + public InstanceState GetState() + { + Ext.LogCall(nameof(SetupInstance), nameof(GetState)); + return InstanceState.Complete; + } + + public bool IsComplete() + { + Ext.LogCall(nameof(SetupInstance), nameof(IsComplete)); + return true; + } + + public bool IsLaunchable() + { + Ext.LogCall(nameof(SetupInstance), nameof(IsLaunchable)); + return true; + } + + #endregion + + #region ISetupPropertyStore Members + + public string[] GetNames() + { + Ext.LogCall(nameof(SetupInstance), nameof(GetNames)); + throw new NotImplementedException(); + } + + public object GetValue(string pwszName) + { + Ext.LogCall(nameof(SetupInstance), nameof(GetValue)); + throw new NotImplementedException(); + } + + #endregion + } +} \ No newline at end of file diff --git a/VisualStudioMock/VisualStudioMock.csproj b/VisualStudioMock/VisualStudioMock.csproj new file mode 100644 index 0000000..c0cb437 --- /dev/null +++ b/VisualStudioMock/VisualStudioMock.csproj @@ -0,0 +1,12 @@ + + + + Exe + net472 + false + + + + + + diff --git a/VisualStudioMockTest/Program.cs b/VisualStudioMockTest/Program.cs new file mode 100644 index 0000000..55c0137 --- /dev/null +++ b/VisualStudioMockTest/Program.cs @@ -0,0 +1,23 @@ +using System; +using System.Runtime.InteropServices; +using Microsoft.VisualStudio.Setup.Configuration; + +class A { + public static void Main(string[] args) { + Console.WriteLine("Creating instance of SetupConfiguration"); + SetupConfiguration setup = new SetupConfiguration(); + Console.WriteLine("Created instance of SetupConfiguration"); + + var a = setup.EnumAllInstances(); + ISetupInstance[] b = new ISetupInstance[1]; + while (true) { + a.Next(1, b, out _); + if (b[0] == null) + break; + + ISetupInstance c = b[0]; + Console.WriteLine("Instance - " + c.GetDisplayName()); + } + } +} + diff --git a/VisualStudioMockTest/VisualStudioMockTest.csproj b/VisualStudioMockTest/VisualStudioMockTest.csproj new file mode 100644 index 0000000..2e0e567 --- /dev/null +++ b/VisualStudioMockTest/VisualStudioMockTest.csproj @@ -0,0 +1,10 @@ + + + + Exe + net472 + + + + +