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. [ABP教程]第一章 创建服务端

    Web应用程序开发教程 - 第一章: 创建服务端 关于本教程 在本系列教程中, 你将构建一个名为 Acme.BookStore 的用于管理书籍及其作者列表的基于ABP的应用程序. 它是使用以下技术开发 ...

  2. RPC框架学习+小Demo实例

    一.什么是RPC协议? 全称:远程过程调度协议 效果:使消费者向调用本地方法一样调用远程服务方法,对使用者透明 目前常用:Dubbo.Thirft.Sofa.... 功能: 建立远程通信(socket ...

  3. 如何保持json序列化的顺序性?

    说到json,相信没有人会陌生,我们天天都在用.那么,我们来讨论个问题,json有序吗?是谁来决定的呢?如何保持? 说到底,json是框架还是啥?实际上它只是一个数据格式,一个规范标准,它永远不会限制 ...

  4. Lesson_strange_words3

    can 电话耳机,(头戴式听筒) determine 决定 perform 执行 regular 常规的 duty 责任 compatible 兼容 preset 预置,预设 in either or ...

  5. 我是如何在短期内快速掌握Dubbo的原理和源码的(纯干货)?

    写在前面 上周,在[Dubbo系列专题]中更新了两篇文章<冰河开始对Dubbo下手了!>和<俯瞰Dubbo全局,阅读源码前必须掌握这些!!>,收到了很多小伙伴的微信私聊消息,大 ...

  6. 【SpringBoot1.x】SpringBoot1.x 安全

    SpringBoot1.x 安全 文章源码 环境搭建 SpringSecurity 是针对 Spring 项目的安全框架,也是 SpringBoot 底层安全模块默认的技术选型.他可以实现强大的 we ...

  7. kubernets之pv以及pvc

    一 持久卷以及持久卷声明的由来 由于不管是哪种卷,开发者都需要提前预知kubernets集群里面的存储类型,这样就在一定程度上违背了kubernets集群的设计理念,kubernets的设计理念是在由 ...

  8. SSTI

    最牛bypass:https://blog.csdn.net/solitudi/article/details/107752717 SSTI的奇怪绕过姿势:https://blog.csdn.net/ ...

  9. SAP里会话结束方法(杀死进程)

    在SAP的ERP里,有很多方法可以结束一个会话,然而在不同情况下,需要使用的方法也不同.下面从先后顺序来简单说明:1.SM04:最常用的方法,在SM04点击工具栏的会话->结束会话,来关闭一个会 ...

  10. C++:I/O流的概念和流类库的结构

    一.C++输入输出包含以下三个方面的内容: 对系统指定的标准设备的输入和输出.即从键盘输入数据,输出到显示器屏幕.这种输入输出称为标准的输入输出,简称标准I/O. 以外存磁盘文件为对象进行输入和输出, ...