Merge pull request #144 from Lightczx/master

This commit is contained in:
HolographicHat
2025-10-14 16:17:21 +08:00
committed by GitHub
5 changed files with 146 additions and 37 deletions

View File

@@ -0,0 +1,104 @@
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Threading;
using static Microsoft.CodeAnalysis.CSharp.SyntaxFactory;
namespace YaeAchievement.SourceGeneration;
[Generator(LanguageNames.CSharp)]
public sealed class MinHookAttachGenerator : IIncrementalGenerator
{
public void Initialize(IncrementalGeneratorInitializationContext context)
{
IncrementalValueProvider<ImmutableArray<AttachInfo>> provider = context.SyntaxProvider.CreateSyntaxProvider(Filter, Transform).Collect();
context.RegisterSourceOutput(provider, Generate);
}
private static bool Filter(SyntaxNode node, CancellationToken token)
{
return node is InvocationExpressionSyntax
{
Expression: MemberAccessExpressionSyntax
{
Expression: IdentifierNameSyntax { Identifier.Text: "MinHook" },
Name.Identifier.Text: "Attach"
}
};
}
private static AttachInfo Transform(GeneratorSyntaxContext context, CancellationToken token)
{
InvocationExpressionSyntax invocation = (InvocationExpressionSyntax)context.Node;
SeparatedSyntaxList<ArgumentSyntax> args = invocation.ArgumentList.Arguments;
if (args.Count is not 3)
{
return null;
}
string type = context.SemanticModel.GetTypeInfo(args[0].Expression).Type?.ToDisplayString(SymbolDisplayFormat.MinimallyQualifiedFormat);
if (string.IsNullOrEmpty(type))
{
return null;
}
return new()
{
MinimallyQualifiedType = type,
};
}
private static void Generate(SourceProductionContext context, ImmutableArray<AttachInfo> infoArray)
{
CompilationUnitSyntax unit = CompilationUnit()
.WithMembers(List<MemberDeclarationSyntax>(
[
FileScopedNamespaceDeclaration(ParseName("Yae.Utilities")),
ClassDeclaration("MinHook")
.WithModifiers(TokenList(Token(SyntaxKind.InternalKeyword), Token(SyntaxKind.StaticKeyword), Token(SyntaxKind.PartialKeyword)))
.WithMembers(List(GenerateMethods(infoArray)))
]));
context.AddSource("MinHook.Attach.g.cs", unit.NormalizeWhitespace().ToFullString());
}
private static IEnumerable<MemberDeclarationSyntax> GenerateMethods(ImmutableArray<AttachInfo> infoArray)
{
foreach (AttachInfo info in infoArray)
{
TypeSyntax type = ParseTypeName(info.MinimallyQualifiedType);
yield return MethodDeclaration(PredefinedType(Token(SyntaxKind.VoidKeyword)), Identifier("Attach"))
.WithModifiers(TokenList(Token(SyntaxKind.PublicKeyword), Token(SyntaxKind.StaticKeyword), Token(SyntaxKind.UnsafeKeyword)))
.WithParameterList(ParameterList(SeparatedList(
[
Parameter(Identifier("origin")).WithType(type),
Parameter(Identifier("handler")).WithType(type),
Parameter(Identifier("trampoline")).WithType(type).WithModifiers(TokenList(Token(SyntaxKind.OutKeyword)))
])))
.WithBody(Block(List<StatementSyntax>(
[
ExpressionStatement(InvocationExpression(IdentifierName("Attach"))
.WithArgumentList(ArgumentList(SeparatedList(
[
Argument(CastExpression(IdentifierName("nint"), IdentifierName("origin"))),
Argument(CastExpression(IdentifierName("nint"), IdentifierName("handler"))),
Argument(DeclarationExpression(IdentifierName("nint"), SingleVariableDesignation(Identifier("trampoline1"))))
.WithRefKindKeyword(Token(SyntaxKind.OutKeyword))
])))),
ExpressionStatement(AssignmentExpression(
SyntaxKind.SimpleAssignmentExpression,
IdentifierName("trampoline"),
CastExpression(type, IdentifierName("trampoline1"))))
])));
}
}
private record AttachInfo
{
public required string MinimallyQualifiedType { get; init; }
}
}

View File

@@ -0,0 +1,21 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netstandard2.0</TargetFramework>
<LangVersion>preview</LangVersion>
<EnforceExtendedAnalyzerRules>true</EnforceExtendedAnalyzerRules>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.CodeAnalysis.Analyzers" Version="3.11.0">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="Microsoft.CodeAnalysis.CSharp" Version="4.14.0" />
<PackageReference Include="PolySharp" Version="1.15.0">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
</ItemGroup>
</Project>

View File

@@ -1,4 +1,5 @@
<Solution>
<Project Path="YaeAchievementLib\YaeAchievementLib.csproj" Type="Classic C#" />
<Project Path="YaeAchievement\YaeAchievement.csproj" Type="Classic C#" />
<Solution>
<Project Path="YaeAchievement.SourceGeneration/YaeAchievement.SourceGeneration/YaeAchievement.SourceGeneration.csproj" Id="3c1c5723-c1e4-4119-9ad5-de5a415c5980" />
<Project Path="YaeAchievementLib\YaeAchievementLib.csproj" />
<Project Path="YaeAchievement\YaeAchievement.csproj" />
</Solution>

View File

@@ -25,20 +25,20 @@
</ItemGroup>
<ItemGroup>
<DirectPInvoke Include="NTDLL"/>
<DirectPInvoke Include="USER32"/>
<DirectPInvoke Include="KERNEL32"/>
<DirectPInvoke Include="libMinHook.x64"/>
<NativeLibrary Include="lib\libMinHook.x64.lib"/>
<DirectPInvoke Include="NTDLL" />
<DirectPInvoke Include="USER32" />
<DirectPInvoke Include="KERNEL32" />
<DirectPInvoke Include="libMinHook.x64" />
<NativeLibrary Include="lib\libMinHook.x64.lib" />
</ItemGroup>
<ItemGroup>
<AssemblyAttribute Include="System.Runtime.CompilerServices.DisableRuntimeMarshallingAttribute"/>
<AssemblyAttribute Include="System.Runtime.CompilerServices.DisableRuntimeMarshallingAttribute" />
</ItemGroup>
<ItemGroup>
<Using Include="System.Diagnostics"/>
<Using Include="System.Diagnostics.CodeAnalysis"/>
<Using Include="System.Diagnostics" />
<Using Include="System.Diagnostics.CodeAnalysis" />
</ItemGroup>
<PropertyGroup>
@@ -54,11 +54,18 @@
</PropertyGroup>
<ItemGroup>
<None Include="$(PublishDir)\$(TargetName)$(NativeBinaryExt)" Pack="true" PackagePath="runtimes\win-x64\native" Visible="false"/>
<None Include="$(PublishDir)\$(TargetName)$(NativeBinaryExt)" Pack="true" PackagePath="runtimes\win-x64\native" Visible="false" />
</ItemGroup>
<ItemGroup>
<ProjectReference OutputItemType="Analyzer" Include="..\YaeAchievement.SourceGeneration\YaeAchievement.SourceGeneration\YaeAchievement.SourceGeneration.csproj">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</ProjectReference>
</ItemGroup>
<Target Name="GenerateNuGetPackage" AfterTargets="CopyNativeBinary">
<Exec Command="dotnet pack --no-build --nologo" UseUtf8Encoding="Always" EchoOff="true"/>
<Exec Command="dotnet pack --no-build --nologo" UseUtf8Encoding="Always" EchoOff="true" />
</Target>
</Project>

View File

@@ -159,30 +159,6 @@ internal static partial class MinHook {
}
}
// todo: auto gen
public static unsafe void Attach(delegate*unmanaged<byte*, int, ushort> origin, delegate*unmanaged<byte*, int, ushort> handler, out delegate*unmanaged<byte*, int, ushort> trampoline) {
Attach((nint) origin, (nint) handler, out var trampoline1);
trampoline = (delegate*unmanaged<byte*, int, ushort>) trampoline1;
}
// todo: auto gen
public static unsafe void Attach(delegate*unmanaged<nint, int, double, double, int, void> origin, delegate*unmanaged<nint, int, double, double, int, void> handler, out delegate*unmanaged<nint, int, double, double, int, void> trampoline) {
Attach((nint) origin, (nint) handler, out var trampoline1);
trampoline = (delegate*unmanaged<nint, int, double, double, int, void>) trampoline1;
}
// todo: auto gen
public static unsafe void Attach(delegate*unmanaged<nint, void> origin, delegate*unmanaged<nint, void> handler, out delegate*unmanaged<nint, void> trampoline) {
Attach((nint) origin, (nint) handler, out var trampoline1);
trampoline = (delegate*unmanaged<nint, void>) trampoline1;
}
// todo: auto gen
public static unsafe void Attach(delegate*unmanaged<int, void*, int, int> origin, delegate*unmanaged<int, void*, int, int> handler, out delegate*unmanaged<int, void*, int, int> trampoline) {
Attach((nint) origin, (nint) handler, out var trampoline1);
trampoline = (delegate*unmanaged<int, void*, int, int>) trampoline1;
}
public static void Attach(nint origin, nint handler, out nint trampoline) {
uint result;
if ((result = MinHookCreate(origin, handler, out trampoline)) != 0) {