Added new module for cpp compatibility
This commit is contained in:
@@ -7,6 +7,7 @@ namespace DiscordBotCore.Interfaces.Modules
|
|||||||
public enum ModuleType
|
public enum ModuleType
|
||||||
{
|
{
|
||||||
Logger,
|
Logger,
|
||||||
|
Compatibility,
|
||||||
Other
|
Other
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ using System.Threading.Tasks;
|
|||||||
|
|
||||||
namespace DiscordBotCore.Others;
|
namespace DiscordBotCore.Others;
|
||||||
|
|
||||||
public class JsonManager
|
public static class JsonManager
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Save to JSON file
|
/// Save to JSON file
|
||||||
|
|||||||
@@ -19,6 +19,18 @@ public class Result
|
|||||||
_Result = result;
|
_Result = result;
|
||||||
Exception = null;
|
Exception = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public bool IsSuccess => _Result.HasValue && _Result.Value;
|
||||||
|
|
||||||
|
public void HandleException(Action<Exception> action)
|
||||||
|
{
|
||||||
|
if(IsSuccess)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
action(Exception!);
|
||||||
|
}
|
||||||
|
|
||||||
public static Result Success() => new Result(true);
|
public static Result Success() => new Result(true);
|
||||||
public static Result Failure(Exception ex) => new Result(ex);
|
public static Result Failure(Exception ex) => new Result(ex);
|
||||||
@@ -36,6 +48,12 @@ public class Result
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public TResult Match<TResult>(Func<TResult> successAction, Func<Exception,TResult> errorAction)
|
||||||
|
{
|
||||||
|
return IsSuccess ? successAction() : errorAction(Exception!);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public class Result<T>
|
public class Result<T>
|
||||||
|
|||||||
13
Modules/CppCompatibilityModule/CppCompatibilityModule.csproj
Normal file
13
Modules/CppCompatibilityModule/CppCompatibilityModule.csproj
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
|
<PropertyGroup>
|
||||||
|
<TargetFramework>net8.0</TargetFramework>
|
||||||
|
<ImplicitUsings>enable</ImplicitUsings>
|
||||||
|
<Nullable>enable</Nullable>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<ProjectReference Include="..\..\DiscordBotCore\DiscordBotCore.csproj" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
</Project>
|
||||||
75
Modules/CppCompatibilityModule/Entry.cs
Normal file
75
Modules/CppCompatibilityModule/Entry.cs
Normal file
@@ -0,0 +1,75 @@
|
|||||||
|
using DiscordBotCore;
|
||||||
|
using DiscordBotCore.Interfaces.Modules;
|
||||||
|
using DiscordBotCore.Others;
|
||||||
|
|
||||||
|
namespace CppCompatibilityModule;
|
||||||
|
|
||||||
|
public class Entry : IModule
|
||||||
|
{
|
||||||
|
public ModuleType ModuleType => ModuleType.Compatibility;
|
||||||
|
public string Name => "CppCompatibility";
|
||||||
|
public IDictionary<string, string> MethodMapping => new Dictionary<string, string>()
|
||||||
|
{
|
||||||
|
{"create_application", "CreateApplication"},
|
||||||
|
{"stop_application", "StopApplication"},
|
||||||
|
{"execute_function_with_parameter", "CallFunctionWithParameter"},
|
||||||
|
{"execute_function_without_parameter", "CallFunctionWithoutParameter"}
|
||||||
|
};
|
||||||
|
|
||||||
|
private ExternalApplicationManager? _ExternalApplicationManager;
|
||||||
|
|
||||||
|
public Task Initialize()
|
||||||
|
{
|
||||||
|
_ExternalApplicationManager = new ExternalApplicationManager();
|
||||||
|
return Task.CompletedTask;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Guid CreateApplication(string dllFilePath)
|
||||||
|
{
|
||||||
|
if(_ExternalApplicationManager is null)
|
||||||
|
{
|
||||||
|
Application.Logger.Log("Failed to create application because the manager is not initialized. This should have never happened in the first place !!!", this, LogType.Critical);
|
||||||
|
return Guid.Empty;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(_ExternalApplicationManager.TryCreateApplication(dllFilePath, out Guid appId))
|
||||||
|
{
|
||||||
|
return appId;
|
||||||
|
}
|
||||||
|
|
||||||
|
return Guid.Empty;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void StopApplication(Guid applicationId)
|
||||||
|
{
|
||||||
|
if(_ExternalApplicationManager is null)
|
||||||
|
{
|
||||||
|
Application.Logger.Log("Failed to stop application because the manager is not initialized. This should have never happened in the first place!!!", this, LogType.Critical);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
_ExternalApplicationManager.FreeApplication(applicationId);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void CallFunctionWithParameter(Guid appId, string functionName, ref object parameter)
|
||||||
|
{
|
||||||
|
if(_ExternalApplicationManager is null)
|
||||||
|
{
|
||||||
|
Application.Logger.Log("Failed to call function because the manager is not initialized. This should have never happened in the first place!!!", this, LogType.Critical);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
_ExternalApplicationManager.ExecuteApplicationFunctionWithParameter(appId, functionName, ref parameter);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void CallFunctionWithoutParameter(Guid appId, string functionName)
|
||||||
|
{
|
||||||
|
if(_ExternalApplicationManager is null)
|
||||||
|
{
|
||||||
|
Application.Logger.Log("Failed to call function because the manager is not initialized. This should have never happened in the first place!!!", this, LogType.Critical);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
_ExternalApplicationManager.ExecuteApplicationFunctionWithoutParameter(appId, functionName);
|
||||||
|
}
|
||||||
|
}
|
||||||
18
Modules/CppCompatibilityModule/Extern/Delegates.cs
vendored
Normal file
18
Modules/CppCompatibilityModule/Extern/Delegates.cs
vendored
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
using System.Runtime.InteropServices;
|
||||||
|
|
||||||
|
namespace CppCompatibilityModule.Extern;
|
||||||
|
|
||||||
|
public static class Delegates
|
||||||
|
{
|
||||||
|
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
|
||||||
|
public delegate void ProcessObject(ref object obj);
|
||||||
|
|
||||||
|
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
|
||||||
|
public delegate void ExecuteDelegateFunction();
|
||||||
|
|
||||||
|
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
|
||||||
|
public delegate void SetExternFunctionPointerDelegate(IntPtr funcPtr);
|
||||||
|
|
||||||
|
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
|
||||||
|
public delegate void CsharpFunctionDelegate();
|
||||||
|
}
|
||||||
133
Modules/CppCompatibilityModule/Extern/ExternLibrary.cs
vendored
Normal file
133
Modules/CppCompatibilityModule/Extern/ExternLibrary.cs
vendored
Normal file
@@ -0,0 +1,133 @@
|
|||||||
|
using System.Runtime.InteropServices;
|
||||||
|
using DiscordBotCore;
|
||||||
|
using DiscordBotCore.Others;
|
||||||
|
|
||||||
|
namespace CppCompatibilityModule.Extern
|
||||||
|
{
|
||||||
|
public sealed class ExternLibrary
|
||||||
|
{
|
||||||
|
public string LibraryPath { get; init; }
|
||||||
|
public IntPtr LibraryHandle { get; private set; }
|
||||||
|
|
||||||
|
public ExternLibrary(string libraryPath)
|
||||||
|
{
|
||||||
|
LibraryPath = libraryPath;
|
||||||
|
LibraryHandle = IntPtr.Zero;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Result InitializeLibrary()
|
||||||
|
{
|
||||||
|
if(LibraryHandle != IntPtr.Zero)
|
||||||
|
{
|
||||||
|
return Result.Success();
|
||||||
|
}
|
||||||
|
|
||||||
|
Application.Logger.Log($"Loading library {LibraryPath}");
|
||||||
|
|
||||||
|
|
||||||
|
if(!NativeLibrary.TryLoad(LibraryPath, out IntPtr hModule))
|
||||||
|
{
|
||||||
|
return Result.Failure(new DllNotFoundException($"Unable to load library {LibraryPath}"));
|
||||||
|
}
|
||||||
|
|
||||||
|
Application.Logger.Log($"Library {LibraryPath} loaded successfully [{hModule}]");
|
||||||
|
|
||||||
|
LibraryHandle = hModule;
|
||||||
|
|
||||||
|
return Result.Success();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void FreeLibrary()
|
||||||
|
{
|
||||||
|
if(LibraryHandle == IntPtr.Zero)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
NativeLibrary.Free(LibraryHandle);
|
||||||
|
LibraryHandle = IntPtr.Zero;
|
||||||
|
|
||||||
|
Application.Logger.Log($"Library {LibraryPath} freed successfully");
|
||||||
|
}
|
||||||
|
|
||||||
|
private IntPtr GetFunctionPointer(string functionName)
|
||||||
|
{
|
||||||
|
if(LibraryHandle == IntPtr.Zero)
|
||||||
|
{
|
||||||
|
throw new InvalidOperationException("Library is not loaded");
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!NativeLibrary.TryGetExport(LibraryHandle, functionName, out IntPtr functionPointer))
|
||||||
|
{
|
||||||
|
throw new EntryPointNotFoundException($"Unable to find function {functionName}");
|
||||||
|
}
|
||||||
|
|
||||||
|
return functionPointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
public T GetDelegateForFunctionPointer<T>(string methodName) where T : Delegate
|
||||||
|
{
|
||||||
|
IntPtr functionPointer = GetFunctionPointer(methodName);
|
||||||
|
|
||||||
|
Application.Logger.Log($"Function pointer for {methodName} obtained successfully [address: {functionPointer}]");
|
||||||
|
|
||||||
|
T result = (T)Marshal.GetDelegateForFunctionPointer(functionPointer, typeof(T));
|
||||||
|
|
||||||
|
Application.Logger.Log($"Delegate for {methodName} created successfully");
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
private IntPtr GetFunctionPointerForDelegate<T>(T functionDelegate) where T : Delegate
|
||||||
|
{
|
||||||
|
IntPtr functionPointer = Marshal.GetFunctionPointerForDelegate(functionDelegate);
|
||||||
|
|
||||||
|
Application.Logger.Log($"Function pointer for delegate {functionDelegate.Method.Name} obtained successfully [address: {functionPointer}]");
|
||||||
|
|
||||||
|
return functionPointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Tells the extern setter function to point its function to this C# function instead.
|
||||||
|
/// This function takes the name of the extern setter function and the C# function to be executed.
|
||||||
|
/// <para><b>How it works:</b></para>
|
||||||
|
/// Find the external setter method by its name. It should take one parameter, which is the pointer to the function to be executed.
|
||||||
|
/// Take the delegate function that should be executed and get its function pointer.
|
||||||
|
/// Call the external setter with the new function memory address. This should replace the old C++ function with the new C# function.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="setterExternFunctionName">The setter function name</param>
|
||||||
|
/// <param name="executableFunction">The function that the C++ setter will make its internal function to point to</param>
|
||||||
|
/// <typeparam name="ExecuteDelegate">A delegate that reflects the executable function structure</typeparam>
|
||||||
|
/// <typeparam name="SetDelegate">The Setter delegate </typeparam>
|
||||||
|
/// <returns>A response if it exists as an object</returns>
|
||||||
|
public object? SetExternFunctionSetterPointerToCustomDelegate<SetDelegate, ExecuteDelegate>(string setterExternFunctionName, ExecuteDelegate executableFunction) where ExecuteDelegate : Delegate where SetDelegate : Delegate
|
||||||
|
{
|
||||||
|
SetDelegate setterDelegate = GetDelegateForFunctionPointer<SetDelegate>(setterExternFunctionName);
|
||||||
|
IntPtr executableFunctionPtr = GetFunctionPointerForDelegate(executableFunction);
|
||||||
|
|
||||||
|
var result = setterDelegate.DynamicInvoke(executableFunctionPtr);
|
||||||
|
|
||||||
|
Application.Logger.Log($"Function {setterExternFunctionName} bound to local action successfully");
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void CallFunction(string methodName, ref object parameter)
|
||||||
|
{
|
||||||
|
var functionDelegate = GetDelegateForFunctionPointer<Delegates.ProcessObject>(methodName);
|
||||||
|
|
||||||
|
functionDelegate(ref parameter);
|
||||||
|
|
||||||
|
Application.Logger.Log($"Function {methodName} called successfully with parameter");
|
||||||
|
}
|
||||||
|
|
||||||
|
public void CallFunction(string methodName)
|
||||||
|
{
|
||||||
|
var functionDelegate = GetDelegateForFunctionPointer<Delegates.ExecuteDelegateFunction>(methodName);
|
||||||
|
|
||||||
|
functionDelegate();
|
||||||
|
|
||||||
|
Application.Logger.Log($"Function {methodName} called successfully");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
55
Modules/CppCompatibilityModule/Extern/ExternalApplication.cs
vendored
Normal file
55
Modules/CppCompatibilityModule/Extern/ExternalApplication.cs
vendored
Normal file
@@ -0,0 +1,55 @@
|
|||||||
|
using DiscordBotCore;
|
||||||
|
using DiscordBotCore.Others;
|
||||||
|
|
||||||
|
namespace CppCompatibilityModule.Extern;
|
||||||
|
|
||||||
|
public class ExternalApplication
|
||||||
|
{
|
||||||
|
public Guid ApplicationId { get; private set; }
|
||||||
|
private readonly ExternLibrary _ExternLibrary;
|
||||||
|
|
||||||
|
private ExternalApplication(Guid applicationGuid, ExternLibrary library)
|
||||||
|
{
|
||||||
|
this.ApplicationId = applicationGuid;
|
||||||
|
this._ExternLibrary = library;
|
||||||
|
}
|
||||||
|
|
||||||
|
internal void CallFunction(string methodName, ref object parameter)
|
||||||
|
{
|
||||||
|
_ExternLibrary.CallFunction(methodName, ref parameter);
|
||||||
|
}
|
||||||
|
|
||||||
|
internal void CallFunction(string methodName)
|
||||||
|
{
|
||||||
|
_ExternLibrary.CallFunction(methodName);
|
||||||
|
}
|
||||||
|
|
||||||
|
internal T GetDelegateForFunctionPointer<T>(string methodName) where T : Delegate
|
||||||
|
{
|
||||||
|
return _ExternLibrary.GetDelegateForFunctionPointer<T>(methodName);
|
||||||
|
}
|
||||||
|
|
||||||
|
internal void SetExternFunctionToPointToFunction(string externalFunctionName, Delegates.CsharpFunctionDelegate localFunction)
|
||||||
|
{
|
||||||
|
_ExternLibrary.SetExternFunctionSetterPointerToCustomDelegate<Delegates.SetExternFunctionPointerDelegate, Delegates.CsharpFunctionDelegate>(externalFunctionName, localFunction);
|
||||||
|
}
|
||||||
|
|
||||||
|
internal void FreeLibrary()
|
||||||
|
{
|
||||||
|
_ExternLibrary.FreeLibrary();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static ExternalApplication? CreateFromDllFile(string dllFilePath)
|
||||||
|
{
|
||||||
|
ExternLibrary library = new ExternLibrary(dllFilePath);
|
||||||
|
var result = library.InitializeLibrary();
|
||||||
|
|
||||||
|
return result.Match<ExternalApplication?>(
|
||||||
|
() => new ExternalApplication(Guid.NewGuid(), library),
|
||||||
|
(ex) => {
|
||||||
|
Application.Logger.Log(ex.Message, LogType.Error);
|
||||||
|
library.FreeLibrary();
|
||||||
|
return null;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
72
Modules/CppCompatibilityModule/ExternalApplicationManager.cs
Normal file
72
Modules/CppCompatibilityModule/ExternalApplicationManager.cs
Normal file
@@ -0,0 +1,72 @@
|
|||||||
|
using CppCompatibilityModule.Extern;
|
||||||
|
using DiscordBotCore;
|
||||||
|
|
||||||
|
namespace CppCompatibilityModule;
|
||||||
|
|
||||||
|
public class ExternalApplicationManager
|
||||||
|
{
|
||||||
|
private List<ExternalApplication> _ExternalApplications;
|
||||||
|
|
||||||
|
public ExternalApplicationManager()
|
||||||
|
{
|
||||||
|
_ExternalApplications = new List<ExternalApplication>();
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool TryCreateApplication(string applicationFileName, out Guid applicationId)
|
||||||
|
{
|
||||||
|
ExternalApplication? externalApplication = ExternalApplication.CreateFromDllFile(applicationFileName);
|
||||||
|
|
||||||
|
if(externalApplication is null)
|
||||||
|
{
|
||||||
|
applicationId = Guid.Empty;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
_ExternalApplications.Add(externalApplication);
|
||||||
|
|
||||||
|
applicationId = externalApplication.ApplicationId;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void FreeApplication(Guid applicationId)
|
||||||
|
{
|
||||||
|
var application = _ExternalApplications.FirstOrDefault(app => app.ApplicationId == applicationId, null);
|
||||||
|
if(application is null)
|
||||||
|
{
|
||||||
|
Application.Logger.Log($"Couldn't find application with id {applicationId}");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
application.FreeLibrary();
|
||||||
|
_ExternalApplications.Remove(application);
|
||||||
|
|
||||||
|
Application.Logger.Log($"Application with id {applicationId} freed successfully");
|
||||||
|
}
|
||||||
|
|
||||||
|
public void ExecuteApplicationFunctionWithParameter(Guid appId, string functionName, ref object parameter)
|
||||||
|
{
|
||||||
|
var application = _ExternalApplications.FirstOrDefault(app => app.ApplicationId == appId);
|
||||||
|
if(application is null)
|
||||||
|
{
|
||||||
|
Application.Logger.Log($"Couldn't find application with id {appId}");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
application.CallFunction(functionName, ref parameter);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void ExecuteApplicationFunctionWithoutParameter(Guid appId, string functionName)
|
||||||
|
{
|
||||||
|
var application = _ExternalApplications.FirstOrDefault(app => app.ApplicationId == appId);
|
||||||
|
if(application is null)
|
||||||
|
{
|
||||||
|
Application.Logger.Log($"Couldn't find application with id {appId}");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
application.CallFunction(functionName);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
@@ -30,6 +30,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "WebUI", "Plugins\WebUI\WebU
|
|||||||
EndProject
|
EndProject
|
||||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "DiscordBotWebUI", "DiscordBotWebUI\DiscordBotWebUI.csproj", "{362161EF-531F-4413-B200-19ACD0FC355B}"
|
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "DiscordBotWebUI", "DiscordBotWebUI\DiscordBotWebUI.csproj", "{362161EF-531F-4413-B200-19ACD0FC355B}"
|
||||||
EndProject
|
EndProject
|
||||||
|
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CppCompatibilityModule", "Modules\CppCompatibilityModule\CppCompatibilityModule.csproj", "{C67908F9-4A55-4DD8-B993-C26C648226F1}"
|
||||||
|
EndProject
|
||||||
Global
|
Global
|
||||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||||
Debug|Any CPU = Debug|Any CPU
|
Debug|Any CPU = Debug|Any CPU
|
||||||
@@ -72,6 +74,10 @@ Global
|
|||||||
{362161EF-531F-4413-B200-19ACD0FC355B}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
{362161EF-531F-4413-B200-19ACD0FC355B}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
{362161EF-531F-4413-B200-19ACD0FC355B}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
{362161EF-531F-4413-B200-19ACD0FC355B}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
{362161EF-531F-4413-B200-19ACD0FC355B}.Release|Any CPU.Build.0 = Release|Any CPU
|
{362161EF-531F-4413-B200-19ACD0FC355B}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
|
{C67908F9-4A55-4DD8-B993-C26C648226F1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
|
{C67908F9-4A55-4DD8-B993-C26C648226F1}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
|
{C67908F9-4A55-4DD8-B993-C26C648226F1}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
|
{C67908F9-4A55-4DD8-B993-C26C648226F1}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
EndGlobalSection
|
EndGlobalSection
|
||||||
GlobalSection(SolutionProperties) = preSolution
|
GlobalSection(SolutionProperties) = preSolution
|
||||||
HideSolutionNode = FALSE
|
HideSolutionNode = FALSE
|
||||||
@@ -83,6 +89,7 @@ Global
|
|||||||
{F3C61A47-F758-4145-B496-E3ECCF979D89} = {5CF9AD7B-6BF0-4035-835F-722F989C01E1}
|
{F3C61A47-F758-4145-B496-E3ECCF979D89} = {5CF9AD7B-6BF0-4035-835F-722F989C01E1}
|
||||||
{367F3197-8B9E-4BDC-A6DE-226E721F9ED1} = {EA4FA308-7B2C-458E-8485-8747D745DD59}
|
{367F3197-8B9E-4BDC-A6DE-226E721F9ED1} = {EA4FA308-7B2C-458E-8485-8747D745DD59}
|
||||||
{962A4815-6FAE-4DEF-976E-5622253A5E3B} = {5CF9AD7B-6BF0-4035-835F-722F989C01E1}
|
{962A4815-6FAE-4DEF-976E-5622253A5E3B} = {5CF9AD7B-6BF0-4035-835F-722F989C01E1}
|
||||||
|
{C67908F9-4A55-4DD8-B993-C26C648226F1} = {EA4FA308-7B2C-458E-8485-8747D745DD59}
|
||||||
EndGlobalSection
|
EndGlobalSection
|
||||||
GlobalSection(ExtensibilityGlobals) = postSolution
|
GlobalSection(ExtensibilityGlobals) = postSolution
|
||||||
SolutionGuid = {3FB3C5DE-ED21-4D2E-ABDD-3A00EE4A2FFF}
|
SolutionGuid = {3FB3C5DE-ED21-4D2E-ABDD-3A00EE4A2FFF}
|
||||||
|
|||||||
Reference in New Issue
Block a user