• 使用.NE平台调用服务访问非托管 DLL 中的函数


    使用.NET平台调用服务访问非托管 DLL 中的函数

    概述:描述在Microsoft.Net框架下使用Win32API的方法。

    关键字:平台调用,InvokeDllImport

    一.背景

    最近公司在做一个面向电信小灵通用户的SP信息服务软件,其中需要和省电信公司的短信网关通信,对方提供了一个开发包SmGwAPI.DLLwin32动态链接库)和一些文档,由于实在不想使用VCDEPHI来开发该项目,所以最终决定使用dotNET平台调用技术来使用该API

    二.准备知识

    先了解一下Win32动态链接库在VC里是如何定义的:

    extern "C"  __declspec(dllexport)  int Validate(const char *);

           extern "C" 表示生成的DLL中的函数名称与定义时一致,否则就会生成形如

    ?MyMessage@@YAHPBDZ@Z 类似的函数名称。

    导入上面的函数:

    using System.Runtime.InteropServices;

    [DllImport("Register.dll")]

    public extern static int Validate(string str1);

    导入系统函数:

    using System.Runtime.InteropServices;

    [DllImport("user32.dll")]

    public static extern int MessageBox(int hWnd, String text, String caption, uint type);

    三.平台调用

    1.      DllImport

    先看一个示例:

    [DllImport("KERNEL32.DLL", EntryPoint="MoveFileW", SetLastError=true,

    CharSet=CharSet.Unicode, ExactSpelling=true,

    CallingConvention=CallingConvention.StdCall)]

    public static extern bool MoveFile(String src, String dst);

    EntryPoint:指定函数入口,可以用来重新命名函数。(DumpBin.EXE

    [DllImport("Register.dll" EntryPoint="?MyMessage@@YAHPBDZ@Z ")]

    public extern static int MyMessage (string str1);

    ExactSpellingCharSet:名称匹配

    DllImportAttribute.ExactSpelling 字段为 true 时(Visual Basic .NET 中默认值为true),平台调用将只搜索您指定的名称。例如,如果指定 MessageBox,则平台调用将搜索 MessageBox,如果它找不到完全相同的拼写则失败。

           ExactSpelling 字段为 false(它是 C++ 托管扩展和 C# 中的默认值),平台调用将首先搜索指定的别名 (MessageBox),如果没有找到未处理的别名,则将搜索已处理的名称。当CharSet=CharSet.Ansi时搜索MessageBoxA,找不到则失败;当CharSet= CharSet.Unicode时搜索MessageBoxW,找不到则失败;当CharSet=CharSet.Auto 平台调用在运行时根据目标平台在 ANSI Unicode 格式之间进行选择。

    2.      参数传递

    先看原函数的定义(已简化)

    int __stdcall SMGPSendSingle(

    const int nNeedReply,

            const char *sServiceID,

    char *sMsgID,

            int *nErrorCode);

    1

        [DllImport("SmGwAPI.Dll")]

        public extern static int SMGPSendSingle(

               int  nNeedReply,

               string sServiceID,

               StringBuilder sMsgID,

               ref int nErrorCode   );

        (2)

    [DllImport("SmGwAPI.Dll")]

        public extern static int SMGPSendSingle(

               int  nNeedReply,

               string sServiceID,

               [MarshalAs(UnmanagedType.LPArray)] byte[]  sMsgID,

               ref int nErrorCode   );

    如果sMsgID返回的内容是ASCII码或UNICODE码的字符串,那么使用以上两种定义均可获得正确结果,如果sMsgID返回的内容是二进制码、BCD码等,就必须使用第二种定义方式,才能正确获得返回信息。

    3.      结构

    原定义(已简化

    typedef struct

    {

    unsigned int nIsReport;

    char       sMsgID[10+1];

    char       sMsgContent[252+1];

    }DeliverResp;

    SMGPAPI_EXPORTS SMGPDeliver(const int nTimeoutIn, DeliverResp *pDeliverResp);

    封装处理:

        [StructLayout(LayoutKind.Sequential,CharSet=CharSet.Ansi)]

        public struct DeliverResp

        {

               public uint nIsReport;         

               [MarshalAs(UnmanagedType.ByValTStr,SizeConst=11)]

               public string sMsgID;          

               [MarshalAs(UnmanagedType.ByValTStr,SizeConst=253)]

               public string sMsgContent;        

        }

                 

    [DllImport("SmGwAPI.Dll")]

    public extern static int SMGPDeliver(int nTimeoutIn, ref DeliverResp pDeliverResp);            

    如果sMsgID返回的内容是ASCII码或UNICODE码的字符串,那么使用上面定义可获得正确结果,如果sMsgID返回的内容是二进制码、BCD码等,就必须使用下面的定义方式,才能正确获得返回信息。

    [StructLayout(LayoutKind.Sequential,CharSet=CharSet.Ansi)]

        public struct DeliverResp

        {

               public uint nIsReport;

               [MarshalAs(UnmanagedType.ByValArray,SizeConst=11 )]

           public byte[] sMsgID;

               [MarshalAs(UnmanagedType.ByValTStr,SizeConst=253)]

               public string sMsgContent;

        }

    [DllImport("SmGwAPI.Dll")]

    public extern static int SMGPDeliver(int nTimeoutIn, ref DeliverResp pDeliverResp);     

    4.    参数对应表

     

    C/C++

    C#

    Int, Long

    int

    Int *

    Ref int

    LPCSTR, const char *

    [MarshalAs(UnmanagedType.LPSTR)]

    string

    LPCTSTR, const TCHAR *

    [MarshalAs(UnmanagedType.LPTSTR)]

    string

    LPSTR, char *

    [MarshalAs(UnmanagedType.LPSTR)]

    stringBuilder

    LPTSTR, TCHAR *

    [MarshalAs(UnmanagedType.LPTSTR)]

    stringBuilder

    Byte [n] str

    [MarshalAs(UnmanagedType.LPArray)]

     byte[]str

    WORD

    uInt16

    Byte, unsigned char

    byte

    Short

    Int16

    float

    single

    double

    double

    DWORD, unsigned long, Ulong

    [MarshalAs(UnmanagedType.U4)]

    UInt32

    bool

    bool

    HANDLE, LPDWORD, LPVOID, void*

    IntPtr

    NULL pointer

    IntPtr.Zero

  • 相关阅读:
    C#学习笔记之——一些应用
    C#学习笔记之——面向对象编程
    C#学习笔记之——一些练习(包含了一些out的使用,string的使用,StringBuilder的使用,类的属性,最大公约数的求法,还有英雄,武器类的设置)
    C#学习笔记之——数据类型,引用参数,输出参数,数组参数,命名参数,可选参数
    C#学习笔记之——类、对象
    离散实践1
    计算机书籍
    2013年12月大学英语六级作文预测:挑战与改变
    TCP协议详解
    Uip学习简介及网址
  • 原文地址:https://www.cnblogs.com/seabluescn/p/917390.html
Copyright © 2020-2023  润新知