Linux内核中的常用宏container_of其实很简单

by admin on 2020年1月27日

如何根据数据结构中的某个数据的地址来获取这个整个结构的地址是多少?
比如有这样的一个结构体:struct test{ int array[100];int k;};
在程序中有一个test的对象TestObject,现在知道这个对象里的k的地址为pK,
如果去获取这个TestObject的地址呢?可能很简单:pK – 100*sizeof(int),
正确,那假如这个test结构体究竟是怎么样子的你根本不知道,那也就不知道
在k前面有什么数据,那又如何求呢?甚至很多情况下还要考虑内存对齐,那怎么办呢?

本篇是
使用DirectPlay进行网络互联(1)的续篇。

   
开发平台:Ubuntu11.04

其实道理是一样的,肯定是用k的地址pK来
减去k在这个结构中的地址偏移,这里可以借助NULL来求偏移地址:&(((test*)NULL)->k),
哎,其实vs中本来就有定义宏
PCHAR CONTAINING_RECORD(
PCHAR Address**, TYPE Type, PCHAR Field;
来实现这个功能了,不过我也是才知道。 以下来自MSDN
Parameters
Address
Pointer to a field in an instance of a structure of type Type.
Type
The name of the type of the structure whose base address is to be
returned, for example, type IRP.
Field
The name of the field pointed to by Address contained in a structure
of type Type.
Return Values*
Returns a pointer to the base of the structure containing
Field*.

使用地址

    编 译器:gcc version 4.5.2 (Ubuntu/Linaro4.5.2-8ubuntu4) 

这个宏在winnt.h中是这样被定义的
#define CONTAINING_RECORD(address, type, field) ((type *)(
                                                  (PCHAR)(address) –

                                                  (ULONG_PTR)(&((type
*)0)->field)))图片 1

一个网络使用IP地址和端口来传送数据,在DirectPlay中,使用DirectPlay专用对象IDirectPlay8Address来构造地址。常用的
IDirectPlay8Address方法有三个:

   
Container_of在Linux内核中是一个常用的宏,用于从包含在某个结构中的指针获得结构本身的指针,通俗地讲就是通过结构体变量中某个成员的首地址进而获得整个结构体变量的首地址。

IDirectPlay8Address::Clear 清除所有地址数据
IDirectPlay8Address::SetSP 设置服务提供者
IDirectPlay8Address::AddComponent 添加地址组件

    Container_of的定义如下: 

使用地址对象之前,需要使用CoCreateInstance函数来创建它:

  1. #define container_of(ptr, type, member) ({      
      
  2.     const typeof( ((type *)0)->member ) *__mptr = (ptr);      
  3.     (type *)( (char *)__mptr – offsetof(type,member) );})  

IDirectPlay8Server *          g_dp_server;     //  DirectPlay Server

    其实它的语法很简单,只是一些指针的灵活应用,它分两步:

//  create DirectPlay Server component
if (FAILED(CoCreateInstance(CLSID_DirectPlay8Server, NULL, CLSCTX_INPROC, IID_IDirectPlay8Server,
( void ** )
& g_dp_server)))
     return  FALSE;

    第一步,首先定义一个临时的数据类型(通过typeof( ((type
*)0)->member
)获得)与ptr相同的指针变量__mptr,然后用它来保存ptr的值。

添加组件

    第二步,用(char
*)__mptr减去member在结构体中的偏移量,得到的值就是整个结构体变量的首地址(整个宏的返回值就是这个首地址)。

一个地址对象只是包含Unicode文本字符串的简单对象,该文本字符串包含服务提供者、端口号以及其他可选信息。AddComponent函数的惟一用途就是创建该字符串以供其他对象使用。

    其中的语法难点就是如何得出成员相对结构体的偏移量?

Adds a component to the address. If the component is part of the
address, it is replaced by the new value in this call.

    通过例子说明,如清单1: 

Values are specified in native formats when making this call. Therefore,
the lpvData parameter should be a recast pointer to a variable that
holds the data in the native format. For example, if the component is a
GUID, the lpvData parameter should be a recast pointer to a GUID.

  1. /* linux-2.6.38.8/include/linux/compiler-gcc4.h */  
  2. #define __compiler_offsetof(a,b) __builtin_offsetof(a,b)
      
  3.   
  4. /* linux-2.6.38.8/include/linux/stddef.h */  
  5. #undef offsetof   
  6. #ifdef __compiler_offsetof   
  7. #define offsetof(TYPE,MEMBER) __compiler_offsetof(TYPE,MEMBER)
      
  8. #else   
  9. #define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER)
      
  10. #endif   
  11.   
  12. #include <stdio.h>   
  13.   
  14. struct test_struct {  
  15.     int num;  
  16.     char ch;  
  17.     float fl;  
  18. };  
  19.   
  20. int main(void)  
  21. {  
  22.     printf(“offsetof(struct test_struct, num) = %dn”,   
  23.             offsetof(struct test_struct, num));  
  24.       
  25.     printf(“offsetof(struct test_struct,  ch) = %dn”,   
  26.             offsetof(struct test_struct, ch));  
  27.       
  28.     printf(“offsetof(struct test_struct,  fl) = %dn”,   
  29.             offsetof(struct test_struct, fl));  
  30.       
  31.     return 0;  
  32. }  

This method validates that the predefined component types are the right
format.

   
说明,__builtin_offsetof(a,b)是GCC的内置函数,可认为它的实现与((size_t)
&((TYPE *)0)->MEMBER)这段代码是一致的。

HRESULT AddComponent(
const WCHAR *const pwszName,
const void *const lpvData,
const DWORD dwDataSize,
const DWORD dwDataType 
);

    例子输出结果:

Parameters

pwszName
[in] NULL-terminated Unicode string that contains the key for the
component.

lpvData
[in] Pointer to a buffer that contains the value associated with the
specified key. Data should be specified in its native format.

dwDataSize
[in] Size, in bytes, of the data in the buffer located at lpvData.
The size depends on the data type. If the size is not specified
correctly, the method returns DPNERR_INVALIDPARAM.

DWORD
Size = sizeof( DWORD )

GUID
Size = sizeof( GUID )

String
Size = size of the string in bytes, including NULL terminator.

dwDataType
[in] Data type of the value associated with this key. The data type
can be one of the following:

DPNA_DATATYPE_STRING
Data is a NULL-terminated string.

DPNA_DATATYPE_DWORD
Data is a DWORD.

DPNA_DATATYPE_GUID
Data is a GUID.

DPNA_DATATYPE_BINARY
Data is in raw binary format.

  1. offsetof(struct test_struct, num) = 0  
  2. offsetof(struct test_struct,  ch) = 4  
  3. offsetof(struct test_struct,  fl) = 8  

Return Values

Returns S_OK if successful, or one of the following error values.

DPNERR_INVALIDPARAM
DPNERR_INVALIDPOINTER
DPNERR_NOTALLOWED

    其中代码难以理解的地方就是它灵活地运用了0地址。如果觉得&(
(structtest_struct *)0
)->ch这样的代码不好理解,那么我们可以假设在0地址分配了一个结构体变量structtest_struct
a,然后定义结构体指针变量p并指向a(struct test_struct *p =
&a),如此我们就可以通过&p->ch获得成员ch的地址。由于a的首地址为0x0,所以成员ch的首地址为0x4。

Remarks

See DirectPlay Addressing for a discussion of various address components
and their keys.

设置服务提供者

下一步要做的就是选择服务提供者,调用
IDirectPlay8Address::SetSP函数来设置。

Sets the service provider GUID in the address object. If a service
provider is specified for this address, it is overwritten by this call.

HRESULT SetSP(
const GUID *const pguidSP
);

 图片 2

Parameters

pguidSP
[in] Pointer to the service provider GUID.

    最后通过强制类型转换(size_t)把一个地址值转换为一个整数。

Return Values

Returns S_OK if successful, or one of the following error values.

DPNERR_INVALIDPOINTER
DPNERR_NOTALLOWED

选择端口

    分析完container_of的定义,接下来举两个例子来体会一下它的使用方法。

无论是主持一次会话(作为服务器端或单点)还是使用客户端网络模型连接到一个远程系统,接下来必须要做的就是选择端口,如果要连接到远程系统,就必须知道应用程序所使用的端口,以便能够连接到远程系统以及发送数据到远程系统。端口号可以随意选择,但是不要使用保留端口号(1

1024),选择1024以上的端口号会比较安全。可以通过指定端口号0而让DirectPlay来选择一个端口号,但是这样做的弊病在于端口号可能为任意数字,就需要对端口号进行查询,推荐的做法是自己选择一个端口号。

设置端口号使用 IDirectPlay8Address::AddComponent函数。

IDirectPlay8Address *     dp_address;

DWORD port = 21234;

//  Set port
if (FAILED(dp_address -> AddComponent(DPNA_KEY_PORT,  & port,  sizeof (DWORD), DPNA_DATATYPE_DWORD)))
{
      dp_address -> Release();
       return  FALSE;
}

使用消息处理函数

以下是一个简单的消息处理函数代码实例:

// —————————————————————————————-
//  Callback function that receives all messages from the client, and receives indications 
//  of session changes from the IDirectPlay8Client interface. 
// —————————————————————————————-

HRESULT WINAPI Net_Msg_Handle(PVOID user_context, DWORD message_id, PVOID msg_buffer)
{
    DPNMSG_CREATE_PLAYER *
   create_player;   //  contains information for the DPN_MSGID_CREATE_PLAYER system message

    DPNMSG_DESTROY_PLAYER
*   destroy_player;  //  contains information for the DPN_MSGID_DESTROY_PLAYER system message

     switch (message_id)
    {
     //  Microsoft DirectPlay generates the DPN_MSGID_CREATE_PLAYER message when a player is added to a 
     //  peer-to-peer or client/server session.

     case  DPN_MSGID_CREATE_PLAYER:
        create_player  =
 (DPNMSG_CREATE_PLAYER * ) msg_buffer
        
         //  处理创建玩家
        
         return  S_OK;

     //  Microsoft DirectPlay generates the DPN_MSGID_DESTROY_PLAYER message when a player leaves a peer-to-peer 
     //  or client/server session.
     case  DPN_MSGID_DESTROY_PLAYER:
        destroy_player  =
 (DPNMSG_DESTROY_PLAYER  * ) msg_buffer;

         //  处理销毁玩家
        
         return  S_OK;       
    }

     return  S_FAIL;
}

通过switch-case语句,可以快速找到要处理的消息,而略过其他消息。如果处理消息成功,就返回S_OK,否则返回E_FAIL表示处理失败。每个消息都带有一个数据缓冲区,这些缓冲区被类型转换为适当的结构体,除了它们是以DPNMSG_而不是DPN_MSGID_开头之外,这些结构体和消息宏几乎具有相同的命名规则。

配置会话信息

每一个网络对象都需要知道一些要主持或要加入的会话信息,这些信息包含在一个结构体中:

Describes the settings for a Microsoft® DirectPlay® application.

typedef struct _DPN_APPLICATION_DESC{
 DWORD dwSize;
 DWORD dwFlags;
 GUID guidInstance;
 GUID guidApplication;
 DWORD dwMaxPlayers;
 DWORD dwCurrentPlayers;
 WCHAR* pwszSessionName;
 WCHAR* pwszPassword;
 PVOID pvReservedData;
 DWORD dwReservedDataSize;
 PVOID pvApplicationReservedData;
 DWORD dwApplicationReservedDataSize;
} DPN_APPLICATION_DESC, *PDPN_APPLICATION_DESC;

    正确的例子,如清单2: 

Members

dwSize
Size of the DPN_APPLICATION_DESC structure. The application must
set this member before it uses the structure.

dwFlags
One of the following flags describing application behavior.

DPNSESSION_CLIENT_SERVER
This type of session is client/server. This flag cannot be combined with
DPNSESSION_MIGRATE_HOST.

DPNSESSION_MIGRATE_HOST
Used in peer-to-peer sessions, enables host migration. This flag cannot
be combined with DPNSESSION_CLIENT_SERVER.

DPNSESSION_NODPNSVR
Do not forward enumerations to your host from DPNSVR. See Using the
DirectPlay DPNSVR Application for details.

DPNSESSION_REQUIREPASSWORD
The session is password protected. If this flag is set, pwszPassword
must be a valid string.

guidInstance
Globally unique identifier (GUID) that is generated by DirectPlay at
startup, representing the instance of this application. This member is
an [out] parameter when calling the GetApplicationDesc method
exposed by the IDirectPlay8Peer, IDirectPlay8Client, and
IDirectPlay8Server interfaces. It is an optional [in] parameter
when calling the Connect method exposed by the IDirectPlay8Peer
and IDirectPlay8Client interfaces. It must be set to GUID NULL when
you call the SetApplicationDesc method exposed by the
IDirectPlay8Server and IDirectPlay8Peer interfaces. You cannot
obtain this GUID by calling the IDirectPlay8Server::Host or
IDirectPlay8Peer::Host methods. You must obtain the GUID by calling
a GetApplicationDesc method.

guidApplication
Application GUID.

dwMaxPlayers
Variable of type DWORD, specifying the maximum number of players
allowed in the session. Set this member to 0 to specify an unlimited
number of players.

dwCurrentPlayers
Variable of type DWORD specifying the number of players currently
connected to the session. This member is an [out] parameter that is
set only by the GetApplicationDescription method exposed by
IDirectPlay8Peer, IDirectPlay8Client, and
IDirectPlay8Server.

pwszSessionName
Pointer to a variable of type WCHAR specifying the Unicode name of
the session. This member is set by the host or server only for
informational purposes. A client cannot use this name to connect to a
host or server.

pwszPassword
Pointer to a variable of type WCHAR specifying the Unicode password
that is required to connect to the session. This must be NULL if the
DPNSESSION_REQUIREPASSWORD is not set in the dwFlags member.

pvReservedData
Pointer to DirectPlay reserved data. An application should never modify
this value.

dwReservedDataSize
Variable of type DWORD specifying the size of data contained in the
pvReservedData member. An application should never modify this
value.

pvApplicationReservedData
Pointer to application-specific reserved data. This value is optional
and may be set to NULL.

dwApplicationReservedDataSize
Variable of type DWORD specifying the size of the data in the
pvApplicationReservedData member. This value is optional and may be
set to 0.

  1. /* linux-2.6.38.8/include/linux/compiler-gcc4.h */  
  2. #define __compiler_offsetof(a,b) __builtin_offsetof(a,b)
      
  3.   
  4. /* linux-2.6.38.8/include/linux/stddef.h */  
  5. #undef offsetof   
  6. #ifdef __compiler_offsetof   
  7. #define offsetof(TYPE,MEMBER) __compiler_offsetof(TYPE,MEMBER)
      
  8. #else   
  9. #define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER)
      
  10. #endif   
  11.   
  12. /* linux-2.6.38.8/include/linux/kernel.h * 
  13.  * container_of – cast a member of a structure out to the containing structure 
  14.  * @ptr: the pointer to the member. 
  15.  * @type:   the type of the container struct this is embedded in. 
  16.  * @member:    the name of the member within the struct. 
  17.  * 
  18.  */  
  19. #define container_of(ptr, type, member) ({      
      
  20.     const typeof( ((type *)0)->member ) *__mptr = (ptr);      
  21.     (type *)( (char *)__mptr – offsetof(type,member) );})  
  22.   
  23. #include <stdio.h>   
  24.   
  25. struct test_struct {  
  26.     int num;  
  27.     char ch;  
  28.     float fl;  
  29. };  
  30.   
  31. int main(void)  
  32. {  
  33.     struct test_struct init_test_struct = { 99, ‘C’, 59.12 };  
  34.   
  35.     char *char_ptr = &init_test_struct.ch;  
  36.   
  37.     struct test_struct *test_struct = container_of(char_ptr, struct test_struct, ch);  
  38.       
  39.     printf(” test_struct->num = %dn test_struct->ch = %cn test_struct->fl = %fn”,   
  40.         test_struct->num, test_struct->ch, test_struct->fl);  
  41.       
  42.     return 0;  
  43. }  

Remarks

Multiple instances of the application can run simultaneously.
“Application” refers to a specific instance of an application.

The dwMaxPlayers, pvApplicationReservedData,
dwApplicationReservedDataSize, pwszPassword, and
pwszSessionName members can be set when calling the Host or
SetApplicationDesc methods exposed by the IDirectPlay8Server and
IDirectPlay8Peer interfaces.

When connecting to a password protected session, the data in the
pwszPassword member is transmitted in clear text to the host.

会话数据

一个服务器端需要配置允许的最大玩家数(如果需要进行限制)、会话名称和登陆密码、会话标志以及应用程序GUID,如果不想限制最大玩家数,将dwMaxPlayers字段设置为0即可。

//  application GUID
GUID g_app_guid  =  { 
0xababbe60 ,  0x1ac0 ,  0x11d5 , {  0x90 ,  0x89 ,  0x44 ,  0x45 ,  0x53 ,  0x54 ,  0x0
,  0x1  } };

DPN_APPLICATION_DESC app_desc;   //  Describes the settings for a Microsoft DirectPlay application

//  Setup the application description structure

ZeroMemory( & app_desc,  sizeof (DPN_APPLICATION_DESC));

app_desc.dwSize           =
  sizeof (DPN_APPLICATION_DESC);
app_desc.dwFlags          =
 DPNSESSION_CLIENT_SERVER;
app_desc.guidApplication  =
 g_app_guid;
app_desc.pwszSessionName  =
 L ” SercverSession ” ;
app_desc.dwMaxPlayers     =
  256 ;

客户端结构体所需设置的信息包括要加入的会话名称和密码、客户端 /
服务器端会话标志以及应用程序GUID。一定要使用和服务器端相同的应用程序GUID,这样客户端和服务器端才能在网络中找到对方。

服务器端的处理

获得网络的第一步是创建一个服务器端。服务器端扮演了网络游戏的中央处理单元,所有玩家通过客户端应用程序连接到服务器端并在二者之间来回传输数据。服务器端保持游戏数据同步并告知玩家当前的游戏状态,虽然对于小型网络游戏而言,这并不是最快的办法,但对于大型网络游戏而言,则是最好的方法。

要创建服务器端,需要调用IDirectPlay8Server::Host来主持会话。

Creates a new client/server session, hosted by the local computer.

HRESULT Host(
const DPN_APPLICATION_DESC *const pdnAppDesc,
IDirectPlay8Address **const prgpDeviceInfo,
const DWORD cDeviceInfo,
const DPN_SECURITY_DESC *const pdpSecurity,
const DPN_SECURITY_CREDENTIALS *const pdpCredentials,
VOID *const pvPlayerContext,
const DWORD dwFlags
);

例子输出结果:

Parameters

pdnAppDesc
[in] Pointer to a DPN_APPLICATION_DESC structure that describes
the application.

prgpDeviceInfo
[in] Pointer to an array of IDirectPlay8Address objects containing
device addresses that should be used to host the application.

cDeviceInfo
[in] Variable of type DWORD that specifies the number of device
address objects in the array pointed to by prgpDeviceInfo.

pdpSecurity
[in] Reserved. Must be set to NULL.

pdpCredentials
[in] Reserved. Must be set to NULL.

pvPlayerContext
[in] Pointer to the context value of the player. This value is preset
when the local computer handles the DPN_MSGID_CREATE_PLAYER
message. This parameter is optional, and may be set to NULL.

dwFlags
[in] The following flag can be specified.

DPNHOST_OKTOQUERYFORADDRESSING
Setting this flag will display a standard Microsoft® DirectPlay® dialog
box, which queries the user for more information if not enough
information is passed in this method.

  1. test_struct->num = 99  
  2. test_struct->ch = C  
  3. test_struct->fl = 59.119999  

Return Values

Returns S_OK if successful, or the following error value.

DPNERR_DATATOOLARGE
DPNERR_INVALIDPARAM

不适当的例子,如清单3: 

Remarks

If you set the DPNHOST_OKTOQUERYFORADDRESSING flag in dwFlags, the
service provider may attempt to display a dialog box to ask the user to
complete the address information. You must have a visible window present
when the service provider tries to display the dialog box, or your
application will lock.

The maximum size of the application data that you assign to the
pvApplicationReservedData member of the DPN_APPLICATION_DESC
structure is limited by the service provider’s Maximum Transmission
Unit. If your application data is too large, the method will fail and
return DPNERR_DATATOOLARGE.

以下是创建服务器端会话的代码示例:

HWND g_hwnd;

//  application GUID
GUID g_app_guid  =  { 
0xababbe60 ,  0x1ac0 ,  0x11d5 , {  0x90 ,  0x89 ,  0x44 ,  0x45 ,  0x53 ,  0x54 ,  0x0
,  0x1  } };

IDirectPlay8Server *          g_dp_server;     //  DirectPlay Server

BOOL g_is_hosting;               //  flag indicates whether host started or not

// ——————————————————————————–
//  Start hosting a server which GUID specified by adapter_guid.
// ——————————————————————————–

BOOL Start_Session(GUID
*  adapter_guid)
{
     //  Make sure there’s an adapter
     if (adapter_guid  ==  NULL)
         return  FALSE;

     //  Need to re-assign a network handler as quitting a previous session to clears it.
     //  Close the connection first before assigning a new network handler.
     //
     //  Closes the open connnection to a session.

    g_dp_server -> Close( 0 );

     //  Initialize DirectPlay Server
     if (FAILED(g_dp_server -> Initialize(NULL, Net_Msg_Handle,  0 )))
         return  FALSE;

    IDirectPlay8Address *
    dp_address;

     //  Create an address object and fill it with information

     if (FAILED(CoCreateInstance(CLSID_DirectPlay8Address, NULL, CLSCTX_INPROC, IID_IDirectPlay8Address,
( void ** )
& dp_address)))
         return  FALSE;

     //  Set the protocol to TCP/IP
     //
     //  Sets the service provider GUID in the address object.
     //  If a service provider is specified for this address, it is overwrittern by this call.

     if (FAILED(dp_address -> SetSP( & CLSID_DP8SP_TCPIP)))
    {
        dp_address ->
Release();
         return  FALSE;
    }

     //  Set the port
    DWORD port  =  
21234 ;

     //  Adds a component to the address.
     //  If the component is part of the address, it is replaced by the new value in this call.

     //  Set port
     if (FAILED(dp_address -> AddComponent(DPNA_KEY_PORT,  & port,  sizeof (DWORD), DPNA_DATATYPE_DWORD)))
    {
        dp_address ->
Release();
         return  FALSE;
    }

     //  Set the adapter
     if (FAILED(dp_address -> AddComponent(DPNA_KEY_DEVICE, adapter_guid, 
sizeof (GUID), DPNA_DATATYPE_GUID)))
    {
        dp_address ->
Release();
         return  FALSE;
    }

    DPN_APPLICATION_DESC app_desc;   //  Describes the settings for a Microsoft DirectPlay application

     //  Setup the application description structure

    ZeroMemory( & app_desc,  sizeof (DPN_APPLICATION_DESC));

    app_desc.dwSize           =  
sizeof (DPN_APPLICATION_DESC);
    app_desc.dwFlags          =  DPNSESSION_CLIENT_SERVER;
    app_desc.guidApplication  =  g_app_guid;
    app_desc.pwszSessionName  =  L
” SercverSession ” ;
    app_desc.dwMaxPlayers     =  
256 ;

     //  Start hosting
     //
     //  Creates a new client/server session, hosted by the local computer.

     if (FAILED(g_dp_server -> Host( & app_desc,  & dp_address,  1 , NULL, NULL, NULL,  0 )))
    {
        dp_address ->
Release();
         return  FALSE;
    }

     //  Release the address component
    dp_address -> Release();

     //  Disables combo-box control.
     //
     //  Enables or disables mouse and keyboard input to the specified window or control. 
     //  When input is disabled, the window does not receive input such as mouse clicks and key presses. 
     //  When input is enabled, the window receives all input. 

    EnableWindow(GetDlgItem(g_hwnd, IDC_ADAPTERS), FALSE);

     //  Setup the dialog information
    SetWindowText(GetDlgItem(g_hwnd, IDC_STARTSTOP), 
” Stop ”
);

    g_is_hosting  =  TRUE;

     return  TRUE;
}

处理玩家

服务器端创建好之后,接收到的第一个消息就是创建一个玩家。创建的第一个玩家总是主机玩家,然后其他玩家才开始被创建和释放,但是在整个会话过程中,主机玩家始终都存在。创建玩家消息通过DPN_MSGID_CREATE_PLAYER定义,并且消息缓冲区被类型转换成
DPNMSG_CREATE_PLAYER结构体。

Microsoft® DirectPlay® generates the DPN_MSGID_CREATE_PLAYER message
when a player is added to a peer-to-peer or client/server session.

The DPNMSG_CREATE_PLAYER structure contains information for the
DPN_MSGID_CREATE_PLAYER system message.

typedef struct _DPNMSG_CREATE_PLAYER{
 DWORD dwSize;
 DPNID dpnidPlayer;
 PVOID pvPlayerContext;
} DPNMSG_CREATE_PLAYER, *PDPNMSG_CREATE_PLAYER;

dwSize
Size of this structure.

dpnidPlayer
DPNID of the player that was added to the session.

pvPlayerContext
Player context value.

  1. /* linux-2.6.38.8/include/linux/compiler-gcc4.h */  
  2. #define __compiler_offsetof(a,b) __builtin_offsetof(a,b)
      
  3.   
  4. /* linux-2.6.38.8/include/linux/stddef.h */  
  5. #undef offsetof   
  6. #ifdef __compiler_offsetof   
  7. #define offsetof(TYPE,MEMBER) __compiler_offsetof(TYPE,MEMBER)
      
  8. #else   
  9. #define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER)
      
  10. #endif   
  11.   
  12. /* linux-2.6.38.8/include/linux/kernel.h * 
  13.  * container_of – cast a member of a structure out to the containing structure 
  14.  * @ptr: the pointer to the member. 
  15.  * @type:   the type of the container struct this is embedded in. 
  16.  * @member:    the name of the member within the struct. 
  17.  * 
  18.  */  
  19. #define container_of(ptr, type, member) ({      
      
  20.     const typeof( ((type *)0)->member ) *__mptr = (ptr);      
  21.     (type *)( (char *)__mptr – offsetof(type,member) );})  
  22.   
  23. #include <stdio.h>   
  24.   
  25. struct test_struct {  
  26.     int num;  
  27.     char ch;  
  28.     float fl;  
  29. };  
  30.   
  31. int main(void)  
  32. {  
  33.     char real_ch = ‘A’;  
  34.     char *char_ptr = &real_ch;  
  35.   
  36.     struct test_struct *test_struct = container_of(char_ptr, struct test_struct, ch);  
  37.   
  38.     printf(” char_ptr = %p  test_struct = %pnn”, char_ptr, test_struct);  
  39.   
  40.     printf(” test_struct->num = %dn test_struct->ch = %cn test_struct->fl = %fn”,   
  41.         test_struct->num, test_struct->ch, test_struct->fl);  
  42.       
  43.     return 0;  
  44. }  

Return Values

Return DPN_OK.

    例子输出结果: 

Remarks

The only method of setting the player context value is through this
system message. You can either set the player context value directly,
through this message, or indirectly through
DPN_MSGID_INDICATE_CONNECT. Once a player context value has been
set, it cannot be changed.

玩家相关数据指针pvPlayerContext是用来在应用程序中定义玩家的信息,该数据指针可以指向一个结构体、类实例或包含了玩家名称、健康状态、年龄、当前武器以及护甲等的数据缓冲区。通过把玩家相关数据指针传递给DirectPlay,它就能带来更快的访问速度。因为时间关系,必须很快遍历一个已登录玩家的列表来查找一个匹配的玩家ID时,这样做就能节省时间。

一个玩家有一个与之关联的名称,而且要能够取回玩家名称以便在游戏中使用(因为没有人想使用一个数字作为其名称),这就是IDirectPlay8Server::
GetClientInfo函数所实现的功能。

Retrieves the client information set for the specified client.

HRESULT GetClientInfo(
const DPNID dpnid,
DPN_PLAYER_INFO *const pdpnPlayerInfo,
DWORD *const pdwSize,
const DWORD dwFlags
);
  1. char_ptr = 0xbfb72d7f  test_struct = 0xbfb72d7b  
  2.   
  3. test_struct->num = -1511000897  
  4. test_struct->ch = A  
  5. test_struct->fl = 0.000000  

Parameters

dpnid
[in] Variable of type DPNID that specifies the identifier of the
client to retrieve the information for.

pdpnPlayerInfo
[out] Pointer to a DPN_PLAYER_INFO structure that is filled with
client information. If pdwSize is not set to NULL, you must set
pdpnPlayerInfo.dwSize to an appropriate value.

pdwSize
[in,out] Pointer to a variable of type DWORD that contains the
size of the client data, in bytes, returned in the pdpnPlayerInfo
parameter. If the buffer is too small, this method returns
DPNERR_BUFFERTOOSMALL and this parameter contains the size of the
required buffer.

dwFlags
[in] Flags describing the information returned for the client.
Currently, both of the following flags are returned.

DPNINFO_NAME
The DPN_PLAYER_INFO structure contains the name set for the
client.

DPNINFO_DATA
The DPN_PLAYER_INFO structure contains the data set for the
client.

   
注意,由于这里并没有一个具体的结构体变量,所以成员num和fl的值是不确定的。

Return Values

Returns S_OK if successful, or one of the following error values.

DPNERR_BUFFERTOOSMALL
DPNERR_INVALIDPARAM
DPNERR_INVALIDPLAYER

图片 3

Remarks

Call this method after the server receives a
DPN_MSGID_CLIENT_INFO message from the application. This message
indicates that a client has updated its information.

Microsoft® DirectPlay® returns the DPN_PLAYER_INFO structure, and
the pointers assigned to the structure’s pwszName and pvData
members in a contiguous buffer. If the two pointers were set, you must
have allocated enough memory for the structure, plus the two pointers.
The most robust way to use this method is to first call it with
pdwSize set to NULL. When the method returns, pdwSize will point to
the correct value. Use that value to allocate memory for the structure
and call the method a second time to retrieve the information.

When the method returns, the dwInfoFlags member of the
DPN_PLAYER_INFO structure will always have the DPNINFO_DATA and
DPNINFO_NAME flags set, even if the corresponding pointers are set to
NULL. These flags are used when calling
IDirectPlay8Client::SetClientInfo, to notify DirectPlay of which
values have changed.

Transmission of nonstatic information should be handled with the
IDirectPlay8Client::Send method because of the high cost of using
the IDirectPlay8Client::SetPeerInfo method.

The player sets the information by calling
IDirectPlay8Client::SetClientInfo.

pdpnPlayerInfo指向玩家信息结构体。

Describe static player information.

typedef struct _DPN_PLAYER_INFO{ DWORD dwSize; DWORD dwInfoFlags;
PWSTR pwszName; PVOID pvData; DWORD dwDataSize; DWORD dwPlayerFlags; }
DPN_PLAYER_INFO, *PDPN_PLAYER_INFO;

Members

dwSize
Variable of type DWORD describing the size of this structure.

dwInfoFlags
Variable of type DWORD containing flags that specify the type of
information contained in this structure. When a GetPlayerInfo method
returns, the dwInfoFlags member of the DPN_PLAYER_INFO will
always have both flags set, even if the corresponding pointers are set
to NULL. These flags are used when calling
IDirectPlay8Peer::SetPeerInfo, to notify Microsoft® DirectPlay®
which values have changed.

DPNINFO_NAME
The pwszName member contains valid data.

DPNINFO_DATA
The pvData member contains valid data.

pwszName
Pointer to a variable of type PWSTR specifying the Unicode name of
the player.

pvData
Pointer to the data describing the player.

dwDataSize
Variable of type DWORD that specifies the size of the data contained
in the pvData member.

dwPlayerFlags
Variable of type DWORD that may contain one of the following flags.

DPNPLAYER_LOCAL
This information is for the local player.

DPNPLAYER_HOST
This player is the host for the application.

Remarks

When using this structure in the IDirectPlay8Peer::GetPeerInfo and
IDirectPlay8Server::GetClientInfo methods, dwInfoFlags must be
set to 0.

When using this structure in the IDirectPlay8Client::SetClientInfo,
IDirectPlay8Peer::SetPeerInfo, or IDirectPlay8Server::SetServerInfo
methods, dwPlayerFlags should be set to zero.

以下是处理创建玩家的代码实例:










   // 
    application variables 


   struct 
    PLAYER_INFO
{
    DPNID   player_id;   
   // 
    DirectPlay Player ID 


        
   char 
       name[ 
   26 
   ];    
   // 
    Player Name 



    PLAYER_INFO()   { player_id  
   = 
     
   0 
   ; }
};


   // 
    window handles, class. 


   HWND g_hwnd;

   char 
    g_class_name[]  
   = 
     
   " 
   ServerClass 
   " 
   ;


   // 
    application GUID 


   GUID g_app_guid  
   = 
    {  
   0xababbe60 
   ,  
   0x1ac0 
   ,  
   0x11d5 
   , {  
   0x90 
   ,  
   0x89 
   ,  
   0x44 
   ,  
   0x45 
   ,  
   0x53 
   ,  
   0x54 
   ,  
   0x0 
   ,  
   0x1 
    } };

IDirectPlay8Server 
   * 
            g_dp_server;     
   // 
    DirectPlay Server 


   DPN_SERVICE_PROVIDER_INFO 
   * 
     g_adapter_list;  
   // 
    adapters 


   DWORD                       g_num_adapters;  
   // 
    number of adapters 



PLAYER_INFO g_player_info[ 
   256 
   ];  
   // 
    player information 


   BOOL g_is_hosting;               
   // 
    flag indicates whether host started or not


   // 
   ----------------------------------------------------------------------------------------

   // 
    Callback function that receives all messages from the client, and receives indications 

   // 
    of session changes from the IDirectPlay8Client interface. 

   // 
   ---------------------------------------------------------------------------------------- 


   HRESULT WINAPI Net_Msg_Handle(PVOID user_context, DWORD message_id, PVOID msg_buffer)
{
    DPNMSG_CREATE_PLAYER 
   * 
      create_player;   
   // 
    contains information for the DPN_MSGID_CREATE_PLAYER system message 


       DPNMSG_DESTROY_PLAYER 
   * 
     destroy_player;  
   // 
    contains information for the DPN_MSGID_DESTROY_PLAYER system message 


       DPNMSG_RECEIVE 
   * 
            receive_data;    
   // 
    contains information for the DPN_MSGID_RECEIVE system message 


       DPN_PLAYER_INFO 
   * 
           dpn_player_info; 
   // 
    describes static player informaion 


       DPN_BUFFER_DESC         buffer_desc;     
   // 
    used dy DirectPlay for generic buffer information 



    DPNHANDLE       async_handle;
    PLAYER_INFO 
   * 
       player_info;
     
   int 
                index;
    DWORD           size;
     
   char 
               message[ 
   512 
   ];
    HRESULT         rv;

     
   switch 
   (message_id)
    {
     
   // 
    Microsoft DirectPlay generates the DPN_MSGID_CREATE_PLAYER message when a player is added to a 
     
   // 
    peer-to-peer or client/server session. 


        
   case 
    DPN_MSGID_CREATE_PLAYER:
        create_player  
   = 
    (DPNMSG_CREATE_PLAYER 
   * 
   ) msg_buffer;

         
   // 
    get player name and save it 



        size  
   = 
     
   0 
   ;
        dpn_player_info  
   = 
    NULL;

         
   // 
    Retrieves the client information set for the specified client 


           rv  
   = 
    g_dp_server 
   -> 
   GetClientInfo(create_player 
   -> 
   dpnidPlayer, dpn_player_info,  
   & 
   size,  
   0 
   );

         
   if 
   (FAILED(rv)  
   && 
    rv  
   != 
    DPNERR_BUFFERTOOSMALL)
        {
             
   // 
    skip this if this is a host player 


                
   if 
   (rv  
   == 
    DPNERR_INVALIDPLAYER)
                 
   break 
   ;

             
   return 
    E_FAIL;
        }

         
   if 
   ((dpn_player_info  
   = 
    (DPN_PLAYER_INFO 
   * 
   )  
   new 
    BYTE[size])  
   == 
    NULL)
             
   return 
    E_FAIL;

        ZeroMemory(dpn_player_info, size);
        dpn_player_info 
   -> 
   dwSize  
   = 
     
   sizeof 
   (DPN_PLAYER_INFO);

         
   // 
    retrieves the client information set again 


            
   if 
   (FAILED(g_dp_server 
   -> 
   GetClientInfo(create_player 
   -> 
   dpnidPlayer, dpn_player_info,  
   & 
   size,  
   0 
   )))
        {
            delete[] dpn_player_info;
             
   return 
    E_FAIL;
        }

         
   // 
    Find an empty player structure to use 



        index  
   = 
     
   - 
   1 
   ;

         
   for 
   ( 
   int 
    i  
   = 
     
   0 
   ; i  
   < 
     
   256 
   ; i 
   ++ 
   )
        {
             
   if 
   (g_player_info[i].player_id  
   == 
     
   0 
   )
            {
                index  
   = 
    i;
                 
   break 
   ;
            }
        }

         
   if 
   (index  
   == 
     
   - 
   1 
   )
        {
            delete[] dpn_player_info;
             
   return 
    E_FAIL;
        }

         
   // 
    set player context pointer 


           create_player 
   -> 
   pvPlayerContext  
   = 
    ( 
   void 
   * 
   )  
   & 
   g_player_info[index];

         
   // 
    save player ID 


           g_player_info[index].player_id  
   = 
    create_player 
   -> 
   dpnidPlayer;
        
        wcstombs(g_player_info[index].name, dpn_player_info 
   -> 
   pwszName,  
   256 
   );
        
         
   // 
    add player to list 


           SendMessage(GetDlgItem(g_hwnd, IDC_USERS), LB_ADDSTRING,  
   0 
   , (LPARAM) g_player_info[index].name);

         
   // 
    send a message to all players notifying someone joined 


           sprintf(message,  
   " 
   %s joined! 
   " 
   , g_player_info[index].name);
        Send_Text_Msg(DPNID_ALL_PLAYERS_GROUP, message);

        delete[] dpn_player_info;

         
   break 
   ;
    }

     
   // 
    return S_OK to signify the message was handled OK. 


        
   return 
    S_OK;
} 

发表评论

电子邮件地址不会被公开。 必填项已用*标注

网站地图xml地图