VS2015 C++/CLI 发布版本中的 NullReferenceException
NullReferenceException in VS2015 C++/CLI Release Build
我的发布版本正在 "System.NullReferenceException: Object reference not set to an instance of an object."。我已经创建了一个示例应用程序来模仿我的生产代码中的内容。
void Abc::LogService::Log(String^ message)
{
try
{
int ret = DoProcessing(message);
Exception^ ex;
if (ret == 0)
{
ex = gcnew ArgumentException("Processing done.");
}
else
{
ex = gcnew ArgumentNullException("message", "Null args");
}
throw ex;
}
finally
{
//do someother thing.
}
}
使用上面的代码,它报告的异常行是:
at Abc.LogService.Log(String message) in logservice.cpp:line 19
对应代码中的throw ex;
语句。
此函数的发布版本中的 MSIL 如下所示:
.method public hidebysig instance void Log(string message) cil managed
{
// Code size 46 (0x2e)
.maxstack 4
.locals ([0] class [mscorlib]System.Exception V_0,
[1] class [mscorlib]System.Exception ex)
.try
{
IL_0000: ldarg.0
IL_0001: ldarg.1
IL_0002: call instance int32 Abc.LogService::DoProcessing(string)
IL_0007: ldnull
IL_0008: stloc.1
IL_0009: brtrue.s IL_0018
IL_000b: ldstr "Processing done."
IL_0010: newobj instance void [mscorlib]System.ArgumentException::.ctor(string)
IL_0015: stloc.0
IL_0016: br.s IL_0028
IL_0018: ldstr "message"
IL_001d: ldstr "Null args"
IL_0022: newobj instance void [mscorlib]System.ArgumentNullException::.ctor(string,
string)
IL_0027: stloc.0
IL_0028: ldloc.1
IL_0029: throw
IL_002a: leave.s IL_002d
} // end .try
finally
{
IL_002c: endfinally
} // end handler
IL_002d: ret
} // end of method LogService::Log
从 MSIL 代码可以看出,在语句 IL_0028 处,它加载了一个空值并在后续语句中调用了 throw。
奇怪的是只有当我有 try-finally 块时才会发生这种情况。
上述代码的调试构建工作正常。
这听起来像是 VS2015 v140 工具包中的错误吗?
是的,这是一个优化器错误。非常不寻常,这是我第一次看到 C++/CLI,这是一种抖动应该承担繁重工作的语言。它似乎通过在 try 块中声明 ex
变量而被触发,让它在初始化保证上窒息。看起来像是流分析错误。
除了使用 /Od 进行编译之外,一种解决方法是将变量移出 try 块
void Log(String^ message) {
Exception^ ex;
try {
// etc...
}
同时生成更好的 MSIL,完全消除变量:
.method public hidebysig instance void Log(string message) cil managed
{
// Code size 41 (0x29)
.maxstack 4
.try
{
IL_0000: ldarg.0
IL_0001: ldarg.1
IL_0002: call instance int32 Test::DoProcessing(string)
IL_0007: brtrue.s IL_0015
IL_0009: ldstr "Processing done."
IL_000e: newobj instance void [mscorlib]System.ArgumentException::.ctor(string)
IL_0013: br.s IL_0024
IL_0015: ldstr "message"
IL_001a: ldstr "Null args"
IL_001f: newobj instance void [mscorlib]System.ArgumentNullException::.ctor(string,
string)
IL_0024: throw
IL_0025: leave.s IL_0028
} // end .try
finally
{
IL_0027: endfinally
} // end handler
IL_0028: ret
} // end of method Test::Log
优化器错误很糟糕,您可以在 connect.microsoft.com
报告
我的发布版本正在 "System.NullReferenceException: Object reference not set to an instance of an object."。我已经创建了一个示例应用程序来模仿我的生产代码中的内容。
void Abc::LogService::Log(String^ message)
{
try
{
int ret = DoProcessing(message);
Exception^ ex;
if (ret == 0)
{
ex = gcnew ArgumentException("Processing done.");
}
else
{
ex = gcnew ArgumentNullException("message", "Null args");
}
throw ex;
}
finally
{
//do someother thing.
}
}
使用上面的代码,它报告的异常行是:
at Abc.LogService.Log(String message) in logservice.cpp:line 19
对应代码中的throw ex;
语句。
此函数的发布版本中的 MSIL 如下所示:
.method public hidebysig instance void Log(string message) cil managed
{
// Code size 46 (0x2e)
.maxstack 4
.locals ([0] class [mscorlib]System.Exception V_0,
[1] class [mscorlib]System.Exception ex)
.try
{
IL_0000: ldarg.0
IL_0001: ldarg.1
IL_0002: call instance int32 Abc.LogService::DoProcessing(string)
IL_0007: ldnull
IL_0008: stloc.1
IL_0009: brtrue.s IL_0018
IL_000b: ldstr "Processing done."
IL_0010: newobj instance void [mscorlib]System.ArgumentException::.ctor(string)
IL_0015: stloc.0
IL_0016: br.s IL_0028
IL_0018: ldstr "message"
IL_001d: ldstr "Null args"
IL_0022: newobj instance void [mscorlib]System.ArgumentNullException::.ctor(string,
string)
IL_0027: stloc.0
IL_0028: ldloc.1
IL_0029: throw
IL_002a: leave.s IL_002d
} // end .try
finally
{
IL_002c: endfinally
} // end handler
IL_002d: ret
} // end of method LogService::Log
从 MSIL 代码可以看出,在语句 IL_0028 处,它加载了一个空值并在后续语句中调用了 throw。 奇怪的是只有当我有 try-finally 块时才会发生这种情况。 上述代码的调试构建工作正常。
这听起来像是 VS2015 v140 工具包中的错误吗?
是的,这是一个优化器错误。非常不寻常,这是我第一次看到 C++/CLI,这是一种抖动应该承担繁重工作的语言。它似乎通过在 try 块中声明 ex
变量而被触发,让它在初始化保证上窒息。看起来像是流分析错误。
除了使用 /Od 进行编译之外,一种解决方法是将变量移出 try 块
void Log(String^ message) {
Exception^ ex;
try {
// etc...
}
同时生成更好的 MSIL,完全消除变量:
.method public hidebysig instance void Log(string message) cil managed
{
// Code size 41 (0x29)
.maxstack 4
.try
{
IL_0000: ldarg.0
IL_0001: ldarg.1
IL_0002: call instance int32 Test::DoProcessing(string)
IL_0007: brtrue.s IL_0015
IL_0009: ldstr "Processing done."
IL_000e: newobj instance void [mscorlib]System.ArgumentException::.ctor(string)
IL_0013: br.s IL_0024
IL_0015: ldstr "message"
IL_001a: ldstr "Null args"
IL_001f: newobj instance void [mscorlib]System.ArgumentNullException::.ctor(string,
string)
IL_0024: throw
IL_0025: leave.s IL_0028
} // end .try
finally
{
IL_0027: endfinally
} // end handler
IL_0028: ret
} // end of method Test::Log
优化器错误很糟糕,您可以在 connect.microsoft.com
报告