Results 1 to 5 of 5

Thread: Message handler callback routine

  1. #1

    Message handler callback routine

    I already posted this message on the gamedev.net forums, but I'm gonna try my luck here too.

    I downloaded the Clootie's DirectX 9 header conversions and wanted to try out the DirectPlay stuff, but I ran into problems with my message handling callback routine.

    Here's my callback routine:
    Code:
    function TDirectPlayServer.DPMessageHandler(pvUserContext: Pointer; dwMessageType: DWORD; pMessage: Pointer): HRESULT; stdcall;
    begin
    	Result := S_OK;
    end;
    I try to use it like this (like I've done with the Windows API callbacks before):
    Code:
    ...
    hr := DPServer.Initialize(NIL, @DPMessageHandler, 0);
    ...
    The problems is that my code won't compile, the error message that I get is "Variable required".

    The initialize function is declared like this:
    Code:
    function Initialize(pvUserContext: Pointer; pfn: TFNDPNMessageHandler; dwFlags: DWORD): HResult; stdcall;
    And the callback is declared like this:
    Code:
    TFNDPNMessageHandler = function (pvUserContext: Pointer; dwMessageType: DWORD; pMessage: Pointer): HRESULT; stdcall;
      {$NODEFINE TFNDPNMessageHandler}
      {$HPPEMIT 'typedef PFNDPNMESSAGEHANDLER TFNDPNMessageHandler;'}
    I can easily set the handler if I declare it outside the class like this:
    Code:
    function DirectPlayMessageHandler(pvUserContext: Pointer; dwMessageType: DWORD; pMessage: Pointer): HRESULT; stdcall;
    begin
    	Result := S_OK;
    end;
    But it's kinda useless, because I can't access the private methods/variables on my server class.

    The solution is simple, but I just can't grasp it right now.

    Any help greatly appreciated.
    If you develop an idiot proof system, the nature develops better idiots.

  2. #2

    Message handler callback routine

    what i think you could do is to make DPMessageHandler a nonclass function.

    when you then call initialize then do it like this:
    DPServer.Initialize(@self, @DPMessageHandler, 0);

    in your callback function you can then use the servers properties by writing: name_of_the_server_class(pvUserContext).values_in_ the_server

    this was how i did a little hack with ode
    Peregrinus, expectavi pedes meos in cymbalis
    Nullus norvegicorum sole urinat

  3. #3

    Message handler callback routine

    What JSoftware said. You must declare the message handler as a normal function outside of the class. When you call Initialize, pass Self as the pvUserContext parameter. In the message handler, typecast the pvUserContext parameter to TDirectPlayServer to access members of the class.

    This is how I have done the same thing in my own DirectPlay classes.

  4. #4

    Message handler callback routine

    Thanks guys, now it works.

    I got similar answer from Clootie too.

    Still having trouble with the client part tho, can't get it to connect to my server...

    I keep getting "DPNERR_INVALIDHOSTADDRESS" when making the connection.

    Code:
    ..
       pHostName: PWideChar;
       pw: array [0..127] of WideChar;
    ...
    pHostName := StringToWideChar(pServerAddress, pw, Length(pServerAddress) + 1);
    	hr := DPHostAddress.AddComponent(DPNA_KEY_HOSTNAME,      //pwszName
                                        pHostName,                //lpvData
                                        (Length(pServerAddress) + 1) * 2,        //dwDataSize in bytes
                                        DPNA_DATATYPE_STRING );   //dwDataType
    Is the data size wrong or what? But I don't get any kind of errors here, only later when trying to make the connection.
    EDIT: pServerAddress is a String property.

    Code:
    DPDeviceAddress: IDirectPlay8Address;
    DPAppDesc: DPN_APPLICATION_DESC;
    DPClient: IDirectPlay8Client;
    DPHostAddress: IDirectPlay8Address;
    ...
    hr := DPClient.Connect(DPAppDesc,        // pdnAppDesc
                             DPHostAddress,       // pHostAddr
                             DPDeviceAddress,   // pDeviceInfo
                             NIL,               // pdnSecurity
                             NIL,               // pdnCredentials
                             NIL, 0,            // pvUserConnectData/Size
                             NIL,               // pvAsyncContext
                             NIL,               // pvAsyncHandle
                             DPNCONNECT_SYNC);   // dwFlags
    Any idea what's wrong this time?
    If you develop an idiot proof system, the nature develops better idiots.

  5. #5

    Message handler callback routine

    This is how I create the address object.

    DKNetCheck() is my own function that checks for errors and raises an exception if anything goes wrong.

    Code:
    procedure TDKNetClient.CreateHostAddress(out Address: IDirectPlay8Address);
    begin
      { Create the address object }
      if CoCreateInstance&#40;CLSID_DirectPlay8Address, nil, CLSCTX_INPROC_SERVER, IID_IDirectPlay8Address, Address&#41; <> S_OK then
        raise EDKNetClientError.Create&#40;'Could not create DirectPlay8Address COM object'&#41;;
    end;
    
    procedure TDKNetClientIP.CreateHostAddress&#40;out Address&#58; IDirectPlay8Address&#41;;
    begin
      inherited;
    
      &#123; Set the service provider for the address object &#125;
      DKNetCheck&#40;Address.SetSP&#40;CLSID_DP8SP_TCPIP&#41;&#41;;
    
      &#123; Set the port component of the address &#125;
      if FHostPort > 0 then
        DKNetCheck&#40;Address.AddComponent&#40;DPNA_KEY_PORT, @FHostPort, SizeOf&#40;FHostPort&#41;, DPNA_DATATYPE_DWORD&#41;&#41;;
    
      &#123; Set the hostname component of the address &#125;
      if Length&#40;FHostName&#41; > 0 then
        DKNetCheck&#40;Address.AddComponent&#40;DPNA_KEY_HOSTNAME, PWideChar&#40;FHostName&#41;, 2 * &#40;Length&#40;FHostName&#41; + 1&#41;, DPNA_DATATYPE_STRING&#41;&#41;;
    end;
    FHostPort is declared as an integer. FHostName is declared as WideString. Here's a neat trick:
    Code:
      TDKNetClientIP = class&#40;TDKNetClient&#41;
      private
        FHostName&#58; WideString;
        function GetHostName&#58; String;
        procedure SetHostName&#40;const Value&#58; String&#41;;
      public
        property HostName&#58; String read GetHostName write SetHostName;
      end;
    
    function TDKNetClientIP.GetHostName&#58; String;
    begin
      Result &#58;= FHostName;
    end;
    
    procedure TDKNetClientIP.SetHostName&#40;const Value&#58; String&#41;;
    begin
      FHostName &#58;= Value;
    end;
    Now you can access the HostName property as a normal string in your application (read and write as a standard string), but to DirectPlay it is a WideString (so you can typecast directly to PWideChar).

    Hint: if you have the DirectX SDK installed, change to the debug version of DirectPlay (in the DirectX Control Panel applet) and set the debug output fairly high. Then open the Event Log from the View/Debug Windows menu (if you have Delphi Professional or higher). If not, go to http://www.sysinternals.com and get DebugView. It is a small utility that allows you to see all the debug information that an application can spit out. Remember to switch back to the retail version of DirectPlay after debugging though!

    Hope that helps.

Bookmarks

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •