C# VC6调用VC6的动态库DLL

C# VC6调用VC6的动态库DLL


一 VC创建动态库


1. DLL的创建
启动VC6.0, 新建一个“Win32 Dynamic-Link Library”工程,选择“A simple dll project”的工程名称为Vc6MakeDll
2.  打开vc_dll.cpp,在其末尾添加如下代码:
 动态链接库导出的一般有两种调用协议,_stdcall和_cdecl,默认是_cdecl
// Vc6MakeDll.cpp : Defines the entry point for the DLL application.
//
#include "stdafx.h"
BOOL APIENTRY DllMain( HANDLE hModule,
                       DWORD  ul_reason_for_call,
                       LPVOID lpReserved
      )
{
    return TRUE;
}
extern "C" __declspec(dllexport) int add(int a, int b)
{//没有使用,等同 _cdecl
 return a+b;
}
extern "C" __declspec(dllexport) int _stdcall sub(int a, int b)
{//使用了 _stdcall
 return a-b;
}
3 编译选项
在编译指定的dll时一般编译器会指定/MD进行编译,/MD选项不会把引用的相关Dll的引入库进行联接到你自己的Dll中,所以你要打开项目的属性,菜单 project--setting--C/C++—>在Category中选择—> 代码生成 Code Generation—> 在运行时库 Use run-time library 中选择 多线程(/MT)Debug Multithreaded,也就是将/MD编译改变为/MT ,然后编译生成动态库Vc6MakeDll.dll。
4 查看函数的名称变动
C:\Program Files\Microsoft Visual Studio\VC98\Bin\Dumpbin.exe
使用vc自带工具Dumpbin.exe或者从网络上下载 eXeScope或者 Depends(更好)查看到
使用_cdecl的函数add名称没有改变,而使用_stdcall 的函数 sub 的名称已经变为 _sub@8。
调用时虽然vc6仍然使用sub,但其它语言就要使用_sub@8。因此,除非特别需要,建议使用_cdecl,以便各语言不用改名就能使用。
 
--------
二 VC6调用VC6的动态库DLL
 
1 VC中加载DLL的LIB文件的方法有以下3种:
 (1) LIB文件直接加入到工程文件列表中
 在VC中打开File View一页,选中工程名,单击鼠标右键,然后选中“Add Files to Project”菜单,在弹出的文件对话框中选中要加入DLL的LIB文件即可。
 (2) 设置工程的 Project Settings来加载DLL的LIB文件
 打开工程的 Project Settings菜单,选中Link,然后在Object/library modules下的文本框中输入DLL的LIB文件。
 (3) 通过程序代码的方式
 #pragma comment (lib,"x.lib"),还要把DLL对应的函数原型声明的头文件包含到其中 #include "x.h"
2 VC6调用动态库DLL方法有以下2种:
 (1) VC6新建一个对话框工程Vc6UseVc6Dll
 (2) 将文件Vc6MakeDll.dll复制到本目录,也可能复制.lib和.h
方法1:隐式链接,就是在程序开始执行时就将DLL文件加载到应用程序当中。
// 使用以下预编译语句装入库
   #pragma comment(lib,"Vc6MakeDll.lib")
// 使用以下语句导入函数声明,或者它们在头文件中 #include "Vc6MakeDll.h" 引入也可
   extern "C" _declspec(dllimport) int add(int a,int b);
   extern "C" _declspec(dllimport) int sub(int a,int b);
void CVc6UseVc6DllDlg::OnButton1()
{//需要有Vc6MakeDll.dll和Vc6MakeDll.lib
 char ch[90];
 int a=1,b=2,c=0;
 c=add(a,b);
 sprintf(ch,"%d+%d=%d",a,b,c);
 MessageBox(ch);
}
方法2:显式链接,是应用程序在执行过程中随时可以加载DLL文件,也可以随时卸载DLL文件
void CVc6UseVc6DllDlg::OnButton2()
{//只需要有Vc6MakeDll.dll即可
 typedef int(*padd)(int a,int b);//定义指向和DLL中相同的函数原型指针
 typedef int(*psub)(int a,int b);
 HINSTANCE hDLL=LoadLibrary("Vc6MakeDll.dll");//加载动态链接库Vc6MakeDll.dll 文件;
 padd add=(padd)GetProcAddress(hDLL,"add");//得到函数入口地址
 c=add(a,b);
 sprintf(ch,"%d+%d=%d",a,b,c);
 MessageBox(ch);
 FreeLibrary(hDLL);//卸载MyDll.dll文件;
}
--------


三 C#调用VC6的动态库DLL
1 使用vs2010 创建一个,项目 -- Visual C# -- Windows窗体应用程序,名称为Vs2010CsharpUseVc6Dll
2 将vc创建的动态库 Vc6MakeDll.dll 复制到工程执行目录\bin\Debug下面
3 文件开头引用
using System.Runtime.InteropServices; // 用 DllImport 需用此 命名空间
using System.Reflection; // 使用 Assembly 类需用此 命名空间
using System.Reflection.Emit; // 使用 ILGenerator 需用此 命名空间
4 使用方法
  注意:与VC动态链接库导出的两种调用协议_stdcall和_cdecl对应,
方法1:使用DllImport导入库, 需要StdCall和Cdecl,否则匹配错误。
      [DllImport("Vc6MakeDll.dll", EntryPoint = "add", ExactSpelling = false, CallingConvention = CallingConvention.Cdecl)]
      static extern int addY(int a, int b);//上面有EntryPoint ,这里就可以使用别名,注意使用Cdecl
      [DllImport("Vc6MakeDll.dll", EntryPoint = "sub", ExactSpelling = false, CallingConvention = CallingConvention.StdCall)]
      static extern int subY(int a, int b);//上面有EntryPoint ,这里就可以使用别名,注意使用StdCall
方法2:使用LoadLibrary,GetProcAddress,装载库和函数之后,在声明委托时要指定调用约定时,也要注意 StdCall和Cdecl
      //方法2  加入Cdecl等,否则调用导致堆栈不对称。原因可能是托管的 PInvoke 签名与非托管的目标签名不匹配
      [UnmanagedFunctionPointerAttribute(CallingConvention.Cdecl)]//注意使用Cdecl
      delegate int add(int a, int b);
 
      [UnmanagedFunctionPointerAttribute(CallingConvention.StdCall)]//注意使用StdCall
      delegate int sub(int a, int b);
 
5 完整代码
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Runtime.InteropServices;// 用 DllImport 需用此 命名空间
using System.Reflection; // 使用 Assembly 类需用此 命名空间
using System.Reflection.Emit; // 使用 ILGenerator 需用此 命名空间
namespace Vs2010CsharpUseVc6Dll
{
    class csharp_call_vc6_dll
    {
        //这里是集成的用于访问动态库的一个C#类call_vc_dll
        //若要使用其它函数名,可以使用EntryPoint属性设置,如:
        //[DllImport("user32.dll", EntryPoint="MessageBoxA")]
        //static extern int MsgBox(int hWnd, string msg, string caption, int type);
        //其它可选的 DllImportAttribute 属性:
        //CharSet 指示用在入口点中的字符集,如:CharSet=CharSet.Ansi;
        //SetLastError 指示方法是否保留 Win32"上一错误",如:SetLastError=true;
        //ExactSpelling 指示 EntryPoint 是否必须与指示的入口点的拼写完全匹配,如:ExactSpelling=false;
        //PreserveSig指示方法的签名应当被保留还是被转换, 如:PreserveSig=true;
        //CallingConvention指示入口点的调用约定,如:CallingConvention=CallingConvention.Winapi;


        //1 DllImport 调用没有静态变量的dll
        //2 使用LoadLibrary调用含有静态变量的dll,当然动态也可以
        [DllImport("kernel32.dll")]
        static extern IntPtr LoadLibrary(string lpFileName);
        [DllImport("kernel32.dll")]
        static extern IntPtr GetProcAddress(IntPtr hModule, string lpFuncName);
        [DllImport("kernel32", EntryPoint = "FreeLibrary", SetLastError = true)]//EntryPoint指原函数名
        static extern bool FreeLibrary(IntPtr hModule);//这里就可以是别名
        private IntPtr hModule = IntPtr.Zero;
        public IntPtr farFunc = IntPtr.Zero;
        public void LoadDll(string lpDllFileName)
        {
            hModule = LoadLibrary(lpDllFileName);
            if (hModule == IntPtr.Zero)
                throw (new Exception(" 没有找到动态库 :" + lpDllFileName + "."));
        }
        public void LoadDll(IntPtr HMODULE)
        {
            if (HMODULE == IntPtr.Zero)
                throw (new Exception(" 所传入的函数库模块的句柄 HMODULE 为空 ."));
            hModule = HMODULE;
        }
        public void LoadFun(string lpFuncName)
        { // 若函数库模块的句柄为空,则抛出异常
            if (hModule == IntPtr.Zero)
                throw (new Exception(" 函数库模块的句柄为空 , 请确保已进行 LoadDll 操作 !"));
            // 取得函数指针
            farFunc = GetProcAddress(hModule, lpFuncName);
            // 若函数指针,则抛出异常
            if (farFunc == IntPtr.Zero)
                throw (new Exception(" 没有找到 :" + lpFuncName + " 这个函数的入口点 "));
        }
        public void LoadFun(string lpDllFileName, string lpFuncName)
        { // 取得函数库模块的句柄
            hModule = LoadLibrary(lpDllFileName);
            // 若函数库模块的句柄为空,则抛出异常
            if (hModule == IntPtr.Zero)
                throw (new Exception(" 没有找到 :" + lpDllFileName + "."));
            // 取得函数指针
            farFunc = GetProcAddress(hModule, lpFuncName);
            // 若函数指针,则抛出异常
            if (farFunc == IntPtr.Zero)
                throw (new Exception(" 没有找到 :" + lpFuncName + " 这个函数的入口点 "));
        }
        public void UnLoadDll()
        {
            FreeLibrary(hModule);
            hModule = IntPtr.Zero;
            farFunc = IntPtr.Zero;
        }
    }
}


6 装载动态库,得到其函数地址的集成类
这里是集成的用于访问vc6动态库的C#类csharp_call_vc6_dll.cs,来自网上
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Runtime.InteropServices; // 用 DllImport 需用此 命名空间
using System.Reflection; // 使用 Assembly 类需用此 命名空间
using System.Reflection.Emit; // 使用 ILGenerator 需用此 命名空间
public enum ModePass
{
    ByValue = 0x0001,
    ByRef = 0x0002
}
namespace Vs2010CsharpUseVc6Dll
{
    class csharp_call_vc6_dll
    {
        //这里是集成的用于访问vc6动态库的C#类csharp_call_vc6_dll
       
        //若要使用其它函数名,可以使用EntryPoint属性设置,如:
        //[DllImport("user32.dll", EntryPoint="MessageBoxA")]
        //static extern int MsgBox(int hWnd, string msg, string caption, int type);
        //其它可选的 DllImportAttribute 属性:
        //CharSet 指示用在入口点中的字符集,如:CharSet=CharSet.Ansi;
        //SetLastError 指示方法是否保留 Win32"上一错误",如:SetLastError=true;
        //ExactSpelling 指示 EntryPoint 是否必须与指示的入口点的拼写完全匹配,如:ExactSpelling=false;
        //PreserveSig指示方法的签名应当被保留还是被转换, 如:PreserveSig=true;
        //CallingConvention指示入口点的调用约定,如:CallingConvention=CallingConvention.Winapi;


        //1 DllImport 调用没有静态变量的dll
        //2 使用LoadLibrary调用含有静态变量的dll,当然动态也可以
       
        [DllImport("kernel32.dll")]
        static extern IntPtr LoadLibrary(string lpFileName);
        [DllImport("kernel32.dll")]
        static extern IntPtr GetProcAddress(IntPtr hModule, string lpFuncName);
        [DllImport("kernel32", EntryPoint = "FreeLibrary", SetLastError = true)]//EntryPoint指原函数名
        static extern bool FreeLibrary(IntPtr hModule);//这里就可以是别名
        private IntPtr hModule = IntPtr.Zero;
        public IntPtr farFunc = IntPtr.Zero;
        public void LoadDll(string lpDllFileName)
        {
            hModule = LoadLibrary(lpDllFileName);
            if (hModule == IntPtr.Zero)
                throw (new Exception(" 没有找到动态库 :" + lpDllFileName + "."));
        }
        public void LoadDll(IntPtr HMODULE)
        {
            if (HMODULE == IntPtr.Zero)
                throw (new Exception(" 所传入的函数库模块的句柄 HMODULE 为空 ."));
            hModule = HMODULE;
        }
        public void LoadFun(string lpFuncName)
        { // 若函数库模块的句柄为空,则抛出异常
            if (hModule == IntPtr.Zero)
                throw (new Exception(" 函数库模块的句柄为空 , 请确保已进行 LoadDll 操作 !"));
            // 取得函数指针
            farFunc = GetProcAddress(hModule, lpFuncName);
            // 若函数指针,则抛出异常
            if (farFunc == IntPtr.Zero)
                throw (new Exception(" 没有找到 :" + lpFuncName + " 这个函数的入口点 "));
        }
        public void LoadFun(string lpDllFileName, string lpFuncName)
        { // 取得函数库模块的句柄
            hModule = LoadLibrary(lpDllFileName);
            // 若函数库模块的句柄为空,则抛出异常
            if (hModule == IntPtr.Zero)
                throw (new Exception(" 没有找到 :" + lpDllFileName + "."));
            // 取得函数指针
            farFunc = GetProcAddress(hModule, lpFuncName);
            // 若函数指针,则抛出异常
            if (farFunc == IntPtr.Zero)
                throw (new Exception(" 没有找到 :" + lpFuncName + " 这个函数的入口点 "));
        }
        public void UnLoadDll()
        {
            FreeLibrary(hModule);
            hModule = IntPtr.Zero;
            farFunc = IntPtr.Zero;
        }


    }
}

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值