用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. item pipeline 实例:爬取360摄像图片

    生成项目 scrapy startproject image360 cd Image360 && scrapy genspider images  images.so.com 一. 构 ...

  2. Working with Metal—Overview

    看完这个 WWDC 之后的总结. Metal 可以在单位时间内提供 10 倍的 draw call 调用. Background About Draw Call 每一次 draw call 调用都必须 ...

  3. 苹果的 Metal 工程

    Basic Buffers 当向顶点着色器传递数据过多(大于 4096 字节)时, setVertexBytes:length:atIndex: 方法不允许使用,应该使用 setVertexBytes ...

  4. Python中的匿名函数lambda的用法

    一.lambda函数的简介  对lambda函数,它其实是一个类似于def的函数,只不过lambda是一个不需要定义函数名的匿名函数.当我们在有些时候,需要做一些简单的数学计算时,如果定义一个def函 ...

  5. SPI 用户空间的读写操作

    spi_device 虽然用户空间不需要直接用到spi_device结构体,但是这个结构体和用户空间的程序有密切的关系,理解它的成员有助于理解SPI设备节点的IOCTL命令,所以首先来介绍它.在内核中 ...

  6. python学习,day4:生成器,通过yield实现单线程情况下的并发运算

    首先了解一个斐波那契函数的实现,了解下生成器的工作流程 # coding=utf-8 # Author: RyAn Bi def fib(max): n,a,b=0,0,1 while n < ...

  7. 对 云寻觅贴吧(http://tieba.yunxunmi.com/)的简要分析

    1. 云寻觅的用户需求:一方面是很多用户有很多问题,需要高质量的答案,但是搜索引擎无法满足这种需求,百度知道做得不够好,所以用户需要一个平台可以解决他们的问题:另外一方面,又有很多经济良好,时间较为充 ...

  8. flot中文详解

    调用plot函数的方法如下: var plot = $.plot(placeholder, data, options) Data的结构: data应该是data series的一个数组: [ ser ...

  9. 【文档】一、Mysql Binlog概述

    Binlog是一系列日志文件,他们包含的内容是Mysql数据内容的改变.如果想开启binlog功能,需要在启动时带上--log-bin参数. binlog是从Mysql3.23.14版本开始的.它包含 ...

  10. Springboot用官方建议访问Html页面并接传值

    Springboot用官方建议访问Html页面并接传值 我们以前通常习惯用webapp来防止jsp页面,但是到了Springboot中,官方建议用Static文件夹来存放及静态的资源, 用templa ...