C#平台调用,参考和值类型C风格的联盟风格、类型、联盟、平台

2023-09-06 17:27:31 作者:长恨春归晚

我想马歇尔以下结构

struct OpalMessage {
  OpalMessageType m_type;   ///< Type of message
  union {
    const char *             m_commandError;       ///< Used by OpalIndCommandError
    OpalParamGeneral         m_general;            ///< Used by OpalCmdSetGeneralParameters
    OpalParamProtocol        m_protocol;           ///< Used by OpalCmdSetProtocolParameters
    OpalParamRegistration    m_registrationInfo;   ///< Used by OpalCmdRegistration
    OpalStatusRegistration   m_registrationStatus; ///< Used by OpalIndRegistration
    OpalParamSetUpCall       m_callSetUp;          ///< Used by OpalCmdSetUpCall/OpalIndProceeding/OpalIndAlerting/OpalIndEstablished
    const char *             m_callToken;          ///< Used by OpalCmdHoldcall/OpalCmdRetrieveCall/OpalCmdStopRecording
    OpalStatusIncomingCall   m_incomingCall;       ///< Used by OpalIndIncomingCall
    OpalParamAnswerCall      m_answerCall;         ///< Used by OpalCmdAnswerCall/OpalCmdAlerting
    OpalStatusUserInput      m_userInput;          ///< Used by OpalIndUserInput/OpalCmdUserInput
    OpalStatusMessageWaiting m_messageWaiting;     ///< Used by OpalIndMessageWaiting
    OpalStatusLineAppearance m_lineAppearance;     ///< Used by OpalIndLineAppearance
    OpalStatusCallCleared    m_callCleared;        ///< Used by OpalIndCallCleared
    OpalParamCallCleared     m_clearCall;          ///< Used by OpalCmdClearCall
    OpalStatusMediaStream    m_mediaStream;        ///< Used by OpalIndMediaStream/OpalCmdMediaStream
    OpalParamSetUserData     m_setUserData;        ///< Used by OpalCmdSetUserData
    OpalParamRecording       m_recording;          ///< Used by OpalCmdStartRecording
    OpalStatusTransferCall   m_transferStatus;     ///< Used by OpalIndTransferCall
    OpalStatusIVR            m_ivrStatus;          ///< Used by OpalIndCompletedIVR
  } m_param;
};

以上为C#。最明显的问题是两个字符串,这将不可避免地引用类型。

over to C#. The obvious problem is the two strings which will inevitably be reference types.

所以,我想这样的:

    [StructLayout(LayoutKind.Explicit)]
    public struct OpalMessageStrUnion
    {
      [FieldOffset(0)]
      [MarshalAs(UnmanagedType.LPStr)]
      public string m_commandError;       ///< Used by OpalIndCommandError

      [FieldOffset(0)]
      [MarshalAs(UnmanagedType.LPStr)]
      public string m_callToken;          ///< Used by OpalCmdHoldcall/OpalCmdRetrieveCall/OpalCmdStopRecording
    }

    [StructLayout(LayoutKind.Explicit)]
    public struct OpalMessageUnion
    {
      [FieldOffset(0)]      
      public OpalParamGeneral m_general;            ///< Used by OpalCmdSetGeneralParameters

      [FieldOffset(0)]
      public OpalParamProtocol m_protocol;           ///< Used by OpalCmdSetProtocolParameters

      [FieldOffset(0)]
      public OpalParamRegistration m_registrationInfo;   ///< Used by OpalCmdRegistration

      [FieldOffset(0)]
      public OpalStatusRegistration m_registrationStatus; ///< Used by OpalIndRegistration

      [FieldOffset(0)]
      public OpalParamSetUpCall m_callSetUp;          ///< Used by OpalCmdSetUpCall/OpalIndProceeding/OpalIndAlerting/OpalIndEstablished     

      [FieldOffset(0)]
      public OpalStatusIncomingCall m_incomingCall;       ///< Used by OpalIndIncomingCall

      [FieldOffset(0)]
      public OpalParamAnswerCall m_answerCall;         ///< Used by OpalCmdAnswerCall/OpalCmdAlerting

      [FieldOffset(0)]
      public OpalStatusUserInput m_userInput;          ///< Used by OpalIndUserInput/OpalCmdUserInput

      [FieldOffset(0)]
      public OpalStatusMessageWaiting m_messageWaiting;     ///< Used by OpalIndMessageWaiting

      [FieldOffset(0)]
      public OpalStatusLineAppearance m_lineAppearance;     ///< Used by OpalIndLineAppearance

      [FieldOffset(0)]
      public OpalStatusCallCleared m_callCleared;        ///< Used by OpalIndCallCleared

      [FieldOffset(0)]
      public OpalParamCallCleared m_clearCall;          ///< Used by OpalCmdClearCall

      [FieldOffset(0)]
      public OpalStatusMediaStream m_mediaStream;        ///< Used by OpalIndMediaStream/OpalCmdMediaStream

      [FieldOffset(0)]
      public OpalParamSetUserData m_setUserData;        ///< Used by OpalCmdSetUserData

      [FieldOffset(0)]
      public OpalParamRecording m_recording;          ///< Used by OpalCmdStartRecording

      [FieldOffset(0)]
      public OpalStatusTransferCall m_transferStatus;     ///< Used by OpalIndTransferCall

      [FieldOffset(0)]
      public OpalStatusIVR m_ivrStatus;          ///< Used by OpalIndCompletedIVR

    }

    /// <summary>
    /// Message to/from OPAL system.
    /// This is passed via the OpalGetMessage() or OpalSendMessage() functions.
    /// </summary> 
    [StructLayout(LayoutKind.Explicit)]
    public struct OpalMessage
    {
      //this guy is an enumeration b.t.w.
      /// <summary>
      /// type of message
      /// </summary> 
      [FieldOffset(0)]
      public OpalMessageType m_type;

      [FieldOffset(4)]
      public OpalMessageUnion m_param;

      [FieldOffset(4)]
      public OpalMessageStrUnion m_strParam;
    }

不过,我仍然得到封送处理错误,告诉我​​,这是行不通的,因为我是混合的对象与非对象类型相同的内存位置。我现在假设结构本身(即OpalParamGeneral等等)也不能混用的参考和值类型,即使它们被布置顺序?

However, I am still getting the marshaling error telling me that this won't work because I am mixing an object with non-object types in the same memory location. I am now assuming that the structures themselves (i.e. OpalParamGeneral and the like) also cannot mix reference and value types even though they are laid out sequentially?

否则一个单独的函数调用的每个结构是在路上没有一个选项。我宁愿写一个COM包装比做到这一点。

Doing a separate function call for each structure is not an option by the way. I would rather write a COM wrapper than do that.

推荐答案

在[StructLayout(LayoutKind.Explicit)]属性是不寻常的,它影响的结构,不仅当它被封的布局,但也影响了布局托管版本一个结构。这是相当方便的,它允许实施托管语言的工会,即使语言本身不支持它。

The [StructLayout(LayoutKind.Explicit)] attribute is unusual, it affects the layout of a structure not only when it is marshaled but also affects the layout of the managed version of a struct. Which can be quite handy, it allows implementing unions in a managed language even when the language itself doesn't support it.

他们造成麻烦但是,垃圾收集与他们的问题。它需要能够找到引用引用类型,并且不能在一个联盟这样做可靠。因为它不能告诉到底是什么类型的引用。这也是一个问题可靠性,可以重叠,比方说,一个IntPtr引用类型和发现对象的地址的方式。

They cause trouble however, the garbage collector has a problem with them. It needs to be able to find references to reference types and cannot do so reliably in a union. Since it cannot tell exactly what type is referenced. It is also a reliability problem, you could overlap a reference type with, say, an IntPtr and discover the address of the object that way.

CLR的类加载器检查此条件,如果出现重叠时抛出一个TypeLoadException。

The CLR class loader checks for this condition and throws a TypeLoadException if overlap occurs.

的解决方法是明确的,你需要确保只使用Blittable型。您的所有OpalXxx类型的需要是,自己只包含Blittable型结构。解决方法的字符串是声明他们作为IntPtr的,而不是和使用Marshal类值转换。更多关于Blittable型在此 MSDN库文章。

The workaround for this is clear, you need to make sure to only use blittable types. All of your OpalXxx types need to be structures that themselves contain only blittable types. The workaround for the strings is to declare them as IntPtr instead and use the Marshal class to convert the values. More about blittable types in this MSDN library article.