用C#掉用C++的dll直接import就可以之前有不同的类型对应,当要传递结构体的时候就有点麻烦了,这里有一个结构体里边有char*类型,这个类型在C#中调用没法声明,传string是不行的默认string是对应const char*,传stringbuilder得指定大小,不然是没法传的,

查了好久,最后只能用unsafe代码来实现了

用C/C++写一个标准的动态链接库:

头文件,定义了三个接口函数,

#pragma once
#define TESTDLL _declspec(dllexport) #ifndef PKI_DATA_ST
#define PKI_DATA_ST
typedef struct PKI_DATA_st {
int size;
char *value;
}PKI_DATA, *PKI_PDATA;
#endif extern "C"
{
TESTDLL double Add(double a, double b); TESTDLL double Subtract(double a, double b); TESTDLL int TestDn(PKI_DATA cert, PKI_DATA* dn); }

简单实现:

#include "stdafx.h"
#include "TestClass.h" double Add(double a, double b)
{
printf("Add \n");
return a + b;
} double Subtract(double a, double b)
{
printf("Subtract");
return a - b;
} int TestDn(PKI_DATA cert, PKI_DATA* dn)
{
printf("TestDn cert.value: %s\n", cert.value);
dn->size = ;
//dn->value = "helloworld"; //这个种写法需要转换
dn->value =(char*) TEXT("helloworld");//这种写法在选择unicode字符集时C#是可以直接接受的
return ;
}

C#调用,路径可以用当前程序的相对路径,也可以是系统的环境变量中的路径,声明import

        [DllImport("USBLoad.dll", EntryPoint = "Add", CharSet = CharSet.Unicode, CallingConvention = CallingConvention.StdCall)]
static extern double Add(double a,double b); [DllImport("USBLoad.dll", EntryPoint = "TestDn", CharSet = CharSet.Unicode, CallingConvention = CallingConvention.StdCall)]
static extern int TestDn(PKI_DATA cert, ref PKI_DATA dn); [DllImport("USBLoad.dll", EntryPoint = "Subtract", CharSet = CharSet.Unicode, CallingConvention = CallingConvention.StdCall)]
static extern double Subtract(double a, double b);

声明结构体:

        internal unsafe struct PKI_DATA
{
internal int size;
internal char* value;
}

调用:记得属性中勾上允许不安全代码

       [HandleProcessCorruptedStateExceptions]//这个可以捕获C++中的异常 
    [SecurityCritical]
static unsafe void TestUSBLoad()//加unsafe
{
try
{
var a = Add(, );
w(a);
a = Subtract(, );
w(a); PKI_DATA cert = new PKI_DATA();
cert.size = ;
char[] test = "test".ToCharArray();
string s = "";
fixed (char * v=test)//传值初始化。需要fixed关键字
{
cert.value = v;
PKI_DATA dn = new PKI_DATA();
TestDn(cert, ref dn);
var encode = Encoding.ASCII;
var c = dn.value;
List<char> list = new List<char>();
for (int i=;i<dn.size;++i,++c)
{
list.Add(*c);
}
            //兼容ansi编码,先转byte
var bytes = Encoding.Unicode.GetBytes(list.ToArray());
s = Encoding.ASCII.GetString(bytes);//转码
w(new string(dn.value));//设置unicode字符集的情况下可以直接这么写就能得到要字符
}
s = s.Replace("\0","");//转码的时候后边会有很多\0,移除
w(string.Format("[{0}]",s)); }
catch (Exception ex)
{
w(ex.Message);
} }

顺便研究了下C++的动态调用、

如果是静态调用需要头文件和lib库,直接引用头文件,加入lib的链接路径,引用后可以直接用,编译就可以了

动态调用用loadlibrary,记得freelibrary,这些函数在windows.h中,这里建的是QT的项目所以引用的是qt_windows.h:

#include "qt_windows.h"

#define DLLPATH  (LPCTSTR)"USBLoad.dll"

#ifndef PKI_DATA_ST
#define PKI_DATA_ST
typedef struct PKI_DATA_st {
int size;
char *value;
}PKI_DATA, *PKI_PDATA;
#endif //声明入口函数
typedef double(*Add_Func)(double, double); typedef int(*TestDn)(PKI_DATA, PKI_DATA*); int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv); qDebug() << "hello world!";
HINSTANCE m_hModule = LoadLibrary(DLLPATH);//加载dll
if (m_hModule == NULL)
{
printf("LoadLibrary %s failed,err %d\n", DLLPATH, GetLastError());
goto END;
}
Add_Func add;
add = (Add_Func)GetProcAddress(m_hModule,"Add");//获取方法地址,后边调用
double c = add(, );
printf("Add_Func: %lf \n", c);
TestDn test;
test = (TestDn)GetProcAddress(m_hModule, "TestDn");
PKI_DATA cert;
PKI_DATA dn;
cert.size = ;
cert.value = (char*)"test";
int res = test(cert, &dn);
printf("TestDn:%s\n", dn.value); END:
if (m_hModule != NULL)
{
FreeLibrary(m_hModule);
m_hModule = NULL;
printf("FreeLibrary\n");
}
return a.exec();
}

关于怎么把char*转码到正确的编码格式下的C#string,

 char* cchar=data
IntPtr value = new IntPtr(cchar);
var s = Marshal.PtrToStringAnsi(value);

可以用上边的方法,看fcl的源码可以看到上边的方法实质是把char*强转为sbyte*,然后直接new string,这样可以避免C++和C#的byte差异

marshal还有touni的方法,只转unicode的方法,实质是直接用char* new string,

char*和sbyte*两种初始化参数string类型都接受。

C# 调用C/C++动态链接库,结构体中的char*类型的更多相关文章

  1. C/C++中指向结构体变量的指针,调用指向的那个结构体中的成员

    设p是指向结构体变量的指针,则可以通过以下的方式,调用指向的那个结构体中的成员: (1)结构体变量.成员名.如,stu.num. (2)(*p).成员名.如,(*p).num. (3)p->成员 ...

  2. C语言 结构体中的零长度数组

    /* C语言零长度数组大小和取值问题 */ #include <stdio.h> #include <stdlib.h> #include <string.h> s ...

  3. C结构体中数据的内存对齐问题

    转自:http://www.cnblogs.com/qwcbeyond/archive/2012/05/08/2490897.html 32位机一般默认4字节对齐(32位机机器字长4字节),64位机一 ...

  4. C#调用C++ dll时,结构体引用传参的方法

    写了一个C++的LogLog Logit 四参数等算法的接口dll,给C#调用,但是发现传参有问题 如 extern "C" _declspec(dllexport)  bool ...

  5. C#调用C++DLL传递结构体数组的终极解决方案

    在项目开发时,要调用C++封装的DLL,普通的类型C#上一般都对应,只要用DllImport传入从DLL中引入函数就可以了.但是当传递的是结构体.结构体数组或者结构体指针的时候,就会发现C#上没有类型 ...

  6. 绝对好文C#调用C++DLL传递结构体数组的终极解决方案

    C#调用C++DLL传递结构体数组的终极解决方案 时间 2013-09-17 18:40:56 CSDN博客相似文章 (0) 原文  http://blog.csdn.net/xxdddail/art ...

  7. C++结构体中sizeof(1)

    sizeof sizeof操作符的作用是返回一个对象或类型名的长度,长度的单位是字节. 返回值的类型是标准库命名为size_t的类型,size_t类型定义在cstddef头文件中,该头文件是C标准库的 ...

  8. C语言中free()函数释放struct结构体中的规律

    并不是什么新鲜的事情,不过值得注意.首先我们知道,在使用struct来定义并声明一个变量时,将会自动划分出一个连续的储存空间(虽然根据某些对齐原则会出现内存间隙,但是大体上来说还是连续的)这一块连续空 ...

  9. golang 结构体中的匿名接口

    golang 结构体中的匿名接口 代码示例 golang 中,可以给结构体增加匿名field,可参考 unknwon 大神的书. 匿名字段和内嵌结构体 但,golang同时也可以给结构体定义一个匿名i ...

随机推荐

  1. django入门-模型-part2

    尊重作者的劳动,转载请注明作者及原文地址 http://www.cnblogs.com/txwsqk/p/6511177.html 完全翻译自官方文档 https://docs.djangoproje ...

  2. Leetcode 88 合并两个有序数组 Python

    合并两个有序数组 给定两个有序整数数组 nums1 和 nums2,将 nums2 合并到 nums1 中,使得 num1 成为一个有序数组. 说明: 初始化 nums1 和 nums2 的元素数量分 ...

  3. vue环境搭建简介

    简单整理下vue的安装的新建项目 安装node.js和npm 参考其他教程 安装vue npm install vue 安装脚手架 vue-cli npm install --global vue-c ...

  4. SQL语句01

    SQL(Structured Query Language):结构化查询语言 SQL分类:    数据操纵语言DML(Data Manipulation Language)        SELECT ...

  5. Monkey and Banana

    Monkey and BananaTime Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others) ...

  6. mysql使用常见问题

    常见问题之一: 启动mysql时显示: The server quit without updating PID file 1.可能是/usr/local/mysql/data/mysql.pid文件 ...

  7. python socket文件传输实现

    简单版 server(服务端) import socket import subprocess import struct import json import os share_dir = r'E: ...

  8. $scope作用域与依赖注入

    一.$scope与$rootscope作用域 $scope下的数据作为该控制器下的数据moduel,只有在该控制器下才能够访问:而$rootScope则可以可以再任何有效的地方访问到,这个有效的地方指 ...

  9. 把AspDotNetCoreMvc程序运行在Docker上-part2:修改容器以及发布镜像

    在上一个part<把AspDotNetCoreMvc程序运行在Docker上-part1>,已经将成功将aspdotnetcore程序运行在两个不同的容器中,目前两个容器的内容完全相同,只 ...

  10. echo(),print(),print_r()之间的区别?

    echo是PHP语句, print和print_r是函数,语句没有返回值,函数可以有返回值(即便没有用)  print只能打印出简单类型变量的值(如int,string)  print_r可以打印出复 ...