Dll深入与DllImport

8 篇文章 0 订阅

MSDN官网资料:

.dll
.lib

库包括两种:
 动态链接库
:.lib文件是不包含程序的相关代码,只需要.dll文件
 静态链接库:(不需要.dll文件的),只需要.lib文件(既包含符号、函数名等相关信息,又包含程序的相关代码)

COM : 是一种跨应用和语言共享二进制代码的方法,可以是DLL或exe;  Windows使用DLLs在二进制级共享代码, 

    COM的限制不支持Genrics(泛型)、方法重载 和 带参数的构造函数;
 所以一般的应用或库需要结合ComVisible这个Attribute来使用( 默认缺省的是[ComVisible(true)] );
 ComVisible特性应用于程序集、接口、类、结构、委托、枚举、字段、方法或属性。

DLL文件: 是一种PE格式的文件.
动态链接库dll的作用:程序的模块化,方便升级,只需要替换dll文件即可,不需要把整个程序都替换一遍。

extern "C" : 由于C++支持函数重载,所以C++编译器会对函数名修改的;所以强制使用C语言的方式导出此函数,这样函数名就不会被改变。
_declspec(dllexport) :导出dll需要用到此关键字来导出

导出:


Dll(动态链接库)的调用:DLL的调用分为 load-time and run-time dynamic linking
 1.静态调用: 需要用到.dll和.lib文件; 程序一启动时就加载dll文件
  extern "C" _declspec(dllimport) int add(int a, int b);

  引用lib文件:有两种方式
      //2.1
        //#pragma comment(lib, "Dll.lib")
     //2.2 项目设置: VC++目录->库目录, 编辑并添加刚才lib的目录
               // 链接器->附加依赖项, 把Dll.lib这个名字添加进去

  int main()
  {
     std::cout<< add(2,4);
     return 0;
  }
 

 

 2.动态调用:run-time dynamic linking,需要用到的时候才加载; 只需要.dll文件(不需要.lib文件) ; 需要使用到windows提供的API,步骤:
    //1.LoadLibrary
    //2.GetProcAddress
    //3.FreeeLibrary

      #include <Windows.h>
    HINSTANCE hDLL = LoadLibrary(L"DLL.dll");
    typedef int(*p_add)(int a, int b);
    p_add addFunction = (p_add)GetProcAddress(hDLL, "add");
    std::cout << addFunction(2,4);
      BOOL fFreeResult = FreeLibrary(hDLL);
      //样例详见

     根据MSDN LoadLibrary的文档, 程序中需要使用DLL时搜索路径的先后顺序 :

  1. The directory from which the application loaded.
  2. The current directory
  3. The system directory. Use the GetSystemDirectory() function to get the path of this directory.
  4. The 16-bit system directory.
  5. The Windows directory. Use the GetWindowsDirectory() function to get the path of this directory.
  6. The directories that are listed in the PATH environment variable.

   //当然实际上不同的windows系统,有的目录会不一样的.

上面提到的都是C++上的操作.

3. C#中的DllImport是静态调用;  需要使用动态调用,直接使用上面C++的动态调用的方法(已在kernel32.dll里包含了),套一层wrapper,给C#这边P/Invoke;

 

4. __cdecl(默认的,C), __stdcall(C++), __fastcall, __thiscall

   规定了dll文件的调用方式 ,入栈方式,清理方式等,https://blog.csdn.net/hanchengxi/article/details/8491650

5.Marshalling native type to .Net type:

  Managed类型中分为两大类:simple type和compound type:
    simple type又分3类: numeric, textual and handles.
       数字类型:System.Byte, System.Int32, System.Double等  
       文本类型:System.Char  //注: System.String是复合类型,因为它实际上是字符数组
       句柄类型:System.IntPtr
    compound type有:structure, class, string

 Blittable type 和 non-blittable type
   Blittable Type : 大部分简单类型在数据封送时不需要处理,因为在非托管中有对应的副本(对应的类型),这些类型叫Blittable类型,因为它                             们在托管和非托管之间传参时不需要转换。
   Non-blittable type : 需要特殊处理,且它是由其它类型组合起来的。

  简单类型中是Blittable的有:
     System.Byte 、System.SByte 、System.Int16 、 System.UInt16 、System.Int32 、System.UInt32 、System.Int64 、
    System.UInt64 、System.IntPtr 、System.UIntPtr 、System.Single 、System.Double
  复合类型满足以下条件之一的,也是blittable:
     blittable类型的一维数组
     Formatted value types that contain only blittable types (and classes if they are marshalled as formatted types).
  剩下的其它类型就是non-blittable类型,需要做特殊处理

结构体:
    一般原生和托管的结构体在内存中的表示是不一样的,所以memory layout需要考虑。
     windows(原生)使用地址来访问结构体的成员,而CLR使用名字来访问结构体的成员。
     且托管中的结构体出于性能考虑可reorder,而非托管中的结构体是按照声明时的顺序连续存放的。
 托管中通过StructLayoutAttribute声明,有两类:LayoutKind.Sequential 和 LayoutKind.Explicit;
 第一类是与非托管的是对应的,第二类用于用户自定义的内存布局,且需要在每一个成员上使用FieldOffsetAttribute,来标注偏移结构体的开始位置多少个字节 :
   [StructLayout(LayoutKind.Sequential)]
   public struct SystemTime
   {
     public short wYear;
     public short wMonth;
  }

  [StructLayout(LayoutKind.Explicit)]
  public struct SystemTime
  {
    [FieldOffset(0)]
   public short wYear;
   [FieldOffset(2)]
   public short wMonth;
 }

封送处理:

 MarshalAs:指示如何在托管代码和非托管代码之间封送数据
    //UnmanagedType:指定如何将参数或字段封送到非托管内存块
    [MarshalAs(UnmanagedType.ByValArray, SizeConst = define.MAX_LENGTH_OF_IDENTICARDID)]
    public byte[] identicardid; 
   

/*********scriptlet  1************/

extern "C"
{    
 EXPORT_API const char*  PrintHello(){
    return "Hello";
 }

 EXPORT_API int PrintANumber(){
    return 5;
 }
}    

//Lets make our calls from the Plugin
 [DllImport("ASimplePlugin", CallingConvention = CallingConvention.Cdecl)]
  private static extern int PrintANumber();

 [DllImport("ASimplePlugin", CallingConvention = CallingConvention.Cdecl)]
  private static extern IntPtr PrintHello();

 void Start()
 {
    Debug.Log(PrintANumber());
    Debug.Log(Marshal.PtrToStringAnsi(PrintHello()));
 }

/*********scriptlet  2************/

     float[] _managed_data  =... // this is the c# managed data
    GCHandle unmanaged_data_handle = GCHandle.Alloc(_managed_data, GCHandleType.Pinned); //这里将标记_managed_data暂时不能被gc回收,并且固定对象的地址
    func(unmanaged_data_handle.AddrOfPinnedObject(),_managed_data.Length);//这里将拿到非托管内存的固定地址,传给c++
    unmanaged_data_handle.Free();//使用完毕后,将其handle free,这样c#可以正常gc这块内存

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值