为非托管 (C++) 代码编写托管包装器 - 自定义类型/结构

Posted

技术标签:

【中文标题】为非托管 (C++) 代码编写托管包装器 - 自定义类型/结构【英文标题】:Writing a managed wrapper for unmanaged (C++) code - custom types/structs 【发布时间】:2010-05-31 02:28:46 【问题描述】:
faacEncConfigurationPtr FAACAPI faacEncGetCurrentConfiguration(
       faacEncHandle hEncoder);

我正在尝试为这个 C++ 库提供一个简单的包装器;我以前从来没有做过比非常简单的 p/invoke 互操作更多的事情——比如一个带有原始参数的函数调用。

那么,以上面的C++函数为例,如何处理返回类型和参数呢?

FAACAPI 定义为:#define FAACAPI __stdcall

faacEncConfigurationPtr 已定义:

typedef struct faacEncConfiguration

    int version;
    char *name;
    char *copyright;
    unsigned int mpegVersion;
    unsigned long bitRate;
    unsigned int inputFormat;
    int shortctl;

    psymodellist_t *psymodellist;

    int channel_map[64]; 

 faacEncConfiguration, *faacEncConfigurationPtr;

AFAIK 这意味着函数的返回类型是对该结构的引用?

而 faacEncHandle 是:

typedef struct 
    unsigned int numChannels;
    unsigned long sampleRate;
...
    SR_INFO *srInfo;
    double *sampleBuff[MAX_CHANNELS];
...
    double *freqBuff[MAX_CHANNELS];
    double *overlapBuff[MAX_CHANNELS];

    double *msSpectrum[MAX_CHANNELS];

    CoderInfo coderInfo[MAX_CHANNELS];
    ChannelInfo channelInfo[MAX_CHANNELS];
    PsyInfo psyInfo[MAX_CHANNELS];
    GlobalPsyInfo gpsyInfo;
    faacEncConfiguration config;

    psymodel_t *psymodel;

    /* quantizer specific config */
    AACQuantCfg aacquantCfg;

 /* FFT Tables */
    FFT_Tables fft_tables;

    int bitDiff;
 faacEncStruct, *faacEncHandle;

所以在这个结构中我们看到了很多其他类型......嗯。

基本上,我想弄清楚如何在我的托管包装器中处理这些类型? 我是否需要在 C# 中创建这些类型/结构的版本?像这样的:

[StructLayout(LayoutKind.Sequential)]
struct faacEncConfiguration

    uint useTns;
    ulong bitRate;
...

如果是这样,那么运行时是否可以自动将这些对象“映射”到彼此上? 而且,我是否必须为这些返回类型/参数类型层次结构中的所有类型创建这些“映射”类型,一直到我得到所有原语?

我知道这是一个广泛的话题,任何关于快速了解我需要学习什么以实现这一目标的建议将不胜感激!谢谢!

【问题讨论】:

【参考方案1】:

您在创建托管结构以表示未托管结构以用于 P/Invoke 方面处于正确的轨道上。

然而,这并不是与非托管库互操作的最佳策略,因为在 C# 中使用这个 API 仍然感觉像在使用 C API - 创建和初始化一个结构,将其传递给一个函数并获得其他一些结构作为回报。

可以将 P/Invoke 用于不以其他方式公开为 .NET API 的奇怪函数调用,但对于完整的 API 包装器,我强烈建议使用托管 C++(C++/CLI)。在为 .NET 创建非托管互操作层方面绝对是无与伦比的。

最大的挑战是将这个本质上是 C 的接口转换为面向对象的接口,您可以在其中调用对象的方法,而不是调用具有所有公共成员结构的全局函数。

当您开始为 P/Invoke 编写精细的结构图时,您自己将不得不处理相当多的“魔术”,这些“魔术”控制托管原始类型如何转换为非托管类型。通常,使用不正确的类型会导致运行时错误。

使用托管 C++ (IJW - It Just Works),您可以在 C++ 中定义托管结构、类、接口,从而可以更自然地使用底层库并为您的 C# 应用程序提供更原生的接口。

This 是对 C++/CLI 的一个很好的概述。此外,MSDN 还为所有托管 C++ 功能提供了大量文档。

【讨论】:

很好的回复,感谢您提供的信息,没有想到托管 C++,将尝试研究它.. 到目前为止,我继续尝试 C#,虽然它有点笨拙..说包装一下: int FAACAPI faacEncEncode(faacEncHandle hEncoder, int32_t inputBuffer, unsigned int samplesInput, unsigned char *outputBuffer, unsigned int bufferSize);为什么是无符号字符?那会映射到字符串吗? 'int' 是否确实映射到 System.Int16 (如另一个答复所示?)等,这些问题在托管 C++ 解决方案中会更明显吗?嗯 [很高兴在纯 .NET 中正常工作:)]【参考方案2】:

是的,您需要在 c# 中声明所有这些结构。小心声明具有正确大小的成员。例如,“long”在 C++ 中是 32 位,但在 C# 中是 64 位。对于 C++ 中的指针或 void*,请使用 C# 中的 IntPtr 等。

【讨论】:

以上是关于为非托管 (C++) 代码编写托管包装器 - 自定义类型/结构的主要内容,如果未能解决你的问题,请参考以下文章

从 .NET 代码中销毁非托管对象

从非托管 c++ 调用 C# 函数(通过托管包装器)

如何附加调试器以从托管(C#)包装器进入本机(C++)代码?

从Finalize中删除非托管对象会引发AccessViolationException

如何围绕 C++ 代码编写 C 包装器以公开类方法

使用 C++/CLI 包装器将二维数组从 C# 传递到非托管 C++