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 部分反编译:

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

  1. [DllImport("kernel32.dll")]
  2. private extern static IntPtr LoadLibrary(String path);
  3.  
  4. [DllImport("kernel32.dll")]
  5. private extern static IntPtr GetProcAddress(IntPtr lib, String funcName);
  6.  
  7. [DllImport("kernel32.dll")]
  8. private extern static bool FreeLibrary(IntPtr lib);

//  没验证过

LoadLibrary的装载和函数调用

  1. public class DllInvoke
  2. {
  3. [DllImport("kernel32.dll")]
  4. private extern static IntPtr LoadLibrary(String path);
  5.  
  6. [DllImport("kernel32.dll")]
  7. private extern static IntPtr GetProcAddress(IntPtr lib, String funcName);
  8.  
  9. [DllImport("kernel32.dll")]
  10. private extern static bool FreeLibrary(IntPtr lib);
  11.  
  12. private IntPtr hLib;
  13.  
  14. public DllInvoke(String DLLPath)
  15. {
  16. hLib = LoadLibrary(DLLPath);
  17. }
  18.  
  19. ~DllInvoke() // 还整出来析构??
  20. {
  21. FreeLibrary(hLib);
  22. }
  23.  
  24. //将要执行的函数转换为委托
  25. public Delegate Invoke(String APIName,Type t)
  26. {
  27. IntPtr api = GetProcAddress(hLib, APIName);
  28. return (Delegate)Marshal.GetDelegateForFunctionPointer(api,t);
  29. }
  30. }

LoadLibrary

下面代码进行调用

  1. // delegate头顶上是不是也加点什么属性【】
  2. // [...]
  3. public delegate int Compile(String command, StringBuilder inf);
  4. //编译
  5. DllInvoke dll new DllInvoke(Server.MapPath(@"~/Bin/Judge.dll"));
  6. Compile compile = (Compile)dll.Invoke("Compile", typeof(Compile));
  7. StringBuilder inf;
  8. 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的值:

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

[UnmanagedFunctionPointer(CallingConvention.Cdecl)]

public static delegate {callback};

下面自己写了一个:

  1. [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
  2. 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. 风炫安全web安全学习第三十二节课 Python代码执行以及代码防御措施

    风炫安全web安全学习第三十二节课 Python代码执行以及代码防御措施 Python 语言可能发生的命令执行漏洞 内置危险函数 eval和exec函数 eval eval是一个python内置函数, ...

  2. Npoi XWPF Word 导出时插入图片无法显示 bug 完美解决

    一.来自客户的需求 最近客户来个新需求生成一个word 标签纸,并且需要在标签纸上插入一个logo,并且将erp 中的数据取出来自动写在文档上,不由得淡淡一笑,这不难呀! 于是乎我就写下了这样的代码: ...

  3. i5 11300H 怎么样 相当于什么水平

    i5-11300H 为 4 核 8 线程,主频 3.1GHz,睿频 4.4GHz,三级缓存 8MBi5-11300H 怎么样看完你就知道了 https://list.jd.com/list.html?

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

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

  5. try catch finally语句块中存在return语句时的执行情况剖析

    2种场景 (1) try中有return,finally中没有return(注意会改变返回值的情形);(2) try中有return,finally中有return; 场景代码分析(idea亲测) 场 ...

  6. python实现99乘法表

    for x in range(1,10): for y in range(1,10): if x>=y: print("%d*%d=%d\t"%(y,x,x*y),end=' ...

  7. git的简单使用方式(基本操作部分)

    git的简单使用方式(基本操作部分) 1.简单介绍GIT的工作流程 git一般的工作流程: 克隆git的资源作为工作目录(一般会使用命令git clone进行克隆); 在克隆的资源上对文件进行增加或者 ...

  8. CSS 奇技淫巧:动态高度过渡动画

    这个问题源自于掘金上的一个留言,一个朋友问到,为什么我下面这段代码的高度过渡动画失效了? 伪代码大概是这样: { height: unset; transition: all 0.3s linear; ...

  9. Tomcat-8.5.23 基于域名和端口的虚拟主机

    下载tomcat yum install java -y cd /opt/ wget http://mirror.bit.edu.cn/apache/tomcat/tomcat-8/v8.5.23/b ...

  10. freopen函数总结

    函数原型: freopen(const char * __restrict__ _Filename,const char * __restrict__ _Mode,FILE * __restrict_ ...