0
votes

We have a delphi 7 dde client application that communicates with a dde server from another company ( ISIS Papyrus printing system ) Both are 32 bits and works fine on windows 7 32 bits.

Now our company is switching to windows 7 64 bits and the communication between the client and server doesn't work good anymore. We use a callback function with DdeInitialize

We use this because we want to know when the process done by the server is really finnished and the document is generated.

On windows 7 64bits we get roubbish data for PTMonMsgStruct(pData)^.wMsg

Anyone knows whats goes wrong ?

Code :

DdeInitialize(FDdeInstId, SVDdeCallBack, MF_POSTMSGS + MF_SENDMSGS, 0);

and the SVDdeCallBack

function SVDdeCallBack(
   CallType, Fmt : UINT; //transaction type ; format atom of the data sent from the server
   Conv: HConv;          //a handle the conversation
   hsz1, hsz2: HSZ;      // hsz1: topic name ; hsz2: item name
   Data: HDDEData;       //a handle to the data associated with the topic and item name pair
   Data1, Data2: ULONG)  //
   : HDDEData; stdcall;
var
  pData: Pointer;
  Len: Integer;
type
  PTMonMsgStruct = ^TMonMsgStruct;
begin
  Result := 0;
  // Check if the calltype is the monitoring DDE call
    if CallType = XTYP_MONITOR then
  begin
    // Get the DDE data
        pData := DdeAccessData( Data, @Len);
    // If data
        if pData <> nil then
    begin
        // If data is a posted message
        if Data2 = MF_POSTMSGS then
      begin
                try
            // Check if the message was an acknowledge message
          //myMonitorStruct := PTMonMsgStruct(pData^);
            if PTMonMsgStruct(pData)^.wMsg = WM_DDE_ACK then
          begin
            WriteLogFile('prcDDE.DDECallBack: TMonMsgStruct(pData^).wMsg = WM_DDE_ACK');
            WriteLogFile('prcDDE.DDECallBack: Acknowledge message');
            // Detect only the acknowledge messages with no busy flag
              if ((PTMonMsgStruct(pData)^.dmhd.uilo and DDE_FACK) = DDE_FACK) and
               ((PTMonMsgStruct(pData)^.dmhd.uilo and DDE_FBUSY) = 0) then
            begin
                // The DDE command has terminated
              WriteLogFile('prcDDE.DDECallback: ((TMonMsgStruct(pData^).dmhd.uilo and DDE_FACK) = DDE_FACK) and ((TMonMsgStruct(pData^).dmhd.uilo and DDE_FBUSY) = 0)');
              WriteLogFile('prcDDE.DDECallback: Acknowledge with BUSY Flag = 0');
                    ApplData.WaitStat := False;
              WriteLogFile('prcDDE.DDECallback: ApplData.WaitStat := False');
            end
            else
            begin
              WriteLogFile('prcDDE.DDECallback: Acknowledge with BUSY Flag <> 0')
            end;
          end;
          if PTMonMsgStruct(pData)^.wMsg = WM_DDE_TERMINATE then
          begin
            // Check if in close
            WriteLogFile('prcDDE.DDECallBack:TMonMsgStruct(pData^).wMsg = WM_DDE_TERMINATE');
            WriteLogFile('prcDDE.DDECallBack: DDE_terminate message');
            if not scrFrontOffice.FInClose then
               begin
                  // Check if there is a second file
                  if ((Length(ApplData.DataFile2) > 0) and (ApplData.DataFile <> ApplData.DataFile2)) then
                     begin
                        // Activate the timer
                        // 2 following commands removed by VDP -> added in PrcFrontOffice
                        if bInternWaitForView then
                           begin
                              scrFrontOffice.FTimerProcess := True;
                              scrFrontOffice.tmrClose.Enabled := True;
                           end;
                     end
                  else
                     begin
                        // The DDE connection is inactive
                        scrFrontOffice.FDDEConnectionActive := False;
                            // Close the program
                        scrFrontOffice.Close;
                     end;
               end;
          end;
                finally
            // Free the accessed data
                    DdeUnaccessData(Data);
                end;
        end
      else
      begin
        //WriteLogFile('Data is no posted message');
      end;
    end
    else
    begin
      //WriteLogFile('No data received with DdeAccessData');
    end;
  end;
end;
1
Can we presume you check the return value of DDEInitialize and have verified that it is DMLERR_NO_ERROR? Otherwise, likely DDE problems are often win7 installations being not up to date (some bad KBs have trashed DDE in the past), antivirus applications blocking DDE messages, bad hardware drivers, UAC issues... DDE is pretty fragile - it's almost 30 years old and has been dead for almost 20. Not really enough information here to be able to help, unfortunately. - J...
DDE was somewhat replaced with COM about 20 years ago. Perhaps the software has a COM automation interface of something even more modern. I had a quick look and here it's said that it has a 'MQ/SOAP/HTML/REST' interface whatever that means, I could not find any effective developer documentation rightaway. - Stijn Sanders
This exact same question was asked 2 weeks ago in the Embarcadero forums: DDECallback with Delphi 7 on win7 64 bits machine. - Remy Lebeau
Yes i know. Now the company asked me to fix it :-( - Peter Everx

1 Answers

0
votes

First, why are you implementing DDE manually instead of using Delphi's TDdeClientConv component?

Two mistakes I see in your manual code:

  1. the callback is declared incorrectly. The Data1 and Data2 parameters are declared as ULONG, but they need to be ULONG_PTR instead. See the definition on MSDN:

    DdeCallback callback function

    HDDEDATA CALLBACK DdeCallback(
      _In_ UINT      uType,
      _In_ UINT      uFmt,
      _In_ HCONV     hconv,
      _In_ HSZ       hsz1,
      _In_ HSZ       hsz2,
      _In_ HDDEDATA  hdata,
      _In_ ULONG_PTR dwData1,
      _In_ ULONG_PTR dwData2
    );
    

    ULONG is always 32bit in size, even on a 64bit system. ULONG_PTR is either 32bit or 64bit depending on whether you are compiling for 32bit or 64bit. So, in this case, your callback is going to truncate those parameter values if you recompile your app for 64bit.

  2. the callback is looking for XTYP_MONITOR transactions, but APPCLASS_MONITOR is not included in the call to DdeInitialize().

With that said, if you are getting garbage from TMonMsgStruct, chances are it is not declared correctly. In Delphi 7 (and all the way up to XE), I see TMonMsgStruct in Delphi's DDeml unit is declared as a packed record, which means it uses byte alignment. But in Delphi XE2, the packed directive was finally removed so TMonMsgStruct uses more natural alignment (as it should be). So, you may have to re-declare TMonMsgStruct in your own code to use proper alignment (4 or 8 byte) that is compatible with 64bit systems.

Update: on a 32bit system, you are receiving 32bit data, and on a 64bit system you are receiving 64bit data even though your app is compiled for 32bit. The size of TMonMsgStruct is 72 bytes in 32bit, and is 112 bytes in 64bit. TMonMsgStruct contains several fields that use data types which are 4 bytes in 32bit and 8 bytes in 64bit (namely HWND, THandle, WPARAM, LPARAM, and UINT_PTR). Those fields, along with alignment padding, account for the difference in sizes. So, you need to either:

  1. compile a 64bit version of your app to run on 64bit systems. You cannot do that with Delphi 7, though, you need Delphi XE2 or later.

  2. have your 32bit app define a 64bit-compatible version of TMonMsgStruct, and then use the Len value returned from DdeAccessData() to decide whether to use the 32bit or 64bit record to access the data.