gpt4 book ai didi

c# - 重写 IL 以在方法调用周围注入(inject) try-finally

转载 作者:行者123 更新时间:2023-11-30 20:39:40 25 4
gpt4 key购买 nike

我想将 sql 日志注入(inject)到几个方法中。基本上我想改造

    public static object IDbCommandTest_ExecuteScalar(IDbCommand command)
{
// .. do stuff
command.CommandText = "SELECT ...";
var obj = command.ExecuteScalar();
Console.WriteLine("SQL returned " + obj);
// do other stuff
return obj;
}

进入

    public static object IDbCommandTest_ExecuteScalar_Transformed(IDbCommand command)
{
// .. do stuff
command.CommandText = "SELECT ...";
object obj;
using (SqlLogger.Enter(command, "IDbCommand", "ExecuteScalar"))
{
obj = command.ExecuteScalar();
}
Console.WriteLine("SQL returned " + obj);
// do other stuff
return obj;
}

我得到了简单的案例,我目前面临的问题是在输入 .try 时堆栈必须为空。

我最初的假设是 IDbCommand 本身在调用 ExecuteScalar 之前直接加载,所以我搜索 callvirt 然后使用 Instruction.Previous 作为 .try 的开始。

但是如果之后使用ExecuteScalar的返回值,编译器生成如下IL:

    IL_004d: ldarg.0
IL_004e: ldloc.1
IL_004f: callvirt instance object [System.Data]System.Data.IDbCommand::ExecuteScalar()
IL_0054: call string Class_which_uses_obj::DoStuff(object)

用我的原始算法,这被转化为

    IL_0050: ldarg.0
.try
{
IL_0051: ldloc.1
IL_0052: dup
IL_0053: ldstr "IDbCommand"
IL_0058: ldstr "get_FileName"
IL_005d: call class [mscorlib]System.IDisposable SqlLogger::Enter(class [System.Data]System.Data.IDbCommand, string, string)
IL_0062: stloc.3
IL_0063: callvirt instance object [System.Data]System.Data.IDbCommand::ExecuteScalar()
IL_0068: stloc.s 4
IL_006a: leave.s IL_0077
} // end .try
finally
{
IL_006c: nop
IL_006d: ldloc.3
IL_006e: brfalse.s IL_0076

IL_0070: ldloc.3
IL_0071: callvirt instance void [mscorlib]System.IDisposable::Dispose()

IL_0076: endfinally
} // end handler

IL_0077: nop
IL_0078: ldloc.s 4
IL_007a: call string IL_0050: ldarg.0
.try
{
IL_0051: ldloc.1
IL_0052: dup
IL_0053: ldstr "IDbCommand"
IL_0058: ldstr "ExecuteScalar"
IL_005d: call class [mscorlib]System.IDisposable Nemetschek.Allready.SqlLogger::Enter(class [System.Data]System.Data.IDbCommand, string, string)
IL_0062: stloc.3
IL_0063: callvirt instance object [System.Data]System.Data.IDbCommand::ExecuteScalar()
IL_0068: stloc.s 4
IL_006a: leave.s IL_0077
} // end .try
finally
{
IL_006c: nop
IL_006d: ldloc.3
IL_006e: brfalse.s IL_0076

IL_0070: ldloc.3
IL_0071: callvirt instance void [mscorlib]System.IDisposable::Dispose()

IL_0076: endfinally
} // end handler

IL_0077: nop
IL_0078: ldloc.s 4
IL_007a: call string Nemetschek.Allready.Logistics.DbTools.CDbTools::GetSafeStringEmpty(object)(object)

然后 PEVERIFY 提示我输入了一个非空堆栈的 .try。

是否有一种简单的方法可以通过 ExecuteScalar 注入(inject) try-finally,或者我是否需要对整个方法进行完整的流程分析,计算任意点的堆栈深度,然后加载/恢复之前/之后的值尝试/终于?

编辑:

我已经能够通过向上/向下扫描来让它工作,直到找到堆栈深度为 0 的两个点。在我有限的测试中,这似乎可行,但我仍然对“干净”感兴趣实现而不是盲目地扫描 IL。

最佳答案

我会将对 ExecuteScalar 的调用重写为对辅助方法的调用:

static object ExecuteScalarWrapper(SqlCommand command, string logString) {
using (SqlLogger.Enter(command, logString))
{
return command.ExecuteScalar();
}
}

ExecuteScalarWrapper 将是一个静态辅助方法,您可以用 C# 编写并引用它。

然后,您不需要注入(inject)任何 try block 。你只需要更换图案

ld... command
call ExecuteScalar

ld... command
ld... logString
call ExecuteScalarWrapper

这应该更容易,因为堆栈布局和修改是本地定义的,不需要任何复杂的推理。

现在 JIT 会为您完成所有繁重的工作。

关于c# - 重写 IL 以在方法调用周围注入(inject) try-finally,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/34134092/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com