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

Create a managed implementation of assembly binder #91400

Closed
wants to merge 143 commits into from
Closed
Show file tree
Hide file tree
Changes from 138 commits
Commits
Show all changes
143 commits
Select commit Hold shift + click to select a range
dcecbdc
Definition of AssemblyVersion and AssemblyIdentity
huoyaoyuan Aug 30, 2023
c946228
Port logic body of AssemblyName.Init
huoyaoyuan Aug 30, 2023
b4c0ba8
Port HashCode and Equals for AssemblyName
huoyaoyuan Aug 30, 2023
a6bf121
AssemblyName.GetDisplayName
huoyaoyuan Aug 30, 2023
bf85741
Assembly
huoyaoyuan Aug 30, 2023
61143ce
Some methods in AssemblyBinderCommon and BindReseult
huoyaoyuan Aug 30, 2023
5fdf8a7
ApplicationContext
huoyaoyuan Aug 30, 2023
7175e30
Basic assembly binder wrapper
huoyaoyuan Aug 30, 2023
6cfdc44
Refactor FailureCache
huoyaoyuan Aug 30, 2023
52dae46
BindLocked
huoyaoyuan Aug 30, 2023
e7789cd
FindInExecutionContext
huoyaoyuan Aug 30, 2023
c32ec98
BindByTpaList
huoyaoyuan Aug 30, 2023
9a47f75
RegisterAndGetHostChosen
huoyaoyuan Aug 30, 2023
21ea3e1
BindAssemblyByProbingPaths
huoyaoyuan Aug 31, 2023
93b4b10
Use HResult instead of exception
huoyaoyuan Aug 31, 2023
a7de55a
GetAssembly
huoyaoyuan Aug 31, 2023
9c77d7e
BindUsingPEImage
huoyaoyuan Aug 31, 2023
116f7bf
Guard behind feature switch
huoyaoyuan Aug 31, 2023
9fff7e2
Reuse IsPathFullyQualified
huoyaoyuan Sep 1, 2023
b092474
Reuse ComputePublicKeyToken
huoyaoyuan Sep 1, 2023
6ceb626
Merge branch 'main' into managed-binder
huoyaoyuan Sep 3, 2023
b4dbcea
Revert "Guard behind feature switch"
huoyaoyuan Sep 3, 2023
6d71c26
Satisfy sealed analyzer
huoyaoyuan Sep 4, 2023
a85d223
Tracing ResolutionAttemptedOperation
huoyaoyuan Sep 5, 2023
18da994
Event source qcall
huoyaoyuan Sep 7, 2023
fb8f831
GetClrInstanceId
huoyaoyuan Sep 7, 2023
f71b175
KnownPathProbed event
huoyaoyuan Sep 7, 2023
1390d93
Fix compilation
huoyaoyuan Sep 7, 2023
e13468a
MdImport for AssemblyName
huoyaoyuan Sep 7, 2023
6196fbe
AssemblyNameData
huoyaoyuan Sep 7, 2023
c459c76
Basic AssemblyBinder
huoyaoyuan Sep 7, 2023
be6ec57
DefaultAssemblyBinder and BindUsingHostAssemblyResolver
huoyaoyuan Sep 7, 2023
0c74a2a
Complete most of DefaltAssemblyBinder
huoyaoyuan Sep 7, 2023
d6cbc65
CustomAssemblyBinder
huoyaoyuan Sep 7, 2023
bfff7c2
Fix ALC compilation
huoyaoyuan Sep 8, 2023
4e29ef7
Interop with LoaderAllocator
huoyaoyuan Sep 8, 2023
cd37220
Fix CollectionsMarshal usage
huoyaoyuan Sep 8, 2023
167be6c
Name tracing for binder
huoyaoyuan Sep 8, 2023
a426eac
Store default binder in AppDomain
huoyaoyuan Sep 8, 2023
6f4de1b
Store custom assembly binder in ALC
huoyaoyuan Sep 9, 2023
20593a2
Fix compilation
huoyaoyuan Sep 9, 2023
e959600
Use managed binder in ALC
huoyaoyuan Sep 9, 2023
e073323
PEImage QCalls
huoyaoyuan Sep 9, 2023
57039c7
Make BinderAcquireImport and BinderAcquirePEImage QCall.
huoyaoyuan Sep 9, 2023
8eb96cf
Move QCalls to assemblynative and fix compilation
huoyaoyuan Sep 9, 2023
163b51c
Store managed binder in PEAssembly
huoyaoyuan Sep 9, 2023
6a693d0
Call GetLoaderAllocator
huoyaoyuan Sep 9, 2023
6cf329f
Set DomainAssembly
huoyaoyuan Sep 9, 2023
1449079
Use binder in native
huoyaoyuan Sep 9, 2023
0a021ed
Use more in native
huoyaoyuan Sep 14, 2023
1fecd66
Use more in native
huoyaoyuan Sep 16, 2023
c37ec2e
AssemblySpec
huoyaoyuan Sep 16, 2023
fc91661
BindToSystemSatellite
huoyaoyuan Sep 16, 2023
f3795d0
Compilation of casting
huoyaoyuan Sep 16, 2023
8ea0e39
Remaining in appdomainnative and assemblynative
huoyaoyuan Sep 16, 2023
c0b851e
NativeImage and DeclareDependencyOnMvid
huoyaoyuan Sep 17, 2023
25b6187
Make native part compiling
huoyaoyuan Sep 19, 2023
7aa42e0
Merge branch 'main' into managed-binder
huoyaoyuan Sep 19, 2023
85acdca
Fullfill PEAssembly QCalls
huoyaoyuan Sep 19, 2023
b157302
QCall for VMAssembly
huoyaoyuan Sep 19, 2023
9073bc3
AssemblyNative_LoadFromPEImage
huoyaoyuan Sep 19, 2023
38aee24
Refine corelib metasig
huoyaoyuan Sep 19, 2023
2ac9719
CoreLib bootstrap
huoyaoyuan Sep 20, 2023
77276c7
Managed binder initialization
huoyaoyuan Sep 20, 2023
ef2786e
Revert ALC.Load* to be native
huoyaoyuan Sep 21, 2023
148df35
Revert QCalls for ALC.Load*
huoyaoyuan Sep 21, 2023
791c478
Create assembly object for corelib
huoyaoyuan Sep 22, 2023
c323031
Fix some compilation and assert during corelib loading
huoyaoyuan Sep 22, 2023
8f6fde9
Fix more asserts during loading
huoyaoyuan Sep 23, 2023
754a81c
Fix calls to BindUsingPEImage
huoyaoyuan Sep 24, 2023
6ee05b2
Assembly object layout
huoyaoyuan Sep 24, 2023
9884e7b
Load application assembly
huoyaoyuan Sep 24, 2023
2ae3cd1
AssemblyBinder layout
huoyaoyuan Oct 2, 2023
4458280
Fix BinderAcquirePEImage usage
huoyaoyuan Oct 3, 2023
9a2df4f
Enable AssemblySpecBindingCache
huoyaoyuan Oct 6, 2023
38736bc
Merge branch 'main' into managed-binder
huoyaoyuan Oct 6, 2023
20c1347
StartProfileOptimization
huoyaoyuan Oct 7, 2023
db8c08c
AppBundle support
huoyaoyuan Oct 7, 2023
ef3f57b
Implement BindSatelliteResource
huoyaoyuan Oct 7, 2023
d6972b5
Delete unused QCalls
huoyaoyuan Oct 7, 2023
244aedd
Make ProbeAppBundle compatible to extern "C"
huoyaoyuan Oct 7, 2023
f954fea
Register managed binder to native LoaderAllocator
huoyaoyuan Oct 15, 2023
e8efdea
Fix IsNeutralCulture
huoyaoyuan Nov 5, 2023
62995fb
Restore AssemblyBinder usage in AppDomain
huoyaoyuan Nov 9, 2023
26c8224
Restore native binder lifetime
huoyaoyuan Nov 9, 2023
5e412b8
Restore AssemblyBinder usage in AssemblySpec
huoyaoyuan Nov 9, 2023
1bdcf2f
Restore AssemblyBinder usage in PEAssembly
huoyaoyuan Nov 9, 2023
7c233c6
Restore some more binder usage
huoyaoyuan Nov 9, 2023
e5dbd2e
Delete unused HashIdentity
huoyaoyuan Nov 9, 2023
2d7acad
Typo
huoyaoyuan Nov 8, 2023
f5376c1
Update CoreLib bootstrap to use PEImage
huoyaoyuan Nov 8, 2023
f0ebbcf
Restore native binder in native ALC object
huoyaoyuan Nov 9, 2023
68a6918
Get managed ALC object from native binder
huoyaoyuan Nov 9, 2023
2c981b9
Restore usages in AssemblyNative
huoyaoyuan Nov 9, 2023
219e9e6
Restore lifetime management with LoaderAllocator
huoyaoyuan Nov 9, 2023
37702da
Fix some compilation
huoyaoyuan Nov 9, 2023
4f20259
Fix more usage
huoyaoyuan Nov 9, 2023
b832f39
Give up exposing ALC ref in AssemblyBinder
huoyaoyuan Nov 9, 2023
51decaa
Fold managed AssemblyBinder into ALC
huoyaoyuan Nov 10, 2023
82ec2ac
Resolve the gap between managed and native ALC
huoyaoyuan Nov 10, 2023
680b6bc
Move LoaderAllocator usage to native side
huoyaoyuan Nov 10, 2023
0686653
Move BindUsingHostAssemblyResolver to ALC
huoyaoyuan Nov 10, 2023
5567e6b
Move to S.R.Loader folder
huoyaoyuan Nov 10, 2023
a73f546
Move namespace and rename. Allows usage of System.Reflection.
huoyaoyuan Nov 10, 2023
82fe458
Remove unused QCall and accessibility change, arrange QCall
huoyaoyuan Nov 10, 2023
ce4204c
Update the structure of BindUsingHostAssemblyResolver
huoyaoyuan Nov 14, 2023
2dc24bd
Cleanup unused binder implementation
huoyaoyuan Nov 14, 2023
7108a48
Redo CoreLib bootstrap changes
huoyaoyuan Nov 14, 2023
3f7c634
Remove BinderAssemblyObject and reorder vm to corelib call to ALC
huoyaoyuan Nov 15, 2023
d37aabf
Merge branch 'main'
huoyaoyuan Nov 15, 2023
6836892
Uncomment remaining native binder related code
huoyaoyuan Nov 15, 2023
55975ce
Cleanup AppContext
huoyaoyuan Nov 15, 2023
e75e644
Delete BindResult and FailureCache
huoyaoyuan Nov 8, 2023
5cf324e
Delete BINDER_SPACE::Assembly
huoyaoyuan Nov 8, 2023
485ed02
Cleanup remaining tracing and assembly code
huoyaoyuan Nov 15, 2023
dc3f5fc
Fill managed tracer
huoyaoyuan Nov 16, 2023
fd58a10
Cleanup indent changes
huoyaoyuan Nov 16, 2023
87a89e4
Fix
huoyaoyuan Nov 16, 2023
258b857
Align tracing with existing ALC tracing
huoyaoyuan Nov 16, 2023
e83e472
Fix GC mode for an assert
huoyaoyuan Nov 16, 2023
ce07241
Cleanup
huoyaoyuan Nov 16, 2023
354c359
SourceGen can't generate for ref struct
huoyaoyuan Nov 16, 2023
e5487b3
Remove usage of record struct
huoyaoyuan Nov 16, 2023
e35aa05
Load lock cleanup
huoyaoyuan Nov 16, 2023
5af68d7
Cleanup exception
huoyaoyuan Nov 16, 2023
1f53d77
Fix callsite compiling
huoyaoyuan Nov 16, 2023
65b2198
Fix GC mode in CreateDynamic
huoyaoyuan Nov 16, 2023
8098ff4
Move CoreLib binder setup after g_fEEStarted
huoyaoyuan Nov 16, 2023
5363aec
Fix AssemblyName equality
huoyaoyuan Nov 22, 2023
f50c271
Fix binder tracing
huoyaoyuan Nov 22, 2023
55e2b6c
Simplify AttemptResult tracing
huoyaoyuan Nov 23, 2023
f74c57d
Update Crst level
huoyaoyuan Nov 23, 2023
ab91f17
Fix
huoyaoyuan Nov 24, 2023
5b1b5e1
Unloadability
huoyaoyuan Nov 24, 2023
106333a
Fix BindSatelliteResourceByProbingPaths
huoyaoyuan Nov 25, 2023
cc74f32
Merge branch 'main' into managed-binder
jkotas Dec 6, 2023
77ff24e
Merge branch 'main'
huoyaoyuan Mar 10, 2024
146c0f7
Use separated helper to handle nullable array
huoyaoyuan Mar 10, 2024
8afeb7d
Don't use InvariantCulture
huoyaoyuan Mar 13, 2024
8ee2a01
Merge branch 'main'
huoyaoyuan May 13, 2024
dfeaf1b
Update MDImport to latest pattern
huoyaoyuan May 13, 2024
22a25ba
Use class to reduce a generic instantiation
huoyaoyuan May 13, 2024
dbb3a20
Remove more generic instantiation on Dictionary
huoyaoyuan May 13, 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
14 changes: 13 additions & 1 deletion src/coreclr/System.Private.CoreLib/System.Private.CoreLib.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -222,7 +222,19 @@
<Compile Include="$(BclSourcesRoot)\System\Runtime\InteropServices\MemoryMarshal.CoreCLR.cs" />
<Compile Include="$(BclSourcesRoot)\System\Runtime\InteropServices\NativeLibrary.CoreCLR.cs" />
<Compile Include="$(BclSourcesRoot)\System\Runtime\Intrinsics\X86\X86Base.CoreCLR.cs" />
<Compile Include="$(BclSourcesRoot)\System\Runtime\Loader\ApplicationContext.cs" />
<Compile Include="$(BclSourcesRoot)\System\Runtime\Loader\Assembly.cs" />
<Compile Include="$(BclSourcesRoot)\System\Runtime\Loader\AssemblyLoadContext.Binder.cs" />
<Compile Include="$(BclSourcesRoot)\System\Runtime\Loader\AssemblyLoadContext.BinderCommon.cs" />
<Compile Include="$(BclSourcesRoot)\System\Runtime\Loader\AssemblyLoadContext.CoreCLR.cs" />
<Compile Include="$(BclSourcesRoot)\System\Runtime\Loader\AssemblyLoadContext.CustomBinder.cs" />
<Compile Include="$(BclSourcesRoot)\System\Runtime\Loader\AssemblyLoadContext.DefaultBinder.cs" />
<Compile Include="$(BclSourcesRoot)\System\Runtime\Loader\AssemblyIdentity.cs" />
<Compile Include="$(BclSourcesRoot)\System\Runtime\Loader\AssemblyName.cs" />
<Compile Include="$(BclSourcesRoot)\System\Runtime\Loader\AssemblyVersion.cs" />
<Compile Include="$(BclSourcesRoot)\System\Runtime\Loader\BindResult.cs" />
<Compile Include="$(BclSourcesRoot)\System\Runtime\Loader\TextualIdentityParser.cs" />
<Compile Include="$(BclSourcesRoot)\System\Runtime\Loader\Tracing\ResolutionAttemptedOperation.cs" />
<Compile Include="$(BclSourcesRoot)\System\RuntimeArgumentHandle.cs" />
<Compile Include="$(BclSourcesRoot)\System\RuntimeHandles.cs" />
<Compile Include="$(BclSourcesRoot)\System\RuntimeType.ActivatorCache.cs" />
Expand Down Expand Up @@ -341,4 +353,4 @@
<FileWrites Include="@(EventingSourceFile)" />
</ItemGroup>
</Target>
</Project>
</Project>
Original file line number Diff line number Diff line change
Expand Up @@ -551,6 +551,38 @@ public bool IsValidToken(int token)
{
return _IsValidToken(m_metadataImport2, token);
}

[MethodImpl(MethodImplOptions.InternalCall)]
private static extern void _GetAssemblyFromScope(IntPtr scope, out uint tkAssembly);
public uint GetAssemblyFromScope()
{
_GetAssemblyFromScope(m_metadataImport2, out uint tkAssembly);
return tkAssembly;
}

[MethodImpl(MethodImplOptions.InternalCall)]
private static extern unsafe void _GetAssemblyProps(
IntPtr scope,
uint mda,
out byte* ppbPublicKey,
out uint pcbPublicKey,
out uint pulHashAlgId,
void** pszName,
void* pMetadata,
out uint pdwAsselblyFlags);
public unsafe void GetAssemblyProps(
uint assemblyToken,
out byte* publicKey,
out uint publicKeyLength,
out uint hashAlgId,
out string assemblyName,
void* pMetadata,
out uint asselblyFlags)
{
void* _name;
_GetAssemblyProps(m_metadataImport2, assemblyToken, out publicKey, out publicKeyLength, out hashAlgId, &_name, pMetadata, out asselblyFlags);
assemblyName = new MdUtf8String(_name).ToString();
}
#endregion
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,281 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Threading;

namespace System.Runtime.Loader
{
internal struct TPAEntry
{
public string? ILFileName;
public string? NIFileName;
}

internal sealed class ApplicationContext
{
private volatile int _version;

public int Version => _version;

public Dictionary<BinderAssemblyName, BinderAssembly> ExecutionContext { get; } = new Dictionary<BinderAssemblyName, BinderAssembly>();

public Dictionary<(string SimpleName, AssemblyVersion Version), int> FailureCache { get; } = new Dictionary<(string SimpleName, AssemblyVersion Version), int>();

public object ContextCriticalSection { get; } = new object();

public List<string> PlatformResourceRoots { get; } = new List<string>();

public List<string> AppPaths { get; } = new List<string>();

public Dictionary<string, TPAEntry>? TrustedPlatformAssemblyMap { get; private set; }

public void IncrementVersion() => Interlocked.Increment(ref _version);

private static bool GetNextPath(string paths, ref int startPos, out string outPath)
{
bool wrappedWithQuotes = false;

// Skip any leading spaces or path separators
while (startPos < paths.Length && paths[startPos] is ' ' or PathInternal.PathSeparator)
startPos++;

if (startPos == paths.Length)
{
// No more paths in the string and we just skipped over some white space
outPath = string.Empty;
return false;
}

// Support paths being wrapped with quotations
while (startPos < paths.Length && paths[startPos] == '\"')
{
startPos++;
wrappedWithQuotes = true;
}

int iEnd = startPos; // Where current path ends
int iNext; // Where next path starts

static int IndexOfInRange(ReadOnlySpan<char> str, int start, char ch)
{
int index = str[start..].IndexOf(ch);
return index >= 0 ? index + start : index;
}

if (wrappedWithQuotes)
{
iEnd = IndexOfInRange(paths, iEnd, '\"');
if (iEnd != -1)
{
// Find where the next path starts - there should be a path separator right after the closing quotation mark
iNext = IndexOfInRange(paths, iEnd, PathInternal.PathSeparator);
if (iNext != -1)
{
iNext++;
}
else
{
iNext = paths.Length;
}
}
else
{
// There was no terminating quotation mark - that's bad
throw new ArgumentException(nameof(paths));
}
}
else if ((iEnd = IndexOfInRange(paths, iEnd, PathInternal.PathSeparator)) != -1)
{
iNext = iEnd + 1;
}
else
{
iNext = iEnd = paths.Length;
}

// Skip any trailing spaces
while (paths[iEnd - 1] == ' ')
{
iEnd--;
}

Debug.Assert(startPos < iEnd);

outPath = paths[startPos..iEnd];
startPos = iNext;
return true;
}

private static bool GetNextTPAPath(string paths, ref int startPos, bool dllOnly, out string outPath, out string simpleName, out bool isNativeImage)
{
isNativeImage = false;

while (true)
{
if (!GetNextPath(paths, ref startPos, out outPath))
{
simpleName = string.Empty;
return false;
}

if (!Path.IsPathFullyQualified(outPath))
{
throw new ArgumentException(nameof(paths));
}

// Find the beginning of the simple name
int iSimpleNameStart = outPath.LastIndexOf(PathInternal.DirectorySeparatorChar);
if (iSimpleNameStart == -1)
{
iSimpleNameStart = 0;
}
else
{
// Advance past the directory separator to the first character of the file name
iSimpleNameStart++;
}

if (iSimpleNameStart == outPath.Length)
{
throw new ArgumentException(nameof(paths));
}

if (dllOnly && (outPath.EndsWith(".exe", StringComparison.OrdinalIgnoreCase)
|| outPath.EndsWith(".ni.exe", StringComparison.OrdinalIgnoreCase)))
{
// Skip exe files when the caller requested only dlls
continue;
}

if (outPath.EndsWith(".ni.dll", StringComparison.OrdinalIgnoreCase)
|| outPath.EndsWith(".ni.exe", StringComparison.OrdinalIgnoreCase))
{
simpleName = outPath[iSimpleNameStart..^7];
isNativeImage = true;
}
Comment on lines +171 to +176
Copy link
Member Author

@huoyaoyuan huoyaoyuan May 13, 2024

Choose a reason for hiding this comment

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

Is ni.dll still a thing? I remember it's output of NGen.

Even if it's still a thing, we should probably remove this too because the shared framework (TPA) does not contain any ni files.
The performance difference is negligible though.

else if (outPath.EndsWith(".dll", StringComparison.OrdinalIgnoreCase)
|| outPath.EndsWith(".exe", StringComparison.OrdinalIgnoreCase))
{
simpleName = outPath[iSimpleNameStart..^4];
}
else
{
// Invalid filename
throw new ArgumentException(nameof(paths));
}

return true; ;
}
}

public void SetupBindingPaths(string trustedPlatformAssemblies, string platformResourceRoots, string appPaths, bool acquireLock)
{
if (acquireLock)
{
lock (ContextCriticalSection)
{
Core(trustedPlatformAssemblies, platformResourceRoots, appPaths);
}
}
else
{
Core(trustedPlatformAssemblies, platformResourceRoots, appPaths);
}

void Core(string trustedPlatformAssemblies, string platformResourceRoots, string appPaths)
{
if (TrustedPlatformAssemblyMap != null)
{
return;
}

//
// Parse TrustedPlatformAssemblies
//

TrustedPlatformAssemblyMap = new Dictionary<string, TPAEntry>(StringComparer.InvariantCultureIgnoreCase);
huoyaoyuan marked this conversation as resolved.
Show resolved Hide resolved
for (int i = 0; i < trustedPlatformAssemblies.Length;)
{
if (!GetNextTPAPath(trustedPlatformAssemblies, ref i, dllOnly: false, out string fileName, out string simpleName, out bool isNativeImage))
{
break;
}

if (TrustedPlatformAssemblyMap.TryGetValue(simpleName, out TPAEntry existingEntry))
{
//
// We want to store only the first entry matching a simple name we encounter.
// The exception is if we first store an IL reference and later in the string
// we encounter a native image. Since we don't touch IL in the presence of
// native images, we replace the IL entry with the NI.
//
if ((existingEntry.ILFileName != null && !isNativeImage) ||
(existingEntry.NIFileName != null && isNativeImage))
{
continue;
}
}

if (isNativeImage)
{
existingEntry.NIFileName = fileName;
}
else
{
existingEntry.ILFileName = fileName;
}

TrustedPlatformAssemblyMap[simpleName] = existingEntry;
}

//
// Parse PlatformResourceRoots
//

for (int i = 0; i < platformResourceRoots.Length;)
{
if (!GetNextPath(platformResourceRoots, ref i, out string pathName))
{
break;
}

if (!Path.IsPathFullyQualified(pathName))
{
throw new ArgumentException(nameof(pathName));
}

PlatformResourceRoots.Add(pathName);
}

//
// Parse AppPaths
//

for (int i = 0; i < appPaths.Length;)
{
if (!GetNextPath(appPaths, ref i, out string pathName))
{
break;
}


if (!Path.IsPathFullyQualified(pathName))
{
throw new ArgumentException(nameof(pathName));
}

AppPaths.Add(pathName);
}
}
}

public void AddToFailureCache(BinderAssemblyName assemblyName, int hresult)
{
FailureCache.Add((assemblyName.SimpleName, assemblyName.Version), hresult);
IncrementVersion();
}
}
}
Loading
Loading