zoukankan      html  css  js  c++  java
  • .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&)

    stloc.0

    ldloc.0

    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

     

  • 相关阅读:
    vi/vim 跳转到指定行
    svn如何过滤常见的bin/obj/package/log等文件夹的文件
    WIN10:你不能访问此共享文件夹,解决方法
    WinServer2019的IIS上无法安装framework3.5的问题
    浏览器播放实时视频,通过rtsp码流播放
    easyui comboxtree 如何获得选中值的其它属性
    iphone上传拍照图片时图片会旋转90度,从相册选择就不会,安卓手机也不会,怎么解决?
    如何快速清空微信浏览器中的缓存
    js种 new Date(str)的时候,在google下正常,ie11下异常的解决办法
    layer第二个按钮点击后关闭的解决方法
  • 原文地址:https://www.cnblogs.com/prowyh/p/2802275.html
Copyright © 2011-2022 走看看