Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Modern .NET Support #20

Merged
merged 38 commits into from
Jan 9, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
38 commits
Select commit Hold shift + click to select a range
18b7897
Added SDK configs
shmuelie Dec 8, 2023
35bbd4c
Upgrade to net8 and fix all the messages
shmuelie Dec 8, 2023
9932c13
Simplify interface project
shmuelie Dec 8, 2023
0acec8e
Adding NET8 server
shmuelie Dec 8, 2023
ea4dfaf
Removed AnyCPU configuration and fixed Net6 server config
shmuelie Dec 16, 2023
1e02dad
Updated dependencies
shmuelie Dec 16, 2023
e96d5a6
Remove AoT stuff
shmuelie Dec 16, 2023
091cb68
Stuff
shmuelie Dec 16, 2023
5bd8979
Migrated to use CsWin32
shmuelie Dec 16, 2023
9c422c2
Debug symbols need to be full
shmuelie Dec 17, 2023
95c0802
Changed to use CsWinRT APIs for interop instead of built in ones on net8
shmuelie Dec 17, 2023
9419ee6
Probably not going to do anything but..
shmuelie Dec 17, 2023
7783e1a
Minor refactoring
shmuelie Dec 17, 2023
e33dac2
Removed usage of ThrowExceptionForHR for ThrowOnFailure
shmuelie Dec 17, 2023
5866423
Fix lookup for factories
shmuelie Dec 17, 2023
3245581
Uping version and release notes
shmuelie Dec 17, 2023
cb9e039
Upload MSBuild binary log even on build failure
shmuelie Dec 17, 2023
f3fc018
Upgrade dependencies
shmuelie Dec 17, 2023
7824141
Add explict windows version
shmuelie Dec 17, 2023
31a2a6c
Don't do IID optimizations
shmuelie Dec 17, 2023
40c71e0
Property is opt out
shmuelie Dec 17, 2023
ea30f4b
Fix C++ projects
shmuelie Dec 17, 2023
f7e9d49
Remove unused dependencies
shmuelie Dec 17, 2023
f01a284
Removed SRC props file
shmuelie Dec 28, 2023
f5d44f1
Changed to use PolySharp for SupportedOSPlatform
shmuelie Dec 28, 2023
84ccbbe
Sorted and cleaned usings
shmuelie Dec 28, 2023
ec47964
Added IsAotCompatible for .NET8 builds
shmuelie Dec 28, 2023
f36620c
Fixing up Sample Package
shmuelie Dec 28, 2023
c8f3073
Added image for samples
shmuelie Dec 28, 2023
aa3e4fe
Added more details to the README
shmuelie Dec 28, 2023
cd7e1fa
Fixed up XML docs
shmuelie Dec 28, 2023
9a0e3d9
Added more SupportedOSPlatform
shmuelie Dec 28, 2023
7f50cd4
Fixed release notes
shmuelie Dec 28, 2023
306de14
Removed IsAotCompatible for now
shmuelie Dec 28, 2023
64ff0eb
Cleaned up project file
shmuelie Dec 28, 2023
cecc9e2
Fixes for when pack is run on tests
shmuelie Dec 31, 2023
3d7ab25
Moved some properties to shared
shmuelie Dec 31, 2023
ea27f53
Update Packaging configuration and remove ico from binary
shmuelie Jan 9, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions .editorconfig
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
[*.{cs,vb}]
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Consider just copying the same editorconfig/etc. files from eg. ComputeSharp?

Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So much is just copied from ComputeSharp lol. Should make a repo template from it lol


# IDE0046: Convert to conditional expression
dotnet_style_prefer_conditional_expression_over_return = false

# CA1510: Use ArgumentNullException throw helper
dotnet_diagnostic.CA1510.severity = none

# CA1513: Use ObjectDisposedException throw helper
dotnet_diagnostic.CA1513.severity = none
1 change: 1 addition & 0 deletions .github/workflows/dotnet.yml
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ jobs:
name: msbuild_log_${{matrix.configuration}}
path: msbuild.binlog
if-no-files-found: error
if: always()

# Build the only in Release to generate all the NuGet packages.
# This workflow also uploads the resulting packages as artifacts.
Expand Down
9 changes: 6 additions & 3 deletions Directory.Build.props
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
<Project>
<PropertyGroup>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
<LangVersion>10.0</LangVersion>
<LangVersion>12</LangVersion>
<Nullable>enable</Nullable>
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
<NoWarn>$(NoWarn);MSB3270</NoWarn>
<CsWinRTIIDOptimizerOptOut>true</CsWinRTIIDOptimizerOptOut>
</PropertyGroup>

<!-- Centralized location for all generated artifacts -->
Expand All @@ -18,7 +19,7 @@
As such, this needs to be changed before a new release as well.
-->
<PropertyGroup>
<ShmuelieWinRTServerPackageVersion>1.1.1</ShmuelieWinRTServerPackageVersion>
<ShmuelieWinRTServerPackageVersion>1.2.0</ShmuelieWinRTServerPackageVersion>
<IsCommitOnReleaseBranch>false</IsCommitOnReleaseBranch>
</PropertyGroup>

Expand Down Expand Up @@ -60,10 +61,12 @@
<Copyright>Copyright (c) 2022 Shmueli Englard</Copyright>
<PackageLicenseExpression>MIT</PackageLicenseExpression>
<PackageRequireLicenseAcceptance>true</PackageRequireLicenseAcceptance>
<PackageIcon>icon.png</PackageIcon>
<PackageIcon>$(MSBuildProjectName)-128.png</PackageIcon>
<PackageIconUrl>https://github.com/shmuelie/Shmuelie.WinRTServer/blob/main/Shmuelie.WinRTServer-64.png?raw=true</PackageIconUrl>
<PackageTags>dotnet net netcore netstandard csharp library windows com winrt oop outofprocess rpc</PackageTags>
<GenerateDocumentationFile>true</GenerateDocumentationFile>
<PackageReadmeFile>README.md</PackageReadmeFile>
<Description>Out-of-Process WinRT/COM Server</Description>
</PropertyGroup>

<ItemGroup>
Expand Down
30 changes: 27 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,11 @@ Many of the types you are used to using are supported out of the box like
collection types, map types, streams, etc. This allows you to not have to worry
about how the IPC works.

## Security

COM provides various ways to secure usage and creation of objects. For more
details, see [Security in COM][7].

# Usage

Currently to create an Out-of-Process server requires the C++/WinRT tooling
Expand Down Expand Up @@ -111,16 +116,35 @@ similar).

# Sample

To help understand usage, a sample using a UWP client app, and WPF app, and a C++ console app is included under the
test directory. Simply run the `Shmuelie.WinRTServer.Sample.Package` project to
see it in action.
To help understand usage and show what can be done samples can be found under
the tests folder. The sample has:

- .NET Framework Server
- .NET 7 Server
- UWP .NET Client App
- C++/WinRT Console Client App
- WPF .NET Framework Client App
- WPF .NET 7 Client App

> **Note**: If Visual Studio fails to build the Metadata project restarting
> Visual Studio should fix the problem.

# Troubleshooting

If you are having issues, check on these things:

- Make sure the client app includes the WinMDs (Interface and Metadata)
- Make sure that the interface project uses the
`Windows.Foundation.Metadata.GuidAttribute` attribute, not the
`System.Runtime.InteropServices.GuidAttribute` attribute.
- Make sure that the server project uses the
`System.Runtime.InteropServices.GuidAttribute` attribute, not the
`Windows.Foundation.Metadata.GuidAttribute` attribute.

[1]: https://github.com/Shmuelie/Shmuelie.WinRTServer/actions
[2]: https://www.nuget.org/stats/packages/Shmuelie.WinRTServer?groupby=Version
[3]: https://www.nuget.org/packages/Shmuelie.WinRTServer/
[4]: https://learn.microsoft.com/en-us/windows/apps/develop/platform/csharp-winrt/net-mappings-of-winrt-types
[5]: https://learn.microsoft.com/en-us/uwp/midl-3/
[6]: https://devblogs.microsoft.com/ifdef-windows/the-journey-of-moving-from-cpp-winrt-to-csharp-in-the-microsoft-store/
[7]: https://learn.microsoft.com/en-us/windows/win32/com/security-in-com
15 changes: 12 additions & 3 deletions Shmuelie.WinRTServer.sln
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ VisualStudioVersion = 17.3.32929.385
MinimumVisualStudioVersion = 10.0.40219.1
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "_SolutionItems", "_SolutionItems", "{50DD472E-AD00-45C5-A2F7-4CB77CB8CD67}"
ProjectSection(SolutionItems) = preProject
.editorconfig = .editorconfig
.gitignore = .gitignore
Directory.Build.props = Directory.Build.props
Directory.Build.targets = Directory.Build.targets
Expand All @@ -21,9 +22,6 @@ EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Shmuelie.WinRTServer", "src\Shmuelie.WinRTServer\Shmuelie.WinRTServer.csproj", "{F09FEFE0-ED5E-4A17-9C54-DB6C1CD559E9}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Src", "Src", "{E1670C4F-46C0-45BE-A351-E504DCB17A45}"
ProjectSection(SolutionItems) = preProject
src\Directory.Build.props = src\Directory.Build.props
EndProjectSection
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Shmuelie.WinRTServer.Sample.Interfaces", "tests\Shmuelie.WinRTServer.Sample.Interfaces\Shmuelie.WinRTServer.Sample.Interfaces.csproj", "{7E420EEB-3D6C-49FB-9E60-AE448BAC8B1F}"
EndProject
Expand All @@ -47,6 +45,8 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Shmuelie.WinRTServer.Sample
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Shmuelie.WinRTServer.Sample.WpfNet6Client", "tests\Shmuelie.WinRTServer.Sample.WpfNet6Client\Shmuelie.WinRTServer.Sample.WpfNet6Client.csproj", "{8FFAA094-2D9C-49B5-BB36-4B2BB7512444}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Shmuelie.WinRTServer.Sample.ServerNet6", "tests\Shmuelie.WinRTServer.Sample.ServerNet6\Shmuelie.WinRTServer.Sample.ServerNet6.csproj", "{F37FE3DF-B23B-44C7-A3C1-1E4F20885DB3}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|x64 = Debug|x64
Expand Down Expand Up @@ -135,6 +135,14 @@ Global
{8FFAA094-2D9C-49B5-BB36-4B2BB7512444}.Release|x64.Build.0 = Release|x64
{8FFAA094-2D9C-49B5-BB36-4B2BB7512444}.Release|x86.ActiveCfg = Release|x86
{8FFAA094-2D9C-49B5-BB36-4B2BB7512444}.Release|x86.Build.0 = Release|x86
{F37FE3DF-B23B-44C7-A3C1-1E4F20885DB3}.Debug|x64.ActiveCfg = Debug|x64
{F37FE3DF-B23B-44C7-A3C1-1E4F20885DB3}.Debug|x64.Build.0 = Debug|x64
{F37FE3DF-B23B-44C7-A3C1-1E4F20885DB3}.Debug|x86.ActiveCfg = Debug|x86
{F37FE3DF-B23B-44C7-A3C1-1E4F20885DB3}.Debug|x86.Build.0 = Debug|x86
{F37FE3DF-B23B-44C7-A3C1-1E4F20885DB3}.Release|x64.ActiveCfg = Release|x64
{F37FE3DF-B23B-44C7-A3C1-1E4F20885DB3}.Release|x64.Build.0 = Release|x64
{F37FE3DF-B23B-44C7-A3C1-1E4F20885DB3}.Release|x86.ActiveCfg = Release|x86
{F37FE3DF-B23B-44C7-A3C1-1E4F20885DB3}.Release|x86.Build.0 = Release|x86
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
Expand All @@ -149,6 +157,7 @@ Global
{6B799403-7548-4FD8-9AF8-76BD13473029} = {CB701FA8-9663-444D-9610-1A8CDFF30E61}
{A78E8601-3191-4987-B61A-E48256869A39} = {CB701FA8-9663-444D-9610-1A8CDFF30E61}
{8FFAA094-2D9C-49B5-BB36-4B2BB7512444} = {CB701FA8-9663-444D-9610-1A8CDFF30E61}
{F37FE3DF-B23B-44C7-A3C1-1E4F20885DB3} = {CB701FA8-9663-444D-9610-1A8CDFF30E61}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {97F14D53-B029-4E3E-B7DE-51A510B2D428}
Expand Down
14 changes: 9 additions & 5 deletions global.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
{
"msbuild-sdks":
{
"MSBuild.Sdk.Extras": "3.0.38"
}
}
"msbuild-sdks": {
"MSBuild.Sdk.Extras": "3.0.38"
},
"sdk": {
"version": "8.0.100",
"rollForward": "latestFeature",
"allowPrerelease": false
}
}
7 changes: 0 additions & 7 deletions src/Directory.Build.props

This file was deleted.

6 changes: 3 additions & 3 deletions src/Shmuelie.WinRTServer/BaseActivationFactory.cs
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
using System;
using System.Runtime.InteropServices.WindowsRuntime;
using System.Runtime.Versioning;

namespace Shmuelie.WinRTServer;

/// <summary>
/// Base for a WinRT Activation Factory for a .NET type.
/// </summary>
/// <seealso cref="IActivationFactory"/>
public abstract class BaseActivationFactory : IActivationFactory
[SupportedOSPlatform("windows8.0")]
public abstract class BaseActivationFactory
{
/// <inheritdoc/>
public abstract object ActivateInstance();
Expand Down
3 changes: 2 additions & 1 deletion src/Shmuelie.WinRTServer/BaseClassFactory.cs
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
using System;
using System.Runtime.Versioning;

namespace Shmuelie.WinRTServer;

/// <summary>
/// Base for a COM class factory for a .NET type.
/// </summary>
/// <seealso cref="Interop.Windows.IClassFactory"/>
/// <remarks>Does not support aggregation. Will always return <c>CLASS_E_NOAGGREGATION</c> if requested.</remarks>
[SupportedOSPlatform("windows6.0.6000")]
public abstract class BaseClassFactory
{
/// <summary>
Expand Down
23 changes: 13 additions & 10 deletions src/Shmuelie.WinRTServer/ComServer.cs
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
using System;
using System.Collections.Generic;
using System.Runtime.InteropServices;
using System.Runtime.Versioning;
using System.Threading.Tasks;
using System.Timers;
using Shmuelie.Interop.Windows;
using static Shmuelie.Interop.Windows.ComBaseAPI;
using static Shmuelie.Interop.Windows.Windows;
using Windows.Win32.Foundation;
using Windows.Win32.System.Com;
using static Windows.Win32.PInvoke;

namespace Shmuelie.WinRTServer;

Expand All @@ -14,12 +15,13 @@ namespace Shmuelie.WinRTServer;
/// </summary>
/// <see cref="IAsyncDisposable"/>
/// <threadsafety static="true" instance="false"/>
[SupportedOSPlatform("windows6.0.6000")]
public sealed class ComServer : IAsyncDisposable
{
/// <summary>
/// Map of class factories and the registration cookie from the CLSID that the factory creates.
/// </summary>
private readonly Dictionary<Guid, (BaseClassFactory factory, uint cookie)> factories = new();
private readonly Dictionary<Guid, (BaseClassFactory factory, uint cookie)> factories = [];

/// <summary>
/// Collection of created instances.
Expand All @@ -42,8 +44,9 @@ public sealed class ComServer : IAsyncDisposable
public unsafe ComServer()
{
using ComPtr<IGlobalOptions> options = default;
Guid clsid = IGlobalOptions.CLSID;
if (CoCreateInstance(&clsid, null, (uint)CLSCTX.CLSCTX_INPROC_SERVER, __uuidof<IGlobalOptions>(), (void**)options.GetAddressOf()) == S.S_OK)
Guid clsid = CLSID_GlobalOptions;
Guid iid = IGlobalOptions.IID_Guid;
if (CoCreateInstance(&clsid, null, CLSCTX.CLSCTX_INPROC_SERVER, &iid, (void**)options.GetAddressOf()) == HRESULT.S_OK)
{
options.Get()->Set(GLOBALOPT_PROPERTIES.COMGLB_RO_SETTINGS, (nuint)GLOBALOPT_RO_FLAGS.COMGLB_FAST_RUNDOWN);
}
Expand Down Expand Up @@ -128,7 +131,7 @@ public unsafe bool RegisterClassFactory(BaseClassFactory factory)
proxy.Attach(BaseClassFactoryProxy.Create(factory));

uint cookie;
Marshal.ThrowExceptionForHR(CoRegisterClassObject(&clsid, (IUnknown*)proxy.Get(), (uint)CLSCTX.CLSCTX_LOCAL_SERVER, (uint)(REGCLS.REGCLS_MULTIPLEUSE | REGCLS.REGCLS_SUSPENDED), &cookie));
CoRegisterClassObject(&clsid, (IUnknown*)proxy.Get(), CLSCTX.CLSCTX_LOCAL_SERVER, (REGCLS.REGCLS_MULTIPLEUSE | REGCLS.REGCLS_SUSPENDED), &cookie).ThrowOnFailure();

factories.Add(clsid, (factory, cookie));
return true;
Expand Down Expand Up @@ -161,7 +164,7 @@ public unsafe bool UnregisterClassFactory(Guid clsid)

data.factory.InstanceCreated -= Factory_InstanceCreated;

Marshal.ThrowExceptionForHR(CoRevokeClassObject(data.cookie));
CoRevokeClassObject(data.cookie).ThrowOnFailure();
return true;
}

Expand Down Expand Up @@ -199,7 +202,7 @@ public void Start()

firstInstanceCreated = new();
lifetimeCheckTimer.Start();
Marshal.ThrowExceptionForHR(CoResumeClassObjects());
CoResumeClassObjects().ThrowOnFailure();
}

/// <summary>
Expand All @@ -219,7 +222,7 @@ public void Stop()

firstInstanceCreated = null;
lifetimeCheckTimer.Stop();
Marshal.ThrowExceptionForHR(CoSuspendClassObjects());
CoSuspendClassObjects().ThrowOnFailure();
}

/// <summary>
Expand Down
2 changes: 2 additions & 0 deletions src/Shmuelie.WinRTServer/ComServerExtensions.cs
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
using System;
using System.Runtime.Versioning;

namespace Shmuelie.WinRTServer;

/// <summary>
/// Extensions for <see cref="ComServer"/>.
/// </summary>
[SupportedOSPlatform("windows6.0.6000")]
public static class ComServerExtensions
{
/// <summary>
Expand Down
16 changes: 5 additions & 11 deletions src/Shmuelie.WinRTServer/DelegateActivationFactory.cs
Original file line number Diff line number Diff line change
@@ -1,24 +1,18 @@
using System;
using System.Runtime.Versioning;

namespace Shmuelie.WinRTServer;

/// <summary>
/// Delegate based activation factory for .NET types.
/// </summary>
/// <typeparam name="T">The type the factory creates.</typeparam>
/// <param name="factory">Delegate to create instances.</param>
/// <seealso cref="BaseActivationFactory"/>
public sealed class DelegateActivationFactory<T> : BaseActivationFactory where T : class
[SupportedOSPlatform("windows8.0")]
public sealed class DelegateActivationFactory<T>(Func<T> factory) : BaseActivationFactory where T : class
{
private readonly Func<T> factory;

/// <summary>
/// Initializes a new instance of the <see cref="DelegateActivationFactory{T}"/> class.
/// </summary>
/// <param name="factory">Delegate to create instances.</param>
public DelegateActivationFactory(Func<T> factory)
{
this.factory = factory;
}
private readonly Func<T> factory = factory;

/// <inheritdoc/>
public override string ActivatableClassId => typeof(T).FullName ?? throw new InvalidOperationException($"Unable to get activation class ID for type {typeof(T)}");
Expand Down
16 changes: 5 additions & 11 deletions src/Shmuelie.WinRTServer/DelegateClassFactory.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using System;
using System.Runtime.Versioning;

namespace Shmuelie.WinRTServer;

Expand All @@ -7,19 +8,12 @@ namespace Shmuelie.WinRTServer;
/// </summary>
/// <typeparam name="T">Type the factory creates.</typeparam>
/// <typeparam name="TInterface">Interface that <typeparamref name="T"/> implements.</typeparam>
/// <param name="factory">Delegate to create instances.</param>
/// <seealso cref="BaseClassFactory"/>
public sealed class DelegateClassFactory<T, TInterface> : BaseClassFactory where T : class, TInterface
[SupportedOSPlatform("windows6.0.6000")]
public sealed class DelegateClassFactory<T, TInterface>(Func<T> factory) : BaseClassFactory where T : class, TInterface
{
private readonly Func<T> factory;

/// <summary>
/// Initializes a new instance of the <see cref="DelegateClassFactory{T, TInterface}"/> class.
/// </summary>
/// <param name="factory">Delegate to create instances.</param>
public DelegateClassFactory(Func<T> factory)
{
this.factory = factory;
}
private readonly Func<T> factory = factory;

/// <inheritdoc/>
protected internal override Guid Clsid => typeof(T).GUID;
Expand Down
2 changes: 2 additions & 0 deletions src/Shmuelie.WinRTServer/GeneralActivationFactory.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using System;
using System.Runtime.Versioning;

namespace Shmuelie.WinRTServer;

Expand All @@ -7,6 +8,7 @@ namespace Shmuelie.WinRTServer;
/// </summary>
/// <typeparam name="T">The type the factory creates.</typeparam>
/// <seealso cref="BaseActivationFactory"/>
[SupportedOSPlatform("windows8.0")]
public sealed class GeneralActivationFactory<T> : BaseActivationFactory where T : class, new()
{
/// <inheritdoc/>
Expand Down
2 changes: 2 additions & 0 deletions src/Shmuelie.WinRTServer/GeneralClassFactory.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using System;
using System.Runtime.Versioning;

namespace Shmuelie.WinRTServer;

Expand All @@ -8,6 +9,7 @@ namespace Shmuelie.WinRTServer;
/// <typeparam name="T">Type the factory creates.</typeparam>
/// <typeparam name="TInterface">Interface that <typeparamref name="T"/> implements.</typeparam>
/// <seealso cref="BaseClassFactory"/>
[SupportedOSPlatform("windows6.0.6000")]
public sealed class GeneralClassFactory<T, TInterface> : BaseClassFactory where T : class, TInterface, new()
{
/// <inheritdoc/>
Expand Down
Loading