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;
        }

}
}

C# VC6调用VC6的动态库DLL相关推荐

  1. C#总结:C#调用C++的动态库Dll遇到的问题[动态库调用/结构体指针调用/union共同体定义]

    记录使用C#调用C++的生成的DLL手柄键盘驱动库包括****.sys(驱动文件)和****.dll(库文件)的全部问题. C#调用C++的库有两种:静态调用和动态调用 静态调用,使用.net 提供的 ...

  2. C#调用C/C++动态库dll异常:对 PInvoke 函数调用导致堆栈不对称问题

    结论:如果你是用C#调用C的动态库,如果出现"对 PInvoke 函数调用导致堆栈不对称问题",建议优先调整CallingConvention的值,建议改为CallingConve ...

  3. C#调用C/C++动态库Dll时几个注意事项:PInvoke错误

    经常需要封装一些C/C++函数放入动态库中给C#程序调用,通常情况下直接写成如下形式即可: C#封装调用: [DllImport("depressor.dll")] //错误调用方 ...

  4. 实现C++调用C#的动态库dll

    在实际的项目过程中,有时会遇到在C++的项目中调用某个C#的dll来完成特定的某个功能,我们都知道,Native C++是没办法直接调用.NET平台的dll的.那有没有办法来做到这一点了?答案是肯定的 ...

  5. c 调用c语言写的dll文件路径,手把手教你用C/C++语言创建及调试动态库DLL程序

    引子 动态链接库DLL文件不仅可以实现代码.资源和数据的共享,同时也可以对源代码起保护作用,对于开发者来讲,DLL的生成及调试是程序员必须掌握的一种技术,下面通过一个具体的例子,演示使用C/C++语言 ...

  6. C#.net调用动态库dll注意事项

    C#.net调用动态库dll注意事项 本文主要描述作者在工作中所遇到技术难点及问题,最后提出相关的解决方案 场景 公司有个业务需求涉及到ID读卡器,构建成在ID卡读卡器一刷ID卡,系统就自动显示其ID ...

  7. java调用C或者C++动态库dll

    java调用C或者C++动态库dll,本文章使用的是IntelliJ IDEA Community Edition 2021.2.3版本测试的 1.新建项目 linjie.demo,添加类HelloL ...

  8. C# 调用其他的动态库开发应注意的问题

    1.背景 程序开发语言可以说是五花八门,这就引出了一个新问题 ,不同语言开发的系统进行对接时相关调用的问题. 下面我主要说一下我自己在做接口开发时遇到的问题及解决方法仅供参考,我使用的C#开发进行对接 ...

  9. graalvm把java编译为c/c++能够使用的动态库(dll/so)

    graalvm把java编译为c/c++能够使用的动态库(dll/so) 1.安装graalvm oracle官方企业版 github的openjdk版本 1.1 下载对应系统版本,配置环境变量 本人 ...

最新文章

  1. 怎么用IDEA快速查看类图关系?
  2. c语言statistics函数,Statistics基本定理
  3. jQuery css-dom
  4. MyBatis-19MyBatis代码生成器-XML配置详解
  5. 基于glibc的程序在android上的移植
  6. c++ socket编程_C/C++中的Socket编程
  7. 阿里云积极落实等级保护制度,政务云全国首个通过等保2.0合规评测
  8. 记一次EF Core连接MySql、Oracle
  9. win7如何看计算机用户名和密码怎么办,win7系统电脑查看共享文件夹时不显示用户名和密码输入窗口的解决方法...
  10. 揭秘Python并发编程——协程
  11. Halcon学习笔记:xyz_attrib_to_object_model_3d示例
  12. 【数据库基础知识】plsql安装及配置
  13. Linux 命令(67)—— time 命令
  14. Linux内存是怎么工作的
  15. sys.dm_db_wait_stats
  16. 收到华为offer后的阶段性总结
  17. YT8511H的原理图设计
  18. Fiddler 安装使用教程
  19. prev_permutation函数
  20. 一次JVM调优的笔记

热门文章

  1. 计算机械效率的公式四种,浮力的四种计算公式分别是什么?
  2. 【CSS】自定义动画
  3. PNM格式图片转换与JAI ImageIO
  4. 「Python语法结构」求员工的实际工资
  5. 隐私计算--23--纵向联邦学习
  6. 180217 加密-专用加密软件(3)
  7. KB Kb kB 大小写的区别
  8. 解决Excel转CSV格式中文不能正常显示的问题
  9. SpringCloud、SpringBoot2.0 整合Oauth2 (四) 配置文件快速配置url过滤
  10. 18.3 万美元offer到手!ChatGPT 通过谷歌 L3 面试:留给谷歌的时间不多了