Skip to content

Commit 19390e1

Browse files
authored
feat: 新的插件结构和加载逻辑, 插件项目模板 (#18)
* feat: 新插件加载逻辑, 插件模板 * fix: 生成 .deps 文件
1 parent a13bcdb commit 19390e1

49 files changed

Lines changed: 419 additions & 448 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.
Lines changed: 8 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,37 +1,31 @@
11
<Project Sdk="Microsoft.NET.Sdk">
22

3+
<Import Project="..\..\Plugins\Plugin.props" />
4+
35
<PropertyGroup>
46
<OutputType>Library</OutputType>
57
<TargetFramework>net8.0-windows</TargetFramework>
8+
<GenerateDependencyFile>true</GenerateDependencyFile>
69

710
<!-- 自动复制NuGet包到输出目录 -->
811
<CopyLocalLockFileAssemblies>true</CopyLocalLockFileAssemblies>
12+
<GenerateDependencyFile>false</GenerateDependencyFile>
913
</PropertyGroup>
1014

1115
<ItemGroup>
1216
<Compile Include="SHLoadIndirectStringList.fs" />
1317
</ItemGroup>
1418

1519
<ItemGroup>
16-
<ProjectReference Include="..\..\CurvaLauncher.Plugins\CurvaLauncher.Plugins.csproj" />
20+
<Content Include="Manifest.json">
21+
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
22+
</Content>
1723
</ItemGroup>
1824

1925
<ItemGroup>
2026
<PackageReference Update="FSharp.Core" Version="8.0.101" />
2127
</ItemGroup>
2228

23-
24-
<Target Name="CopyFSharpCore" AfterTargets="Build">
25-
<Copy SourceFiles="$(OutDir)\FSharp.Core.dll" DestinationFiles="..\..\CurvaLauncher\bin\$(Configuration)\$(TargetFramework)\Libraries\FSharp.Core.dll"></Copy>
26-
</Target>
27-
28-
<Target Name="CopyOutputDebug" AfterTargets="Build" Condition="'$(Configuration)'=='Debug'">
29-
<Copy SourceFiles="$(OutDir)\$(MSBuildProjectName).dll" DestinationFiles="..\..\CurvaLauncher\bin\$(Configuration)\$(TargetFramework)\Plugins\$(MSBuildProjectName).dll"></Copy>
30-
<Copy SourceFiles="$(OutDir)\$(MSBuildProjectName).pdb" DestinationFiles="..\..\CurvaLauncher\bin\$(Configuration)\$(TargetFramework)\Plugins\$(MSBuildProjectName).pdb"></Copy>
31-
</Target>
32-
33-
<Target Name="CopyOutputRelease" AfterTargets="Build" Condition="'$(Configuration)'=='Release'">
34-
<Copy SourceFiles="$(OutDir)\$(MSBuildProjectName).dll" DestinationFiles="..\..\CurvaLauncher\bin\$(Configuration)\$(TargetFramework)\AdditionalPlugins\$(MSBuildProjectName).dll"></Copy>
35-
</Target>
29+
<Import Project="..\..\Plugins\PackAfterBuild.targets" />
3630

3731
</Project>
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
{
2+
"ID": "SHLoadIndirectStringList",
3+
"Assembly": "CurvaLauncher.Plugins.SHLoadIndirectStringList.dll"
4+
}

src/AdditionalPlugins/CurvaLauncher.Plugins.SHLoadIndirectStringList/SHLoadIndirectStringList.fs

Lines changed: 0 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -31,20 +31,6 @@ type public StringQueryResult(title, desc, weight) =
3131
type public SHLoadIndirectStringPlugin(context: CurvaLauncherContext) =
3232
inherit SyncPlugin(context)
3333

34-
static do
35-
AppDomain.CurrentDomain.add_AssemblyResolve(fun _ args ->
36-
let asmName = new AssemblyName(args.Name)
37-
let dllName = "FSharp.Core.dll"
38-
if asmName.Name = "FSharp.Core" then
39-
[|
40-
(Path.Combine(AppContext.BaseDirectory, dllName))
41-
(Path.Combine(AppContext.BaseDirectory, "Libraries", dllName));
42-
(Path.Combine(Directory.GetCurrentDirectory(), dllName))
43-
|].Where(File.Exists).First() |> Assembly.LoadFrom
44-
else
45-
null
46-
)
47-
4834
[<DllImport("shlwapi.dll", EntryPoint = "SHLoadIndirectString", CharSet = CharSet.Unicode, ExactSpelling = true)>]
4935
static extern uint SHLoadIndirectString(string pszSource, char& pszOutBuf, int cchOutBuf, nativeint ppvReserved)
5036

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
using System.Diagnostics;
2+
using System.IO;
3+
using System.Reflection;
4+
using System.Runtime.Loader;
5+
6+
namespace CurvaLauncher.Plugins;
7+
8+
public sealed class PluginAssemblyLoadContext : AssemblyLoadContext
9+
{
10+
private readonly AssemblyDependencyResolver resolver;
11+
12+
public PluginAssemblyLoadContext(string pluginID, string dllPath)
13+
: base($"ZipPlugin ALC - {pluginID}")
14+
{
15+
resolver = new(dllPath);
16+
}
17+
18+
protected override Assembly? Load(AssemblyName assemblyName)
19+
{
20+
string? resolvedPath = resolver.ResolveAssemblyToPath(assemblyName);
21+
return resolvedPath is not null ? LoadFromAssemblyPath(resolvedPath) : null;
22+
}
23+
24+
protected override nint LoadUnmanagedDll(string unmanagedDllName)
25+
{
26+
string? resolvedPath = resolver.ResolveUnmanagedDllToPath(unmanagedDllName);
27+
return resolvedPath is not null ? LoadUnmanagedDllFromPath(resolvedPath) : IntPtr.Zero;
28+
}
29+
}
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
namespace CurvaLauncher.Plugins;
2+
3+
public sealed record PluginManifest(string ID, string Assembly);
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
namespace CurvaLauncher.Plugins;
2+
3+
[AttributeUsage(AttributeTargets.Assembly)]
4+
public sealed class PluginTypeAttribute : Attribute
5+
{
6+
public Type PluginType { get; private set; }
7+
8+
public PluginTypeAttribute(Type pluginType)
9+
{
10+
PluginType = pluginType;
11+
}
12+
}

src/CurvaLauncher.sln

Lines changed: 3 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -39,13 +39,12 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "CurvaLauncher.Plugins.Every
3939
EndProject
4040
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{F36EDE5B-9C67-44F4-87A9-792390FECF85}"
4141
ProjectSection(SolutionItems) = preProject
42+
Plugins\CopyAfterBuild.targets = Plugins\CopyAfterBuild.targets
4243
CurvaLauncher.props = CurvaLauncher.props
44+
Plugins\PackAfterBuild.targets = Plugins\PackAfterBuild.targets
45+
Plugins\Plugin.props = Plugins\Plugin.props
4346
EndProjectSection
4447
EndProject
45-
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Templates", "Templates", "{45D0660D-436E-4419-AEB9-B6ED5BC3E0ED}"
46-
EndProject
47-
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CurvaLauncher.PluginTemplate", "Templates\CurvaLauncher.PluginTemplate\CurvaLauncher.PluginTemplate.csproj", "{702E5FF3-99D0-4FD3-86E2-04C0A2E82560}"
48-
EndProject
4948
Global
5049
GlobalSection(SolutionConfigurationPlatforms) = preSolution
5150
Debug|Any CPU = Debug|Any CPU
@@ -174,14 +173,6 @@ Global
174173
{C7AC6C75-89C6-42E5-8D2A-D06994949F05}.Release|Any CPU.Build.0 = Release|Any CPU
175174
{C7AC6C75-89C6-42E5-8D2A-D06994949F05}.Release|x64.ActiveCfg = Release|x64
176175
{C7AC6C75-89C6-42E5-8D2A-D06994949F05}.Release|x64.Build.0 = Release|x64
177-
{702E5FF3-99D0-4FD3-86E2-04C0A2E82560}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
178-
{702E5FF3-99D0-4FD3-86E2-04C0A2E82560}.Debug|Any CPU.Build.0 = Debug|Any CPU
179-
{702E5FF3-99D0-4FD3-86E2-04C0A2E82560}.Debug|x64.ActiveCfg = Debug|Any CPU
180-
{702E5FF3-99D0-4FD3-86E2-04C0A2E82560}.Debug|x64.Build.0 = Debug|Any CPU
181-
{702E5FF3-99D0-4FD3-86E2-04C0A2E82560}.Release|Any CPU.ActiveCfg = Release|Any CPU
182-
{702E5FF3-99D0-4FD3-86E2-04C0A2E82560}.Release|Any CPU.Build.0 = Release|Any CPU
183-
{702E5FF3-99D0-4FD3-86E2-04C0A2E82560}.Release|x64.ActiveCfg = Release|Any CPU
184-
{702E5FF3-99D0-4FD3-86E2-04C0A2E82560}.Release|x64.Build.0 = Release|Any CPU
185176
EndGlobalSection
186177
GlobalSection(SolutionProperties) = preSolution
187178
HideSolutionNode = FALSE
@@ -198,7 +189,6 @@ Global
198189
{F3F6F783-4636-457F-80E1-CC489F524B43} = {BAACD50D-2F94-4A65-8B13-49031D617CAC}
199190
{8CFC1C29-51AA-45ED-A91F-01F513182002} = {4A86F98E-B276-4F75-9847-8D0E4280D887}
200191
{C7AC6C75-89C6-42E5-8D2A-D06994949F05} = {BAACD50D-2F94-4A65-8B13-49031D617CAC}
201-
{702E5FF3-99D0-4FD3-86E2-04C0A2E82560} = {45D0660D-436E-4419-AEB9-B6ED5BC3E0ED}
202192
EndGlobalSection
203193
GlobalSection(ExtensibilityGlobals) = postSolution
204194
SolutionGuid = {3FC4E11A-3D67-43DE-84D8-DCA1841F0D71}

src/CurvaLauncher/App.xaml.cs

Lines changed: 1 addition & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,6 @@ private static IServiceProvider BuildServiceProvider()
5252
services.AddSingleton<HotkeyService>();
5353
services.AddSingleton<PluginService>();
5454
services.AddSingleton<ConfigService>();
55-
services.AddSingleton<LibraryService>();
5655
services.AddSingleton<ThemeService>();
5756
services.AddSingleton<I18nService>();
5857
services.AddTransient<PageService>();
@@ -81,8 +80,6 @@ protected override void OnStartup(StartupEventArgs e)
8180
.GetRequiredService<HotkeyService>();
8281
var themeService = ServiceProvider
8382
.GetRequiredService<ThemeService>();
84-
var libraryService = ServiceProvider
85-
.GetRequiredService<LibraryService>();
8683
var globalizationService = ServiceProvider
8784
.GetRequiredService<I18nService>();
8885

@@ -91,9 +88,6 @@ protected override void OnStartup(StartupEventArgs e)
9188
//new WindowInteropHelper(mainWindow)
9289
// .EnsureHandle();
9390

94-
// 加载库
95-
libraryService.Setup();
96-
9791
// 加载插件
9892
pluginService.LoadAllPlugins();
9993

@@ -215,7 +209,7 @@ public static void ShowLauncherWithQuery(string queryText)
215209

216210
public static void CloseLauncher()
217211
{
218-
var mainWindow =
212+
var mainWindow =
219213
ServiceProvider.GetRequiredService<MainWindow>();
220214

221215
mainWindow.ViewModel.QueryText = string.Empty;

src/CurvaLauncher/Services/LibraryService.cs

Lines changed: 0 additions & 58 deletions
This file was deleted.

src/CurvaLauncher/Services/PluginService.cs

Lines changed: 15 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -6,16 +6,14 @@
66
using System.Linq;
77
using System.Reflection;
88
using System.Text.Json;
9-
using System.Text.Json.Nodes;
10-
using System.Text.Json.Serialization.Metadata;
119
using System.Threading.Tasks;
1210
using CommunityToolkit.Mvvm.Input;
1311
using CurvaLauncher.Models;
1412
using CurvaLauncher.Plugins;
15-
using CurvaLauncher.PluginInteraction;
1613
using System.Diagnostics;
1714
using System.IO.Compression;
1815
using System.Runtime.Loader;
16+
using IOPath = System.IO.Path;
1917

2018
namespace CurvaLauncher.Services;
2119

@@ -141,34 +139,20 @@ private bool CoreLoadZipPlugin(AppConfig config, string zipFilePath, [NotNullWhe
141139
try
142140
{
143141
using var zipFile = File.OpenRead(zipFilePath);
144-
using var zipArchive = new ZipArchive(zipFile, ZipArchiveMode.Read);
145-
var assemblyLoadContext = new AssemblyLoadContext(null, false);
146-
147-
foreach (var entry in zipArchive.Entries)
148-
{
149-
if (!entry.FullName.EndsWith(".dll", StringComparison.OrdinalIgnoreCase))
150-
{
151-
continue;
152-
}
153-
154-
using var entryStream = entry.Open();
155-
156-
try
157-
{
158-
var assembly = assemblyLoadContext.LoadFromStream(entryStream);
159-
160-
if (pluginInstance is null)
161-
{
162-
CoreLoadPluginFromAssembly(config, assembly, out pluginInstance);
163-
}
164-
}
165-
catch (Exception ex)
166-
{
167-
Debug.WriteLine($"DLL load failed, {ex}");
168-
}
169-
}
170-
171-
return pluginInstance is not null;
142+
string extractDir = IOPath.Combine(".cache", IOPath.GetFileNameWithoutExtension(zipFilePath));
143+
if (Directory.Exists(extractDir))
144+
Directory.Delete(extractDir, true);
145+
ZipFile.ExtractToDirectory(zipFile, extractDir);
146+
147+
var manifestJson = File.ReadAllText(IOPath.Combine(extractDir, "Manifest.json"));
148+
var manifest = JsonSerializer.Deserialize<PluginManifest>(manifestJson);
149+
if (manifest is null)
150+
return false;
151+
152+
var assemblyPath = IOPath.GetFullPath(IOPath.Combine(extractDir, manifest.Assembly));
153+
var alc = new PluginAssemblyLoadContext(manifest.ID, assemblyPath);
154+
Assembly assembly = alc.LoadFromAssemblyPath(assemblyPath);
155+
return CoreLoadPluginFromAssembly(config, assembly, out pluginInstance);
172156
}
173157
catch (Exception ex)
174158
{

0 commit comments

Comments
 (0)