The Delphi Bug List

Entry No.
22
VCL - System - DdeMan - TDdeClientConv
If TDdeClientConv.ExecuteMacro or TDdeClientConv.ExecuteMacroLines is called with the WaitFlg parameter set to True, the TDdeClientConv.WaitStat property remains True for ever (it should become False when the server completes the processing of the macro)
1.02 2.01 3.0 3.01 3.02 4.0 4.01 4.02 4.03 5.0 5.01 6.0 6.01 6.02 Kylix 1.0
UnknownExistsExistsExistsExistsExistsExistsExistsExistsUnknownUnknownUnknownUnknownUnknownN/A
Description
Reported by Chris Jobson; checked by David Boissy; Randy Shaw
If TDdeClientConv.ExecuteMacro or TDdeClientConv.ExecuteMacroLines is called with the WaitFlg parameter set to True, the TDdeClientConv.WaitStat property remains True for ever more (it should become False when the server completes the processing of the macro).
I have experienced and investigated this with Delphi 3, but the Delphi 2 source looks identical and so I asume the problem will occur in Delphi 2 too.

Cause:

Delphi sets FWaitStat to True before issuing the Execute transaction to the DDEML. It uses the TIMEOUT_ASYNC flag in the Execute, so it expects to get a DDE callback of type XTYP_XACT_COMPLETE when the server finishes the transaction. The DDE callback routine should then call TDdeClientConv.XactComplete which sets FWaitStat to false.
The problem is that Delphi is using the DDEML DDESetUserHandle and DDEQueryConvInfo functions to store the TDdeClientConv pointer in the hUser member of the CONVINFO structure maintained by the DDEML. In the DDE callback handler it uses DDEQueryConvInfo to get the TDdeClientConv associated with the callback, passing it the transaction id for XTYP_XACT_COMPLETE callbacks and QID_SYNC for all other callbacks. Unfortunately it only calls DDESetUserHandle with QID_SYNC (when the conversation is opened), and never calls it with the transaction id for async transactions, so it never finds the TDdeClientConv for XTYP_XACT_COMPLETE callbacks.

Solution / workaround
Modify the code in ddeman.pas as follows.
function DdeMgrCallBack(CallType, Fmt: UINT; Conv: HConv; hsz1, hsz2: HSZ;
  Data: HDDEData; Data1, Data2: DWORD): HDDEData; stdcall;
var
  ci: TConvInfo;
  ddeCli: TComponent;
  ddeSrv: TDdeSrvrConv;
  ddeObj: TComponent;
  xID: Integer;
begin
  Result := 0;
  case CallType of
    XTYP_CONNECT:         Result := HDdeData(ddeMgr.AllowConnect(hsz2, hsz1));
    XTYP_WILDCONNECT:     Result := ddeMgr.AllowWildConnect(hsz2, hsz1);
    XTYP_CONNECT_CONFIRM: ddeMgr.Connect(Conv, hsz1, Boolean(Data2));
  end;
  if Conv <> then
  begin
    ci.cb := sizeof(TConvInfo);
// FIX TO BUG - COMMENT OUT NEXT 3 LINES
//    if CallType = XTYP_XACT_COMPLETE then 
//      xID := Data1
//    else xID := QID_SYNC;
On 25 March 1999, David Boissy confirmed that this bug still existed in Delphi 4.03, but that the above mentioned fix does not work for him. Any further information (confirmation/denial; explanation) to this is welcomed.

And here is some:
On 2 July 1999, Randy Shaw submitted the following:
Found a possible fix for bug # 22 on Deja News. I just typed in DdeSetUserHandle for the keywords and *delphi* for the forum and found this fix by Chad Crawley way back in March of 1997.

I'm successfully using this fix with version 3.02 of delphi but he was using it for version 2 so I suspect this will work for all versions from 2 to 3.02 but I have not confirmed this. The bug fix on your site does not work for me.

Modify the TDdeClientConv.ExecuteMacro function in ddeman.pas to the following.

{ Modified to set the user handle for the transaction.  The DdeMsgCallBack  }
{ function will not reset FWaitStat without this handle, effectively        }
{ disabling your connection after the first macro execution.  CIC  03/10/97 }
{ by Chad Crawley <snafo_AT_kcnet.com> }
function TDdeClientConv.ExecuteMacro(Cmd: PChar; waitFlg: Boolean): Boolean;
var
  hszCmd: HDDEData;
  hdata: HDDEData;
  ddeRslt: LongInt;
  bSetHandle: Boolean;
begin
  Result := False;
  if (FConv = 0) or FWaitStat then Exit;
  hszCmd := DdeCreateDataHandle(ddeMgr.DdeInstId, Cmd, StrLen(Cmd) + 1,
    0, 0, FDdeFmt, 0);
  if hszCmd = 0 then Exit;
  if waitFlg = True then FWaitStat := True;
  hdata := DdeClientTransaction(Pointer(hszCmd), -1, FConv, 0, FDdeFmt,
     XTYP_EXECUTE, TIMEOUT_ASYNC, @ddeRslt);
  bSetHandle := DdeSetUserHandle(FConv, ddeRslt, DWord(Self));
  if (hdata = 0) or (bSetHandle = False) then FWaitStat := False
  else Result := True;
end;
Hope you find this useful.
Randy Shaw
User-contributed comments
Stuart
24 Apr 2002  06:50 PM GMT
Neither of the 2 fixes work for D5 is my experience.
I do not have a solution for this.
Stuart
24 Apr 2002  06:50 PM GMT
Neither of the 2 fixes work for D5 is my experience.
I do not have a solution for this.
Latest update of this entry: 1999-07-15

Post a comment on this bug


Index page
Delphi Bug List home page
The Delphi Bug Lists are presently maintained by Jordan Russell, who has taken over this task from Reinier Sterkenburg since August 2000.
All feedback is appreciated. See also the feedback section of the Delphi Bug List home page.