今天在debug的时候,又出现了空指针,我这次真的火了!每次遇到空指针,.net给出的信息总是非常的少,我根本不知道是哪里Throw出来的,只能反复检查代码。
思路大概是:
获取exception的调用堆栈。
获取exception相关的这个方法的方法的IL代码
结合excpetion的IL偏移量和方法的IL,把调用源找出来。
代码
class Program
{
static void Main(string[] args)
{
try
{
string hello3 = null;
hello3 = hello3.ToUpper();
}
catch (Exception ex)
{
//获取调用堆栈
StackTrace trace = new StackTrace(ex, true);
StackFrame frame = trace.GetFrame(0);
int offset = frame.GetILOffset();
byte[] il = frame.GetMethod().GetMethodBody().GetILAsByteArray();
//获取调用指令
offset++;
ushort instruction = il[offset++];
//打开潘多拉魔盒
ILGlobals global = new ILGlobals();
global.LoadOpCodes();
//翻译
OpCode code = OpCodes.Nop;
if (instruction != 0xfe)
{
code = global.SingleByteOpCodes[(int)instruction];
}
else
{
instruction = il[offset++];
code = global.MultiByteOpCodes[(int)instruction];
instruction = (ushort)(instruction | 0xfe00);
}
//获取方法信息
int metadataToken = ReadInt32(il, ref offset);
MethodBase callmethod = frame.GetMethod().Module.ResolveMethod(metadataToken,
frame.GetMethod().DeclaringType.GetGenericArguments(),
frame.GetMethod().GetGenericArguments());
//完成
Console.WriteLine(callmethod.DeclaringType + "." + callmethod.Name);
Console.Read();
}
}
private static int ReadInt32(byte[] il, ref int position)
{
return (((il[position++] | (il[position++] << 8)) | (il[position++] << 0x10)) | (il[position++] << 0x18));
}
}
public class ILGlobals
{
private OpCode[] multiByteOpCodes;
private OpCode[] singleByteOpCodes;
/// <summary>
/// Loads the OpCodes for later use.
/// </summary>
public void LoadOpCodes()
{
singleByteOpCodes = new OpCode[0x100];
multiByteOpCodes = new OpCode[0x100];
FieldInfo[] infoArray1 = typeof(OpCodes).GetFields();
for (int num1 = 0; num1 < infoArray1.Length; num1++)
{
FieldInfo info1 = infoArray1[num1];
if (info1.FieldType == typeof(OpCode))
{
OpCode code1 = (OpCode)info1.GetValue(null);
ushort num2 = (ushort)code1.Value;
if (num2 < 0x100)
{
singleByteOpCodes[(int)num2] = code1;
}
else
{
if ((num2 & 0xff00) != 0xfe00)
{
throw new Exception("Invalid OpCode.");
}
multiByteOpCodes[num2 & 0xff] = code1;
}
}
}
}
/// <summary>
/// Retrieve the friendly name of a type
/// </summary>
/// <param name="typeName">
/// The complete name to the type
/// </param>
/// <returns>
/// The simplified name of the type (i.e. "int" instead f System.Int32)
/// </returns>
public static string ProcessSpecialTypes(string typeName)
{
string result = typeName;
switch (typeName)
{
case "System.string":
case "System.String":
case "String":
result = "string"; break;
case "System.Int32":
case "Int":
case "Int32":
result = "int"; break;
}
return result;
}
public OpCode[] MultiByteOpCodes
{
get { return multiByteOpCodes; }
}
public OpCode[] SingleByteOpCodes
{
get { return singleByteOpCodes; }
}
}
该文的应用场景估计是:(1)程序在一台机器上已经开发调试OK了;(2)Release编译并部署到另一台机器。在这里出现了NRE的话,.NET FW是不能给你这么多的信息的,只能重新到开发机器进行调试。所以,LZ这种方法还是不错的。
但是,更明智的方法是进行精密的异常管理。