C#遇见C++的江湖 不行!得不到就干掉
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++的江湖 不行!得不到就干掉的更多相关文章
- 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 ...
- let和const在es6中的异同点
let和const这两个都是声明一个变量或函数的方法与var差不太多的效果 let的声明在for循环中,当你定义的是多少,最后你的值就是多少开始的,它只进行一次循环,不会像var那样去一遍一遍的去遍历 ...
- 【码在江湖】前端少侠的json故事(下):jsonp的应用
jsonp的应用 话说天下大势,分久必合,合久必分,代码江湖自进入21世纪以来,前后端分离成为了大势所趋,代码分工更为精细,更为深入,而正所谓码在江湖,身不由己,为了更好的实现需求,程序猿们必须不断学 ...
- Linux江湖01:玩转Linux系统的方法论 (转载)
http://www.blogjava.net/youxia/archive/2015/01/08/linux001.html 2014年上半年,我是在写RCP系列.然后,由于要准备研究生毕业论文和答 ...
- selenium自动化遇见Cannot find class in classpath问题
今天遇见了Cannot find class in classpath的问题, org.testng.TestNGException: Cannot find class in classpath: ...
- android MultiDex multidex原理原理下遇见的N个深坑(二)
android MultiDex 原理下遇见的N个深坑(二) 这是在一个论坛看到的问题,其实你不知道MultiDex到底有多坑. 不了解的可以先看上篇文章:android MultiDex multi ...
- 一梦江湖费六年——QQ群聊天分析
本文结构: 一.那些年我们加过的QQ群 二.数据读入和整理(一)--来自蓝翔的挖掘机 二.数据读入和整理(二)--你不知道的事 三.聊天宏观(1)--寤寐思服 三.聊天宏观(2)日月篇 三.聊天宏观( ...
- IT江湖--这个冬天注定横尸遍野
今年江湖大事繁起,又至寒冬,冻的不仅是温度,更是人心. 这两天上班途中看到多个公众号和媒体发了很多 "XXX公司裁员50%" 等等诸如此类的文章,也真是撼动人心.寒冬,比以往来的更 ...
- spring 为什么可以一统江湖
spring 为什么可以一统江湖 Inversion of Contro 简称IOC 是面向对象编程中的一种设计原则,可以用来减低计算机代码之间的耦合度.也就是面向接口编程的思想. 简单的说就是使用配 ...
随机推荐
- 风炫安全web安全学习第三十二节课 Python代码执行以及代码防御措施
风炫安全web安全学习第三十二节课 Python代码执行以及代码防御措施 Python 语言可能发生的命令执行漏洞 内置危险函数 eval和exec函数 eval eval是一个python内置函数, ...
- Npoi XWPF Word 导出时插入图片无法显示 bug 完美解决
一.来自客户的需求 最近客户来个新需求生成一个word 标签纸,并且需要在标签纸上插入一个logo,并且将erp 中的数据取出来自动写在文档上,不由得淡淡一笑,这不难呀! 于是乎我就写下了这样的代码: ...
- i5 11300H 怎么样 相当于什么水平
i5-11300H 为 4 核 8 线程,主频 3.1GHz,睿频 4.4GHz,三级缓存 8MBi5-11300H 怎么样看完你就知道了 https://list.jd.com/list.html?
- 消息队列 ---常用的 MQ 中间件
目前市面上比较常用的 MQ(Message Queue,消息队列)中间件有 RabbitMQ.Kafka.RocketMQ,如果是轻量级的消息队列可以使用 Redis 提供的消息队列,其中 Redis ...
- try catch finally语句块中存在return语句时的执行情况剖析
2种场景 (1) try中有return,finally中没有return(注意会改变返回值的情形);(2) try中有return,finally中有return; 场景代码分析(idea亲测) 场 ...
- 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=' ...
- git的简单使用方式(基本操作部分)
git的简单使用方式(基本操作部分) 1.简单介绍GIT的工作流程 git一般的工作流程: 克隆git的资源作为工作目录(一般会使用命令git clone进行克隆); 在克隆的资源上对文件进行增加或者 ...
- CSS 奇技淫巧:动态高度过渡动画
这个问题源自于掘金上的一个留言,一个朋友问到,为什么我下面这段代码的高度过渡动画失效了? 伪代码大概是这样: { height: unset; transition: all 0.3s linear; ...
- 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 ...
- freopen函数总结
函数原型: freopen(const char * __restrict__ _Filename,const char * __restrict__ _Mode,FILE * __restrict_ ...