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

Is it possible to target DllImport methods? #268

Closed
macsux opened this issue Mar 9, 2020 · 8 comments
Closed

Is it possible to target DllImport methods? #268

macsux opened this issue Mar 9, 2020 · 8 comments

Comments

@macsux
Copy link

macsux commented Mar 9, 2020

Is your feature request related to a problem? Please describe.
I'm trying to replace one of the methods declared as DllImport. Wonder if this is possible? The regular hook says to use Transpiler when running, but even when applying transpiler same message comes up.

Describe the solution you'd like
Targeting and replacing the functionality of a method that is defined externally (DllImport)

Additional info:
This is the method I'm trying to replace with my own implementation

    [DllImport("QCall", CharSet = CharSet.Unicode)]
    private static extern TimerQueue.AppDomainTimerSafeHandle CreateAppDomainTimer(
      uint dueTime,
      int id);
@pardeike
Copy link
Owner

pardeike commented Mar 9, 2020

External methods might have different calling conventions than normal methods. This isn’t an exact science and depends a lot on your runtime. The underlying mechanics are undocumented so you need to test and hope it works. I got preliminary support for external methods as a pull request from someone and assumed it might work good enough but I am not surprised to see that it does not work.

To be clear: the only way this would ever work is to use a transpiler and ignore the input in it and let it create the full replacement method.

@macsux
Copy link
Author

macsux commented Mar 9, 2020

I'm trying this on windows atm, but ideally, I want it working on mac & Linux as well. Usually, I use CoreHook or EasyHook which allows me to intercept native calls, but in this case, the call is actually to a function implemented as unmanaged code inside CLR itself (QCall). I'm completely fine with doing this via transpiler, but I think there's a validation happening that prevents this from even being invoked. It's throwing here:

throw new Exception("Methods without body cannot have prefixes. Use a transpiler instead.");

My current test to see if this will even hook looks like this:

public static async Task Main(string[] args)
{
    var harmony = new Harmony("main");
    harmony.PatchAll();
    await Task.Delay(100);
}

[HarmonyPatch]
class Patch
{
    static MethodBase TargetMethod() => AccessTools.Method(Type.GetType("System.Threading.TimerQueue"),"CreateAppDomainTimer", new []{typeof(uint),typeof(int)});
    [HarmonyTranspiler]
    static  IEnumerable<CodeInstruction> Transpiler(uint dueTime, int id, ref object __result)
    {
        Console.WriteLine("Test");
        return Enumerable.Empty<CodeInstruction>();
    }
}

@pardeike On a completely unrelated note, I sent you an email on Jan 29 titled "Harmony usages". My company may be able to offer support for your project as we're using it heavily, so let me know what kind of support you could use.

@pardeike
Copy link
Owner

pardeike commented Mar 9, 2020

I think the oversight in this case is the missing check for an empty prefixes list (in which the exception shouldn’t be thrown).

Can you verify if that solves the problem?

@pardeike pardeike added the bug label Mar 9, 2020
@macsux
Copy link
Author

macsux commented Mar 9, 2020

Ok, I went back to commit d3c7a06 and checked that it does in fact call my transpiler as expected. Something in that code changed since then that made it stop working.

@pardeike
Copy link
Owner

pardeike commented Mar 9, 2020

I’ll fix this in the next update. Hopefully by the end of this week.

@ayaka209
Copy link

ayaka209 commented Sep 6, 2024

Harmony 2.3 breaks dllimport support, while 2.2 runs well.

@pardeike
Copy link
Owner

pardeike commented Sep 6, 2024

Harmony 2.3 breaks dllimport support, while 2.2 runs well.

And what does that mean in detail?

@ayaka209
Copy link

ayaka209 commented Sep 6, 2024

The following code runs normally in version 2.2, with no issues and getting the result 42, while in version 2.3.3 it prompts:

Error applying patch: HarmonyLib.HarmonyException: Patching exception in method static System.UInt32 Program::GetTickCount() ---> System.InvalidOperationException: Could not get entry point normally, GetFunctionPointer() = 00000000036a082c
at HarmonyLib.PatchFunctions.UpdateWrapper(MethodBase original, PatchInfo patchInfo) in C:\Users\Brrainz\Source\Repos\Harmony\Harmony\Internal\PatchFunctions.cs:line 42
at HarmonyLib.PatchClassProcessor.ProcessPatchJob(Job job) in C:\Users\Brrainz\Source\Repos\Harmony\Harmony\Public\PatchClassProcessor.cs:line 217
--- End of inner exception stack trace ---
at HarmonyLib.PatchClassProcessor.ReportException(Exception exception, MethodBase original) in C:\Users\Brrainz\Source\Repos\Harmony\Harmony\Public\PatchClassProcessor.cs:line 313
at HarmonyLib.PatchClassProcessor.Patch() in C:\Users\Brrainz\Source\Repos\Harmony\Harmony\Public\PatchClassProcessor.cs:line 107
at HarmonyLib.Harmony.b__10_0(Type type) in C:\Users\Brrainz\Source\Repos\Harmony\Harmony\Public\Harmony.cs:line 105
at HarmonyLib.CollectionExtensions.Do[T](IEnumerable1 sequence, Action1 action) in C:\Users\Brrainz\Source\Repos\Harmony\Harmony\Tools\Extensions.cs:line 657
at HarmonyLib.Harmony.PatchAll(Assembly assembly) in C:\Users\Brrainz\Source\Repos\Harmony\Harmony\Public\Harmony.cs:line 105
at HarmonyLib.Harmony.PatchAll() in C:\Users\Brrainz\Source\Repos\Harmony\Harmony\Public\Harmony.cs:line 80
at Program.Main(String[] args) in C:\dev\helloworld\HarmoneyTest\Program.cs:line 40

class Program
{
    [DllImport("kernel32.dll")]
    public static extern uint GetTickCount();
static void Main(string[] args)
    {
        var harmony = new Harmony("com.example.patch");
       harmony.PatchAll();
       var ticks = GetTickCount();
       Console.WriteLine($"Patched GetTickCount result: {ticks}");
    }
}
[HarmonyPatch(typeof(Program), "GetTickCount")]
class GetTickCountPatch
{
    static IEnumerable<CodeInstruction> Transpiler(IEnumerable<CodeInstruction> instructions)
    {
        yield return new CodeInstruction(OpCodes.Ldc_I4, 42);
        yield return new CodeInstruction(OpCodes.Ret);
    }
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

3 participants