/*========================================================================== * * Copyright (C) 1999 Microsoft Corporation. All Rights Reserved. * * File: dvprotocol.h * Content: Defines structures / types for DirectXVoice protocol * * History: * Date By Reason * ==== == ====== * 07/02/99 rodtoll Created It * 07/21/99 rodtoll Added settings confirm message to protocol * 08/18/99 rodtoll Changed the message type to a BYTE * 08/25/99 rodtoll General Cleanup/Modifications to support new * compression sub-system. * 09/07/99 rodtoll Moved definition of settarget message to dvoice.h * 09/30/99 rodtoll Updated to include more comments * 12/16/99 rodtoll Updated to include new messages used by host migration * and update protocol number * 01/14/2000 rodtoll Updated with new speech packets which optimize peer * to peer and allow multiple targets in multicast/mixing * and updated protocol number. * 04/07/2000 rodtoll Code cleanup, proper structure defs * 01/04/2001 rodtoll WinBug #94200 - Remove stray comments * 01/22/2001 rodtoll WINBUG #288437 - IA64 Pointer misalignment due to wire packets * ***************************************************************************/ #ifndef __DVPROTOCOL_H #define __DVPROTOCOL_H #pragma pack(push,1) // DVPROTOCOL_VERSION_XXXXXX // // These are the values developers should use to send in the ucVersionMajor // ucVersionMinor and dwVersionBuild (respectively) members of the ConnectRequest // and connect accept messages. #define DVPROTOCOL_VERSION_MAJOR 1 #define DVPROTOCOL_VERSION_MINOR 0 #define DVPROTOCOL_VERSION_BUILD 3 // // PROTOCOL DESCRIPTION // // Connection Process // ------------------ // // Clients connect to the voice session by sending a DVPROTOCOLMSG_CONNECTREQUEST. // The host then determines if the client is allowed to join the session. If // the client's request is rejected, the client receives a DVPROTOCOLMSG_CONNECTREFUSE. // If the client's request is accepted, the client receives a // DVPROTOCOLMSG_CONNECTACCEPT. This packet contains all the information the client // requires to determine if they are compatible. // // The client will then attempt to initialize themselves using the compression // type transmitted by the server. If the client fails to initialize it takes // no further action. If the client initializes succesfully it transmits a // DVPROTOCOLMSG_SETTINGSCONFIRM message and is added to the session. All players // in the session will then receive a DVPROTOCOLMSG_PLAYERJOIN. // // Until a DVPROTOCOLMSG_SETTINGSCONFIRM message is received by the host, the client is // not considered to be part of the session. As such it will not transmit // speech. (However, it may receive speech). // // The server then sends DVPROTOCOLMSG_PLAYERLIST message(s) to give the client the // current player table. (Peer to peer only). // // Disconnection Process // --------------------- // // If a client wishes to disconnect from the voice session, it sends a // DVPROTOCOLMSG_DISCONNECT message to the host. The host will then transmit // to all players a DVPROTOCOLMSG_PLAYERQUIT message and will send a // DVPROTOCOLMSG_DISCONNECT message back to the player to confirm. // // If the client disconnects unexpectedly the voice host will detect this and // will automatically send DVPROTOCOLMSG_PLAYERQUIT messages to all the other clients // in the session. // // Speech Messages // --------------- // // Speech messages are described in detail in the description for DVPROTOCOLMSG_SPEECH. // // Messages sent from the host are sent with the DVMSGID_SPEECHBOUNCE instead // of DVMSGID_SPEECH so that whena host and client share a single dplay id // the client can ignore it's own speech packets. // // Host Migration // -------------- // // NEW HOST MIGRATION // // Pros/Cons: // // First, the advantages to the new system: // - The new host migration mechanism allows DirectPlay Voice to have a host // - seperate from the directplay host. // - Shutting down the host without stopping the dplay interface causes voice host // to migrate. // - Minimizes changes to directplay. // - Removes requirement that all clients had to run voice clients. // - You can prevent the host from migrating when you call stopsession by // specifying the new DVFLAGS_NOHOSTMIGRATE which causes the session to be // stopped. // // Disadvantages of the new system: // - If there are no voice clients in the session when the host is lost, then the // voice session will be lost. // // Host Election // // In order to elect a new host, each client is given a "Host Order ID" when // they have completed their connection. All clients are informed of an // individual's "Host Order ID" when they find out about the player. These // identifiers start at 0, are a DWORD in size, and are incremented with each // new user. Therefore the first player in the session will have ID 0, the // second will have ID 1, etc. // // By using this method we ensure that the client elected to be the host will be // the oldest client in the session. This has the advantage (because of the way // the name table is distributed) if any client has any clients at all, they // will have the oldest clients. Therefore everyone in the session will either // have just themselves, or at the very least the client who will become the new // host. If a client doesn't have anyone in the session, it means they either // are the only ones in the session (in which case they become the new host) or // they haven't received the name table yet. If they haven't received the name // table they will not yet have a "Host Order ID" and will therefore be // inelligable to become the new host. // // When the host migrates to a new host, the new host offsets a constant (in // this case defined in dvcleng.cpp as DVMIGRATE_ORDERID_OFFSET) from it's own // Host Order ID and uses it as the next Host Order ID to hand out. (So // duplicate host order IDs are not handed out). // // Unless.. you get DVMIGRATE_ORDERID_OFFSET connects between when the new host // joined and a new player joins before a player with a higher ID joins. (If a // player informs the server that it's Host Order ID is > then the seed the host // bumps the seed to be DVMIGRATE_ORDERID_OFFSET from the user's value). In // this case you may end up with > 1 host. (But this case is extremely unlikely). // // How It's Implemented // // Each client in the session knows the DPID of the host. Therefore, to detect // the situation where the host will migrate the client looks at if the session // supports host migration and for one of two things: // // - A DVPROTOCOLMSG_HOSTMIGRATELEAVE message - This is sent by the host when StopSession ( // without the DVFLAGS_NOHOSTMIGRATE flag) or Release is called. // - DirectPlay informs DirectPlay Voice that the player ID belonging to the // session host has left the session. // // Once one of the above occurs, each clients runs the new host election // algorithm. Their action will depend on the situation: // // - If no clients are found that are elligible to become the new host. Then the // client will disconnect. // - If clients are found that are elligible to become the new host but it is not // the local client, the local client takes no action. // - If the local client is to be the new host then it starts up a new host locally. // // Once the new host starts up, it will send a DVPROTOCOLMSG_HOSTMIGRATE message to all // the users in the session. Each client in the session responds with their // current settings using a DVPROTOCOLMSG_SETTINGSCONFIRM message (which includes the // client's current "Host Order ID". Using these messages the new host rebuilds // the player list. Also, in response to the DVPROTOCOLMSG_SETTINGSCONFIRM message each // client receives a copy of the latest name table. For players who respond to // the host migration after you, DVPROTOCOLMSG_CREATEVOICEPLAYER messages will be sent. // This player ensures that every client ends up with a proper and up to date // name table. // // Each client in the session ignores duplicate CREATEVOICEPLAYER messages. // // The client object that creates the new host holds a reference to the new host // and will destroy it when the client is destroyed. (Possibly causing the host // to migrate again). // // Session Losses // // Using this new scheme it is possible to lose the voice session if there are // no voice clients elligeble to become the new voice host. (Meaning there are // no clients properly connected). All clients which are not yet completed // their connection will detect this situation and will disconnect from the // session. // // In addition, when the host goes to stop, it checks to see if there are any // clients elligeble to become the host. If there are none it transmits a // DVPROTOCOLMSG_SESSIONLOST message to all players in the session. // // Notes: // // One thing to watch for is since the DirectPlayVoice host may migrate to a // different client then the DirectPlay host does, just because you know who the // DirectPlay Host is doesn't mean you know who the voice host is. // // In order to implement this new mechanism the following related changes were // made: // // - Clients with differing major/minor/build protocol version numbers are // rejected by the host now. The result is that new clients may be able to // connect to old hosts but may crash, and old clients are now rejected by new // hosts. (Handling of these cases on old clients was not good -- just don't do // it). // - DirectPlay host migration messages are ignored. // - Connection requests are sent to everyone in the session. The client // determines who the host is by seeing who responded to their message. // - Handling of rejection messages now results in the connection failing. (There // was a bug). // - The rejection message now contains the major/minor/build protocol version of // the host. // - Instead of sending individual messages for each user in the current session // to a client when it first joins the host now sends a DVPROTOCOLMSG_PLAYERLIST which // is a list of players in the session. This reduces bandwidth and makes the // connection process shorter. ///////////////////////////////////////////////////////////////////////////////// // // PROTOCOL SPECIFIC DEFINES // ///////////////////////////////////////////////////////////////////////////////// // The maximum size in bytes that a single playerlist packet can be. // // (Holds about 120 entries / packet). // #define DVPROTOCOL_PLAYERLIST_MAXSIZE 1000 #define DVPROTOCOL_HOSTORDER_INVALID 0xFFFFFFFF // DVMIGRATE_ORDERID_OFFSET // // This is the number to add to this client's Host Order ID to be the // next value handed out by the host which the client is creating in // response to host migration. // #define DVMIGRATE_ORDERID_OFFSET 256 ///////////////////////////////////////////////////////////////////////////////// // // MESSAGE IDENTIFIERS // ///////////////////////////////////////////////////////////////////////////////// #define DVMSGID_INTERNALBASE 0x0050 #define DVMSGID_CONNECTREQUEST DVMSGID_INTERNALBASE+0x0001 #define DVMSGID_CONNECTREFUSE DVMSGID_INTERNALBASE+0x0003 #define DVMSGID_DISCONNECT DVMSGID_INTERNALBASE+0x0004 #define DVMSGID_SPEECH DVMSGID_INTERNALBASE+0x0005 #define DVMSGID_CONNECTACCEPT DVMSGID_INTERNALBASE+0x0006 #define DVMSGID_SETTINGSCONFIRM DVMSGID_INTERNALBASE+0x0008 #define DVMSGID_SETTINGSREJECT DVMSGID_INTERNALBASE+0x0009 #define DVMSGID_DISCONNECTCONFIRM DVMSGID_INTERNALBASE+0x000A #define DVMSGID_SPEECHBOUNCE DVMSGID_INTERNALBASE+0x0010 #define DVMSGID_PLAYERLIST DVMSGID_INTERNALBASE+0x0011 #define DVMSGID_HOSTMIGRATELEAVE DVMSGID_INTERNALBASE+0x0012 #define DVMSGID_SPEECHWITHTARGET DVMSGID_INTERNALBASE+0x0013 #define DVMSGID_SPEECHWITHFROM DVMSGID_INTERNALBASE+0x0014 ///////////////////////////////////////////////////////////////////////////////// // // CONNECT MESSAGES // ///////////////////////////////////////////////////////////////////////////////// // // DVPROTOCOLMSG_CONNECTREQUEST // // Session Types: ALL // Message Flow : Voice Client -> Voice Host // // Requests a connection to the existing DirectXVoice session. // typedef UNALIGNED struct _DVPROTOCOLMSG_CONNECTREQUEST { BYTE dwType; // = DVMID_CONNECTREQUEST BYTE ucVersionMajor; // Client's protocol version (major) BYTE ucVersionMinor; // Client's protocol version (minor) DWORD dwVersionBuild; // Client's protocol version (build) } DVPROTOCOLMSG_CONNECTREQUEST, *PDVPROTOCOLMSG_CONNECTREQUEST; // // DVPROTOCOLMSG_CONNECTREFUSE // // Session Types: ALL // Message Flow : Voice Host -> Voice Clients // // Server responds with this if no voice session available or needs // to refuse the connection. E.g. out of memory or incompatible // version. // typedef UNALIGNED struct _DVPROTOCOLMSG_CONNECTREFUSE { BYTE dwType; // = DVMSGID_CONNECTREFUSE HRESULT hresResult; // Reason for refusal (DVERR_XXXXXX) BYTE ucVersionMajor; // Server's protocol version (major) BYTE ucVersionMinor; // Server's protocol version (minor) DWORD dwVersionBuild; // Server's protocol version (build) } DVPROTOCOLMSG_CONNECTREFUSE, *PDVPROTOCOLMSG_CONNECTREFUSE; // // DVPROTOCOLMSG_CONNECTACCEPT // // Session Types: ALL // Message Flow : Voice Host -> Voice Clients // // Indicates to the client that their connect request was accepted. // This message contains information about the voice session that // the client needs to initialize. // typedef UNALIGNED struct _DVPROTOCOLMSG_CONNECTACCEPT { BYTE dwType; // = DVMSGID_CONNECTACCEPT DWORD dwSessionType; // Type of session = DVSESSIONTYPE_XXXXXX BYTE ucVersionMajor; // Server's protocol version (major) BYTE ucVersionMinor; // Server's protocol version (minor) DWORD dwVersionBuild; // Server's protocol version (build) DWORD dwSessionFlags; // Flags for the session (Combination of DVSESSION_XXXXXX values) GUID guidCT; // Compression Type (= DPVCTGUID_XXXXXX) } DVPROTOCOLMSG_CONNECTACCEPT, *PDVPROTOCOLMSG_CONNECTACCEPT; // // DVPROTOCOLMSG_SETTINGSCONFIRM // // Session Types: ALL // Message Flow : Voice Client -> Voice Host // // Sent by client to confirm they can handle current compression // settings. This message is sent once the client has determined that // they can support the specified compression type and the sound // system has succesfully initialized. // // This message is also sent from voice clients to the new voice host // when a migration has taken place. // typedef UNALIGNED struct _DVPROTOCOLMSG_SETTINGSCONFIRM { BYTE dwType; // Message Type = DVMSGID_SETTINGSCONFIRM DWORD dwFlags; // Client Flags (Only valid one is half duplex) DWORD dwHostOrderID; // Host Order ID (=INVALID to assign new one) } DVPROTOCOLMSG_SETTINGSCONFIRM, *PDVPROTOCOLMSG_SETTINGSCONFIRM; // // DVPROTOCOLMSG_PLAYERLIST // // Session Types: Peer to Peer // Message Flow : Voice Host -> Voice Clients // // Builds a list of players in the session to be sent to the // client once they have confirmed they are connected. // // May be spread over multiple packets. // // These messages will be the following header followed by a // list of DVPROTOCOLMSG_PLAYERLIST_ENTRY structures (the # will be // specified in dwNumEntries). // typedef UNALIGNED struct _DVPROTOCOLMSG_PLAYERLIST { BYTE dwType; // = DVMSGID_PLAYERLIST DWORD dwHostOrderID; // Host migration sequence number (for client) DWORD dwNumEntries; // Number of DVPROTOCOLMSG_PLAYERLIST_ENTRY structures // following this header in this packet } DVPROTOCOLMSG_PLAYERLIST, *PDVPROTOCOLMSG_PLAYERLIST; // // DVPROTOCOLMSG_PLAYERLIST_ENTRY // // Sent as part of a DVPROTOCOLMSG_PLAYERLIST message. // // Peer to Peer Only // // Each of these structures represents a player in the session. // They are sent as part of the DVPROTOCOLMSG_PLAYERLIST structure. // typedef UNALIGNED struct _DVPROTOCOLMSG_PLAYERLIST_ENTRY { DVID dvidID; // Player's DVID DWORD dwPlayerFlags; // Player's player flags (DVPLAYERCAPS_XXXXX) DWORD dwHostOrderID; // Host migration sequence number } DVPROTOCOLMSG_PLAYERLIST_ENTRY, *PDVPROTOCOLMSG_PLAYERLIST_ENTRY; ///////////////////////////////////////////////////////////////////////////////////// // // IN-SESSION MESSAGES - SPEECH // ///////////////////////////////////////////////////////////////////////////////////// // // DVPROTOCOLMSG_SPEECHHEADER // // This message is used for transporting speech. Speech packets contain // one of these headers followed by the audio data. After this header // the audio data will be the remaining part of the packet. // // ---- // // Session Types: Peer to Peer // Message Flow : Voice Clients <-> Voice Clients // // Session Types: Mixing // Message Flow : Voice Mixing Server --> Voice Clients // // Session Types: Echo // Message Flow : Voice Host <-> Voice Clients // typedef UNALIGNED struct _DVPROTOCOLMSG_SPEECHHEADER { BYTE dwType; // = DVMSGID_SPEECH BYTE bMsgNum; // Message # for message BYTE bSeqNum; // Sequence # for message } DVPROTOCOLMSG_SPEECHHEADER, *PDVPROTOCOLMSG_SPEECHHEADER; // // DVPROTOCOLMSG_SPEECHWITHTARGET // // This message is used for transporting speech. The message consists // of this header followed by a single DVID for each target the packet // is targetted at. After the target list the audio data will be the // remaining part of the packet. // // ---- // Sesssion Types: Mixing / Forwarding // Message Flow : Voice Clients --> Voice Host / Voice Mixing Server // typedef UNALIGNED struct _DVPROTOCOLMSG_SPEECHWITHTARGET { DVPROTOCOLMSG_SPEECHHEADER dvHeader; // dwType = DVMSGID_SPEECHWITHTARGET DWORD dwNumTargets; // # of targets following this header } DVPROTOCOLMSG_SPEECHWITHTARGET, *PDVPROTOCOLMSG_SPEECHWITHTARGET; // // DVPROTOCOLMSG_SPEECHWITHFROM // // This message is used by forwarding servers when a speech packet is // bounced. The packet contains this header followed by the audio // data. The audio data will be the remaining part of the packet. // // --- // Session Types: Forwarding // Message Flow : Forwarding Server --> Voice Clients // typedef UNALIGNED struct _DVPROTOCOLMSG_SPEECHWITHFROM { DVPROTOCOLMSG_SPEECHHEADER dvHeader; // dwType = DVMSGID_SPEECHWITHFROM DVID dvidFrom; // DVID of the client that this packet originated. } DVPROTOCOLMSG_SPEECHWITHFROM, *PDVPROTOCOLMSG_SPEECHWITHFROM; ///////////////////////////////////////////////////////////////////////////////////// // // IN-SESSION MESSAGES - TARGET MANAGEMENT // ///////////////////////////////////////////////////////////////////////////////////// // // DVPROTOCOLMSG_SETTARGET // // Tells client to switch it's target to the specified value. Used when // the server calls SetTransmitTarget for a particular player. This message // contains this header followed by dwNumTargets DWORDs containing the // players / groups that the client is targetting. // // --- // Session Types: Sessions with Server Controlled Targetting Enabled // Message Flow : Voice Host --> Voice Clients // typedef UNALIGNED struct _DVPROTOCOLMSG_SETTARGET { BYTE dwType; // = DVMSGID_SETTARGETS DWORD dwNumTargets; // # of targets (Can be 0 for no targets). } DVPROTOCOLMSG_SETTARGET, *PDVPROTOCOLMSG_SETTARGET; ///////////////////////////////////////////////////////////////////////////////////// // // IN-SESSION MESSAGES - NAMETABLE MANAGEMENT // ///////////////////////////////////////////////////////////////////////////////////// // // DVPROTOCOLMSG_PLAYERJOIN // // This message is used to inform clients when a new client has connected // to the session. // // --- // Session Types: Peer to Peer // Message Flow : Voice Host --> Voice Clients // typedef UNALIGNED struct _DVPROTOCOLMSG_PLAYERJOIN { BYTE dwType; // = DVMSGID_CREATEVOICEPLAYER DVID dvidID; // ID of the player DWORD dwFlags; // Player's player flags (DVPLAYERCAPS_XXXXX) DWORD dwHostOrderID; // Host Order ID } DVPROTOCOLMSG_PLAYERJOIN, *PDVPROTOCOLMSG_PLAYERJOIN; // // DVPROTOCOLMSG_PLAYERQUIT // // This message is used to inform clients when a client has left the voice // session. // // --- // Session Types: Peer to Peer // Message Flow : Voice Host --> Voice Clients // typedef UNALIGNED struct _DVPROTOCOLMSG_PLAYERQUIT { BYTE dwType; // = DVMSGID_DELETEVOICEPLAYER DVID dvidID; // ID of the player } DVPROTOCOLMSG_PLAYERQUIT, *PDVPROTOCOLMSG_PLAYERQUIT; // // DVPROTOCOLMSG_GENERIC // // Used to determine the type of a DirectPlayVoice message. Used in message // cracking. // typedef UNALIGNED struct _DVPROTOCOLMSG_GENERIC { BYTE dwType; } DVPROTOCOLMSG_GENERIC, *PDVPROTOCOLMSG_GENERIC; ///////////////////////////////////////////////////////////////////////////////////// // // IN-SESSION MESSAGES - HOST MIGRATION MESSAGES // ///////////////////////////////////////////////////////////////////////////////////// // // DVPROTOCOLMSG_HOSTMIGRATED // // This message is sent by the new host when a host migration has taken place. // The message is sent by the new host once they have finished initialization. // All clients should respond to this message with a DVPROTOCOLMSG_SETTINGSCONFIRM. // // --- // Session Types: Peer to Peer (With host migration enabled). // Message Flow : Voice Host (New) --> Voice Clients // typedef UNALIGNED struct _DVPROTOCOLMSG_HOSTMIGRATED { BYTE dwType; // = DVMSGID_HOSTMIGRATED } DVPROTOCOLMSG_HOSTMIGRATED, *PDVPROTOCOLMSG_HOSTMIGRATED; // // DVPROTOCOLMSG_HOSTMIGRATELEAVE // // This message is sent by a voice host if they are shutting down their interface // and host migration is enabled. It informs clients that they have to run their // election algorithm. // // --- // Session Types: Peer To Peer (With Host Migration Enabled) // Message Flow : Voice Host (Old) --> Voice Clients // typedef UNALIGNED struct _DVPROTOCOLMSG_HOSTMIGRATELEAVE { BYTE dwType; // = DVMSGID_HOSTMIGRATELEAVE } DVPROTOCOLMSG_HOSTMIGRATELEAVE, *PDVPROTOCOLMSG_HOSTMIGRATELEAVE; ///////////////////////////////////////////////////////////////////////////////////// // // IN-SESSION MESSAGES - SESSION TERMINATION // ///////////////////////////////////////////////////////////////////////////////////// // // DVPROTOCOLMSG_SESSIONLOST // // This message is sent by the voice host when they are shutting down and // host migration is not enabled or available. // // This message can also be sent if a host migration takes place and a // client encounters a fatal error when starting the new host. // // --- // Session Type: ALL // Message Flow: Voice Host (New) --> Voice Clients // Voice Host --> Voice Clients // typedef UNALIGNED struct _DVPROTOCOLMSG_SESSIONLOST { BYTE dwType; // = DVMSGID_SESSIONLOST HRESULT hresReason; // DVERR_XXXXXX or DV_OK } DVPROTOCOLMSG_SESSIONLOST, *PDVPROTOCOLMSG_SESSIONLOST; // // DVPROTOCOLMSG_DISCONNECT // // This message is sent by voice clients when they wish to disconnect // gracefully. The host responds with the same message to confirm // it received the request. Once the client receives the response // then it is free to disconnect. // // --- // Session Type: ALL // Message Flow: Voice Host --> Voice Client (dwType = DVPROTOCOLMSG_DISCONNECTCONFIRM) // Voice Client --> Voice Host (dwType = DVPROTOCOLMSG_DISCONNECT) // typedef UNALIGNED struct _DVPROTOCOLMSG_DISCONNECT { BYTE dwType; // = DVPROTOCOLMSG_DISCONNECTCONFIRM OR // DVPROTOCOLMSG_DISCONNECT HRESULT hresDisconnect; // HRESULT that caused the disconnect // DV_OK or DVERR_XXXXXX } DVPROTOCOLMSG_DISCONNECT, *PDVPROTOCOLMSG_DISCONNECT; typedef union _DVPROTOCOLMSG_FULLMESSAGE { DVPROTOCOLMSG_GENERIC dvGeneric; DVPROTOCOLMSG_SESSIONLOST dvSessionLost; DVPROTOCOLMSG_PLAYERJOIN dvPlayerJoin; DVPROTOCOLMSG_PLAYERQUIT dvPlayerQuit; DVPROTOCOLMSG_CONNECTACCEPT dvConnectAccept; DVPROTOCOLMSG_CONNECTREFUSE dvConnectRefuse; DVPROTOCOLMSG_CONNECTREQUEST dvConnectRequest; DVPROTOCOLMSG_SPEECHHEADER dvSpeech; DVPROTOCOLMSG_DISCONNECT dvDisconnect; DVPROTOCOLMSG_SETTARGET dvSetTarget; DVPROTOCOLMSG_SETTINGSCONFIRM dvSettingsConfirm; DVPROTOCOLMSG_PLAYERLIST dvPlayerList; DVPROTOCOLMSG_HOSTMIGRATED dvHostMigrated; DVPROTOCOLMSG_HOSTMIGRATELEAVE dvHostMigrateLeave; DVPROTOCOLMSG_SPEECHWITHTARGET dvSpeechWithTarget; DVPROTOCOLMSG_SPEECHWITHFROM dvSpeechWithFrom; } DVPROTOCOLMSG_FULLMESSAGE, *PDVPROTOCOLMSG_FULLMESSAGE; #pragma pack(pop) #endif