好得很程序员自学网

<tfoot draggable='sEl'></tfoot>

.NET的跨平台调用一例(PreserveSig)

.NET的跨平台调用一例(PreserveSig)

这样一段代码:

.class public auto ansi Test extends [mscorlib]System.Object

{

.method public static pinvokeimpl("msvcrt.dll" cdecl)int32 sscanf(string, string, int32&) cil managed { }

.field public static int32 val

.method public static void Main() cil managed

{

.entrypoint

.locals init (int32 n)

ldstr "Enter a number"

call void [mscorlib]System.Console::WriteLine(string)

call string [mscorlib]System.Console::ReadLine()

ldstr "%d"

ldsflda int32 Test::val

call int32 sscanf(string, string, int32&)

brfalse.s Error

// elide for clarity

}

}

基本功能是从控制台取得用户输入的数字,调用  C  的运行库函数  sscanf  将字符串形式的数字转换为数值,并根据  sscanf  的返回值判断字符串是否合法的数字字符串。

奇怪的是, sscanf  总是返回  0  !

查  MSDN ,关于返回值,说明如下:

intsscanf(const char* buffer, const char* format [, argument]…);

[Thefunction] returns the number of fields successfully converted and assigned; thereturn value does not include fields that were read but not assigned. A returnvalue of 0 indicates that no fields were assigned.

该函数返回成功转换的字段数,返回值不包含已读取但没有赋值的字段。返回  0  表示没有字段被赋值。也就是说,转换不成功!

难道这段  IL  代码有问题?为了验证这个疑问,又写了一小段等价的  C#  代码:

usingSystem;

usingSystem.Runtime.InteropServices;

publicstatic class Test

{

[DllImport("msvcrt.dll", CharSet=CharSet.Ansi,CallingConvention=CallingConvention.Cdecl)]

public static extern int sscanf(string, string, out int nval);

public static void Main()

{

int n = 0;

int val = 0;

Console.WriteLine("Enter a number");

string line = Console.ReadLine();

n = sscanf(line, "%d", out val);

Console.WriteLine("n = " + n + ", val = " +val);

}

}

编译运行, n = 1 ,正确!奇怪!

将生成的  exe  程序反编译为  IL  代码,其中  sscanf  函数的声明如下:

.methodpublic hidebysig static pinvokeimpl("msvcrt.dll" ansi cdecl)

int32 sscanf(string sval,

     string fmt,

     [out] int32& nval) cilmanaged preservesig

{ }

将这个声明与上面的  sscanf  的声明对比一下,发现这里多了一个  preservesig  特性。修改上述  IL  代码的  sscanf  声明,增加  preservesig ,编译运行,正确!

MSDN  对  DllImportAttribute.PreserveSig  字段说明如下:

Indicateswhether unmanaged methods that have HRESULT or retval return values aredirectly translated or whether HRESULT or rettval return values areautomatically converted to exceptions.

Set thePreserveSig field to true to directly translate unmanaged signatures withHRESULT or retval values; set it to false to automatically convert HRESULT orretval values to exceptions. By default, the PreserveSig field is true.

也就是说,对于有返回值(不管是 HRESULT  还是其他返回值)的非托管代码, CLR 对返回值的处理有两种方式:或者直接返回给调用者,或者将  HRESULT  或其他返回值转换为异常抛出(如果返回值不是  S_OK  的话)。到底取何种处理方式,取决于  PreserveSig  的设置。如果  PreserveSig  设置为  true ,则直接返回给调用者(这种方式保留了非托管方法的签名,这也是  Preserve Signature  的本义)。如果  PreserveSig  设置为  false ,则将返回值( HRESULT  或其他返回值)转换为异常抛出(如果返回值不等于  S_OK ),此时,如果没有异常抛出,则调用者也得不到返回值(因为没有返回)!

这就是为什么上述的  IL  代码中的  sscanf  函数总是返回  0  的原因。 C#  中的  DllImportAttribute.PreserveSig  默认为  true ,而在  IL  代码中必须显式指定  preservesig 。

 

分类:  C#语言探讨

作者: Leo_wl

    

出处: http://www.cnblogs.com/Leo_wl/

    

本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。

版权信息

查看更多关于.NET的跨平台调用一例(PreserveSig)的详细内容...

  阅读:42次