C#调用C++ DLL返回数据结构

//服务器音乐信息结构体
typedef struct SER_MUSIC_INFO_tag
{
    CString MusicDirName;
    std::vector<CString> MusicInfoVector;
}SER_MUSIC_INFO,*PSER_MUSIC_INFO;
 
C++的函数方法
SDKGetMusicInfoVector 获取歌曲目录信息
函数:std::vector<SER_MUSIC_INFO>  SDKGetMusicInfoVector();
说明:获取主机的目录和目录所在的歌曲
参数:无
返回值:std::vector<SER_MUSIC_INFO>:歌曲目录信息结构体向量

C#方法

         /// <summary>
        /// 获取主机的目录和目录所在的歌曲
        /// </summary>
        /// <returns>歌曲目录信息结构体向量</returns>
        [System.Runtime.InteropServices.DllImportAttribute(DllPathFile, EntryPoint = "SDKGetMusicInfoVector", CallingConvention = CallingConvention.Cdecl)]
        public static extern SER_MUSIC_INFOSDKGetMusicInfoVector();


C#结构体
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
        public struct SER_MUSIC_INFO_tag
        {
            [MarshalAs(UnmanagedType.ByValArray, SizeConst = 32)]
            public string MusicDirName;//目录名称   
            public IntPtr MusicInfoVector;    //对应目录下的歌曲
        }

感觉自己声明的C#结构体有问题,跟C++数据类型对应不起来!得到的结构体的返回值 一直是乱码 要不就是指针!根据上面C++的介绍 C#需要怎么声明 和调用 才能得到需要返回的数据呢?(结构体里面的数据都是中文)

vector 这种是没法映射到C#中的,解决方法:

方法一(推荐):
C#直接跟服务器进行通信,直接拿数据解析

方法二:
再写一个 “转换DLL”,然后按这样方式来调用 C# --> “转换DLL” --> C++ DLL
转换DLL中将所需返回的数据转成C#中能识别的数据格式,推荐转换成单个JSON字符串
C#中解析JSON字符串,得到最终结果

如果无法修改C++代码,只能通过提供的DLL进行调用的话,你可以尝试使用以下方法来获取C++函数返回的结构体向量:

首先,你需要修改C#结构体的定义,以正确映射C++的数据类型:


csharp
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
public struct SER_MUSIC_INFO_tag
{
    [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 32)]
    public string MusicDirName;

    // 由于返回值是std::vector<CString>,我们将MusicInfoVector声明为IntPtr
    public IntPtr MusicInfoVector;
}

接下来,在C#中声明一个帮助函数来处理C++返回的结构体向量,并释放内存:


csharp
private static List<SER_MUSIC_INFO_tag> GetMusicInfoVector(IntPtr vectorPtr)
{
    List<SER_MUSIC_INFO_tag> musicInfoList = new List<SER_MUSIC_INFO_tag>();

    // 通过循环遍历指针中的每个结构体
    IntPtr currentPtr = vectorPtr;
    while (Marshal.ReadIntPtr(currentPtr) != IntPtr.Zero)
    {
        // 从指针中读取一个结构体
        SER_MUSIC_INFO_tag musicInfo = Marshal.PtrToStructure<SER_MUSIC_INFO_tag>(currentPtr);

        // 将结构体添加到列表中
        musicInfoList.Add(musicInfo);

        // 更新指针位置
        currentPtr += Marshal.SizeOf<SER_MUSIC_INFO_tag>();
    }

    // 释放内存
    FreeMusicInfoVector(vectorPtr);

    return musicInfoList;
}

[DllImport(DllPathFile, CallingConvention = CallingConvention.Cdecl)]
private static extern void FreeMusicInfoVector(IntPtr vectorPtr);

以上代码中,GetMusicInfoVector函数用于将指针中的结构体转换为C#中的结构体,并将它们存储在一个列表中。在循环过程中,我们通过递增指针的方式遍历每个结构体。最后,我们调用FreeMusicInfoVector函数释放内存。

最后,你可以使用以下代码来调用C++的函数获取结构体向量:


csharp
[DllImport(DllPathFile, CallingConvention = CallingConvention.Cdecl)]
private static extern IntPtr SDKGetMusicInfoVector();

IntPtr vectorPtr = SDKGetMusicInfoVector();
List<SER_MUSIC_INFO_tag> musicInfoList = GetMusicInfoVector(vectorPtr);

这样,你就能够在不修改C++代码的情况下,通过调用DLL获取C++函数返回的结构体向量了。

请注意,在使用完结构体向量后,需要手动调用FreeMusicInfoVector函数释放内存。

该说的其实楼上都说
CString ,std::vector是C++特有对象,做为调用库对方应该申明成纯C的标准dll(这个是基础共识)
如果象你说对方只给C++的dll,那么你需要自己用C++另外写个用纯C声明的包装器,包装器内部则自己调用那个C++的库
当然楼上也说了用C++/CLI(Managed C++)包装成net的标准件也可以

如果无法得到c++对象,就只能用c++封装成标准的类型,再调用。

c#和c++还是有区别的
有个实例,可以看看
c# 调用C/C++ DLL,传入返回结构体以及指针数组(指针指向自定义的结构体)

方法1:你用C++再写一个中间件,返回给C#能直接调用的格式,这个最方便
方法2:参考这篇文章

,C#通过地址去读取返回的数据,你这里要做2次地址读取

自定义 marshaler

在C#中声明与C++结构体对应的C#结构体:

public struct SER_MUSIC_INFO  
{  
    public string MusicDirName;  
    public List<string> MusicInfoVector;  
}

在C#中声明与C++函数对应的函数,并使用DllImport属性引入C++函数:


[DllImport("your_dll_name.dll")]  
public static extern List<SER_MUSIC_INFO> SDKGetMusicInfoVector();

将"your_dll_name.dll"替换为实际使用的DLL文件名。
在C#中调用函数:


List<SER_MUSIC_INFO> musicInfoVector = SDKGetMusicInfoVector();

引用chatgpt内容作答:
在C#中调用C++ DLL并正确地映射数据结构可以使用以下方法:

首先,需要进行一些修改和调整:

修改C++结构体的定义:

typedef struct SER_MUSIC_INFO_tag
{
    char MusicDirName[32];
    char** MusicInfoVector;
    int MusicInfoVectorSize;
} SER_MUSIC_INFO, *PSER_MUSIC_INFO;

注意,将CString更改为char[],并使用char**来表示std::vector。

修改C++函数的返回类型和参数:

extern "C" __declspec(dllexport) SER_MUSIC_INFO* SDKGetMusicInfoVector();

将返回类型更改为SER_MUSIC_INFO*,以便在C#中正确地访问结构体的指针。

修改C#结构体的定义:

[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
public struct SER_MUSIC_INFO_tag
{
    [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 32)]
    public string MusicDirName;  // 目录名称

    public IntPtr MusicInfoVector;  // 对应目录下的歌曲
    public int MusicInfoVectorSize; // 歌曲信息向量的大小
}

使用MarshalAs属性的UnmanagedType.ByValTStr来表示C++的char[],并添加一个额外的字段MusicInfoVectorSize来保存歌曲信息向量的大小。

修改C#方法的声明:

[System.Runtime.InteropServices.DllImportAttribute(DllPathFile, EntryPoint = "SDKGetMusicInfoVector", CallingConvention = CallingConvention.Cdecl)]
public static extern IntPtr SDKGetMusicInfoVector();

返回类型更改为IntPtr,因为我们将获取指向结构体的指针。

接下来,你可以使用以下代码来正确地调用C++ DLL并访问返回的数据结构:

IntPtr ptr = SDKGetMusicInfoVector();
SER_MUSIC_INFO_tag musicInfo = Marshal.PtrToStructure<SER_MUSIC_INFO_tag>(ptr);
string musicDirName = musicInfo.MusicDirName;

// 访问歌曲信息向量
string[] musicInfoArray = new string[musicInfo.MusicInfoVectorSize];
IntPtr[] musicInfoPointers = new IntPtr[musicInfo.MusicInfoVectorSize];
Marshal.Copy(musicInfo.MusicInfoVector, musicInfoPointers, 0, musicInfo.MusicInfoVectorSize);

for (int i = 0; i < musicInfo.MusicInfoVectorSize; i++)
{
    musicInfoArray[i] = Marshal.PtrToStringAnsi(musicInfoPointers[i]);
}

// 释放内存
for (int i = 0; i < musicInfo.MusicInfoVectorSize; i++)
{
    Marshal.FreeCoTaskMem(musicInfoPointers[i]);
}

Marshal.FreeCoTaskMem(ptr);

这里,我们首先使用Marshal.PtrToStructure将指针转换为C#结构体,然后使用Marshal.PtrToStringAnsi将歌曲信息指针转换为字符串。最后,使用Marshal.FreeCoTaskMem释放分配的内存。

请注意,这只是一个示例代码,你需要根据实际情况进行适当的修改和错误处理。确保在调用C++函数之前正确初始化和设置相关的DLL路径等参数。

希望这可以帮助到你!