首页 诗词 字典 板报 句子 名言 友答 励志 学校 网站地图
当前位置: 首页 > 教程频道 > .NET > C# >

【山之魂】高分C++如何传递列表给C#

2013-10-12 
【山之魂】高分求助: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*的,但是别加那个&amp;,不能按引用传递,因为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协议的,有源码

热点排行