惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

S
Security @ Cisco Blogs
Scott Helme
Scott Helme
Threat Intelligence Blog | Flashpoint
Threat Intelligence Blog | Flashpoint
T
Threat Research - Cisco Blogs
AWS News Blog
AWS News Blog
Spread Privacy
Spread Privacy
D
Darknet – Hacking Tools, Hacker News & Cyber Security
Security Latest
Security Latest
Simon Willison's Weblog
Simon Willison's Weblog
C
Cybersecurity and Infrastructure Security Agency CISA
G
GRAHAM CLULEY
cs.CL updates on arXiv.org
cs.CL updates on arXiv.org
I
Intezer
S
Securelist
Google DeepMind News
Google DeepMind News
S
Schneier on Security
T
Troy Hunt's Blog
Help Net Security
Help Net Security
Microsoft Azure Blog
Microsoft Azure Blog
V
V2EX
Security Archives - TechRepublic
Security Archives - TechRepublic
O
OpenAI News
博客园 - Franky
G
Google Developers Blog
Stack Overflow Blog
Stack Overflow Blog
TaoSecurity Blog
TaoSecurity Blog
MyScale Blog
MyScale Blog
P
Privacy International News Feed
K
KPMG report finds enterprise disconnect between AI and its ROI | CIO
Google Online Security Blog
Google Online Security Blog
Latest news
Latest news
Vercel News
Vercel News
T
The Blog of Author Tim Ferriss
博客园_首页
S
Security Affairs
PCI Perspectives
PCI Perspectives
WordPress大学
WordPress大学
C
Cisco Blogs
Recent Announcements
Recent Announcements
L
LangChain Blog
GbyAI
GbyAI
F
Fortinet All Blogs
N
News and Events Feed by Topic
T
Tor Project blog
IT之家
IT之家
P
Palo Alto Networks Blog
D
DataBreaches.Net
小众软件
小众软件
宝玉的分享
宝玉的分享
F
Full Disclosure

博客园 - 同洲共际海让路

国内首本全面深入介绍.NET互操作技术(P/Invoke『或PInvoke,平台调用』,C++ Interop『C++/CLI』,COM Interop)的参考书籍 博客园开发者征途系列图书——《你必须知道的.NET》 一站式查询八字、五行、星座、五格——美名腾人名解析 一句话让别人立马记住你的名字——美名腾智能人名解析 何谓智能起名? 美名腾智能起名系统有了新界面 千名万名,到美名腾来试金。【写在美名腾起名系统Beta版成功发布】 [原]Bug Report,已提交给微软。平台调用调试时,无法对非托管函数进行单步的错误 [原]动态平台调用 part 3 (Final) [原]动态平台调用 part 1 JavaScript Debugging and Intellisense, JavaScript obfuscator etc from VS 2005 to VS 2008 (VS2005到2008的变迁,JavaScript调试,智能感知/智能感应功能,JavaScript的混淆、压缩等功能的海变桑田) 终于见到了传说中的Scott Guthrie 如何为Team Foundation Server的Daily Build添加自定义操作 客户端安装Team Explorer 2005后,使用时出现package load failure错误的修正 WCF Part 7 : Bindings WCF Part 6 : Address 推荐一个好玩的好东西,ILMerge,微软出的,合并多个.NET组件成一个 WCF Part 5 : Consuming the service [译]WCF Part 4 : Make your service visible through metadata
[原]动态平台调用 part 2
同洲共际海让路 · 2008-07-21 · via 博客园 - 同洲共际海让路

 在前一篇Blog,动态平台调用 part 1 中,介绍了一种进行动态平台调用的方法。

 除了这种方法之外,还能利用.NET提供的强大的反射(Reflection)功能,来达到动态平台调用的目的。System.Reflection.Emit命名空间下的类和方法提供了强大的运行时创建和执行代码的能力,利用这些类和方法进行动态平台调用,主要步骤如下:

1.         添加如下两个命名空间:

using System.Reflection.Emit;

using System.Reflection;

2.         使用DefineDynamicAssembly创建一个动态程序集(assembly)对象;

3.         使用DefineDynamicModule为第二步创建的程序集定义一个动态模块(module);

4.         使用DefinePInvokeMethod为第三步创建的模块定义一个用于访问非托管函数的P/Invoke方法;

5.         调用CreateGlobalFunctions函数完成动态模块的全局函数定义和全局数据定义;

6.         调用GetMethod函数获得之前定义的用于进行平台调用的方法;

7.         调用上一步获得的方法完成对非托管函数的平台调用。

下面的代码演示了如何采用上述方法对非托管函数进行动态平台调用。

class DynamicPInvokeViaEmit

{

    public static void Test()

    {

        string currentDirectory =

            Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);

        string dllPath = Path.Combine(currentDirectory,

            @"nativelibfordynamicpinvoke\NativeLibForDynamicPInvoke.dll");

        //定义参数和返回值的类型

        Type[] parameterTypes = new Type[] { typeof(int), typeof(int) };

        int factorA = 100, factorB = 8;

        Type returnType = typeof(int);

        //设置参数的值

        object[] parameterValues = { factorA, factorB };

        // 设置函数入口点,必须是经过名称重整后的函数名

        // 原因在于非托管DLL使用的调用约定是__stdcall

        // 如果使用Multiply,将会抛出异常

        string entryPoint = "_Multiply@8";

        //设置DllImport的字段

        CallingConvention nativeCallConv = CallingConvention.StdCall;

        CharSet nativeCharSet = CharSet.Ansi;

        //设置方法的修饰符,至少需要设置staticextern

        MethodAttributes methodAttr =

            MethodAttributes.Static

            | MethodAttributes.Public

            | MethodAttributes.PinvokeImpl;

        //进行动态平台调用

        object result = DynamicDllFunctionInvoke(

            dllPath,

            entryPoint,

            methodAttr,

            nativeCallConv,

            nativeCharSet,

            returnType,

            parameterTypes,

            parameterValues

            );

        //打印结果

        Console.WriteLine(

            string.Format("{0} * {1} = {2} ",

            factorA,

            factorB,

            result)

            );

        Console.WriteLine("Press any key to exit.");

        Console.Read();

    }

    public static object DynamicDllFunctionInvoke(

        string dllPath,

        string entryPoint,

        MethodAttributes methodAttr,

        CallingConvention nativeCallConv,

        CharSet nativeCharSet,

        Type returnType,

        Type[] parameterTypes,

        object[] parameterValues

        )

    {

        string dllName = Path.GetFileNameWithoutExtension(dllPath);

        // 创建一个动态组件(assembly)和模块(module)

        AssemblyName assemblyName = new AssemblyName();

        assemblyName.Name = string.Format("A{0}{1}",

            dllName,

            Guid.NewGuid().ToString("N")

            );

        AssemblyBuilder dynamicAssembly =

          AppDomain.CurrentDomain.DefineDynamicAssembly(

          assemblyName, AssemblyBuilderAccess.Run);

        ModuleBuilder dynamicModule =

          dynamicAssembly.DefineDynamicModule(

          string.Format("M{0}{1}",

          dllName,

          Guid.NewGuid().ToString("N"))

          );

        // 使用指定的信息创建平台调用签名

        MethodBuilder dynamicMethod =

            dynamicModule.DefinePInvokeMethod(

            entryPoint,

            dllPath,

            methodAttr,

            CallingConventions.Standard,

            returnType,

            parameterTypes,

            nativeCallConv,

            nativeCharSet

            );

        // 创建函数

        dynamicModule.CreateGlobalFunctions();

        // 获得平台调用的方法

        MethodInfo methodInfo =

            dynamicModule.GetMethod(entryPoint, parameterTypes);

        // 调用非托管函数并获得返回的结果

        object result = methodInfo.Invoke(null, parameterValues);

        return result;

    }

}

在上面的DynamicPInvokeViaEmit类中,定义了用于对非托管方法进行动态平台调用的函数DynamicDllFunctionInvoke。只要传递定义非托管方法的DLL名称、函数入口点、该方法的属性、该方法的调用约定、该方法的返回类型、该方法的参数类型以及平台调用标志,就能完成对该非托管方法的动态平台调用。

DynamicPInvokeViaEmit类的Test方法中,首先对非托管函数的参数和返回值的类型进行了定义,并设置了参数的值。接着设置了平台调用所需的必要信息,比如函数入口点、调用约定,字符集等。这些信息都是为了对非托管函数进行平台调用所需进行的必要准备工作。最后,将这些信息传递给DynamicDllFunctionInvoke方法,就能对非托管函数进行动态平台调用了。

需要特别注意的是函数入口点的名称。由于调用约定能够影响非托管函数被导出的实际名称。由于本示例所采用的非托管DLL在导出函数时,使用了extern "C",并采用了__stdcall这个调用约定,因此最终导出的函数名就会被重整成_Multiply@8。这样就要求在使用DefinePInvokeMethod方法定义用于访问非托管函数的平台调用方法时,必须传递同非托管DLL导出函数时的最终函数名完全相同的名称。因此在指定函数入口点时,就必须采用经过重整后的名称,也就是_Multiply@8。如果使用了Multiply,就会抛出EntryPointNotFoundException异常。这一点同直接采用DllImport属性来声明非托管函数时,无需自己手动指定最终的函数名是完全不同的。这是由于在托管代码中声明非托管函数时,CLR会根据DllImport属性的CallingConvention字段的值,采用相应调用约定所对应的名称重整规则,自动完成函数名的重整工作。并使用经过名称重整后的实际函数名,在非托管DLL中查找对应的非托管函数。

运行Test方法进行测试,同样也能获得完全相同的结果:

100 * 8 = 800

Press any key to exit.