C#  VS C++


C#代码遇见了非托管dll如何处理

问题:托管与非托管,兼容?

方法一:DllImport

托管调试助手 "PInvokeStackImbalance" Message=托管调试助手 "PInvokeStackImbalance":“对 PInvoke 函数“XXXX_Pub_Test!XXXX_Pub_Test.XxxxClient_temp::xxxxclient_config_init”的调用导致堆栈不对称。原因可能是托管的 PInvoke 签名与非托管的目标签名不匹配。请检查 PInvoke 签名的调用约定和参数与非托管的目标签名是否匹配。”

函数调用导致堆栈不对称。原因可能是托管的 PInvoke 签名与非托管的目标签名不匹配。(网上解释)

在DllImport中加入CallingConvention参数就行了,形如以下,

///////////////////////////////////////////////////////////////////////////////////////////////

下面并不能解决问题,只能勉强推送参数。需要推敲着用

///////////////////////////////////////////////////////////////////////////////////////////////


  • [DllImport(xxxx.dll, CharSet = CharSet.Auto, CallingConvention = CallingConvention.Cdecl)]

DllImport 部分反编译:

public class DllImportAttribute: Attribute
{
public DllImportAttribute(string dllName) {…} //定位参数为dllName
public CallingConvention CallingConvention; //入口点调用约定
public CharSet CharSet; //入口点采用的字符接
public string EntryPoint; //入口点名称
public bool ExactSpelling; //是否必须与指示的入口点拼写完全一致,默认false
public bool PreserveSig; //方法的签名是被保留还是被转换
public bool SetLastError; //FindLastError方法的返回值保存在这里
public string Value { get {…} }
}
网上找的说明:
1、DllImport只能放置在方法声明上。
2、DllImport具有单个定位参数:指定包含被导入方法的 dll 名称的 dllName 参数。
3、DllImport具有五个命名参数:
   a、CallingConvention 参数指示入口点的调用约定。如果未指定CallingConvention,则使用默认值CallingConvention.Winapi。
   b、CharSet参数指定用在入口点的字符集。如果未指定CharSet,则使用默认值CharSet.Auto。 // CharSet = CharSet.Ansi,试验用这个
   c、EntryPoint参数给出dll中入口点的名称。如果未指定EntryPoint,则使用方法本身的名称。
   d、ExactSpelling参数指示EntryPoint是否必须与指示的入口点的拼写完全匹配。如果未指定ExactSpelling,则使用默认值false。
   e、PreserveSig参数指示方法的签名被保留还是被转换。当签名被转换时,它被转换为一个具有HRESULT返回值和该返回值的一个
   名为retval的附加输出参数的签名。如果未指定PreserveSig,则使用默认值true。
   f、SetLastError参数指示方法是否保留Win32“上一错误”。如果未指定SetLastError,则使用默认值false。
4、它是一次性属性类。
5、用DllImport属性修饰的方法必须具有extern修饰符。
DllImport的用法示例(是用来写入ini文件的一个win32api):
#ifdef UNICODE
#define WritePrivateProfileString WritePrivateProfileStringW
#else
#define WritePrivateProfileString WritePrivateProfileStringA
#endif // !UNICODE
// !UNICODE
// typedef int                 BOOL;
WINBASEAPI
BOOL
WINAPI
WritePrivateProfileStringA(
_In_opt_ LPCSTR lpAppName,
_In_opt_ LPCSTR lpKeyName,
_In_opt_ LPCSTR lpString,
_In_opt_ LPCSTR lpFileName
);
[DllImport("kernel32")]
private static extern long WritePrivateProfileString(string mpAppName,string mpKeyName,string mpDefault,string mpFileName);
用此方法调用WinAPI的数据类型对应:C++:DWORD(BOOL/*最新*/)--C#:long/int/uint,C++:LPCTSTR(LPCWSTR或LPCSTR)--C#:string。// 解释和其他不同

///////////////////////////////////////////////////////////////////////////////////////////////

dll的位置,看了很多办法,总结一下:

DllImport会按照顺序自动去寻找的地方:

1、可运行文件exe所在目录 ,// 用过,用的这个

2、System32文件目录 ,// 用过,COM/DCOM注册的dll最好也放这

3、环境变量目录,// 没用过

只需要你把引用的DLL 拷贝到这三个目录下 就可以不用写路径了 或者

可以这样server.MapPath(.\bin\*.dll)web中的,同时也是应用程序中的 后来发现用[DllImport(@"C:\OJ\Bin\Judge.dll")]这样指定DLL的绝对路径就可以正常装载。 // [DllImport(@"C:\OJ\Bin\Judge.dll")],用没成功。其他划线没用过

Web 引用第三方非托管dll:

c#的dllimport使用方法详解未尝不是个解决办法,去里面多了解,简单总结一下。

具体做法如下:  

1. 首先我们在服务器上随便找个地方新建一个目录,假如为C:\DLL  

2. 然后,在环境变量中,给Path变量添加这个目录  

3. 最后,把所有的非托管文件都拷贝到C:\DLL中。或者更干脆的把dll放到system32目录  

对于可以自己部署的应用程序,这样未偿不是一个解决办法。然而,如果用的是虚拟空间,是没办法把注册PATH变量或者把我们自己的DLL拷到system32目录的。同时也不一定知道dll的物理路径。

DllImport里面只能用字符串常量,而不能够用Server.MapPath(@"~/Bin/Judge.dll")来确定物理路径。ASP.NET中要使用DllImport的,必须在先“using System.Runtime.InteropServices;”不过,我发现,调用这种"非托管Dll”相当的慢,可能是因为我的方法需要远程验证吧,但是实在是太慢了。经过一翻研究,终于想到了一个完美的解决办法,分别取得了LoadLibrary和GetProcAddress函数的地址,再通过这两个函数来取得我们的DLL里面的函数。

我们可以先用Server.MapPath(@"~/Bin/Judge.dll")来取得我们的DLL的物理路径,然后再用LoadLibrary进行载入,最后用GetProcAddress取得要用的函数地址。// 这个类似方法对C#托管dll使用过,动态加载dll,说的不对的请指正

[DllImport("kernel32.dll")]
private extern static IntPtr LoadLibrary(String path); [DllImport("kernel32.dll")]
private extern static IntPtr GetProcAddress(IntPtr lib, String funcName); [DllImport("kernel32.dll")]
private extern static bool FreeLibrary(IntPtr lib);

//  没验证过

LoadLibrary的装载和函数调用

public class DllInvoke
{
[DllImport("kernel32.dll")]
private extern static IntPtr LoadLibrary(String path); [DllImport("kernel32.dll")]
private extern static IntPtr GetProcAddress(IntPtr lib, String funcName); [DllImport("kernel32.dll")]
private extern static bool FreeLibrary(IntPtr lib); private IntPtr hLib; public DllInvoke(String DLLPath)
{
hLib = LoadLibrary(DLLPath);
} ~DllInvoke() // 还整出来析构??
{
FreeLibrary(hLib);
} //将要执行的函数转换为委托
public Delegate Invoke(String APIName,Type t)
{
IntPtr api = GetProcAddress(hLib, APIName);
return (Delegate)Marshal.GetDelegateForFunctionPointer(api,t);
}
}

LoadLibrary

下面代码进行调用

// delegate头顶上是不是也加点什么属性【】
// [...]
public delegate int Compile(String command, StringBuilder inf);
//编译
DllInvoke dll = new DllInvoke(Server.MapPath(@"~/Bin/Judge.dll"));
Compile compile = (Compile)dll.Invoke("Compile", typeof(Compile));
StringBuilder inf;
compile(@“gcc a.c -o a.exe“,inf);//这里就是调用我的DLL里定义的Compile函数

///////////////////////////////////////////////////////////////////////////////////////////////

继续上面的CharSet 

////////////////////////////////////////////////////////////////////////

CharSet = CharSet.Ansi才不会报错。//但不同机器结果也不同,难搞哦!

////////////////////////////////////////////////////////////////////////

改为:

  • [DllImport(xxxx.dll, CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)]
  • 或者[DllImport(xxxx.dll, CharSet = CharSet.None, CallingConvention = CallingConvention.Cdecl)] // 其他机器报上述错误

System.BadImageFormatException

HResult=0x8007000B

Message=试图加载格式不正确的程序。 (异常来自 HRESULT:0x8007000B)

Source=XxxxClient_Test

StackTrace:

at XXXX_Test.XxxxClient_temp.xxxxclient_init()

at XxxxClient_Test.Program.Main(String[] args) in C:\Users\*****\source\repos\XXXX_Test\XxxxClient_Test\Program.cs:line 38


非托管dll的回调函数,在托管里如何表示:

C#默认情况下委托都是stdcall的调用方式,但可以通过UnmanagedFunctionPointer特性来修改

CallingConvention的值:

    public enum CallingConvention
{
Winapi = 1,// 默认平台调用约定
Cdecl, // C调用约定
StdCall, // 默认约定,这是使用平台invoke调用非托管函数的默认约定。
ThisCall, // 第一个参数是this指针,仅C++,用于对从非托管 DLL 导出的类调用方法
FastCall // 快?
}

[UnmanagedFunctionPointer(CallingConvention.Cdecl)]

public static delegate {callback};

下面自己写了一个:

[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
public delegate void on_message_back(int cfg, int messageId, string name, IntPtr load, int loadlen, int qos, bool retain);

用了默认CallingConvention.StdCall,情况是只调用了一次委托,绝无2次。修改为CallingConvention.Cdecl,估计dll是C风格。

结合网上总结C++与NET中数据类型的对应:

// c++:HANDLE(void *) ---- c#:System.IntPtr  

  // c++:Byte(unsigned char) ---- c#:System.Byte  

  // c++:SHORT(short) ---- c#:System.Int16  

  // c++:WORD(unsigned short) ---- c#:System.UInt16  

  // c++:INT(int) ---- c#:System.Int16

  // c++:INT(int) ---- c#:System.Int32  

  // c++:UINT(unsigned int) ---- c#:System.UInt16

  // c++:UINT(unsigned int) ---- c#:System.UInt32

  // c++:LONG(long) ---- c#:System.Int32  

  // c++:ULONG(unsigned long) ---- c#:System.UInt32  

  // c++:DWORD(unsigned long) ---- c#:System.UInt32  

  // c++:DECIMAL ---- c#:System.Decimal  

  // c++:BOOL(long) ---- c#:System.Boolean  

  // c++:CHAR(char) ---- c#:System.Char  

  // c++:LPSTR(char *) ---- c#:System.String  

  // c++:LPWSTR(wchar_t *) ---- c#:System.String  

  // c++:LPCSTR(const char *) ---- c#:System.String  

  // c++:LPCWSTR(const wchar_t *) ---- c#:System.String  

  // c++:PCAHR(char *) ---- c#:System.String  

  // c++:BSTR ---- c#:System.String  

  // c++:FLOAT(float) ---- c#:System.Single  

  // c++:DOUBLE(double) ---- c#:System.Double  

  // c++:VARIANT ---- c#:System.Object  

  // c++:PBYTE(byte *) ---- c#:System.Byte[]  

  // c++:BSTR ---- c#:StringBuilder

  // c++:LPCTSTR ---- c#:StringBuilder

  // c++:LPCTSTR ---- c#:string

  // c++:LPTSTR ---- c#:[MarshalAs(UnmanagedType.LPTStr)] string  

  // c++:LPTSTR 输出变量名
---- c#:StringBuilder 输出变量名

  // c++:LPCWSTR ---- c#:IntPtr

  // c++:BOOL ---- c#:bool   

  // c++:HMODULE ---- c#:IntPtr   

  // c++:HINSTANCE ---- c#:IntPtr  

  // c++:结构体
---- c#:public struct 结构体{};
 

  // c++:结构体
**变量名
---- c#:out 变量名
//C#中提前申明一个结构体实例化后的变量名

  // c++:结构体
&变量名
---- c#:ref 结构体
变量名

  // c++:WORD ---- c#:ushort

  // c++:DWORD ---- c#:uint

  // c++:DWORD ---- c#:int

// c++:UCHAR ---- c#:int

  // c++:UCHAR ---- c#:byte

  // c++:UCHAR* ---- c#:string

  // c++:UCHAR* ---- c#:IntPtr

  // c++:GUID ---- c#:Guid

  // c++:Handle ---- c#:IntPtr

  // c++:HWND ---- c#:IntPtr

  // c++:DWORD ---- c#:int

  // c++:COLORREF ---- c#:uint
  // c++:unsigned char ---- c#:byte

  // c++:unsigned char * ---- c#:ref byte

  // c++:unsigned char * ---- c#:[MarshalAs(UnmanagedType.LPArray)] byte[]

  // c++:unsigned char * ---- c#:[MarshalAs(UnmanagedType.LPArray)] Intptr

// c++:handle ---- c#:IntPtr
  // c++:hwnd ---- c#:IntPtr

  // c++:unsigned char & ---- c#:ref byte

  // c++:unsigned char 变量名
---- c#:byte 变量名

  // c++:unsigned short 变量名
---- c#:ushort 变量名

  // c++:unsigned int 变量名
---- c#:uint 变量名

  // c++:unsigned long 变量名
---- c#:ulong 变量名

  // c++:char 变量名
---- c#:byte 变量名
// C++中一个字符用一个字节表示,C#中一个字符用两个字节表示

  // c++:char 数组名[数组大小]
---- c#:MarshalAs(UnmanagedType.ByValTStr, SizeConst = 数组大小)]
public string 数组名;
ushort

  // c++:char * ---- c#:string // 传入参数

  // c++:char * ---- c#:StringBuilder // 传出参数

  // c++:char *变量名
---- c#:ref string 变量名

  // c++:char *输入变量名
---- c#:string 输入变量名

  // c++:char *输出变量名
---- c#:[MarshalAs(UnmanagedType.LPStr)] StringBuilder 输出变量名

  // c++:char ** ---- c#:string

  // c++:char **变量名
---- c#:ref string 变量名

  // c++:const char * ---- c#:string

  // c++:char[] ---- c#:string

  // c++:char 变量名[数组大小]
---- c#:[MarshalAs(UnmanagedType.ByValTStr,SizeConst=数组大小)]
public string 变量名;
 

  // c++:struct 结构体名
*变量名
---- c#:ref 结构体名
变量名

  // c++:委托
变量名
---- c#:委托
变量名

  // c++:int ---- c#:int

  // c++:int ---- c#:ref int

  // c++:int & ---- c#:ref int

  // c++:int * ---- c#:ref int  // C#中调用前需定义int 变量名
= 0;



  // c++:*int ---- c#:IntPtr

  // c++:int32 PIPTR * ---- c#:int32[]

  // c++:float PIPTR * ---- c#:float[]

   

  // c++:double** 数组名
---- c#:ref double 数组名

  // c++:double*[] 数组名
---- c#:ref double 数组名

  // c++:long ---- c#:int

  // c++:ulong ---- c#:int

    

  // c++:UINT8 * ---- c#:ref byte // C#中调用前需定义byte 变量名
= new byte(); 
       

  // c++:void * ---- c#:IntPtr   

  // c++:void * user_obj_param ---- c#:IntPtr user_obj_param

  // c++:void * 对象名称
---- c#:([MarshalAs(UnmanagedType.AsAny)]Object 对象名称

  // c++:char, INT8, SBYTE, CHAR ---- c#:System.SByte   

  // c++:short, short int, INT16, SHORT ---- c#:System.Int16   

  // c++:int, long, long int, INT32, LONG32, BOOL , INT ----
c#:System.Int32   

  // c++:__int64, INT64, LONGLONG ---- c#:System.Int64   

  // c++:unsigned char, UINT8, UCHAR , BYTE ---- c#:System.Byte
  

  // c++:unsigned short, UINT16, USHORT, WORD, ATOM, WCHAR , __wchar_t ----
c#:System.UInt16   

  // c++:unsigned, unsigned int, UINT32, ULONG32, DWORD32, ULONG, DWORD,
UINT ---- c#:System.UInt32   

  // c++:unsigned __int64, UINT64, DWORDLONG, ULONGLONG ----
c#:System.UInt64   

  // c++:float, FLOAT ---- c#:System.Single   

  // c++:double, long double, DOUBLE ---- c#:System.Double   



  // Win32 Types ---- CLR Type   

   

  // Struct需要在C#里重新定义一个Struct

  // CallBack回调函数需要封装在一个委托里,delegate
static extern int FunCallBack(string str);



  // unsigned char** ppImage替换成IntPtr ppImage

  // int& nWidth替换成ref
int nWidth

  // int*, int&, 则都可用
ref int 对应

  // 双针指类型参数,可以用
ref IntPtr

  // 函数指针使用c++:
typedef double (*fun_type1)(double); 对应 c#:public delegate double
fun_type1(double);

  // char* 的操作c++:
char*; 对应
c#:StringBuilder;

  // c#中使用指针:在需要使用指针的地方

unsafe
  // unsigned char对应public
byte

  /*

  * typedef void (*CALLBACKFUN1W)(wchar_t*, void* pArg); // 宽字符集

  * typedef void (*CALLBACKFUN1A)(char*, void* pArg);

  * bool BIOPRINT_SENSOR_API dllFun1(CALLBACKFUN1 pCallbackFun1, void*
pArg);

  * 调用方式为

  * [UnmanagedFunctionPointer(CallingConvention.Cdecl)]

  * public delegate void CallbackFunc1([MarshalAs(UnmanagedType.LPWStr)]
StringBuilder strName, IntPtr pArg); // 函数名有的可以不同

  * 

  */


方法二:C++/CLR方式

C#遇见C++的江湖 不行!得不到就干掉的更多相关文章

  1. Redis错误:jedis.exceptions.JedisDataException: ERR Client sent AUTH, but no password is set

    原文链接:http://blog.csdn.net/rchm8519/article/details/48347797 redis.clients.util.Pool.getResource(Pool ...

  2. let和const在es6中的异同点

    let和const这两个都是声明一个变量或函数的方法与var差不太多的效果 let的声明在for循环中,当你定义的是多少,最后你的值就是多少开始的,它只进行一次循环,不会像var那样去一遍一遍的去遍历 ...

  3. 【码在江湖】前端少侠的json故事(下):jsonp的应用

    jsonp的应用 话说天下大势,分久必合,合久必分,代码江湖自进入21世纪以来,前后端分离成为了大势所趋,代码分工更为精细,更为深入,而正所谓码在江湖,身不由己,为了更好的实现需求,程序猿们必须不断学 ...

  4. Linux江湖01:玩转Linux系统的方法论 (转载)

    http://www.blogjava.net/youxia/archive/2015/01/08/linux001.html 2014年上半年,我是在写RCP系列.然后,由于要准备研究生毕业论文和答 ...

  5. selenium自动化遇见Cannot find class in classpath问题

    今天遇见了Cannot find class in classpath的问题, org.testng.TestNGException: Cannot find class in classpath: ...

  6. android MultiDex multidex原理原理下遇见的N个深坑(二)

    android MultiDex 原理下遇见的N个深坑(二) 这是在一个论坛看到的问题,其实你不知道MultiDex到底有多坑. 不了解的可以先看上篇文章:android MultiDex multi ...

  7. 一梦江湖费六年——QQ群聊天分析

    本文结构: 一.那些年我们加过的QQ群 二.数据读入和整理(一)--来自蓝翔的挖掘机 二.数据读入和整理(二)--你不知道的事 三.聊天宏观(1)--寤寐思服 三.聊天宏观(2)日月篇 三.聊天宏观( ...

  8. IT江湖--这个冬天注定横尸遍野

    今年江湖大事繁起,又至寒冬,冻的不仅是温度,更是人心. 这两天上班途中看到多个公众号和媒体发了很多 "XXX公司裁员50%" 等等诸如此类的文章,也真是撼动人心.寒冬,比以往来的更 ...

  9. spring 为什么可以一统江湖

    spring 为什么可以一统江湖 Inversion of Contro 简称IOC 是面向对象编程中的一种设计原则,可以用来减低计算机代码之间的耦合度.也就是面向接口编程的思想. 简单的说就是使用配 ...

随机推荐

  1. 消息队列 ---常用的 MQ 中间件

    目前市面上比较常用的 MQ(Message Queue,消息队列)中间件有 RabbitMQ.Kafka.RocketMQ,如果是轻量级的消息队列可以使用 Redis 提供的消息队列,其中 Redis ...

  2. SpringBoot入门及深入

    一:SpringBoot简介 当前互联网后端开发中,JavaEE占据了主导地位.对JavaEE开发,首选框架是Spring框架.在传统的Spring开发中,需要使用大量的与业务无关的XML配置才能使S ...

  3. Mac使用HomeBrew

    前言 考虑许久终于决定入手mac耍耍,还是因为要找工作了,手上的win本大学入的,现在使用卡顿太多,另外就是mac作为程序员之友仰慕已久.决定在PDD入了.到手后发现mac真的跟win有很大差别.还是 ...

  4. LeetCode485 最大连续1的个数

    给定一个二进制数组, 计算其中最大连续1的个数. 示例 1: 输入: [1,1,0,1,1,1] 输出: 3 解释: 开头的两位和最后的三位都是连续1,所以最大连续1的个数是 3. 注意: 输入的数组 ...

  5. 【Docker】1、 前后端分离项目 下载启动运行

    人人开源前后端分离项目下载与配置 文章目录 人人开源前后端分离项目下载与配置 前后端分离框架介绍 后端项目下载与配置 1.renren-fast后台项目介绍 2.开发环境搭建 3.下载后端renren ...

  6. innodb是怎么刷新日志缓冲的

    当innodb把日志缓冲刷新到磁盘日志文件的时候,先会用一个mutex锁住缓冲区,刷新到所需要的位置,然后移动剩下的条目到缓冲区的前面,当mutex释放时,可能有超过一个事务已经准备好刷新其日志记录, ...

  7. 【Linux】服务器识别ntfs移动磁盘方法

    Linux服务器无法识别ntfs磁盘 如果想识别的话,需要安装一个包ntfs-3g 安装好后,将移动磁盘插入到服务器的usb口中 新建一个目录,将磁盘挂载在新建的目录上 挂载命令如下: mount - ...

  8. kubernets之服务重定向

    一  服务的强大功能之处的其他表现 前面介绍的所有有关服务的说明,都是将集群内部的pod应用暴露出来提供外部客户端或者内部的客户端进行访问,但是服务的强大之处远远不止于此 服务甚至可以将集群外部的应用 ...

  9. 在Jetbrain IDE中自定义TODO功能

    好的IDE能为开发以及学习源码带来效率的提升,今天要介绍的就是Jetbrain家族中IDE自带的TODO功能,我认为利用好它,能够大大的提升阅读源码的效率. 假设我现在需要去阅读源代码,看了半天我终于 ...

  10. v-model语法糖

    其实v-model就是一个结合了v-bind和v-on的语法糖,实现了双向数据绑定. 举个(栗子):