C#与C/C++的交互

C++ plugin for Unity 接口数据传输问题 先了解 c++ 部分的代码是托管模式还是非托管模式,当然托管模式就没必要浪费时间去整这个C++ dll 了(除非项目有需要),我们这里只说非托管接口部分的一小内容。1.接口定义a):有return 返回值的 比如:int UNITY_INTERFACE_EXPORT UNITY_INTERFACE_API  GetChildNodeCount(FbxNode* pNode);b):没有return... 阅读详情

最近在编写Warensoft3D游戏引擎,并预计明年年初发布测试版本,底层引擎使用DirectX和MONO来编写,上层的逻辑使用C#来编写,因此编写了大量C#与C++互调的代码,现在经验写出来与大家分享,并希望后来者少走弯路。

C#与C++交互,总体来说可以有两种方法:

  • 利用C++/CLI作为代理中间层
  • 利用PInvoke实现直接调用

第一种方法:实现起来比较简单直观,并且可以实现C#调用C++所写的类,但是问题是MONO构架不支持C++/CLI功能,因此无法实现脱离Microsoft .NET Framework跨平台运行。

第二种方法:简单的实现并不麻烦,只要添加DllImportAttribute特性即可以导入C/C++的函数,但是问题是PInvoke不能简单的实现对C++类的调用。在Warensoft3D中为了可以使用MONO实现跨平台(当然DirectX是不能跨平台的),所以使用了本方法,下面将对本方法展开详细的说明。

测试平台:

Windows7 64位,VS2010,.NET4.0

注意事项:

PInvoke从功能上来说,只支持函数调用,在被导出的函数前面一定要添加extern "C"来指明导出函数的时候使用C语言方式编译和连接,这样保证函数定义的名字和导出的名字相同,否则如果默认按C++方式导出,那个函数的名字就会变得乱七八糟,我们的程序就无法找到入口点了。

本文将说明以下几点:

  • 互调的基本原理
  • 基本数据类型的传递
  • 指针的传递
  • 函数指针的传递
  • 结构体的传递

 

 

  1. 互调的基本原理

    首先,我们来看一个再常规不过的概念—"数据类型"

    我们知道在大多数的静态语言中定义变量的时候都要先指定其数据类型,所谓数据类型,都是人们强加的一个便于记忆的名称,究其本质就是指明了这个数据在内存里到底是占用了几个字节,程序在运行的时候,首先找到这个数据的地址,然后再按着该类型的长度,读取相对应的内存,然后再处理。

    了解了前面这个事儿,所有编程语言之间进行互调就有点门道儿了。对于不同语言之间的互调,只要将该数据的指针(内存地址)传递给另一个语言,在另一个语言中根据通信协议将指针所指向的数据存储入长度对应的数据类型即可,当然要满足以下几点:

    1. 对于像Java,.NET这样有运行时虚拟机编程语言来讲,由于虚拟机会让堆内存来回转移,因此,在进行互调的时候,要保证正在被互调的数据所在的内存一定要固定,不能被转移。
    2. 有一些编程语言支持指针,有一些语言不支持指针(如Java),这个问题并不重要,所谓指针,其实就是一个内存地址,对于32位OS的指针是一个32位整数,而对于64位机OS的指针是一个64位整数。因为大多数语言中都有整型数,所以可以利用整型来接收指针。
  2. 基本数据类型的传递

互调过程中,最基本要传递的无非是数值和字符,即:int,long,float,char等等,但是此类型非彼类型,C/C++与C#中有一些数据类型长度是不一样的,下表中列出常见数据类型的异同:

C/C++ 

C# 

长度

short 

short 

2Bytes

int

int 

4Bytes 

long(该类型在传递的时候常常会弄混)

int 

4Bytes 

bool 

bool 

1Byte 

char(Ascii码字符)

byte 

1Byte 

wchar_t(Unicode字符,该类型与C#中的Char兼容)

char 

2Bytes 

float

float

4Bytes

double 

double 

8Bytes 

最容易弄混的是就是long,char两个类型,在C/C++中long和int都是4个字节,都对应着C#中的int类型,而C/C++中的char类型占一个字节,用来表示一个ASCII码字符,在C#中能够表示一个字节的是byte类型。与C#中char类型对应的应该是C/C++中的wchar_t类型,对应的是一个2字节的Unicode字符。

下面通过实例来说明调用过程:

第一步:

建立一个C++的Win32DLL,如下图所示:

这里要注意选择"Export symbols"导出符号。点击完成。

第二步:

由于项目的名称是"TestCPPDLL",因此,会自动生成TestCPPDLL.h和TestCPPDLL.cpp两个文件,.h文件是要导出内容的声明文件,为了能清楚的说明问题,我们将TestCPPDLL.h和TestCPPDLL.cpp两个文件中的所有内容都删除,然后在TestCPPDLL.h中添加如下内容:

第一行代码中定义了一个名为"TESTCPPDLL_API"的宏,该宏对应的内容是"__declspec(dllexport)"意思是将后面修饰的内容定义为DLL中要导出的内容。当然你也可以不使用这个宏,可以直接将"__declspec(dllexport)"写在要导出的函数前面。

第二行中的"EXTERN_C",是在"winnt.h"中定义的宏,在函数前面添加"EXTERN_C"等同于在函数前面添加extern "C",意思是该函数在编译和连接时使用C语言的方式,以保证函数名字不变。

第二行的代码是一个函数的声明,说明该函数可以被模块外部调用,其定义实现在TestCPPDLL.cpp中,TestCPPDLL.cpp的代码如下所示:

第三步:

在编译C++DLL之前,需要做以下配置,在项目属性对话框中选择"C/C++"|"Advanced",将Compile AS 选项的值改为"C++"。然后确定,并编译。

生成的DLL文件如下图所示:

第四步:

首先,添加一个C#的应用程序,如果要在C#中调用C++的DLL文件,先要在C#的类中添加一个静态方法,并且使用DllImportAttribute对该方法进行修饰,代码如下所示:

DllImport中的第一个参数是指明DLL文件的位置,第二个参数"EntryPoint"用来指明对应的C/C++中的函数名称是什么。"extern"关键字表明该处声明的这个Add方法是一个外部调用。

该方法声明完毕之后,就可以像调用一个普通的静态方法一样去使用了。

下面是示例程序:

class Program

{

[DllImport(@"E:\ex\TestCPPDLL\Debug\TestCPPDLL.dll", EntryPoint = "Add")]

extern static int Add(int a, int b);

static void Main(string[] args)

{

int c = Add(1,2);

Console.WriteLine(c);

Console.Read();

}

}

在运行C#程序之前,先要修改C#的项目属性,如下图所示:

将platform target设置为x86,并且允许非安全代码(后面有用)。

然后运行该C#程序,其结果如下图所示:

第五步:

前面的Add方法中传递的是数值类型(int),其他的数据类型,如float,double,和bool类型的传递方式是一样的,下面演示如何传递字符串。

在TestCPPDLL.h中添加一个新的函数声明,代码如下:

*content);

这里的参数是wchar_t类型的指针,对应着C#中的char类型。TestCPPDLL.cpp中添加如下代码:

TESTCPPDLL_API void __stdcall WriteString(wchar_t*content)

{

    cout<<content;

}

该代码的功能就是将输入的字符串通过C++在控制台上输出。下面是在C#中的声明:

)]

extern unsafe static void WriteString(char*c);

调用过程如下所示:

//因为使用指针,因为要声明非安全域

unsafe

{

//在传递字符串时,将字符所在的内存固化,

//并取出字符数组的指针

fixed (char* p = &("hello".ToCharArray()[0]))

{

//调用方法

WriteString(p);

}

}

 

其运行效果如下图所示:

 3. 指针的传递

根据前面介绍的数据类型对照表,我们可以直接在方法中传递指针,但是要注意的是我们常常需要将数组的指针(数据入口地址,第一个元素的地址),数据从C/C++到C#时问题不大,但是如果从C#到C/C++时一定要将数组先固化,然后再传递处理。

下面演示如何传递指针,首先在TestCPPDLL.h中添加下列声明:

//传入一个整型指针,将其所指向的内容加1

EXTERN_C TESTCPPDLL_API void __stdcall AddInt(int *i);

//传入一个整型数组的指针以及数组长度,遍历每一个元素并且输出

EXTERN_C TESTCPPDLL_API void __stdcall AddIntArray(int *firstElement,int arraylength);

//在C++中生成一个整型数组,并且数组指针返回给C#

EXTERN_C TESTCPPDLL_API int* __stdcall GetArrayFromCPP();

其实现写在TestCPPDLL.cpp中,代码如下所示:

TESTCPPDLL_API void __stdcall AddInt(int *i)

{

    (*i)++;

}

TESTCPPDLL_API void __stdcall AddIntArray(int *firstElement,int arrayLength)

{

    int*currentPointer=firstElement;

    for (int i = 0; i < arrayLength; i++)

    {

        cout<<*currentPointer;

        currentPointer++;

    }

    cout<<endl;

}

 

int *arrPtr;

TESTCPPDLL_API int* __stdcall GetArrayFromCPP()

{

    arrPtr=new int[10];

    

    for (int i = 0; i < 10; i++)

    {

        arrPtr[i]=i;

    }

    

    return arrPtr;

}

 

对应调用的C#代码如下所示:

[DllImport(@"E:\ex\TestCPPDLL\Debug\TestCPPDLL.dll", EntryPoint = "AddInt")]

extern unsafe static void AddInt(int* i);

[DllImport(@"E:\ex\TestCPPDLL\Debug\TestCPPDLL.dll", EntryPoint = "AddIntArray")]

extern unsafe static void AddIntArray(int* firstElement, int arraylength);

[DllImport(@"E:\ex\TestCPPDLL\Debug\TestCPPDLL.dll", EntryPoint = "GetArrayFromCPP")]

extern unsafe static int* GetArrayFromCPP();

调用过程如下所示:

unsafe

{

// 调用C++中的AddInt方法

int i = 10;

 

AddInt(&i);

Console.WriteLine(i);

 

//调用C++中的AddIntArray方法将C#中的数据传递到C++中,并在C++中输出

int[] CSArray = new int[10];

for (int iArr = 0; iArr < 10; iArr++)

{

CSArray[iArr] = iArr;

}

fixed (int* pCSArray = &CSArray[0])

{

AddIntArray(pCSArray, 10);

}

//调用C++中的GetArrayFromCPP方法获取一个C++中建立的数组

int* pArrayPointer = null;

pArrayPointer = GetArrayFromCPP();

for (int iArr = 0; iArr < 10; iArr++)

{

Console.WriteLine(*pArrayPointer);

pArrayPointer++;

}

}

4. 函数指针的传递

前面说明的都是简单数据类型的及其指针的传递,利用PInvoke我们也可以实现函数指针的传递,C#中并没有函数指针的概念,但是可以使用委托(delegate)来代替函数指针,关于C#中委托的说明,可以参考笔者前面的一个文章:《C#委托及事件》

大家可能会问,为什么要传递函数指针呢?利用PInvoke可以实现C#对C/C++函数的调用,反过来,我们能不能在C/C++程序运行的某一时刻,来调用一个C#对应的函数呢?(例如在C++中存在一个独立线程,该线程可能在任意时刻触发一个事件,并且需要通知C#)。这个时候,我们就有必要将一个C#中已经指向某一个函数的函数指针(委托)传递给C++。

想要传递函数指针,首先要在C#中定义一个委托,并且在C++中定义一个函数指针,同时要保证委托和函数指针具备相同的函数原型,我们首先编写C#的代码,如下所示:

//定义一个委托,返回值为空,存在一个整型参数

public delegate void CSCallback(int tick);

//定义一个用于回调的方法,与前面定义的委托的原型一样

//该方法会被C++所调用

static void CSCallbackFunction(int tick)

{

Console.WriteLine(tick.ToString ());

}

//定义一个委托类型的实例,

//在主程序中该委托实例将指向前面定义的CSCallbackFunction方法

static CSCallback callback;

 

在CS的主程序中让callback指向CSCallbackFunction方法,代码如下所示:

//调用委托所指向的方法

callback = CSCallbackFunction;

 

 

然后在C/C++中定义一个函数指针,并且添加一个用于设置函数指针的函数,TestCPPDLL.h中的代码如下所示:

//定义一个函数指针

typedef void (__stdcall *CPPCallback)(int tick);

//定义一个用于设置函数指针的方法,

//并在该函数中调用C#中传递过来的委托

EXTERN_C TESTCPPDLL_API void SetCallback(CPPCallback callback);

SetCallback函数的实现在TestCPPDLL.cpp中,代码如下所示:

TESTCPPDLL_API void SetCallback(CPPCallback callback)

{

    int tick=rand();

    //下面的代码是对C#中委托进行调用

    callback(tick);

}

在C#中添加SetCallback函数的声明,代码如下所示:

//这里使用CSCallback委托类型来兼容C++里的CPPCallback函数指针

[DllImport(@"E:\ex\TestCPPDLL\Debug\TestCPPDLL.dll", EntryPoint = "SetCallback")]

extern static void SetCallback(CSCallback callback);

在C#中的调用过程如下所示:

//让委托指向将被回调的方法

callback = CSCallbackFunction;

//将委托传递给C++

SetCallback(callback);

SetCallback方法被执行后,在C#中定义的CSCallbackFunction就会被C++所调用。

5. 结构体的传递

传递结构体的想法和传递一个int类型数据类似,struct中的数据是在内存中顺序排列的,只要保证保证以下几点,就可以直接传递结构体,甚至是结构体的指针:

  • 要传递的成员为公有的值类型字段
  • C#中结构体字段类型与C++结构体中的字段类型相兼容
  • C#结构中的字段顺序与C++结构体中的字段顺序相同,要保证该功能,需要将C#结构体标记为[StructLayout( LayoutKind.Sequential)]

下面通过代码进行说明,首先在C#中添加一个结构体,代码如下所示:

[StructLayout( LayoutKind.Sequential)]

struct Vector3

{

public float X, Y, Z;

}

该结构体表示一个3D向量,包括X,Y,Z三个float类型的分量。

然后在TestCPPDLL.h中也定义一个相同结构的结构体,代码如下所示:

struct Vector3

{

    float X,Y,Z;

};

在TestCPPDLL.h中声明一个用于传递Vector3结构体的一个函数,代码如下所示:

EXTERN_C TESTCPPDLL_API void __stdcall SendStructFromCSToCPP(Vector3 vector);

在TestCPPDLL.cpp中将其实现,代码如下所示:

TESTCPPDLL_API void __stdcall SendStructFromCSToCPP(Vector3 vector)

{

    cout<<"got vector3 in cpp,x:";

    cout<<vector.X;

    cout<<",Y:";

    cout<<vector.Y;

    cout<<",Z:";

    cout<<vector.Z;

}

在C#中添加对SendStructFromCSToCPP函数的声明,代码如下所示:

[DllImport(@"E:\ex\TestCPPDLL\Debug\TestCPPDLL.dll", EntryPoint = "SendStructFromCSToCPP")]

extern static void SendStructFromCSToCPP(Vector3 vector);

C#中的调用过程如下所示:

//建立一个Vector3的实例

Vector3 vector = new Vector3() { X =10,Y=20,Z=30 };

//将vector传递给C++并在C++中输出

SendStructFromCSToCPP(vector);

基输出效果如下所示:

 

完整的TestCPPDLL.h代码如下所示:

 

复制代码
#define TESTCPPDLL_API __declspec(dllexport)

EXTERN_C TESTCPPDLL_API int __stdcall Add(int a,int b);

EXTERN_C TESTCPPDLL_API void __stdcall WriteString(wchar_t*content);

//传入一个整型指针,将其所指向的内容加1

EXTERN_C TESTCPPDLL_API void __stdcall AddInt(int *i);

//传入一个整型数组的指针以及数组长度,遍历每一个元素并且输出

EXTERN_C TESTCPPDLL_API void __stdcall AddIntArray(int *firstElement,int arraylength);

//在C++中生成一个整型数组,并且数组指针返回给C#

EXTERN_C TESTCPPDLL_API int* __stdcall GetArrayFromCPP();

 

//定义一个函数指针

typedef void (__stdcall *CPPCallback)(int tick);

//定义一个用于设置函数指针的方法,

//并在该函数中调用C#中传递过来的委托

EXTERN_C TESTCPPDLL_API void __stdcall SetCallback(CPPCallback callback);

 

struct Vector3

{

    float X,Y,Z;

};

EXTERN_C TESTCPPDLL_API void __stdcall SendStructFromCSToCPP(Vector3 vector);
复制代码



 

完整的TestCPPDLL.CPP代码如下所示:

 

复制代码
#include "stdafx.h"

#include <iostream>

#include "TestCPPDLL.h"

using namespace std;

TESTCPPDLL_API int __stdcall Add(int a,int b)

{

    return a+b;

}

TESTCPPDLL_API void __stdcall WriteString(wchar_t*content)

{

    wprintf(content);

    printf("\n");

}

 

TESTCPPDLL_API void __stdcall AddInt(int *i)

{

    (*i)++;

}

 

TESTCPPDLL_API void __stdcall AddIntArray(int *firstElement,int arrayLength)

{

    int*currentPointer=firstElement;

    for (int i = 0; i < arrayLength; i++)

    {

        cout<<*currentPointer;

        currentPointer++;

    }

    cout<<endl;

}

int *arrPtr;

TESTCPPDLL_API int* __stdcall GetArrayFromCPP()

{

    arrPtr=new int[10];

    

    for (int i = 0; i < 10; i++)

    {

        arrPtr[i]=i;

    }

    

    return arrPtr;

}

 

TESTCPPDLL_API void __stdcall SetCallback(CPPCallback callback)

{

    int tick=100;

    //下面的代码是对C#中委托进行调用

    callback(tick);

}

 

TESTCPPDLL_API void __stdcall SendStructFromCSToCPP(Vector3 vector)

{

    cout<<"got vector3 in cpp,x:";

    cout<<vector.X;

    cout<<",Y:";

    cout<<vector.Y;

    cout<<",Z:";

    cout<<vector.Z;

}
复制代码



 

 

完整的C#代码如下所示:

 

复制代码
using System;

using System.Collections.Generic;

using System.Runtime.InteropServices;

using System.Text;

 

namespace ConsoleApplication1

{

class Program

{

[DllImport(@"E:\ex\TestCPPDLL\Debug\TestCPPDLL.dll", EntryPoint = "Add")]

extern static int Add(int a, int b);

[DllImport(@"E:\ex\TestCPPDLL\Debug\TestCPPDLL.dll", EntryPoint = "WriteString")]

extern unsafe static void WriteString(char* c);

[DllImport(@"E:\ex\TestCPPDLL\Debug\TestCPPDLL.dll", EntryPoint = "AddInt")]

extern unsafe static void AddInt(int* i);

[DllImport(@"E:\ex\TestCPPDLL\Debug\TestCPPDLL.dll", EntryPoint = "AddIntArray")]

extern unsafe static void AddIntArray(int* firstElement, int arraylength);

[DllImport(@"E:\ex\TestCPPDLL\Debug\TestCPPDLL.dll", EntryPoint = "GetArrayFromCPP")]

extern unsafe static int* GetArrayFromCPP();

 

 

 

//定义一个委托,返回值为空,存在一个整型参数

public delegate void CSCallback(int tick);

//定义一个用于回调的方法,与前面定义的委托的原型一样

//该方法会被C++所调用

static void CSCallbackFunction(int tick)

{

Console.WriteLine(tick.ToString());

 

}

//定义一个委托类型的实例,

//在主程序中该委托实例将指向前面定义的CSCallbackFunction方法

static CSCallback callback;

 

 

//这里使用CSCallback委托类型来兼容C++里的CPPCallback函数指针

[DllImport(@"E:\ex\TestCPPDLL\Debug\TestCPPDLL.dll", EntryPoint = "SetCallback")]

extern static void SetCallback(CSCallback callback);

 

[StructLayout(LayoutKind.Sequential)]

struct Vector3

{

public float X, Y, Z;

}

 

[DllImport(@"E:\ex\TestCPPDLL\Debug\TestCPPDLL.dll", EntryPoint = "SendStructFromCSToCPP")]

extern static void SendStructFromCSToCPP(Vector3 vector);

 

static void Main(string[] args)

{

int c = Add(1, 2);

Console.WriteLine(c);

//因为使用指针,因为要声明非安全域

unsafe

{

//在传递字符串时,将字符所在的内存固化,

//并取出字符数组的指针

fixed (char* p = &("hello".ToCharArray()[0]))

{

//调用方法

WriteString(p);

}

 

}

unsafe

{

// 调用C++中的AddInt方法

int i = 10;

 

AddInt(&i);

Console.WriteLine(i);

 

//调用C++中的AddIntArray方法将C#中的数据传递到C++中,并在C++中输出

int[] CSArray = new int[10];

for (int iArr = 0; iArr < 10; iArr++)

{

CSArray[iArr] = iArr;

}

fixed (int* pCSArray = &CSArray[0])

{

AddIntArray(pCSArray, 10);

}

//调用C++中的GetArrayFromCPP方法获取一个C++中建立的数组

int* pArrayPointer = null;

pArrayPointer = GetArrayFromCPP();

for (int iArr = 0; iArr < 10; iArr++)

{

Console.WriteLine(*pArrayPointer);

pArrayPointer++;

}

}

 

 

 

 

//让委托指向将被回调的方法

callback = CSCallbackFunction;

//将委托传递给C++

SetCallback(callback);

 

//建立一个Vector3的实例

Vector3 vector = new Vector3() { X = 10, Y = 20, Z = 30 };

//将vector传递给C++并在C++中输出

SendStructFromCSToCPP(vector);

 

 

Console.Read();

}

}

}
复制代码
C#与C++联合开发(C#调用C++) 1.入门实例 (1)C++部分 AddOperate.h extern "C" _declspec(dllexport) int Sum(int a, int b); AddOperate.cpp #include "AddOperate.h" #include "iostream" using namespace std; int Sum(int a, int b) { if (a - (int)a != 0 || b - (int)b != 0) { cout << "请输入整数" 阅读详情

相关推荐

C#与C/C++交互的两种高效DLL调用策略

本文深入探讨了C#与C/C++进行DLL交互的两种核心策略:直接使用DllImport进行P/Invoke调用,以及通过C++/CLI创建CLR封装层。文章详细对比了两种方法在性能、安全性、开发效率及适用场景上的优劣,并提供了处理复杂数据类型、资源管理和异常转换的实战技巧,帮助开发者根据项目需求选择最高效、最稳妥的跨语言互操作方案。

weixin_29296241的博客 616

转载:C#与C/C++的交互

我们知道在大多数的静态语言中定义变量的时候都要先指定其数据类型,所谓数据类型,都是人们强加的一个便于记忆的名称,究其本质就是指明了这个数据在内存里到底是占用了几个字节,程序在运行的时候,首先找到这个数据的地址,然后再按着该类型的长度,读取相对应的内存,然后再处理。前面说明的都是简单数据类型的及其指针的传递,利用PInvoke我们也可以实现函数指针的传递,C#中并没有函数指针的概念,但是可以使用委托(delegate)来代替函数指针,关于C#中委托的说明,可以参考笔者前面的一个文章:《C#委托及事件》

xuejianxinokok的专栏 229

C#与C/C++交互实战:从P/Invoke到架构设计的完整指南

在软件开发中,托管代码与非托管代码的交互是连接不同编程范式、复用现有资产的关键技术。其核心原理在于通过平台调用机制,在托管内存环境与非托管内存环境之间建立安全的数据通道,实现函数调用与资源共享。这项技术的核心价值在于能够充分发挥C#的开发效率与C/C++的性能优势,解决跨语言、跨运行时环境下的系统集成问题。典型应用场景包括调用遗留的C++算法库、在Unity中集成高性能模块、开发工业控制上位机软件等。本文将深入探讨P/Invoke、数据封送、内存管理等关键技术,并分享如何通过C++/CLI、SafeHand

weixin_32466193的博客 249

C#与C/C++的常用交互源码

C++核心代码如下: TestCppDll.h文件 #define TESTCPPDLL_API __declspec(dllexport) struct CAddStruct { int A, B, C; char D[100]; }; EXTERN_C TESTCPPDLL_API double __stdcall MixedOperation(short a, int...

u010248077的博客 653

机器视觉-C#&C++ ROI交互控件以及图形显示

控件主要使用DispImgCtrDll.dll库,整个库支持各种ROI交互和图形显示。 画圆,画圆环,画线,画线段,画Mark,画多边形,画矩形,画旋转矩形,画文本

DHmouse的博客 2376

基于LABVIEW的机器视觉-ROI关注区域.rar

利用labview编译出简易的labview机器视觉的ROI关注区域的程序,比较简易适合初学者

c# 从地址拷贝byte_C#与C/C++的交互

C#与C++交互,总体来说可以有两种方法:利用C++/CLI作为代理中间层利用PInvoke实现直接调用第一种方法:实现起来比较简单直观,并且可以实现C#调用C++所写的类,但是问题是MONO构架不支持C++/CLI功能,因此无法实现脱离Microsoft .NET Framework跨平台运行。第二种方法:简单的实现并不麻烦,只要添加DllImportAttribute特性即可以导入C/C++的...

weixin_28961645的博客 210

STM32使用HAL库实现ModbusRTU主站核心要点

深入解析STM32如何利用HAL库高效实现ModbusRTU主站通信,重点讲解串口配置、定时控制与modbusrtu协议帧的构建方法,提升嵌入式通信稳定性与实时性。

weixin_42668301的博客 373

10 年实战总结:C# 通过 P/Invoke 与 C/C++ 交互的完整教程

内存桥接:CLR 自动封送数据,在托管堆和非托管堆间传递函数定位:通过DllImport属性定位原生函数入口点类型映射:基本类型对应关系:C#int↔ C/C++intC#string↔ C/C++char*C#bool↔ C/C++BOOL(4字节)调用约定匹配Windows API 用GCC 编译库用调试技巧使用验证导出函数捕获定位签名错误性能优化高频调用时缓存Delegate实例避免频繁封送大型数据(>1MB)跨平台方案#endif。

2501_93891101的博客 423

Fofa API高阶玩法:用Python批量获取漏洞资产(含OA系统扫描实战)

本文详细介绍了如何利用Python结合Fofa API实现自动化漏洞资产收集的高阶实战技巧。通过构建健壮的客户端、设计精准查询语法、实现多线程批量采集,并以OA系统漏洞扫描为例,演示了从资产发现、过滤验证到报告生成的全流程,旨在提升安全运维与应急响应的效率。

weixin_28607671的博客 498

FPS游戏自动枪械识别+压枪(以PUBG为例)

使用Python3.9作为开发环境,实现一个通用的FPS游戏自动检测枪械配件+压枪宏的软件开发思路

weixin_37827742的博客 3万+

10 年实战案例:C# 与 C/C++ 交互在工业软件中的落地技巧

在工业软件领域(如自动化控制系统、CAD/CAM工具或仿真平台),C#常用于构建用户界面和业务逻辑,而C/C++则用于高性能核心模块(如实时计算或硬件驱动)。两者的高效交互是项目成功的关键。基于10年实战案例(覆盖多个行业项目),我将逐步分享落地技巧,确保结构清晰、实用可靠。重点包括:交互技术选择、性能优化、错误处理和内存管理。核心技巧:优先P/Invoke for 简单交互,优化数据传递减少拷贝,强化错误处理保障稳定性。性能指标:在工业场景中,目标交互延迟<5ms(通过异步和指针优化实现)。长期维护。

2501_93892493的博客 1044

Oracle集群管理-19.3升级19.18 opatchauto总结(java报错,resume oneoffs缺失目录)

t. (那个认为补丁已经打上了,但是oneoffs目录里面没有。将安装包文件的权限全部改成777.就继续执行了上述命令 ,此时使用opatch lsinv也是报错 ,在正常执行opatchauto apply。1 创建目录,存放补丁包,并赋予权限。于是使用如下命令对补丁进行回滚。

oradbm的博客 1415

C# 与 C/C++ 的交互

P/Invoke 是可用于从托管代码访问非托管库中的结构、回调和函数的一种技术。托管代码与非托管的区别托管代码和非托管代码的主要区别是内存管理方式和对计算机资源的访问方式。托管代码通常运行在托管环境中,如 mono 或 java 虚拟机等,这些环境提供了垃圾回收器(GC)等工具来管理内存。在托管环境中,程序员通常不需要手动分配和释放内存,因为这些任务由运行时系统自动完成。托管代码通常具有更高的安全性和可移植性,因为它们运行在虚拟机中,而不是直接在操作系统上运行。

Truck_Truck的博客 2051

运放+三极管恒流源电路设计实战:从原理到仿真一步到位(附Multisim文件)

本文详细解析了运放与三极管构建恒流源电路的核心原理,重点阐述了负反馈机制如何实现精准的电流控制。通过Multisim仿真实战,从电路搭建、参数计算到负载调整率与动态响应分析,一步步指导设计过程,并提供常见故障的调试技巧与PCB设计要点,帮助工程师从理论平稳过渡到可靠实践。

7z8x2c4v6b9的博客 711

C# 调用halcon中visualize_object_model_3d显示点云

3.1 判断线程是否为空,如果为空,则新创建,如果不为空,则中断线程,清空窗口后再次执行创建操作3.2 创建:调用show()方法实例化显示类的对象,传入显示参数,将线程testvisual 绑定 在 类对象的action方法上(此处action方法内部,只调用了visualize_object_model_3d算子)3.3 启动线程。

weixin_42362219的博客 1403

从入门到精通:10 年经验梳理的 C# 与 C/C++ 交互学习路径

$ \text{C#接口定义} \equiv \text{C++实现} $$开始逐步过渡到C++/CLI,优先解决内存管理问题,性能优化放在后期迭代!(基于10年开发经验梳理,分阶段递进式掌握核心技术):解决复杂数据结构和内存管理。:理解跨语言交互核心机制。:实现高性能与复杂场景。:解决企业级开发痛点。:跨语言交互的本质是。

2501_93892404的博客 452

uniapp - 实现手机端H5网页直播功能效果,拿到后端推流地址或播放资源后进行前端的流畅播放和画面展示功能,uniapp实时推流/拉流直播系统详细教程(提供完整示例代码,一键复制开箱即用!)

uniapp,h5网页,手机端H5网页,uniapp h5直播功能页面,uniapp直播实时推送显示,uniapp h5直播怎么做,怎么使用uniapp去实现移动端手机H5直播,h5 vue+uniapp怎么做直播,uniapp中使用videojs构建H5直播播放器,uniappH5端怎么实现直播推流,uniapp如何实现直播,直播聊天功能,h5平台端直播用什么插件,uniapp手机网页直播用什么组件插件播放器,uniapp手机H5直播播放器详细教程,直播项目,怎么使用vue+uniapp怎么做H5实时直播

✅ 精选推荐文章 8793

C#与C/C++的交互

C#与C/C++的交互 Admin 2011年12月9日 C#与C/C++的交互 warensoft 中科院策画所培训中间  迎接转载,请注明出处及作者 比来在编写Warensoft3D游戏引擎,并估计来岁年初公布测试版本,底层引擎应用DirectX和MONO来编写,上层的逻辑应用C#来编写,是以编写了多量C#与C++互调的代码,如今经验写出来与大师分享,并

千愚千寻 2505

基于工作流的低代码AI应用开发:Flock平台核心架构与实战指南

在AI应用开发领域,工作流编排正成为连接大语言模型(LLM)能力与复杂业务逻辑的关键技术。其核心原理是将任务分解为模块化节点,通过可视化方式定义执行顺序与数据流向,从而实现灵活可控的自动化流程。这种范式极大地提升了开发效率,降低了技术门槛,使得开发者能够聚焦于业务设计而非底层代码。在技术价值层面,工作流编排支持检索增强生成(RAG)、多智能体协作等高级模式,并能通过人工干预节点确保关键流程的可靠性与合规性。其典型应用场景包括智能客服、自动化办公、数据分析助手等。本文以Flock平台为例,深入解析了如何利用其

weixin_26848497的博客 382

文档:亲测可用解决vnc灰屏,黑屏,鼠标大黑叉情况

主要解决vnc-viewer灰屏,黑屏,鼠标大黑叉等现象,详细见文档。

C/C++通过函数指针与C#通信----C#与C/C++的交互

C/C++通过函数指针与C#通信----C#与C/C++的交互

家里蹲大学摸鱼学专业优秀毕业生的博客 3784

深度学习工具包 DeepLearningToolbox

深度学习工具包Deprecation notice.-----This toolbox is outdated and no longer maintained.There are much better tools available for deep learning than this toolbox, e.g. [Theano](http://deeplearning.net/software/theano/), [torch](http://torch.ch/) or [tensorflow](http://www.tensorflow.org/)I would suggest you use one of the tools mentioned above rather than use this toolbox.Best, Rasmus.DeepLearnToolbox================A Matlab toolbox for Deep Learning.Deep Learning is a new subfield of machine learning that focuses on learning deep hierarchical models of data.It is inspired by the human brain's apparent deep (layered, hierarchical) architecture.A good overview of the theory of Deep Learning theory is[Learning Deep Architectures for AI](http://www.iro.umontreal.ca/~bengioy/papers/ftml_book.pdf)For a more informal introduction, see the following videos by Geoffrey Hinton and Andrew Ng.* [The Next Generation of Neural Networks](http://www.youtube.com/watch?v=AyzOUbkUf3M) (Hinton, 2007)* [Recent Developments in Deep Learning](http://www.youtube.com/watch?v=VdIURAu1-aU) (Hinton, 2010)* [Unsupervised Feature Learning and Deep Learning](http://www.youtube.com/watch?v=ZmNOAtZIgIk) (Ng, 2011)If you use this toolbox in your research please cite [Prediction as a candidate for learning deep hierarchical models of data](http://www2.imm.dtu.dk/pubdb/views/publication_details.php?id=6284)```@MASTERSTHESIS\{IMM2012-06284, author = "R. B. Palm", title = "Prediction as a candidate for learning deep hierarchical models of data", year = "2012",}```Contact: rasmusbergpalm at gmail dot comDirectories included in the toolbox-----------------------------------`NN/` - A library for Feedforward Backpropagation Neural Networks`CNN/` - A library for Convolutional Neural Networks`DBN/` - A library for Deep Belief Networks`SAE/` - A library for Stacked Auto-Encoders`CAE/` - A library for Convolutional Auto-Encoders`util/` - Utility functions used by the libraries`data/` - Data used by the examples`tests/` - unit tests to verify toolbox is workingFor references on each library check REFS.mdSetup-----1. Download.2. addpath(genpath('DeepLearnToolbox'));Example: Deep Belief Network---------------------```matlabfunction test_example_DBNload mnist_uint8;train_x = double(train_x) / 255;test_x = double(test_x) / 255;train_y = double(train_y);test_y = double(test_y);%% ex1 train a 100 hidden unit RBM and visualize its weightsrand('state',0)dbn.sizes = [100];opts.numepochs = 1;opts.batchsize = 100;opts.momentum = 0;opts.alpha = 1;dbn = dbnsetup(dbn, train_x, opts);dbn = dbntrain(dbn, train_x, opts);figure; visualize(dbn.rbm{1}.W'); % Visualize the RBM weights%% ex2 train a 100-100 hidden unit DBN and use its weights to initialize a NNrand('state',0)%train dbndbn.sizes = [100 100];opts.numepochs = 1;opts.batchsize = 100;opts.momentum = 0;opts.alpha = 1;dbn = dbnsetup(dbn, train_x, opts);dbn = dbntrain(dbn, train_x, opts);%unfold dbn to nnnn = dbnunfoldtonn(dbn, 10);nn.activation_function = 'sigm';%train nnopts.numepochs = 1;opts.batchsize = 100;nn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.10, 'Too big error');```Example: Stacked Auto-Encoders---------------------```matlabfunction test_example_SAEload mnist_uint8;train_x = double(train_x)/255;test_x = double(test_x)/255;train_y = double(train_y);test_y = double(test_y);%% ex1 train a 100 hidden unit SDAE and use it to initialize a FFNN% Setup and train a stacked denoising autoencoder (SDAE)rand('state',0)sae = saesetup([784 100]);sae.ae{1}.activation_function = 'sigm';sae.ae{1}.learningRate = 1;sae.ae{1}.inputZeroMaskedFraction = 0.5;opts.numepochs = 1;opts.batchsize = 100;sae = saetrain(sae, train_x, opts);visualize(sae.ae{1}.W{1}(:,2:end)')% Use the SDAE to initialize a FFNNnn = nnsetup([784 100 10]);nn.activation_function = 'sigm';nn.learningRate = 1;nn.W{1} = sae.ae{1}.W{1};% Train the FFNNopts.numepochs = 1;opts.batchsize = 100;nn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.16, 'Too big error');```Example: Convolutional Neural Nets---------------------```matlabfunction test_example_CNNload mnist_uint8;train_x = double(reshape(train_x',28,28,60000))/255;test_x = double(reshape(test_x',28,28,10000))/255;train_y = double(train_y');test_y = double(test_y');%% ex1 Train a 6c-2s-12c-2s Convolutional neural network %will run 1 epoch in about 200 second and get around 11% error. %With 100 epochs you'll get around 1.2% errorrand('state',0)cnn.layers = { struct('type', 'i') %input layer struct('type', 'c', 'outputmaps', 6, 'kernelsize', 5) %convolution layer struct('type', 's', 'scale', 2) %sub sampling layer struct('type', 'c', 'outputmaps', 12, 'kernelsize', 5) %convolution layer struct('type', 's', 'scale', 2) %subsampling layer};cnn = cnnsetup(cnn, train_x, train_y);opts.alpha = 1;opts.batchsize = 50;opts.numepochs = 1;cnn = cnntrain(cnn, train_x, train_y, opts);[er, bad] = cnntest(cnn, test_x, test_y);%plot mean squared errorfigure; plot(cnn.rL);assert(er<0.12, 'Too big error');```Example: Neural Networks---------------------```matlabfunction test_example_NNload mnist_uint8;train_x = double(train_x) / 255;test_x = double(test_x) / 255;train_y = double(train_y);test_y = double(test_y);% normalize[train_x, mu, sigma] = zscore(train_x);test_x = normalize(test_x, mu, sigma);%% ex1 vanilla neural netrand('state',0)nn = nnsetup([784 100 10]);opts.numepochs = 1; % Number of full sweeps through dataopts.batchsize = 100; % Take a mean gradient step over this many samples[nn, L] = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.08, 'Too big error');%% ex2 neural net with L2 weight decayrand('state',0)nn = nnsetup([784 100 10]);nn.weightPenaltyL2 = 1e-4; % L2 weight decayopts.numepochs = 1; % Number of full sweeps through dataopts.batchsize = 100; % Take a mean gradient step over this many samplesnn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.1, 'Too big error');%% ex3 neural net with dropoutrand('state',0)nn = nnsetup([784 100 10]);nn.dropoutFraction = 0.5; % Dropout fraction opts.numepochs = 1; % Number of full sweeps through dataopts.batchsize = 100; % Take a mean gradient step over this many samplesnn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.1, 'Too big error');%% ex4 neural net with sigmoid activation functionrand('state',0)nn = nnsetup([784 100 10]);nn.activation_function = 'sigm'; % Sigmoid activation functionnn.learningRate = 1; % Sigm require a lower learning rateopts.numepochs = 1; % Number of full sweeps through dataopts.batchsize = 100; % Take a mean gradient step over this many samplesnn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.1, 'Too big error');%% ex5 plotting functionalityrand('state',0)nn = nnsetup([784 20 10]);opts.numepochs = 5; % Number of full sweeps through datann.output = 'softmax'; % use softmax outputopts.batchsize = 1000; % Take a mean gradient step over this many samplesopts.plot = 1; % enable plottingnn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.1, 'Too big error');%% ex6 neural net with sigmoid activation and plotting of validation and training error% split training data into training and validation datavx = train_x(1:10000,:);tx = train_x(10001:end,:);vy = train_y(1:10000,:);ty = train_y(10001:end,:);rand('state',0)nn = nnsetup([784 20 10]); nn.output = 'softmax'; % use softmax outputopts.numepochs = 5; % Number of full sweeps through dataopts.batchsize = 1000; % Take a mean gradient step over this many samplesopts.plot = 1; % enable plottingnn = nntrain(nn, tx, ty, opts, vx, vy); % nntrain takes validation set as last two arguments (optionally)[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.1, 'Too big error');```[![Bitdeli Badge](https://d2weczhvl823v0.cloudfront.net/rasmusbergpalm/deeplearntoolbox/trend.png)](https://bitdeli.com/free "Bitdeli Badge")

上一篇: 行为模式之解释器模式-INTERPRETER
下一篇: 设计模式之创建型:抽象工厂和生成器的混合使用
普通网友
博客等级 码龄14年 9粉丝 21原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值