求救:我按照'office excel下载2010多个窗口互相独立显示'修改后,所有的excel表格都清空了,如何还原。

C#与C/C++的交互
warensoft 中科院计算所中心&
欢迎转载,请注明出处及作者
最近在编写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++方式导出,那个函数的名字就会变得乱七八糟,我们的程序就无法找到入口点了。
本文将说明以下几点:
互调的基本原理
基本数据类型的传递
指针的传递
函数指针的传递
结构体的传递
互调的基本原理
首先,我们来看一个再常规不过的概念&&数据类型&
我们知道在大多数的静态语言中定义变量的时候都要先指定其数据类型,所谓数据类型,都是人们强加的一个便于记忆的名称,究其本质就是指明了这个数据在内存里到底是占用了几个字节,程序在运行的时候,首先找到这个数据的地址,然后再按着该类型的长度,读取相对应的内存,然后再处理。
了解了前面这个事儿,所有语言之间进行互调就有点门道儿了。对于不同语言之间的互调,只要将该数据的指针(内存地址)传递给另一个语言,在另一个语言中根据通信协议将指针所指向的数据存储入长度对应的数据类型即可,当然要满足以下几点:
对于像,.NET这样有运行时编程语言来讲,由于虚拟机会让堆内存来回转移,因此,在进行互调的时候,要保证正在被互调的数据所在的内存一定要固定,不能被转移。
有一些编程语言支持指针,有一些语言不支持指针(如Java),这个问题并不重要,所谓指针,其实就是一个内存地址,对于32位OS的指针是一个32位整数,而对于64位机OS的指针是一个64位整数。因为大多数语言中都有整型数,所以可以利用整型来接收指针。
基本数据类型的传递
互调过程中,最基本要传递的无非是数值和字符,即:等等,但是此类型非彼类型,与中有一些数据类型长度是不一样的,下表中列出常见数据类型的异同:
该类型在传递的时候常常会弄混
(字符,该类型与中的兼容)
最容易弄混的是就是两个类型,在中和都是个字节,都对应着中的类型,而中的类型占一个字节,用来表示一个码字符,在中能够表示一个字节的是类型。与中类型对应的应该是中的类型,对应的是一个字节的字符。
下面通过实例来说明调用过程:
建立一个的,如下图所示:
这里要注意选择&&导出符号。点击完成。
由于项目的名称是&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中添加一个新的函数声明,代码如下:
EXTERN_C TESTCPPDLL_API void __stdcall WriteString(wchar_t*content);
这里的参数是wchar_t类型的指针,对应着C#中的char类型。TestCPPDLL.cpp中添加如下代码:
TESTCPPDLL_API void __stdcall WriteString(wchar_t*content)
&&&&cout&&
该代码的功能就是将输入的字符串通过C++在控制台上输出。下面是在C#中的声明:
[DllImport(@&E:\ex\TestCPPDLL\Debug\TestCPPDLL.dll&, EntryPoint = &WriteString&)]
extern unsafe static void WriteString(char*c);
调用过程如下所示:
//因为使用指针,因为要声明非安全域
//在传递字符串时,将字符所在的内存固化,
//并取出字符数组的指针
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=firstE
&&& for (int i = 0; i & arrayL i++)
&&&&&&& cout&&*currentP
&&&&&&& currentPointer++;
&&& cout&&
TESTCPPDLL_API int* __stdcall GetArrayFromCPP()
&&& arrPtr=new int[10];
&&& for (int i = 0; i & 10; i++)
&&&&&&& arrPtr[i]=i;
&&& return arrP
对应调用的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();
调用过程如下所示:
// 调用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] = iA
fixed (int* pCSArray = &CSArray[0])
AddIntArray(pCSArray, 10);
//调用C++中的GetArrayFromCPP方法获取一个C++中建立的数组
int* pArrayPointer =
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 CSC
在CS的主程序中让callback指向CSCallbackFunction方法,代码如下所示:
//调用委托所指向的方法
callback = CSCallbackF
然后在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 = CSCallbackF
//将委托传递给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&
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=firstE
&&& for (int i = 0; i & arrayL i++)
&&&&&&& cout&&*currentP
&&&&&&& currentPointer++;
&&& cout&&
TESTCPPDLL_API int* __stdcall GetArrayFromCPP()
&&& arrPtr=new int[10];
&&& for (int i = 0; i & 10; i++)
&&&&&&& arrPtr[i]=i;
&&& return arrP
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.Collections.G
using System.Runtime.InteropS
using System.T
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 CSC
//这里使用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);
//因为使用指针,因为要声明非安全域
//在传递字符串时,将字符所在的内存固化,
//并取出字符数组的指针
fixed (char* p = &(&hello&.ToCharArray()[0]))
//调用方法
WriteString(p);
// 调用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] = iA
fixed (int* pCSArray = &CSArray[0])
AddIntArray(pCSArray, 10);
//调用C++中的GetArrayFromCPP方法获取一个C++中建立的数组
int* pArrayPointer =
pArrayPointer = GetArrayFromCPP();
for (int iArr = 0; iArr & 10; iArr++)
Console.WriteLine(*pArrayPointer);
pArrayPointer++;
//让委托指向将被回调的方法
callback = CSCallbackF
//将委托传递给C++
SetCallback(callback);
//建立一个Vector3的实例
Vector3 vector = new Vector3() { X = 10, Y = 20, Z = 30 };
//将vector传递给C++并在C++中输出
SendStructFromCSToCPP(vector);
Console.Read();RPC学习--C#使用Thrift简介,C#客户端和Java服务端相互交互 - Hi_Amos - 推酷
RPC学习--C#使用Thrift简介,C#客户端和Java服务端相互交互 - Hi_Amos
本文主要介绍两部分内容:
C#中使用Thrift简介
用Java创建一个服务端,用C#创建一个客户端通过thrift与其交互。
其中使用到
,这篇文章创建的Java服务端。
一、C#中使用Thrift简介
关于rpc的简介,可以参考:
1、下载thrift
1) 点击下载:
(或者 http://thrift.apache.org/download)
两个都要下载。
2、引入thrift.dll
这里将下载好的.gz文件解压后,然后找到lib目录
用vs打开后,如下图所示,然后右键--》重新生成---》生成thrift.dll
3、生成cs文件
hello.thrift
HelloWorldService {
string sayHello(1:string username)
使用命令生成cs文件:
thrift-0.9.1.exe -gen csharp hello.thrift
的使用方法可以查看命令:& thrift-0.9.1.exe&-help
将生成的HelloWorldService.cs文件拷入项目中。
二、C#客户端发送消息到Java生成的服务端,实现跨平台操作
1、启动Java版的服务端
2、使用vs新建一个winform程序
button点击事件:
private void button1_Click(object sender, EventArgs e)
if (textBox1.Text != null)
new HelloWorldServiceClient().startClient(textBox1.Text.Trim());
HelloWorldServiceClient.cs
using System.Collections.G
using System.L
using System.T
using System.Threading.T
using Thrift.P
using Thrift.T
namespace thrfitCsharp
class HelloWorldServiceClient
public const string SERVERIP = &localhost&;
public static int SERVERPORT = 8090;
public static int TIMEOUT = 3000;
public void startClient(String username)
TTransport transport = null;
transport = new TSocket(SERVERIP, SERVERPORT, TIMEOUT);
//协议要和服务端一致
TProtocol protocol = new TCompactProtocol(transport);
HelloWorldService.Client client = new HelloWorldService.Client(protocol);
transport.Open();
String result = client.sayHello(username);
Console.WriteLine(&Thrift client result =: & + result);
catch (Exception e)
Console.WriteLine(e.StackTrace);
if (null != transport)
transport.Close();
HelloWroldImpl.cs
using System.Collections.G
using System.L
using System.T
using System.Threading.T
namespace thrfitCsharp
class HelloWroldImpl : HelloWorldService.Iface
string sayHello(string username){
Console.WriteLine(&hello&+username);
return &hello& +
源码 : /amosli/rpc/tree/thriftCsharp
已发表评论数()
请填写推刊名
描述不能大于100个字符!
权限设置: 公开
仅自己可见
正文不准确
标题不准确
排版有问题
主题不准确
没有分页内容
图片无法显示
视频无法显示
与原文不一致谁知道怎么能实现C#与C语言之间的通信方法,求大神教。。socket【c语言吧】_百度贴吧
&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&签到排名:今日本吧第个签到,本吧因你更精彩,明天继续来努力!
本吧签到人数:0成为超级会员,使用一键签到本月漏签0次!成为超级会员,赠送8张补签卡连续签到:天&&累计签到:天超级会员单次开通12个月以上,赠送连续签到卡3张
关注:531,125贴子:
谁知道怎么能实现C#与C语言之间的通信方法,求大神教。。socket收藏
我用C语言写了个模拟进程的程序,我想用C#来做界面,完成进程信息的输入,并传给C语言来完成模拟,可以的话给我举个小例子,方便我理解,小人不甚感激。。。是不是用SOCKET能完成?能的话给讲一下C下面socket怎么用,有相关讲解的网址发个也行。。
登录百度帐号推荐应用
为兴趣而生,贴吧更懂你。或你正在使用的浏览器版本过低,将不能正常浏览和使用知乎。

我要回帖

更多关于 office excel下载 的文章

 

随机推荐