【山之魂】高分求助:C++怎么传递列表给C#?本人这几天在做一个项目,现有一个DICOM查询功能的C++DLL,其中有导
【山之魂】高分求助:C++怎么传递列表给C#? 本人这几天在做一个项目,现有一个DICOM查询功能的C++DLL,其中有导出类(导出类,不是导出函数,有源代码), 我的工作是用C#调用这个C++将查询到的患者列表显示出来(我是做C#的老菜鸟,C++完全不懂)。 现在遇到的问题是,导出类的函数中要用到类成员对象,但是C#只能对其进行静态调用,无法初始化C++类对象。 我想到的方法是将C++导出类再进行封装,用一个导出函数实现查询功能, 又遇到一个问题,C++用什么返回类型来返回患者列表信息? C++没有array,ArrayList什么的,甚至不能返回数组(只能返回数组的指针,但是C#不支持指针)。 查过很多资料都没有办法解决。 请各位C++、C#的大虾帮忙,非常感谢。 时间比较紧。 分我不会吝惜的,近期发的帖子都是200分的,比如 http://topic.csdn.net/u/20120724/21/995f94f2-aa80-43ca-96cf-79e822bc33d5.html?seed=2130642648&r=79335479#r_79335479 [解决办法] c++原型是什么[解决办法] 1.若是类多的话,可以考虑使用托管C++做个中间封装。 2.使用结构体对C++的类进行封装 3.使用COM组件。 PS: 你们DICOM通信部分是使用DCMTK吗? 查询部分可以使用C#来完成呀。[解决办法] 本帖最后由 bdmh 于 2012-08-07 11:43:32 编辑 如果是c++自己的类导出,那c#用不了,可以像4楼说的,再封装一层,暴漏一些通用接口供c#使用[解决办法] C++没有array,ArrayList什么的,甚至不能返回数组(只能返回数组的指针,但是C#不支持指针 对你这句话绝对的不认同 1.STL中有很多的集合对象,能够返回集合,例如放入到vector<string>中,返回 2.C# 也支持指针,放到 unsafe 代码块中[解决办法] 1.封装成COM组件。对C++不是很熟,尤其是COM方面,自己找资料吧…… 2.导出函数封装一些功能。建议返回值用作错误处理,传入/传出都用参数,比如结构数组或者结构指针:
DLL_EXPORT_API int Query(AStruct * output, int outputLength);C#方使用
[DllImport("dll.dll", EntryPoint = "Query", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Unicode)] internal static extern int StructArrayTest([MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 1), Out()]AStruct[] output, int outputLength);封送。内存尽量在调用方(即C#方)申请和释放,dll导出函数中尽量不用malloc/new为佳。
[解决办法] 引用: 引用: c++原型是什么 非托管C++代码,其中有个导出类,里面有几个函数,还有类成员,函数里面要用到类成员,功能是查询DICOM患者列表。 工作电脑不能上网,没法贴出来。 难道你只会复制粘贴?用U盘拷贝或者直接写在纸上,回家后手工输入一遍。
[解决办法] 如果你只是要类型转换,推荐你一个非常好的工具:
P/Invoke Interop Assistant
在最后一个标签页里贴入C++头文件代码(或仅部分定义)
点击下面Generate就可以产生C#对应的定义了。
[解决办法] C#支持指针,用指针写即可,很方便
[解决办法] 引用: 回 bdmh,我也意识到需要再封装,现在的问题是用什么返回类型来返回多条患者信息。 谢谢 kingdom_0 ,我对C++一窍不通,可以写一小段代码给我演示 vector<string> 的使用么? C++代码中也有struct,可是我不知道怎么把C#和C++的struct对照起来(C++的struct中有指针成员),哪位大哥帮忙写个例子,非常感谢! Vector使用例子:
#include "stdafx.h"
#include<string>
#include<vector>
using namespace std;
int _tmain(int argc, _TCHAR* argv[])
{
vector<string> strs;
strs.push_back("1");
strs.push_back("2");
strs.push_back("3");
strs.push_back("4");
for(int i=0;i<strs.size();i++)
{
printf("%s\n",strs[i].c_str());
}
getchar();
return 0;
}
[解决办法] 引用: C#支持指针,用指针写即可,很方便 方便个屁,C#的指针和C++完全不是一样的用法,是受限制的指针,不懂不要乱说,误导楼主。
C#中,指针访问的对象是有限的,无法固定地址的对象是无法通过指针访问的,而非托管堆的对象即使在指针中,也无法直接变为托管对象,你只能看到void*这样的东西,有毛用。
[解决办法] C++中的指针在C#里面用InPtr,如果指针指向的是个结构,那么直接在C#写指针即可,写指针最容易,还有,你可以看一下C#里面的marshal类,里面都是各类转换的方法,用起来很方便。下面给你的例子:
C++里面:
typedef struct _Icon
{
BYTE* imageByte;
DWORD count;
WORD resourceName;
}ICON,*PICON;
typedef struct Icon_Data
{
WORD resourceGroupName;
DWORD count;
PICON lpIcon;
}ICON_DATA,*PICON_DATA;
C#里面
[StructLayout(LayoutKind.Sequential, Pack = 1)]
public struct MyIcon
{
public IntPtr imageByte;
public UInt32 count;
public UInt16 resourceName;
}
[StructLayout(LayoutKind.Sequential, Pack = 1)]
unsafe public struct Icon_Data
{ public UInt16 resourceGroupName; public UInt32 count; public MyIcon* lpIcon; } 例如C++返回一个PICON_DATA类型,那么在C#这边我就用Icon_Data* Icons接收 访问C++中的第二个ICON_DATA的第二个ICON的resourceName值,C#中:string resourceName = (*((*(Icons + 1)).lpIcon + 1)).resourceName.ToString();[解决办法] 看下面这种类型,如果用指针访问,test的长度是4(32位下面),但是用Marshal类来访问,长度是1024,对于托管代码封送的结果,用Marshal来访问才是最恰当的,因为指针看不到MarshalAs特性,不能很好的处理数据结构。
public struct test { [MarshalAs(UnmanagedType.ByValArray,SizeConst = 1024)] byte[] p1; } [解决办法] 引用: 引用: C#支持指针,用指针写即可,很方便 方便个屁,C#的指针和C++完全不是一样的用法,是受限制的指针,不懂不要乱说,误导楼主。 C#中,指针访问的对象是有限的,无法固定地址的对象是无法通过指针访问的,而非托管堆的对象即使在指针中,也无法直接变为托管对象,你只能看到void*这样的东西,有毛用。 还有,别动不动就开骂,说话有必要这样吗,引用那句话“人算什么,你竟然眷顾他”,就不能好好说话?估计是通病,该行业以及人性的通病,看雪这样的人更多。。。
[解决办法] 或者返回字节数组,你根据数据结构逐个的转化为struct即可
[解决办法] 对于字符串,C++一般是使用char*的,但是别加那个&,不能按引用传递,因为char*本身就是地址了,如果你再传递地址的地址,就无法用C#来调用了。
另外C++里面的确没有string类型,我以为你自己定义了一个string类型呢,你的c++代码也很奇怪,也用到了string,那么到底是哪里定义的呢?为啥不能直接使用char*呢?
就你上面那个定义,对应的C#代码可以这样写:
public static extern int abc(string test); public static extern int abc([MarshalAs(UnmanagedType.LPTStr)] out string test); public static extern int abc([MarshalAs(UnmanagedType.LPTStr)] ref string test);看具体情况,参数test是输出型还是输入型,还是输入输出型的。
这里UnmanagedType.LPTStr是和平台相关的字符,如果出现乱码,必须修改它。
[解决办法] 让你那的C++部门提供标准的C API函数
然后你再平台调用
不然你自己封装工作量比较大呀。
而且也不合理,又多了一步,对效率上也是不合算的。
[解决办法] 写了个传递字符串数组的:
C++部分,使用char**:
__declspec(dllexport) int StringArrayTest(char ** input, int inputCount, int bufferLength);C#部分,详细指定MarshalAs:
[DllImport("MyDllTest.dll", EntryPoint = "StringArrayTest", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Unicode)] internal static extern int StringArrayTest([MarshalAs(UnmanagedType.LPArray, ArraySubType = UnmanagedType.LPStr, SizeParamIndex = 1), Out(), In()]string[] input, int inputCount, int bufferLength);调用方负责分配空间:
string[] arr = new string[10]; for (int i = 0; i < arr.Length; i++) { arr[i] = ""; } int r = StringArrayTest(arr, 10, 100); Console.WriteLine("Got {0} Results.", r); Array.ForEach(arr, Console.WriteLine);C++部分可以直接使用
__declspec(dllexport) int StringArrayTest(char ** input, int inputCount, int bufferLength) { int i; char * current; for (i=0;i<inputCount;i++) { current = *(input + i); _sprintf_p(current, bufferLength, "%d ^ 2 = %d.", i, i*i); } return i; }不建议在导出函数中分配动态空间(包括malloc/new),因为没法保证使用者会调用相应的函数进行释放。
[解决办法] 引用: 谢谢楼上,我要在C++中进行字符串拼接,再赋给相应的string,应该怎么写,是不是在这句上面修改? _sprintf_p(current, bufferLength, "%d ^ 2 = %d.", i, i*i); 能写一下怎么把“返回值ID”+“返回值neme”赋进去吗? 差不多,应该是
_sprintf_p(current, bufferLength, "%s,%s", id, name);//得到 `ID,name' 这样的字符串但是C/C++字符串并不限于char *这类的表现形式,合并方式也不尽相同
[解决办法] 既然是C++,字符串就用string类型吧,各类操作会非常简单,等返回时,再c_str(),这样比较简单,如果是字符串数组只需要再返回一个字符串数量即可
[解决办法]
引用: 对于字符串,C++一般是使用char*的,但是别加那个&,不能按引用传递,因为char*本身就是地址了,如果你再传递地址的地址,就无法用C#来调用了。 另外C++里面的确没有string类型,我以为你自己定义了一个string类型呢,你的c++代码也很奇怪,也用到了string,那么到底是哪里定义的呢?为啥不能直接使用char*呢? 就你上面那个定义,对应的C#代码可以这样写: …… 恰好相反,对于字符串,C++ 中一般使用的是string;C 中使用的是char *
[解决办法] 引用: 引用: 既然是C++,字符串就用string类型吧,各类操作会非常简单,等返回时,再c_str(),这样比较简单,如果是字符串数组只需要再返回一个字符串数量即可 可以把相关demo代码贴一下吗? 这个也没有啥代码,C++里面的string用起来很方便,字符串可以直接用“+”拼接,和C#一样,例如:
string str1="str1";
string str2="str2";
string str3=str1+str2;
return str3.c_str();
只要最后返回一个标准C字符串即可,str3必须定义成static或者是全局变量,内存释放用str3.clear()
使用string记得添加:
#include<string>
using namespace std;
[解决办法] 建议楼主可以看下vector和map的使用,这些都是C++中经常用到的,使用很方便,类似C#里面的List,唯一要注意的就是内存释放,例如vector<vector<BYTE*>> vec,C#里直接clear就都清除了,C++里面需要遍历外层vector逐一clear。
我也是C++初学,楼主如有兴趣可以加Q一起讨论,共同学习。QQ:1075375145
[解决办法] 小弟挫见:是不是可以弄成dll。。。
[解决办法] 学习一下,两个之间还没想过要联系起来
[解决办法] 引用: 谢谢楼上各位。 进展报告: iyomumx 大虾的方法我试用了,现在部分成功。非常感谢! for (int i = 0; i < arr.Length; i++) { arr[i] = ""; } 这里 arr[i] = "";不行,要赋个长的占据足够的内存。 现在还有的问题是: 英文和数字…… 把C#部分的CharSet = CharSet.Unicode改为CharSet.Ansi试试。由于编译器的关系,C/C++代码内部使用的编码会有差异。
[解决办法] 引用: 谢谢楼上各位。 进展报告: iyomumx 大虾的方法我试用了,现在部分成功。非常感谢! for (int i = 0; i < arr.Length; i++) { arr[i] = ""; } 这里 arr[i] = "";不行,要赋个长的占据足够的内存。 现在还有的问题是: 英文和数字…… win7下当然可以,iyomumx的例子,你把charset改成unicode,C++里面把char**改成WCHAR**,肯定可以
[解决办法] C++调用成功后序列化成字符串,C#获得这个字符串在反序列化出来就可以了。(推荐用json方式好一些)
要是想偷懒C#直接传个文件名过去,c++往这个文件写数组,c#再读这个文件即可。
[解决办法] [DllImport("dll.dll", EntryPoint = "Query", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Unicode)] internal static extern int StructArrayTest([MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 1), Out()]AStruct[] output, int outputLength);如果还不行应该是你dll 有问题
[解决办法] 不要卡在传递对象这步上。
你要的是结果,而不是关心C++的对象,想办法把查询结果给C#就行了(比如文件)。
[解决办法] 对于不懂C++的菜鸟,建议请使用XML把数据进行序列化,那么c#和C++转化就找到桥梁了
[解决办法] 楼主请考虑用Google.Protocolbuffer来实现,这个比xml的序列化快太多了
[解决办法] 因为那个A的getInfo()函数的逻辑,怎么会是这样的啊。。
int getInfo( )
{
char * =b->c->getcurrent()
}
[解决办法] 函数少写了什么哦。。。。
另外是想返回一个struct的指针是吗?这个还是不能满足的要返回一个列表的需求啊。。。
[解决办法] 不能连续回复,给你找了个,应该可以解决你的问题,搜索“如何通过P/Invoke返回Struct和String Array
”
[解决办法] char和wchar用winapi WideCharToMultiByte 转换
char * =b->c->getcurrent()
这句完全不能过编译吧……
[解决办法] 如果这个还帮不到你的话,还是你自己都不看回复的。。。。
算了,白忙活了,今天台风预警没上班才上来看看的。。。。
引用: 不能连续回复,给你找了个,应该可以解决你的问题,搜索“如何通过P/Invoke返回Struct和String Array ” [解决办法] ExecuteEngineException太可怕了,LZ那么闲不如学点C++/CLI给C++类做包装?
在C#中使用C++编写的类
[解决办法] 我是做C#的老菜鸟。什么意思?
[解决办法] 很显然,答案就是用C++/CLI进行封装非托管C++类,然后再用C#调用。
------解决方案--------------------
用 gsoap 很好用 ,如果不想用dll
[解决办法] 用C++ Interop:实用托管C++,对非托管C++进行封装。
[解决办法] 返回字符串。接口调用,根据固定的符号分割。
[解决办法] 引用: 我的数据在C++里面是 char * 类型的,要让中文能显示,我要用 wchar_t 传出去, 请问下怎么把 char * 转换为 wchar_t* ?? char szA[100]; //An ANSI string buffer
WCHAR szW[100]; //A Unicode string buffer
//Normal sprintf:all strings are ANSI
sprintf(szA, "%s","ANSI Str");
//Converts Unicode string to ANSI
sprintf(szA,"%S",L"Unicode Str");
//Normal swprintf:all strings are Unicode
swprintf(szW,L"%s",L"Unicode Str");
//Converts ANSI string to Unicode
swprintf(szW,L"%S", "ANSI Str");
以上代码来自 windows核心编程
[解决办法] 引用: 回 56楼,我做了4年C#了不过技术还是很菜。 另外,我在VS2005中没有找到如何建立 托管C++类库…… 目前尚余的问题: 我的数据在C++里面是 char * 类型的,要让中文能显示,我要用 wchar_t 传出去, 请问下怎么把 char * 转换为 wchar_t* ?? 在新建项目里Visual C++ -> CLR 里面有个“类库”项目。
我用的08的,按道理05的也应该有的
[解决办法] 引用: 直接用 swprintf 把char * 类型含有汉字的数据传进去,最后得到一堆乱码 用 mbstowcs_s 因为要给C#调用所以内存越界, 最后用 MultiByteToWideChar 解决了,不过据说这个只能在windows下使用。我们的软件据说是要支持其他系统的…… 问题基本都得到解决,有空结贴, 再次感谢各位大虾! 那你可以试试iconv
在http://www.gnu.org/software/libiconv/#downloading下载
gnu协议的,有源码