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

     

  • 相关阅读:
    关于找了很长时间但是找不到原因的bug的解决方法
    牛客_剑指offer题集——二叉树中和为某一值的路径(java实现)
    牛客_剑指offer题集——栈的压入弹出序列(java实现)
    牛客_剑指offer题集——顺时针打印算法(java实现)
    第 7 章 Selenium WebDriver 进阶应用 Selenium 3+Python 3 自动化测试
    第 6 章 Selenium 常用方法(二) Selenium 3+Python 3 自动化测试
    第 6 章 Selenium 常用方法(一) Selenium 3+Python 3 自动化测试
    5.2 Selenium 八大定位 Selenium 3+Python 3 自动化测试
    5.1 Python 基础知识 Selenium 3+Python 3 自动化测试
    第 4 章 前端技术简介 Selenium 3+Python 3 自动化测试
  • 原文地址:https://www.cnblogs.com/prowyh/p/2802275.html
Copyright © 2011-2022 走看看