最近做了一个工程,用到c#调用c++dll,将遇到的情况作了一些小结,方便学习总结
c#代码
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using System.Runtime.InteropServices;
namespace Connecter
{
/// <summary>
/// 总结 当参数有ref修饰符 或者IntPtr 是将引用类型传递 会映射到c++ 参数为 * 或者& 的方法(最终 参数 和返回值相同 才能为找到正确方法)
/// 如果c++ byte* 传给c#使用,如果c# byte[] 按照数组接收是要提前声明数组长度
/// 例如 public delegate bool ByteArrDllcallBack([MarshalAs(UnmanagedType.LPArray, SizeConst = 64)] byte[] arr, int length);
/// [DllImport("/Plugins/CreateDLL.dll", SetLastError = true, CharSet = CharSet.Ansi, CallingConvention = CallingConvention.StdCall)]
/// 如 static extern int xxx(IntPtr md,int a);
/// 会映射到c++ int __stdcall xxx(int * md,int a){} 或者int __stdcall xxx(int &md,int a){}
/// xxx 方法名 c# 可以随便取 根据参数类型 和返回值映射到对应的c++中指定的函数中
/// </summary>
public partial class ConnectControler : Form
{
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi, Pack = 1)]
public struct MyData
{
public Double a;
public float b;
public int l; //c++ long 对应c# int
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 256)]
public char[] szVal;
}
[DllImport("/Plugins/CreateDLL.dll", SetLastError = true, CharSet = CharSet.Ansi, CallingConvention = CallingConvention.StdCall)]
static extern int StructFunc(ref MyData md);
[DllImport("/Plugins/CreateDLL.dll", SetLastError = true, CharSet = CharSet.Ansi, CallingConvention = CallingConvention.StdCall)]
static extern int StructFunc(IntPtr md);
[DllImport("/Plugins/CreateDLL.dll", SetLastError = true, CharSet = CharSet.Ansi, CallingConvention = CallingConvention.StdCall)]
static extern bool StructFuncPointer(ref MyData md);
[DllImport("/Plugins/CreateDLL.dll", SetLastError = true, CharSet = CharSet.Ansi, CallingConvention = CallingConvention.StdCall)]
static extern bool StructFuncPointer(IntPtr md);
[DllImport("/Plugins/CreateDLL.dll", SetLastError = true, CharSet = CharSet.Ansi, CallingConvention = CallingConvention.StdCall)]
static extern int StructFuncNormal(MyData md);
[DllImport("/Plugins/CreateDLL.dll", SetLastError = true, CharSet = CharSet.Ansi, CallingConvention = CallingConvention.StdCall)]
static extern int StructFuncNormal(IntPtr md);
[DllImport("/Plugins/CreateDLL.dll", SetLastError = true, CharSet = CharSet.Ansi, CallingConvention = CallingConvention.StdCall)]
static extern bool CharFunc(IntPtr a);
[DllImport("/Plugins/CreateDLL.dll", SetLastError = true, CharSet = CharSet.Ansi, CallingConvention = CallingConvention.StdCall)]
static extern bool CharFunc(ref string a); //对应c++ char* 当引用类型传递的话有问题,用StringBuilder
[DllImport("/Plugins/CreateDLL.dll", SetLastError = true, CharSet = CharSet.Ansi, CallingConvention = CallingConvention.StdCall)]
static extern bool CharFunc(StringBuilder a); //对应c++ char* 当引用类型传递
[DllImport("/Plugins/CreateDLL.dll", SetLastError = true, CharSet = CharSet.Ansi, CallingConvention = CallingConvention.StdCall)]
static extern bool CharFunc(string a); // static extern bool CharFunc([MarshalAs(UnmanagedType.LPStr)]string a); 意思相同
[DllImport("/Plugins/CreateDLL.dll", SetLastError = true, CharSet = CharSet.Ansi, CallingConvention = CallingConvention.StdCall)]
static extern bool CharFunc(char[] a);
[DllImport("/Plugins/CreateDLL.dll", SetLastError = true, CharSet = CharSet.Ansi, CallingConvention = CallingConvention.StdCall)]
static extern bool IntSigleFunc(int para);
[DllImport("/Plugins/CreateDLL.dll", SetLastError = true, CharSet = CharSet.Ansi, CallingConvention = CallingConvention.StdCall)]
static extern bool IntFunc(ref int para);
/// <summary>
/// 含有委托的方法
/// </summary>
/// <returns></returns>
[UnmanagedFunctionPointer(CallingConvention.StdCall)]
public delegate int DllcallBack(int a, float b, string sz);
[DllImport("/Plugins/CreateDLL.dll", SetLastError = true, CharSet = CharSet.Ansi, CallingConvention = CallingConvention.StdCall)]
static extern bool CallBackFunc(DllcallBack dcb);
public ConnectControler()
{
InitializeComponent();
}
# region 涉及到结构体部分
private void btn_structFuncIntPtr_Click(object sender, EventArgs e)
{
/*
* 对应c++的int __stdcall StructFunc(MyData &md) 备注: 与 __stdcall StructFuncPointer(MyData * md) c#在调用上没有区别
* 将MyData 通过 Marshal 转换成IntPtr
*/
MyData md = new MyData();
IntPtr a = Marshal.AllocHGlobal(Marshal.SizeOf(md));
Marshal.StructureToPtr(md, a, true);
int backValue = StructFunc(a);
md = (MyData)Marshal.PtrToStructure(a, typeof(MyData));
StringBuilder sb = new StringBuilder();
Console.WriteLine("==> md.szVal.Length:" + md.szVal.Length);
foreach (char c in md.szVal)
{
if (!c.Equals("\0"))
{
sb.Append(c);
}
}
string content = sb.ToString(); //256个char 对应的string 没有填充的为(\0 结束符)
int index = content.IndexOf("\0");
content = content.Substring(0, index);
//content = content.Replace("\0", "");
Console.WriteLine("==>content.lenth:" + content.ToCharArray().Length);
string text = "参数-" + "==>a:" + md.a + "==>l:" + md.l + "==>szVal:" + content + "==>回调值:" + backValue;
Console.WriteLine(text);
label_content.Text = text;
Marshal.FreeHGlobal(a);
}
private void StFucSt_Click(object sender, EventArgs e)
{
/*
* 对应c++的int __stdcall StructFunc(MyData &md) 备注: 与 __stdcall StructFuncPointer(MyData * md) c#在调用上没有区别
* 将MyData 直接当成结构体传入
*/
MyData md = new MyData();
//md.a = 1;
//md.b = 2;
//md.l = 3;
//md.szVal = new char[256]; //可以不需要事先给结构体赋值
int backValue = StructFunc(ref md);
StringBuilder sb = new StringBuilder();
foreach (char c in md.szVal)
{
sb.Append(c);
}
var content = sb.ToString(); //256个char 对应的string 没有填充的为空字符
int index = content.IndexOf("\0");
content = content.Substring(0, index);
label_content.Text = "参数-" + "==>a:" + md.a + "==>l:" + md.l + "==>szVal:" + content + "==>回调值:" + backValue;
}
private void btn_SctFucSctNormal_Click(object sender, EventArgs e)
{
/*
* 对应c++的int __stdcall StructFuncNormal(MyData md)
* 将MyData 直接当成结构体传入
*/
MyData md = new MyData();
md.szVal = new char[256];
md.a = 0.1;
md.b = 0.2f;
md.l = 3;
int backValue = StructFuncNormal(md);
label_content.Text = "参数-" + "==>a:" + md.a + "==>b:" + md.b + "==>回调值:" + backValue;
}
private void btn_SFSNormalIntPtr_Click(object sender, EventArgs e)
{
/*
* 注意: 这有个注意点
* 本意想 对应c++的int __stdcall StructFuncNormal(MyData md)
* 实际运行对应是c++的int __stdcall StructFunc(MyData &md) (将结构体MyData 当成对象使用)
* 导致这样的原因 :
* 如static extern int StructFuncNormal(IntPtr md); 中StructFuncNormal 可以随意定义,根据参数类型 和返回值映射到对应的c++中指定的函数中
* static extern int StructFuncNormal(ref MyData md); 也会映射到c++的int __stdcall StructFunc(MyData &md)
* 将MyData 直接当成结构体传入
*/
MyData md = new MyData();
md.szVal = new char[256];
md.a = 0.1;
md.b = 0.2f;
md.l = 3;
IntPtr a = Marshal.AllocHGlobal(Marshal.SizeOf(md));
Marshal.StructureToPtr(md, a, true);
int backValue = StructFunc(a);
md = (MyData)Marshal.PtrToStructure(a, typeof(MyData));
StringBuilder sb = new StringBuilder();
foreach (char c in md.szVal)
{
sb.Append(c);
}
var content = sb.ToString(); //256个char 对应的string 没有填充的为空字符
int index = content.IndexOf("\0");
content = content.Substring(0, index);
label_content.Text = "参数-" + "==>a:" + md.a + "==>l:" + md.l + "==>szVal:" + content + "==>回调值:" + backValue;
Marshal.FreeHGlobal(a);
}
#endregion
#region char
private void btn_charFuncIntPtr_Click(object sender, EventArgs e)
{
string par = "btn_charFuncIntPtr_Click";
IntPtr ip = Marshal.StringToHGlobalAnsi(par);
bool value = CharFunc(ip);
par = Marshal.PtrToStringAnsi(ip);
label_content.Text = "参数-" + "==>par:" + par;
Marshal.FreeHGlobal(ip);
}
private void btn_charFuncOther_Click(object sender, EventArgs e)
{
/*
* 这几种定义都是按照 值传递 par 值不会被改变
static extern bool CharFunc(ref string a); // dll c++ 通过这种方式复制的话 sprintf(a, " %s", "CreateDLL CharFunc a"),c# 会报错
static extern bool CharFunc(string a); // static extern bool CharFunc([MarshalAs(UnmanagedType.LPStr)]string a); 意思相同
static extern bool CharFunc(char[] a);
*/
//这样用才能当引用类型传递
StringBuilder sb = new StringBuilder();
sb.Append("value default");
bool value = CharFunc(sb);
label_content.Text = "参数-" + "==>par:" + sb.ToString() + "==>返回值:" + value;
}
#endregion
#region 基础数据类型 int string float 等
private void btn_IntFuncRef_Click(object sender, EventArgs e)
{
/*
* 对应c++的bool __stdcall IntFunc(int *value)
*/
int par = 0;
bool value = IntFunc(ref par);
label_content.Text = "参数-" + "==>par:" + par + "==>返回值:" + value;
}
private void btn_IntFuncSample_Click(object sender, EventArgs e)
{
/*
* 想对应c++的bool __stdcall IntSigleFunc(int *value)
* 实际对应是c++的bool __stdcall IntFunc(int *value)
* c#调用c++dll 中 参数相同 返回值相同 方法名不同这种情况 后续遇到需要进一步研究
*/
int par = 0;
bool value = IntSigleFunc(par);
label_content.Text = "参数-" + "==>par:" + par + "==>返回值:" + value;
}
#endregion
#region 传递回调方法
private void btn_callBackFunc_Click(object sender, EventArgs e)
{
/*
* 对应c++的bool __stdcall CallBackFunc(lpCallBackFun callBack)
* CallBackFunc 方法名必须和c++方法名一致
*/
bool value = CallBackFunc((a, b, c) =>
{
label_content.Text = "参数-" + "==>a:" + a + "==>b:" + b + "==>c:" + c;
return 1;
});
}
#endregion
}
}
C++代码
// CreateDll.cpp : 定义 DLL 应用程序的导出函数。
//
#include "stdafx.h"
#include "CreateDLL.h"
#include <iostream>
int __stdcall StructFuncNormal(MyData md){
md.a = 1;
md.b = 2;
md.l = 3;
strcpy_s(md.szVal, "来自CreateDLL StructFuncNormal");
return 20;
}
int __stdcall StructFunc(MyData &md){
md.a = 10;
md.b = 20;
md.l = 30;
strcpy_s(md.szVal, "来自CreateDLL StructFunc");
return 200;
}
bool __stdcall StructFuncPointer(MyData * md){
md->a = 100;
md->b = 200;
md->l = 300;
strcpy_s(md->szVal, "来自CreateDLL StructFuncPointer");
return true;
}
bool __stdcall CharFunc(char * a){
//a = "CreateDLL CharFunc a"; 这样复制其实是直接辅助给指针,并不能改变指针a对应内存的值, 指针a存放是内存的地址,和引用 功能一样叫法不同
sprintf(a, " %s", "CreateDLL CharFunc a");
return true;
}
bool __stdcall IntSigleFunc(int value){
value = 10;
return false;
}
bool __stdcall IntFunc(int *value){
*value = 20;
return true;
}
bool __stdcall CallBackFunc(lpCallBackFun callBack){
callBack(10, 11, "CallBackFunc");
return true;
}
测试工程目录简介 Conecter 为C#工程 CreateDll为C++dll工程
测试工程以及总结文档连接:https://pan.baidu.com/s/1cAd2suA1nzV7JHpCKVZ1nQ
创建c++dll工程参考:https://blog.csdn.net/qq385105501/article/details/82253725
c#调用c++dll总结文档:https://wenku.baidu.com/view/db475ca280eb6294dd886cc3.html?from_page=view&from_mod=download