/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/*************************************************************************
 *
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
 *
 * Copyright 2000, 2010 Oracle and/or its affiliates.
 *
 * OpenOffice.org - a multi-platform office productivity suite
 *
 * This file is part of OpenOffice.org.
 *
 * OpenOffice.org is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License version 3
 * only, as published by the Free Software Foundation.
 *
 * OpenOffice.org is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Lesser General Public License version 3 for more details
 * (a copy is included in the LICENSE file that accompanied this code).
 *
 * You should have received a copy of the GNU Lesser General Public License
 * version 3 along with OpenOffice.org.  If not, see
 * <http://www.openoffice.org/license.html>
 * for a copy of the LGPLv3 License.
 *
 ************************************************************************/

/*
    ToDo / Bugs:

    - DdeInitiate: Eigener Thread?
    - Timeout bei Disconnects (IBM:nicht auf Ack warten!)
    - Konzept Errorhandling (globale/lokale Fehler)
    - Bedeutung/Anwendung Conversation-Contexte
    - Bei Zugriffen auf ConversationHandles WindowHandles checken
    - Namen der Partner-App ermitteln
    - Codepage-Geraffel
*/
#define INCL_DOSPROCESS

#include "ddemlimp.hxx"

#define LOGFILE
#define STATUSFILE
#define DDEDATAFILE
#include "ddemldeb.hxx"


namespace binfilter
{

// static
inline BOOL ImpDdeMgr::MyWinDdePostMsg( HWND hWndTo, HWND hWndFrom,
    USHORT nMsg, PDDESTRUCT pData, ULONG nFlags )
{
    BOOL bSuccess = WinDdePostMsg( hWndTo,hWndFrom,nMsg,pData,nFlags);
    if( !bSuccess )
    {
        WRITELOG("WinDdePostMsg:Failed!")
        if ( !(nFlags & DDEPM_NOFREE) )
        {
            MyDosFreeMem( pData,"MyWinDdePostMsg" );
        }
    }
    return bSuccess;
}


// *********************************************************************
// ImpDdeMgr
// *********************************************************************

USHORT ImpDdeMgr::nLastErrInstance = 0;

//
// Conversation-WndProc
// Steuert Transaktionen eines Conversationhandles
//
MRESULT EXPENTRY ConvWndProc(HWND hWnd,ULONG nMsg,MPARAM nPar1,MPARAM nPar2)
{
#if defined(DBG_UTIL) && defined(OV_DEBUG)
    if( nMsg >= WM_DDE_FIRST && nMsg <= WM_DDE_LAST)
    {
        ////WRITELOG("::ConvWndProc:DDE-Msg received")
    }
#endif
    ImpConvWndData* pObj = (ImpConvWndData*)WinQueryWindowULong( hWnd, 0 );
    return pObj->pThis->ConvWndProc( hWnd, nMsg, nPar1, nPar2 );
}

//
// Server-WndProc
// DDE-Server-Window der App
//
MRESULT EXPENTRY ServerWndProc(HWND hWnd,ULONG nMsg,MPARAM nPar1,MPARAM nPar2)
{
#if defined(DBG_UTIL) && defined(OV_DEBUG)
    if( nMsg >= WM_DDE_FIRST && nMsg <= WM_DDE_LAST)
    {
        ////WRITELOG("::ServerWndProc:DDE-Msg received")
    }
#endif
    ImpDdeMgr* pObj = (ImpDdeMgr*)WinQueryWindowULong( hWnd, 0 );
    return pObj->SrvWndProc( hWnd, nMsg, nPar1, nPar2 );
}


inline HDDEDATA ImpDdeMgr::Callback( USHORT nTransactionType,
    USHORT nClipboardFormat, HCONV hConversationHandle, HSZ hsz1,
    HSZ hsz2, HDDEDATA hData, ULONG nData1, ULONG nData2 )
{
    HDDEDATA hRet = (HDDEDATA)0;
    if( pCallback )
        hRet = (*pCallback)(nTransactionType, nClipboardFormat,
            hConversationHandle, hsz1, hsz2, hData, nData1, nData2);
    return hRet;
}



ImpDdeMgr::ImpDdeMgr()
{
    nLastErrInstance    = DMLERR_NO_ERROR;
    pCallback           = 0;
    nTransactFilter     = 0;
    nServiceCount       = 0;
    pServices           = 0;
    pAppTable           = 0;
    pConvTable          = 0;
    pTransTable         = 0;
    bServFilterOn       = TRUE;
    bInSyncTrans        = FALSE;

    CreateServerWnd();
    pData = InitAll();
    if ( !pData )
        nLastErrInstance = DMLERR_MEMORY_ERROR;
    else
        RegisterDDEMLApp();
}

ImpDdeMgr::~ImpDdeMgr()
{
    CleanUp();
    DestroyServerWnd();
// Named Shared Mem vom BS loeschen lassen, da nicht bekannt ist,
// wieviele DDEML-Instanzen die App erzeugt hat, und OS/2
// keinen App-Referenzzaehler fuer shared mem fuehrt.
//  if ( pData )
//      DosFreeMem( pData );
}


BOOL ImpDdeMgr::IsSameInstance( HWND hWnd )
{
    TID tid; PID pid;
    WinQueryWindowProcess( hWnd, &pid, &tid );
    return (BOOL)(pid == pidThis);
}

HSZ ImpDdeMgr::GetAppName( HWND hWnd )
{
    return 0;
}

// static
ImpDdeMgr* ImpDdeMgr::GetImpDdeMgrInstance( HWND hWnd )
{
    ImpDdeMgrData* pData = ImpDdeMgr::AccessMgrData();
    if( !pData )
        return 0;

    ImpDdeMgr* pResult = 0;
    TID tid; PID pidApp;
    WinQueryWindowProcess( hWnd, &pidApp, &tid );
    HWND* pApp = ImpDdeMgr::GetAppTable( pData );
    USHORT nCurApp = 0;
    while( nCurApp < pData->nMaxAppCount )
    {
        HWND hCurWin = *pApp;
        if( hCurWin )
        {
            PID pidCurApp;
            WinQueryWindowProcess( hCurWin, &pidCurApp, &tid );
            if( pidCurApp == pidApp )
            {
                pResult = (ImpDdeMgr*)WinQueryWindowULong( hCurWin, 0 );
                break;
            }
        }
        pApp++;
        nCurApp++;
    }
    return pResult;
}





void ImpDdeMgr::CleanUp()
{
    DisconnectAll();
    ImpService* pPtr = pServices;
    if( pPtr )
    {
        for( USHORT nIdx = 0; nIdx < nServiceCount; nIdx++, pPtr++ )
        {
            HSZ hStr = pPtr->hBaseServName;
            if( hStr )
                DdeFreeStringHandle( hStr );
            hStr = pPtr->hInstServName;
            if( hStr )
                DdeFreeStringHandle( hStr );
        }
        nServiceCount = 0;
        delete pServices;
        pServices = 0;
    }
    bServFilterOn = TRUE;  // default setting DDEML
    UnregisterDDEMLApp();
}

void ImpDdeMgr::RegisterDDEMLApp()
{
    HWND* pPtr = pAppTable;
    HWND hCur;
    USHORT nPos = 0;
    while( nPos < pData->nMaxAppCount )
    {
        hCur = *pPtr;
        if (hCur == (HWND)0 )
        {
            // in Tabelle stellen
            *pPtr = hWndServer;
            break;
        }
        nPos++;
        pPtr++;
    }
}

void ImpDdeMgr::UnregisterDDEMLApp()
{
    HWND* pPtr = pAppTable;
    USHORT nPos = 0;
    while( nPos < pData->nMaxAppCount )
    {
        if (*pPtr == hWndServer )
        {
            *pPtr = 0;
            break;
        }
        nPos++;
        pPtr++;
    }
}

// static
ImpDdeMgrData* ImpDdeMgr::AccessMgrData()
{
    ImpDdeMgrData* pData = 0;
    APIRET nRet = DosGetNamedSharedMem((PPVOID)&pData,DDEMLDATA,PAG_READ|PAG_WRITE);
    DBG_ASSERT(!nRet,"DDE:AccessMgrData failed");
    return pData;
}

USHORT ImpDdeMgr::DdeGetLastError()
{
    USHORT nErr;
    if ( !pData )
        nErr = DMLERR_DLL_NOT_INITIALIZED;
    else if ( nLastErrInstance )
        nErr = nLastErrInstance;
    else
        nErr = pData->nLastErr;

    nLastErrInstance = 0;
    pData->nLastErr = 0;
    return nErr;
}



USHORT ImpDdeMgr::DdeInitialize( PFNCALLBACK pCallbackProc, ULONG nTransactionFilter )
{
    if ( !nLastErrInstance )
    {
        if ( !pCallbackProc )
        {
            nLastErrInstance = DMLERR_INVALIDPARAMETER;
            return nLastErrInstance;
        }
        pCallback = pCallbackProc;
        nTransactFilter = nTransactionFilter;
        nTransactFilter |= CBF_FAIL_SELFCONNECTIONS;
    }
    return nLastErrInstance;
}


// static
HWND ImpDdeMgr::NextFrameWin( HENUM hEnum )
{
    char aBuf[ 10 ];

    HWND hWnd = WinGetNextWindow( hEnum );
    while( hWnd )
    {
        WinQueryClassName( hWnd, sizeof(aBuf)-1, (PCH)aBuf );
        // Frame-Window ?
        if( !strcmp( aBuf, "#1" ) ) // #define WC_FRAME ((PSZ)0xffff0001L)
            break;
        hWnd = WinGetNextWindow( hEnum );
    }
    return hWnd;
}


HCONV ImpDdeMgr::DdeConnectImp( HSZ hszService,HSZ hszTopic,CONVCONTEXT* pCC)
{
    hCurConv = 0;
    if( !pCC )
        pCC = &aDefaultContext;

    ULONG nBufLen;
    PSZ pService = AllocAtomName( (ATOM)hszService, nBufLen );
    PSZ pTopic = AllocAtomName( (ATOM)hszTopic, nBufLen );

#if defined(OV_DEBUG)
    if( !strcmp(pService,"oliver voeltz") )
    {
        WRITESTATUS("Table of connections");
        MyDosFreeMem( pTopic,"DdeConnectImp" );
        MyDosFreeMem( pService,"DdeConnectImp" );
        return 0;
    }
#endif

    // eigener Verbindungsaufbau
    HENUM hEnum = WinBeginEnumWindows( HWND_DESKTOP );
    HWND hWndCurSrv = NextFrameWin( hEnum );
    HWND hWndCurClient = CreateConversationWnd();
    while( hWndCurSrv && !hCurConv )
    {
        if( hWndCurSrv != hWndServer ||
            ((nTransactFilter & CBF_FAIL_SELFCONNECTIONS)==0 ))
        {
            // pro DDE-Server ein Conversation-Window erzeugen
            if( GetConversationWndRefCount(hWndCurClient) >= 2)
            {
                DestroyConversationWnd( hWndCurClient );
                hWndCurClient = CreateConversationWnd();
            }
            MyInitiateDde(hWndCurSrv,hWndCurClient,hszService,hszTopic,pCC);
            if( !bListConnect && hCurConv )
                break;
        }
        hWndCurSrv = NextFrameWin( hEnum );
    }

    if( GetConversationWndRefCount(hWndCurClient) == 0)
        DestroyConversationWnd( hWndCurClient );
    WinEndEnumWindows( hEnum );

    if( !hCurConv )
        nLastErrInstance = DMLERR_NO_CONV_ESTABLISHED;

    MyDosFreeMem( pTopic,"DdeConnectImp" );
    MyDosFreeMem( pService,"DdeConnectImp" );
    return hCurConv;
}

HCONV ImpDdeMgr::DdeConnect( HSZ hszService, HSZ hszTopic, CONVCONTEXT* pCC)
{
    ////WRITELOG("DdeConnect:Start")
    bListConnect = FALSE;
    HCONV hResult = DdeConnectImp( hszService, hszTopic, pCC );
    ////WRITELOG("DdeConnect:End")
    ////WRITESTATUS("DdeConnect:End")
    return hResult;
}


HCONVLIST ImpDdeMgr::DdeConnectList( HSZ hszService, HSZ hszTopic,
    HCONVLIST hConvList, CONVCONTEXT* pCC )
{
    nPrevConv = 0;
    ////WRITESTATUS("Before DdeConnectList")
    if( hConvList )
    {
        HCONV hLastConvInList;

        hCurListId      = hConvList;
        ImpHCONV* pConv = pConvTable;
        pConv          += (USHORT)hConvList;
        if( (USHORT)hConvList >= pData->nMaxConvCount ||pConv->hWndThis==0 )
        {
            nLastErrInstance = DMLERR_INVALIDPARAMETER;
            return 0;
        }
        GetLastServer(pData, hConvList, hLastConvInList);
        nPrevConv = (USHORT)hLastConvInList;
    }
    else
        hCurListId = (HCONVLIST)WinCreateWindow( HWND_OBJECT, WC_FRAME,
            CONVLISTNAME, 0,0,0,0,0, HWND_DESKTOP, HWND_BOTTOM, 0,0,0);

    bListConnect = TRUE;
    DdeConnectImp( hszService, hszTopic, pCC );

    return (HCONVLIST)hCurListId;
}

DDEINIT* ImpDdeMgr::CreateDDEInitData( HWND hWndDestination, HSZ hszService,
    HSZ hszTopic, CONVCONTEXT* pCC )
{
    ULONG nLen1 = 0, nLen2 = 0;
    HATOMTBL hAtomTable = WinQuerySystemAtomTable();

    if( hszService )
        nLen1 = WinQueryAtomLength( hAtomTable, hszService );
    if( hszTopic )
        nLen2 = WinQueryAtomLength( hAtomTable, hszTopic );
    nLen1++; nLen2++;

    DDEINIT* pBuf = 0;

    ULONG nLen = sizeof(DDEINIT) + nLen1+ nLen2 + sizeof(CONVCONTEXT);
    if( !(MyDosAllocSharedMem((PPVOID)&pBuf, NULL, nLen,
            PAG_COMMIT | PAG_READ | PAG_WRITE | OBJ_GIVEABLE | OBJ_ANY,
            "CreateDDEInitData")))
    {
        memset( pBuf, 0, nLen );

        /*
        PID pid; TID tid;
        WinQueryWindowProcess( hWndDestination, &pid, &tid );
        APIRET nRet = DosGiveSharedMem( pBuf, pid, PAG_READ | PAG_WRITE );
        */

        pBuf->cb = nLen;
        pBuf->offConvContext = sizeof( DDEINIT );
        char* pBase = (char*)pBuf;
        pBase += sizeof(DDEINIT);
        if( pCC )
            memcpy( pBase, pCC, sizeof(CONVCONTEXT) );
        pBase += sizeof(CONVCONTEXT);
        pBuf->pszAppName = pBase;
        if( hszService )
            WinQueryAtomName( hAtomTable, hszService, pBase, nLen1 );
        pBase += nLen1;
        pBuf->pszTopic = pBase;
        if( hszTopic )
            WinQueryAtomName( hAtomTable, hszTopic, pBase, nLen2 );
    }
    return pBuf;
}



void ImpDdeMgr::MyInitiateDde( HWND hWndSrv, HWND hWndClient,
    HSZ hszService, HSZ hszTopic, CONVCONTEXT* pCC )
{
    DDEINIT* pBuf = CreateDDEInitData( hWndSrv, hszService, hszTopic, pCC );
    if( pBuf )
    {
        PID pid; TID tid;
        WinQueryWindowProcess( hWndSrv, &pid, &tid );
        APIRET nRet = DosGiveSharedMem( pBuf, pid, PAG_READ | PAG_WRITE );
        WinSendMsg( hWndSrv,WM_DDE_INITIATE,(MPARAM)hWndClient,(MPARAM)pBuf);
        MyDosFreeMem( pBuf,"MyInitiateDde" );
    }
}

// static
ImpHCONV* ImpDdeMgr::GetFirstServer(ImpDdeMgrData* pData, HCONVLIST hConvList,
    HCONV& rhConv )
{
    ImpHCONV* pPtr = GetConvTable( pData );
    HCONV hConv;
    if( !rhConv )
    {
        pPtr++;
        hConv = 1;
    }
    else
    {
        // Startposition
        pPtr += (USHORT)rhConv;
        hConv = rhConv;
        pPtr++; hConv++;    // auf den naechsten
    }
    while( hConv < pData->nMaxConvCount )
    {
        if( pPtr->hConvList == hConvList )
        {
            rhConv = hConv;
            return pPtr;
        }
        pPtr++;
        hConv++;
    }
    rhConv = 0;
    return 0;
}

// static
ImpHCONV* ImpDdeMgr::GetLastServer(ImpDdeMgrData* pData, HCONVLIST hConvList,
    HCONV& rhConv )
{
    ImpHCONV* pPtr = GetConvTable( pData );
    pPtr += pData->nMaxConvCount;
    pPtr--;
    HCONV hConv = pData->nMaxConvCount;
    hConv--;
    while( hConv > 0 )
    {
        if( pPtr->hConvList == hConvList )
        {
            rhConv = hConv;
            return pPtr;
        }
        pPtr--;
        hConv--;
    }
    rhConv = 0;
    return 0;
}

// static
BOOL ImpDdeMgr::CheckConvListId( HCONVLIST hConvListId )
{
    HAB hAB = WinQueryAnchorBlock( (HWND)hConvListId );
    if( hAB )
        return WinIsWindow( hAB, (HWND)hConvListId );
    return FALSE;
    /*
    HAB hAB = WinQueryAnchorBlock( (HWND)hConvListId );
    if( hAB )
    {
        char aBuf[ 16 ];
        WinQueryWindowText( (HWND)hConvListId, sizeof(aBuf), aBuf );
        if( strcmp(aBuf, CONVLISTNAME ) == 0 )
            return TRUE;
    }
    return FALSE;
    */
}

// static
HCONV ImpDdeMgr::DdeQueryNextServer(HCONVLIST hConvList, HCONV hConvPrev)
{
    if( !CheckConvListId( hConvList ) )
        return (HCONV)0;
    ImpDdeMgrData* pData = ImpDdeMgr::AccessMgrData();
    GetFirstServer( pData, hConvList, hConvPrev );
    return hConvPrev;
}

// static

// Idee: DisconnectAll uebergibt das ServerWindow. Zu jedem HCONV
// wird das Creator-Server-Wnd gespeichert. Disconnect braucht
// dann nur noch die Window-Handles zu vergleichen
BOOL ImpDdeMgr::DdeDisconnect( HCONV hConv )
{
    WRITELOG("DdeDisconnect:Start")
    ////WRITESTATUS("DdeDisconnect:Start")

    ImpDdeMgrData* pData = ImpDdeMgr::AccessMgrData();
    if ( !pData )
    {
        ImpDdeMgr::nLastErrInstance = DMLERR_DLL_NOT_INITIALIZED;
        return FALSE;
    }
    ImpHCONV* pConv = GetConvTable(pData) + (USHORT)hConv;

    if( (USHORT)hConv >= pData->nMaxConvCount || pConv->hWndThis==0 )
    {
        nLastErrInstance = DMLERR_NO_CONV_ESTABLISHED;
        return FALSE;
    }

    PID pidApp; TID tid;
    HWND hWndDummy = WinCreateWindow( HWND_OBJECT, WC_FRAME,
        "Bla", 0, 0,0,0,0, HWND_DESKTOP, HWND_BOTTOM, 0, 0,  0 );
    WinQueryWindowProcess( hWndDummy, &pidApp, &tid );
    WinDestroyWindow( hWndDummy );
    PID pidThis; PID pidPartner;

    HWND hWndThis = pConv->hWndThis;
    HWND hWndPartner = pConv->hWndPartner;

    WinQueryWindowProcess( hWndThis, &pidThis, &tid );
    WinQueryWindowProcess( hWndPartner, &pidPartner, &tid );
    if( pidApp != pidThis && pidApp != pidPartner )
        return TRUE;  // gehoert nicht der App -> ueberspringen

    HCONV hConvPartner = pConv->hConvPartner;

    // die App benachrichtigen, dass alle offenen Advise-Loops
    // beendet werden, egal ob sie vom Server oder Client
    // initiiert wurden. Die Dinger aber nicht loeschen, da sie evtl.
    // noch vom Partner gebraucht werden.
    ImpConvWndData* pObj =
        (ImpConvWndData*)WinQueryWindowULong( pConv->hWndThis, 0 );
    ImpDdeMgr* pThis = pObj->pThis;
    pThis->SendUnadvises( hConv, 0, FALSE ); // alle Formate & NICHT loeschen
    pThis->SendUnadvises( hConvPartner, 0, FALSE ); // alle Formate & NICHT loeschen

    pConv->nStatus |= ST_TERMINATED;

    HAB hAB = WinQueryAnchorBlock( pConv->hWndThis );
    // um die MessageQueue inne Gaenge zu halten
    ULONG nTimerId = WinStartTimer( hAB, 0, 0, 50 );

    /*
       Die Partner-App muss ein DDE_TERMINATE posten, auf das
       wir warten muessen, um alle Messages zu bearbeiten, die
       _vor_ dem DdeDisconnect von der Partner-App gepostet
       wurden.
    */
    WRITELOG("DdeDisconnect:Waiting for acknowledge...")
    WinDdePostMsg( hWndPartner, hWndThis, WM_DDE_TERMINATE,
        (PDDESTRUCT)0,DDEPM_RETRY);

    QMSG aQueueMsg;
    BOOL bContinue = TRUE;
    while( bContinue )
    {
        if( WinGetMsg( hAB, &aQueueMsg, 0, 0, 0 ))
        {
            WinDispatchMsg( hAB, &aQueueMsg );
            if( (!WinIsWindow( hAB, hWndPartner)) ||
                (pConv->nStatus & ST_TERMACKREC) )
            {
                bContinue = FALSE;
                if( pConv->nStatus & ST_TERMACKREC )
                {
                    WRITELOG("DdeDisconnect: TermAck received")
                }
                else
                {
                    WRITELOG("DdeDisconnect: Partner died")
                }
            }
        }
        else
            bContinue = FALSE;
    }

    WinStopTimer( hAB, 0, nTimerId );

    // WRITELOG("DdeDisconnect:Freeing data")
    // Transaktionstabelle aufraeumen
    FreeTransactions( pData, hConv );
    if( hConvPartner )
        FreeTransactions( pData, hConvPartner );

    FreeConvHandle( pData, hConv );

    WRITELOG("DdeDisconnect:End")
    //WRITESTATUS("DdeDisconnect:End")
    return TRUE;
}

// static
BOOL ImpDdeMgr::DdeDisconnectList( HCONVLIST hConvList )
{
    if( !CheckConvListId( hConvList ) )
    {
        ImpDdeMgr::nLastErrInstance = DMLERR_INVALIDPARAMETER;
        return FALSE;
    }

    ImpDdeMgrData* pData = ImpDdeMgr::AccessMgrData();
    if ( !pData )
    {
        ImpDdeMgr::nLastErrInstance = DMLERR_DLL_NOT_INITIALIZED;
        return FALSE;
    }
    HCONV hConv = 0;
    GetFirstServer( pData, hConvList, hConv );
    while( hConv )
    {
        DdeDisconnect( hConv );
        GetFirstServer( pData, hConvList, hConv );
    }
    WinDestroyWindow( (HWND)hConvList );
    return TRUE;
}



// static
HCONV ImpDdeMgr::DdeReconnect(HCONV hConv)
{
    ImpDdeMgrData* pData = ImpDdeMgr::AccessMgrData();
    if ( !pData )
    {
        ImpDdeMgr::nLastErrInstance = DMLERR_DLL_NOT_INITIALIZED;
        return 0;
    }
    return 0;
}

// static
USHORT ImpDdeMgr::DdeQueryConvInfo(HCONV hConv, ULONG nTransId, CONVINFO* pCI)
{
    if( !pCI || pCI->nSize == 0)
        return 0;
    ImpDdeMgrData* pData = ImpDdeMgr::AccessMgrData();
    if ( !pData )
    {
        ImpDdeMgr::nLastErrInstance = DMLERR_DLL_NOT_INITIALIZED;
        return 0;
    }
    Transaction* pTrans;
    if( nTransId != QID_SYNC )
    {
        pTrans = ImpDdeMgr::GetTransTable( pData );
        pTrans += nTransId;
        if( nTransId >= pData->nMaxTransCount || pTrans->hConvOwner == 0 )
        {
            ImpDdeMgr::nLastErrInstance = DMLERR_UNFOUND_QUEUE_ID;
            return 0;
        }
    }
    else
        pTrans = 0;

    ImpHCONV* pConv = ImpDdeMgr::GetConvTable( pData );
    pConv += (ULONG)hConv;
    if( hConv >= pData->nMaxConvCount || pConv->hWndThis == 0 )
    {
        ImpDdeMgr::nLastErrInstance = DMLERR_NO_CONV_ESTABLISHED;
        return 0;
    }

    USHORT nSize = pCI->nSize;
    if( nSize > sizeof(CONVINFO) )
        nSize = sizeof(CONVINFO);
    CONVINFO aTempInfo;
    memset( &aTempInfo, 0, sizeof(CONVINFO) );
    aTempInfo.nSize         = pCI->nSize;
    aTempInfo.hConvPartner  = pConv->hConvPartner;
    aTempInfo.hszPartner    = pConv->hszPartner;
    aTempInfo.hszServiceReq = pConv->hszServiceReq;
    aTempInfo.hszTopic      = pConv->hszTopic;
    aTempInfo.nStatus       = pConv->nStatus;
    aTempInfo.hConvList     = pConv->hConvList;
    aTempInfo.aConvCtxt     = pConv->aConvContext;
    if( pTrans )
    {
        aTempInfo.nUser     = pTrans->nUser;
        aTempInfo.hszItem   = pTrans->hszItem;
        aTempInfo.nFormat   = pTrans->nFormat;
        aTempInfo.nType     = pTrans->nType;
        aTempInfo.nConvst   = pTrans->nConvst;
        aTempInfo.nLastError= pTrans->nLastError;
    }
    memcpy( pCI, &aTempInfo, nSize );

    return nSize;
}

// static
BOOL ImpDdeMgr::DdeSetUserHandle(HCONV hConv, ULONG nTransId, ULONG hUser)
{
    ImpDdeMgrData* pData = ImpDdeMgr::AccessMgrData();
    if ( !pData )
    {
        ImpDdeMgr::nLastErrInstance = DMLERR_DLL_NOT_INITIALIZED;
        return FALSE;
    }
    Transaction* pTrans = GetTransTable( pData );
    pTrans += nTransId;
    if( !nTransId || !hConv || nTransId >= pData->nMaxTransCount ||
        pTrans->hConvOwner != hConv )
    {
        ImpDdeMgr::nLastErrInstance = DMLERR_INVALIDPARAMETER;
        return FALSE;
    }
    if( !pTrans->hConvOwner)
    {
        ImpDdeMgr::nLastErrInstance = DMLERR_UNFOUND_QUEUE_ID;
        return FALSE;
    }
    pTrans->nUser = hUser;
    return TRUE;
}

BOOL ImpDdeMgr::DdeAbandonTransaction( HCONV hConv, ULONG nTransId )
{
    ////WRITELOG("DdeAbandonTransaction:Start")
    if( !pData )
    {
        nLastErrInstance = DMLERR_DLL_NOT_INITIALIZED;
        return FALSE;
    }
    ImpHCONV* pConv = pConvTable;
    pConv += (USHORT)hConv;
    if( nTransId < 1 || nTransId >= pData->nMaxTransCount ||
        hConv < 1 || hConv >= pData->nMaxConvCount || !pConv->hWndThis)
    {
        nLastErrInstance = DMLERR_INVALIDPARAMETER;
        return FALSE;
    }
    if( !hConv )
    {
        DBG_ASSERT(0,"DdeAbandonTransaction:NULL-hConv not supported");
        nLastErrInstance = DMLERR_INVALIDPARAMETER;
        return FALSE;
    }
    Transaction* pTrans = pTransTable;
    pTrans += (USHORT)nTransId;
    if( pTrans->hConvOwner != hConv )
    {
        nLastErrInstance = DMLERR_UNFOUND_QUEUE_ID;
        return FALSE;
    }

    if( bInSyncTrans && nTransId == nSyncTransId )
    {
        bSyncAbandonTrans = TRUE;
        return TRUE;
    }
    USHORT nTempType = pTrans->nType;
    nTempType &= (~XTYPF_MASK);
    if( nTempType == (XTYP_ADVREQ & ~(XTYPF_NOBLOCK)))
    {
        ////WRITELOG("DdeAbandTrans:Advise Loop")

// ----------------------------------------------------------------------
// Der von der Deutschen Bank eingesetzte DDE-Server
// "Invision V2.71 Build 36 Mar 12 1999 V4.8.2" hat einen Bug, der
// dazu fuehrt, dass auf per WM_DDE_TERMINATE geschlossene Verbindungen
// nicht mit einem WM_DDE_TERMINATE geantwortet wird, wenn der
// entsprechende Link vorher per WM_DDE_UNADVISE beendet wurde. Dieser
// Bug tritt ab zwei parallel laufenden Links auf. Auf Wunsch der DB
// wurde das folgende Workaround eingebaut.
// ----------------------------------------------------------------------
#define DEUTSCHE_BANK
#ifndef DEUTSCHE_BANK

// Acknowledge ist beim Unadvise nicht ueblich
//#define SO_DDE_ABANDON_TRANSACTION_WAIT_ACK
#ifdef SO_DDE_ABANDON_TRANSACTION_WAIT_ACK
        DDESTRUCT* pOutDDEData = MakeDDEObject( pConv->hWndPartner,
            pTrans->hszItem, DDE_FACKREQ, 0 /*pTrans->nFormat*/, 0, 0);
#else
        DDESTRUCT* pOutDDEData = MakeDDEObject( pConv->hWndPartner,
            pTrans->hszItem, 0, 0 /*pTrans->nFormat*/, 0, 0);
#endif
        WRITELOG("DdeAbandTrans:Waiting for acknowledge...")
        pTrans->nConvst = XST_UNADVSENT;
        if ( !MyWinDdePostMsg( pConv->hWndPartner, pConv->hWndThis,
            WM_DDE_UNADVISE, pOutDDEData, DDEPM_RETRY ) )
        {
            WRITELOG("DdeAbandTrans:PostMsg Failed")
            return FALSE;
        }
#ifdef SO_DDE_ABANDON_TRANSACTION_WAIT_ACK
        WaitTransState( pTrans, nTransId, XST_UNADVACKRCVD, 0 );
#else
        pTrans->nConvst = XST_UNADVACKRCVD;
#endif

#endif // DEUTSCHE_BANK

        WRITELOG("DdeAbandTrans:Ack received->Freeing transaction")
        FreeTransaction( pData, nTransId );
    }
    WRITELOG("DdeAbandonTransaction:End")
    return TRUE;
}

// wird von einem Server aufgerufen, wenn sich die Daten des
// Topic/Item-Paars geaendert haben. Diese Funktion fordert
// dann den Server auf, die Daten zu rendern (bei Hotlinks) und
// benachrichtigt die Clients
BOOL ImpDdeMgr::DdePostAdvise( HSZ hszTopic, HSZ hszItem)
{

    Transaction* pTrans = pTransTable;
    pTrans++;
    USHORT nCurTrans = 1;
    USHORT nUsedTransactions = pData->nCurTransCount;
    while( nUsedTransactions && nCurTrans < pData->nMaxTransCount )
    {
        HCONV hOwner = pTrans->hConvOwner;
        if( hOwner )
        {
            nUsedTransactions--;
            USHORT nTempType = pTrans->nType;
            nTempType &= (~XTYPF_MASK);
            if( nTempType == (XTYP_ADVREQ & (~XTYPF_NOBLOCK) ) )
            {
                ImpHCONV* pConv = pConvTable;
                pConv += (USHORT)hOwner;
                if(hszItem == pTrans->hszItem && pConv->hszTopic == hszTopic)
                {
                    if( pConv->hConvPartner )
                    {
                        // Transaktionen werden immer vom Client erzeugt
                        // -> auf Server-HCONV umschalten
                        hOwner = pConv->hConvPartner;
                        pConv = pConvTable;
                        pConv += (USHORT)hOwner;
                    }
                    HWND hWndClient = pConv->hWndPartner;
                    HWND hWndServer = pConv->hWndThis;
                    DDESTRUCT* pOutDDEData;
                    if ( pTrans->nType & XTYPF_NODATA )
                    {
                        // Warm link
                        pOutDDEData = MakeDDEObject( hWndClient, hszItem,
                            DDE_FNODATA, pTrans->nFormat, 0, 0  );
                    }
                    else
                    {
                        // Hot link
                        pOutDDEData = Callback( XTYP_ADVREQ,
                            pTrans->nFormat, hOwner, hszTopic,
                            hszItem, (HDDEDATA)0, 1, 0 );
                    }
                    if( pOutDDEData )
                    {
                        // todo: FACK_REQ in Out-Data setzen, wenn pTrans->nType & XTYPF_ACKREQ
                        BOOL bSuccess = MyWinDdePostMsg( hWndClient,
                            hWndServer,WM_DDE_DATA, pOutDDEData, DDEPM_RETRY);
                        if( bSuccess )
                        {
                            // auf Acknowledge des Partners warten ?
                            if( pTrans->nType & XTYPF_ACKREQ )
                            {
                                pTrans->nConvst = XST_ADVDATASENT;
                                // Impl. ist falsch! => korrekt: XST_ADVDATAACKRCVD
                                WaitTransState(pTrans, nCurTrans,
                                    XST_UNADVACKRCVD, 0);
                            }
                        }
                        else
                        {
                            ////WRITELOG("DdePostAdvise:PostMsg failed")
                            nLastErrInstance = DMLERR_POSTMSG_FAILED;
                        }
                    }
                    else
                    {
                        ////WRITELOG("DdePostAdvise:No data to send")
                    }
                }
            }
        }
        nCurTrans++;
        pTrans++;
    }
    ////WRITELOG("DdePostAdvise:End")
    return TRUE;
}

BOOL ImpDdeMgr::DdeEnableCallback( HCONV hConv, USHORT wCmd)
{
    return FALSE;
}

// Rueckgabe: 0==Service nicht registriert; sonst Pointer auf Service-Eintrag
ImpService* ImpDdeMgr::GetService( HSZ hszService )
{
    ImpService* pPtr = pServices;
    if( !pPtr || !hszService )
        return 0;
    for( ULONG nIdx = 0; nIdx < nServiceCount; nIdx++, pPtr++ )
    {
        if(( hszService == pPtr->hBaseServName )    ||
           ( hszService == pPtr->hInstServName ) )
            return pPtr;
    }
    return 0;
}


// legt Service in Service-Tabelle ab. Tabelle wird ggf. expandiert
ImpService* ImpDdeMgr::PutService( HSZ hszService )
{
    if( !pServices )
    {
        DBG_ASSERT(nServiceCount==0,"DDE:Bad ServiceCount");
        pServices = new ImpService[ DDEMLSERVICETABLE_INISIZE ];
        memset( pServices, 0, DDEMLSERVICETABLE_INISIZE* sizeof(ImpService));
        nServiceCount = DDEMLSERVICETABLE_INISIZE;
    }
    ImpService* pPtr = pServices;
    USHORT nCurPos = 0;
    while( pPtr )
    {
        if( pPtr->hBaseServName == 0 )
            break;
        nCurPos++;
        if( nCurPos < nServiceCount )
            pPtr++;
        else
            pPtr = 0;
    }
    if( !pPtr )
    {
        // Tabelle vergroessern
        pPtr = new ImpService[ nServiceCount + DDEMLSERVICETABLE_INISIZE ];
        memset( pPtr, 0, DDEMLSERVICETABLE_INISIZE* sizeof(ImpService));
        memcpy( pPtr, pServices, nServiceCount * sizeof(ImpService) );
#ifdef DBG_UTIL
        memset( pServices, 0, nServiceCount * sizeof(ImpService) );
#endif
        delete pServices;
        pServices = pPtr;
        pPtr += nServiceCount; // zeigt auf erste neue Position
        nServiceCount += DDEMLSERVICETABLE_INISIZE;
    }
    DBG_ASSERT(pPtr->hBaseServName==0,"DDE:Service not empty");
    DBG_ASSERT(pPtr->hInstServName==0,"DDE:Service not empty");

    DdeKeepStringHandle( hszService );

    USHORT nStrLen = (USHORT)DdeQueryString( hszService, 0, 0, 0);
    char* pBuf = new char[ nStrLen + 1 ];
    DdeQueryString(hszService, pBuf, nStrLen, 850 /* CodePage*/ );
    pBuf[ nStrLen ] = 0;
    String aStr( (ULONG)hWndServer );
    aStr += pBuf;
    HSZ hszInstServ = DdeCreateStringHandle( (PSZ)(const char*)pBuf, 850 );
    delete[] pBuf;

    pPtr->hBaseServName = hszService;
    pPtr->hInstServName = hszInstServ;
    return pPtr;
}

void ImpDdeMgr::BroadcastService( ImpService* pService, BOOL bRegistered )
{
    DBG_ASSERT(pService,"DDE:No Service");
    if( !pService )
        return;
    MPARAM aMp1 = (MPARAM)(pService->hBaseServName);
    MPARAM aMp2 = (MPARAM)(pService->hInstServName);
    ULONG nMsg;
    if( bRegistered )
        nMsg = WM_DDEML_REGISTER;
    else
        nMsg = WM_DDEML_UNREGISTER;

    HWND* pPtr = pAppTable;
    for( USHORT nPos = 0; nPos < pData->nMaxAppCount; nPos++, pPtr++ )
    {
        HWND hWndCurWin = *pPtr;
        if ( hWndCurWin && hWndCurWin != hWndServer )
            WinSendMsg( hWndCurWin, nMsg, aMp1, aMp2 );
    }
}

HDDEDATA ImpDdeMgr::DdeNameService( HSZ hszService, USHORT afCmd )
{
    HDDEDATA hRet = (HDDEDATA)1;

    if( afCmd & DNS_FILTERON )
        bServFilterOn = TRUE;
    else if( afCmd & DNS_FILTEROFF )
        bServFilterOn = FALSE;
    ImpService* pService = GetService( hszService );
    BOOL bRegister = (BOOL)(afCmd & DNS_REGISTER);
    if( bRegister )
    {
        if( !pService )
        {
            pService = PutService( hszService );
            BroadcastService( pService, TRUE );
        }
    }
    else
    {
        if( pService )
        {
            BroadcastService( pService, FALSE );
            DdeFreeStringHandle( pService->hBaseServName );
            pService->hBaseServName = 0;
            DdeFreeStringHandle( pService->hInstServName );
            pService->hInstServName = 0;
        }
            hRet = (HDDEDATA)0;  // Service nicht gefunden
    }
    return hRet;
}


// static
HDDEDATA ImpDdeMgr::DdeClientTransaction(void* pDdeData, ULONG cbData,
    HCONV hConv, HSZ hszItem, USHORT nFormat, USHORT nType,
    ULONG nTimeout, ULONG* pResult)
{
    if( pResult )
        *pResult = 0;

    ImpDdeMgrData* pData = ImpDdeMgr::AccessMgrData();
    if ( !pData )
    {
        ImpDdeMgr::nLastErrInstance = DMLERR_DLL_NOT_INITIALIZED;
        return (HDDEDATA)0;
    }

    BOOL bIsDdeHandle = (BOOL)(pDdeData && cbData==0xffffffff);
    BOOL bAppOwnsHandle = (BOOL)( bIsDdeHandle &&
        (((DDESTRUCT*)pDdeData)->fsStatus & IMP_HDATAAPPOWNED) );

    BOOL bNoData  = (BOOL)(nType & XTYPF_NODATA)!=0;
    BOOL bAckReq  = (BOOL)(nType & XTYPF_ACKREQ)!=0;
    USHORT nTypeFlags = nType & XTYPF_MASK;
    nType &= (~XTYPF_MASK);

    BOOL bSync = (BOOL)( nTimeout != TIMEOUT_ASYNC ) != 0;
    if( nType == XTYP_ADVSTART )
        bSync = TRUE;

    // Mapping transaction -> OS/2-Message
    USHORT nTimeoutErr, nMsg;
    switch ( nType )
    {
        case XTYP_ADVSTART:
            nMsg = WM_DDE_ADVISE;
            nTimeoutErr = DMLERR_ADVACKTIMEOUT;
            nTimeout = 60000;
            break;

        case XTYP_ADVSTOP:
            nMsg = WM_DDE_UNADVISE;
            nTimeoutErr = DMLERR_UNADVACKTIMEOUT;
            break;

        case XTYP_REQUEST:
            nMsg = WM_DDE_REQUEST;
            nTimeoutErr = DMLERR_DATAACKTIMEOUT;
            break;

        case XTYP_POKE:
            nMsg = WM_DDE_POKE;
            nTimeoutErr = DMLERR_POKEACKTIMEOUT;
            break;

        case XTYP_EXECUTE:
            nMsg = WM_DDE_EXECUTE;
            nTimeoutErr = DMLERR_EXECACKTIMEOUT;
            break;

        default:
            nMsg = 0;
    }
    if(!hConv || (USHORT)hConv>= pData->nMaxConvCount || !nType || !nMsg ||
       (nType != XTYP_EXECUTE && (!hszItem || !nFormat)) )
    {
        WRITELOG("DdeClientTransaction:Invalid parameter")
        ImpDdeMgr::nLastErrInstance = DMLERR_INVALIDPARAMETER;
        if( bIsDdeHandle && !bAppOwnsHandle )
            DdeFreeDataHandle( (HDDEDATA)pDdeData );
        return (HDDEDATA)0;
    }

    // ueber den Conversation handle das ImpDdeMgr-Objekt holen
    ImpHCONV* pConv = GetConvTable( pData );
    pConv += (USHORT)hConv;
    ImpConvWndData* pObj =
        (ImpConvWndData*)WinQueryWindowULong( pConv->hWndThis, 0 );
    ImpDdeMgr* pThis = pObj->pThis;

    if( bSync && pThis->bInSyncTrans )
    {
        WRITELOG("DdeClientTransaction:Already in sync. transaction")
        ImpDdeMgr::nLastErrInstance = DMLERR_REENTRANCY;
        if( bIsDdeHandle && !bAppOwnsHandle )
            DdeFreeDataHandle( (HDDEDATA)pDdeData );
        return (HDDEDATA)0;
    }

    Transaction* pTrans;

    BOOL bReqOnAdvLoop = FALSE;
    ULONG nTransId = GetTransaction( pData, hConv, hszItem, nFormat );
    if( nTransId )
    {
        // WRITELOG("DdeClientTransaction:Transaction found")
        pTrans = GetTransTable( pData );
        pTrans += (USHORT)nTransId;
        USHORT nTransType = pTrans->nType;
        nTransType &= (~XTYPF_MASK);
        if( (nType != XTYP_REQUEST && nTransType == nType) ||
             // wird Advise-Loop schon zum requesten missbraucht ?
             (nType == XTYP_REQUEST &&
             nTransType == XTYP_ADVREQ &&
             pTrans->nConvst == XST_WAITING_REQDATA))
        {
            // dieser Kanal ist dicht!
            WRITELOG("DdeClientTransaction:Transaction already used")
            ImpDdeMgr::nLastErrInstance = DMLERR_REENTRANCY;
            if( bIsDdeHandle && !bAppOwnsHandle )
                DdeFreeDataHandle( (HDDEDATA)pDdeData );
            return (HDDEDATA)0;
        }
        else if( nTransType == XTYP_ADVREQ )
        {
            switch( nType )
            {
                case XTYP_ADVSTOP:
                    //WRITELOG("DdeClientTransaction:Stopping advise trans")
                    pTrans->nType = XTYP_ADVSTOP;
                    break;

                case XTYP_ADVSTART:
                    //WRITELOG("DdeClientTransaction:Adj. Advise-Params")
                    pTrans->nType = XTYP_ADVREQ;
                    if( bNoData )
                        pTrans->nType |= XTYPF_NODATA;
                    if( bAckReq )
                        pTrans->nType |= XTYPF_ACKREQ;
                    if( pResult )
                        *pResult = nTransId;
                    return (HDDEDATA)TRUE;

                case XTYP_REQUEST:
                    // WRITELOG("DdeClientTransaction:Using adv trans for req")
                    // nConvst wird unten auf XST_WAITING_REQDATA gesetzt
                    bReqOnAdvLoop = TRUE;
                    break;

                default:
                    WRITELOG("DdeClientTransaction:Invalid parameter")
                    ImpDdeMgr::nLastErrInstance = DMLERR_INVALIDPARAMETER;
                    if( bIsDdeHandle && !bAppOwnsHandle )
                        DdeFreeDataHandle( (HDDEDATA)pDdeData );
                    return (HDDEDATA)FALSE;
            }
        }
    }
    else
    {
        // WRITELOG("DdeClientTransaction:Creating transaction")
        nTransId = CreateTransaction(pData, hConv, hszItem, nFormat, nType);
    }

    pTrans = GetTransTable( pData );
    pTrans += (USHORT)nTransId;
    pTrans->nConvst = XST_WAITING_ACK;
    if( nType == XTYP_REQUEST )
        pTrans->nConvst = XST_WAITING_REQDATA;

    HWND hWndServer = pConv->hWndPartner;
    HWND hWndClient = pConv->hWndThis;

    HDDEDATA pOutDDEData;
    if( bIsDdeHandle )
    {
        if( bAppOwnsHandle )
        {
            // wir muessen leider leider duplizieren, da uns OS/2
            // keine Chance laesst, diesen Status im Datenobjekt
            // zu versenken.
            ////WRITELOG("DdeClientTransaction:Copying handle")
            HDDEDATA pNew;
            HDDEDATA pData = (HDDEDATA)pDdeData;
            if( !(MyDosAllocSharedMem((PPVOID)&pNew, NULL, pData->cbData,
                PAG_COMMIT | PAG_READ | PAG_WRITE | OBJ_GIVEABLE | OBJ_ANY,
                "MakeDDEObject")))
            {
                memcpy( pNew, pData, pData->cbData );
                pOutDDEData = pNew;
            }
            else
            {
                WRITELOG("DdeClientTransaction:No Memory")
                ImpDdeMgr::nLastErrInstance = DMLERR_LOW_MEMORY;
                return (HDDEDATA)0;
            }
        }
        else
            pOutDDEData = (HDDEDATA)pDdeData;
    }
    else
        pOutDDEData=MakeDDEObject(hWndServer,hszItem,0,nFormat,pDdeData,cbData);

    pOutDDEData->fsStatus |= nTypeFlags;

    HDDEDATA pDDEInData = 0;
    if( bSync )
    {
        if( nType != XTYP_REQUEST )
            pOutDDEData->fsStatus |= DDE_FACKREQ;

        // WRITELOG("DdeClientTransaction:Starting sync. trans.")
        pThis->hSyncResponseData = (HDDEDATA)0;
        pThis->nSyncResponseMsg  = 0;
        pThis->bInSyncTrans      = TRUE;
        pThis->nSyncTransId      = nTransId;
        pThis->bSyncAbandonTrans = FALSE;

        if ( !MyWinDdePostMsg( hWndServer, hWndClient, nMsg, pOutDDEData,
                DDEPM_RETRY) )
        {
            WRITELOG("DdeClientTransaction:PostMsg failed")
            nLastErrInstance = DMLERR_POSTMSG_FAILED;
            if( !bReqOnAdvLoop )
                FreeTransaction( pData, nTransId );
            else
            {
                DBG_ASSERT(pTrans->nType==XTYP_ADVREQ,"DDE:Error!")
                pTrans->nConvst = 0;
            }
            return FALSE;
        }
        HAB hAB = WinQueryAnchorBlock( hWndClient );
        ULONG nDummyTimer = WinStartTimer( hAB, 0, 0, 50 );
        ULONG nTimeoutId = TID_USERMAX - nTransId;
        WinStartTimer( hAB, hWndClient, nTimeoutId, nTimeout );
        QMSG aQueueMsg;
        BOOL bLoop = TRUE;
        while( bLoop )
        {
            if( pThis->nSyncResponseMsg )
                bLoop = FALSE;
            else
            {
                if( WinGetMsg(hAB,&aQueueMsg,0,0,0 ))
                {
                    WinDispatchMsg( hAB, &aQueueMsg );
                }
                else
                    bLoop = FALSE;
            }
        }

        WinStopTimer( hAB, hWndClient, nTimeoutId );
        WinStopTimer( hAB, 0, nDummyTimer );

        //
        // Der Speicherblock pOutDDEData muss vom Server geloescht worden sein!
        // Ueberlegen: Nochmal loeschen, falls Server buggy ist, ansonsten
        // platzt uns bald der Adressraum!
        //
        if( !pThis->nSyncResponseMsg )
        {
            // unsere App wurde beendet
            ////WRITELOG("DdeClientTransaction:App terminated")
            return 0;
        }
        pDDEInData = pThis->hSyncResponseData;
        nMsg = pThis->nSyncResponseMsg;
        pThis->hSyncResponseData= 0;
        pThis->nSyncResponseMsg = 0;
        pThis->bInSyncTrans     = FALSE;
        pThis->nSyncTransId     = 0;
        if( !pDDEInData && nMsg != WM_TIMER )
        {
            DBG_ASSERT(0,"Dde:No data!");
            WRITELOG("DdeClientTransaction: No Data!")
            return (HDDEDATA)0;
        }
        switch( nMsg )
        {
            case WM_TIMER:
                WRITELOG("DdeClientTransaction:Timeout!")
                nLastErrInstance = nTimeoutErr;
                if( bReqOnAdvLoop )
                    // auf normalen Loop-Betrieb zurueckschalten
                    pTrans->nConvst = XST_WAITING_ADVDATA;
                break;

            case WM_DDE_ACK:
                BOOL bPositive = (BOOL)(pDDEInData->fsStatus & DDE_FACK);
                MyDosFreeMem( pDDEInData,"DdeClientTransaction" );
                pDDEInData = (HDDEDATA)bPositive;
                if( nType == XTYP_ADVSTART && pDDEInData )
                {
                    if( bPositive )
                    {
                        pTrans->nType = XTYP_ADVREQ;
                        // Hot/Warmlink, Ack
                        pTrans->nType |= nTypeFlags;
                        // XST_WAITING_ACK ==> XST_WAITING_ADVDATA
                        pTrans->nConvst = XST_WAITING_ADVDATA;
                    }
                }
                break;

            case WM_DDE_DATA:
                // WRITELOG("DdeClientTransaction:Data received")
                // WRITEDATA(pDDEInData)
                if( bReqOnAdvLoop )
                {
                    DBG_ASSERT(pTrans->nConvst==XST_WAITING_REQDATA,"DDE:Bad state");
                    DBG_ASSERT(pTrans->nType==XTYP_ADVREQ,"DDE:Bad state");
                    // auf Loop-Betrieb umschalten
                    pTrans->nConvst = XST_WAITING_ADVDATA;
                }
                break;

            default:
                WRITELOG("DdeClientTransaction:Unexpected msg")
                MyDosFreeMem( pDDEInData,"DdeClientTransaction"  );
                pDDEInData = 0;
        }
        pThis->bSyncAbandonTrans = FALSE;
        pThis->bInSyncTrans = FALSE;
        if( pThis->bSyncAbandonTrans && bReqOnAdvLoop )
            pThis->DdeAbandonTransaction( hConv, nTransId );
    }
    else
    {
        // WRITELOG("DdeClientTransaction:Starting async. trans.")
        pDDEInData = (HDDEDATA)MyWinDdePostMsg( hWndServer, hWndClient, nMsg,
            pOutDDEData, DDEPM_RETRY);
        if( !pDDEInData )
        {
            WRITELOG("DdeClientTransaction:PostMsg failed")
            nLastErrInstance = DMLERR_POSTMSG_FAILED;
            if( !bReqOnAdvLoop )
                FreeTransaction( pData, nTransId );
            else
            {
                DBG_ASSERT(pTrans->nType==XTYP_ADVREQ,"DDE:Error!")
                pTrans->nConvst = 0;
            }
        }
        else
        {
            // WRITELOG("DdeClientTransaction:Async trans. success")
            if( pResult )
                *pResult = nTransId;
        }
    }

    return pDDEInData;
}

MRESULT ImpDdeMgr::DdeRegister( ImpWndProcParams* pParams )
{
    MRESULT nRet = (MRESULT)0;
    if ( !(nTransactFilter & CBF_SKIP_REGISTRATIONS) )
    {
        HSZ hSBaseName = (HSZ)pParams->nPar1;
        HSZ hIBaseName = (HSZ)pParams->nPar2;
        nRet=(MRESULT)Callback(XTYP_REGISTER,0,0,hSBaseName,hIBaseName,0,0,0);
    }
    return nRet;
}

MRESULT ImpDdeMgr::DdeUnregister( ImpWndProcParams* pParams )
{
    MRESULT nRet = (MRESULT)0;
    if ( !(nTransactFilter & CBF_SKIP_UNREGISTRATIONS) )
    {
        HSZ hSBaseName = (HSZ)pParams->nPar1;
        HSZ hIBaseName = (HSZ)pParams->nPar2;
        nRet=(MRESULT)Callback(XTYP_UNREGISTER,0,0,hSBaseName,hIBaseName,0,0,0);
    }
    return nRet;
}

MRESULT ImpDdeMgr::DdeTimeout( ImpWndProcParams* pParams )
{
    ////WRITELOG("DdeTimeout:Received")
    if( nSyncResponseMsg )
    {
        ////WRITELOG("DdeTimeout:Trans already processed->ignoring timeout")
        return (MRESULT)1;
    }
    ULONG nTimerId = (ULONG)pParams->nPar1;
    ULONG nTransId = TID_USERMAX - nTimerId;
    Transaction* pTrans = pTransTable;
    pTrans += (USHORT)nTransId;
    if( nTransId < 1 || nTransId >= pData->nMaxTransCount ||
        pTrans->hConvOwner == 0 )
    {
        DBG_ASSERT(0,"DdeTimeout:Invalid TransactionId");
        return (MRESULT)1;
    }
    if( bInSyncTrans && nTransId == nSyncTransId )
    {
        USHORT nTempType = pTrans->nType;
        nTempType &= (~XTYPF_MASK);
        // advise-loops koennen nur innerhalb synchroner
        // requests timeouts bekommen. die transaktion wird
        // in diesem fall nicht geloescht.
        if( nTempType != (XTYP_ADVREQ & (~XTYPF_NOBLOCK) ))
        {
            FreeTransaction( pData, nTransId );
        }
        nSyncResponseMsg = WM_TIMER;
    }
    else
    {
        pTrans->nConvst = XST_TIMEOUT;
    }
    return (MRESULT)1;
}



MRESULT ImpDdeMgr::DdeTerminate( ImpWndProcParams* pParams )
{
    WRITELOG("DdeTerminate:Received")
    HWND hWndThis = pParams->hWndReceiver;
    HWND hWndPartner = (HWND)(pParams->nPar1);

    HCONV hConv = GetConvHandle( pData, hWndThis, hWndPartner );
    ImpHCONV* pConv = pConvTable + (USHORT)hConv;
    if( hConv )
    {
        // warten wir auf ein DDE_TERMINATE Acknowledge ?
        if( pConv->nStatus & ST_TERMINATED )
        {
            pConv->nStatus |= ST_TERMACKREC;
            return (MRESULT)0;  // DdeDisconnect raeumt jetzt auf
        }

        // sind wir Server?, wenn ja: die App benachrichtigen,
        // dass die Advise loops gestoppt wurden und die
        // Transaktionen loeschen

        // Die das TERMINATE empfangende App muss
        // die Transaction-Tabelle abraeumen, egal ob Server oder Client!!
        // Es muessen alle Trans geloescht werden, die als Owner den
        // Client oder den Server haben!
        // if( !(pConv->nStatus & ST_CLIENT ) )
        SendUnadvises( hConv, 0, FALSE );  // alle Formate & nicht loeschen
        SendUnadvises( pConv->hConvPartner, 0, FALSE );

        // wir werden von draussen gekillt
        if ( !(nTransactFilter & CBF_SKIP_DISCONNECTS) )
        {
            Callback( XTYP_DISCONNECT, 0, hConv, 0, 0, 0,
                0, (ULONG)IsSameInstance(hWndPartner));
        }

        // kann unsere Partner-App DDEML ?
        if( !(pConv->hConvPartner) )
        {
            // nein, deshalb Transaktionstabelle selbst loeschen
            FreeTransactions( pData, hConv );
        }
    }
    else
        nLastErrInstance = DMLERR_NO_CONV_ESTABLISHED;

    if( hConv )
    {
        // hWndThis nicht loeschen, da wir den Handle noch fuer
        // das Acknowledge brauchen
        ////WRITELOG("DdeTerminate:Freeing conversation")
        FreeConvHandle( pData, hConv, FALSE );
    }

    ////WRITELOG("DdeTerminate:Acknowledging DDE_TERMINATE")

#ifdef OV_DEBUG
    DBG_ASSERT(WinIsWindow( 0, hWndThis ),"hWndThis not valid");
#endif

    if( !WinDdePostMsg( hWndPartner, hWndThis, WM_DDE_TERMINATE, 0, DDEPM_RETRY ))
    {
        ////WRITELOG("DdeTerminate:Acknowledging DDE_TERMINATE failed")
    }
    // jetzt hWndThis loeschen
    DestroyConversationWnd( hWndThis );

    return (MRESULT)0;
}


/*
    Zuordnung des Conversationhandles:

    Verbindungsaufbau:
        Client: DdeInitiate( HWNDClient )
        Server: Post( WM_DDE_INITIATEACK( HWNDServer ))
        Client: CreateConvHandle( HWNDClient, HWNDServer )

    Datenaustausch:
        Server: Post(WM_DDE_ACK( HWNDSender ))
        Client: GetConvHandle( HWNDClient, HWNDSender )
*/

MRESULT ImpDdeMgr::ConvWndProc( HWND hWnd,ULONG nMsg,MPARAM nPar1,MPARAM nPar2 )
{
    ImpWndProcParams    aParams;

    MRESULT nRet        = (MRESULT)0;
    aParams.hWndReceiver= hWnd;
    aParams.nPar1       = nPar1;
    aParams.nPar2       = nPar2;

    switch( nMsg )
    {

#ifdef DBG_UTIL
        case WM_DDE_INITIATE :
            DBG_ASSERT(0,"dde:unexpected msg");
            nRet = (MRESULT)TRUE;
            break;
#endif

        case WM_DDE_INITIATEACK : nRet = DdeInitiateAck(&aParams);  break;
        case WM_DDE_ACK         : nRet = DdeAck( &aParams );        break;
        case WM_DDE_ADVISE      : nRet = DdeAdvise( &aParams );     break;
        case WM_DDE_DATA        : nRet = DdeData( &aParams );       break;
        case WM_DDE_EXECUTE     : nRet = DdeExecute( &aParams );    break;
        case WM_DDE_POKE        : nRet = DdePoke( &aParams );       break;
        case WM_DDE_REQUEST     : nRet = DdeRequest( &aParams );    break;
        case WM_DDE_TERMINATE   : nRet = DdeTerminate( &aParams );  break;
        case WM_DDE_UNADVISE    : nRet = DdeUnadvise( &aParams );   break;
        case WM_TIMER           : nRet = DdeTimeout( &aParams );    break;
    }
    return nRet;
}

MRESULT ImpDdeMgr::SrvWndProc( HWND hWnd,ULONG nMsg,MPARAM nPar1,MPARAM nPar2 )
{
    MRESULT nRet = (MRESULT)0;

    ImpWndProcParams aParams;
    aParams.hWndReceiver= hWnd;
    aParams.nPar1       = nPar1;
    aParams.nPar2       = nPar2;

    switch( nMsg )
    {
#ifdef DBG_UTIL
        case WM_DDE_ACK :
        case WM_DDE_ADVISE :
        case WM_DDE_EXECUTE :
        case WM_DDE_POKE :
        case WM_DDE_REQUEST :
        case WM_DDE_UNADVISE :
        case WM_DDE_DATA :
        case WM_DDE_INITIATEACK :
            DBG_ASSERT(0,"dde:unexpected msg");
            nRet = (MRESULT)TRUE;
            break;
#endif

        case WM_DDE_TERMINATE :
            break; // DDE_INITIATE wurde im DDE_INITIATEACK terminiert

        // ein Client will was von uns
        case WM_DDE_INITIATE :
            nRet = DdeInitiate( &aParams );
            break;

        // eine ddeml-faehige App. hat einen Service (typ. AppName) [de]reg.
        case WM_DDEML_REGISTER :
            nRet = DdeRegister( &aParams );
            break;

        case WM_DDEML_UNREGISTER :
            nRet = DdeUnregister( &aParams );
            break;
    };
    return nRet;
}


MRESULT ImpDdeMgr::DdeAck( ImpWndProcParams* pParams )
{
    //WRITELOG("DdeAck:Start")
    HSZ hszItem;
    DDESTRUCT* pInDDEData = (DDESTRUCT*)(pParams->nPar2);
    if( pInDDEData )
    {
        BOOL bPositive = (BOOL)(pInDDEData->fsStatus & DDE_FACK ) != 0;
        BOOL bBusy = bPositive ? FALSE : (BOOL)(pInDDEData->fsStatus & DDE_FBUSY ) != 0;
        BOOL bNotProcessed = (BOOL)(pInDDEData->fsStatus & DDE_NOTPROCESSED ) != 0;
        // ein DDE_ACK niemals bestaetigen (um endlosschleifen zu vermeiden)
        pInDDEData->fsStatus &= (~DDE_FACKREQ);
    }
    else
    {
        //WRITELOG("DdeAck:Received (no data!)")
        return (MRESULT)0;
    }

    HCONV hConv = CheckIncoming(pParams, 0, hszItem);
#ifdef OV_DEBUG
    if( !hConv )
    {
        WRITELOG("DdeAck:HCONV not found")
    }
#endif
    ULONG nTransId=GetTransaction(pData,hConv,hszItem,pInDDEData->usFormat);
    if( !nTransId )
    {
        WRITELOG("DdeAck:Transaction not found")
        MyDosFreeMem( pInDDEData,"DdeAck" );
        return (MRESULT)0;
    }

    BOOL bThisIsSync = (BOOL)( bInSyncTrans && nTransId == nSyncTransId );
    // pruefen, ob die Transaktion abgeschlossen ist.
    Transaction* pTrans = pTransTable;
    pTrans += (USHORT)nTransId;
    ImpHCONV* pConv = pConvTable;
    pConv += (USHORT)hConv;

    if( pTrans->nConvst == XST_UNADVSENT )
    {
        //WRITELOG("DdeAck:Unadvise-Ack received")
        pTrans->nConvst = XST_UNADVACKRCVD;
        MyDosFreeMem( pInDDEData,"DdeAck" );
        DdeFreeStringHandle( hszItem );
        return (MRESULT)0;
    }
    if( pTrans->nConvst == XST_ADVDATASENT )
    {
        //WRITELOG("DdeAck:AdvData-Ack received")
        pTrans->nConvst = XST_ADVDATAACKRCVD;
        MyDosFreeMem( pInDDEData,"DdeAck" );
        DdeFreeStringHandle( hszItem );
        return (MRESULT)0;
    }

    USHORT nType = pTrans->nType;
    nType &= (~XTYPF_MASK);
    // beginn einer advise-loop oder request auf advise-loop ?
    // wenn ja: transaktion nicht loeschen
    BOOL bFinished = (BOOL)(nType != XTYP_ADVSTART &&
                            nType != (XTYP_ADVREQ & (~XTYPF_NOBLOCK)) );
    if( bFinished )
    {
        if( !bThisIsSync )
        {
            ////WRITELOG("DdeAck:Transaction completed")
            Callback( XTYP_XACT_COMPLETE, pInDDEData->usFormat, hConv,
                pConv->hszTopic, hszItem, (HDDEDATA)0, nTransId, 0 );
        }
        ////WRITELOG("DdeAck:Freeing transaction")
        FreeTransaction( pData, nTransId );
    }

    if( bThisIsSync )
    {
        hSyncResponseData = pInDDEData;
        nSyncResponseMsg = WM_DDE_ACK;
    }
    else
    {
        MyDosFreeMem( pInDDEData,"DdeAck" );
    }

    DdeFreeStringHandle( hszItem );

    return (MRESULT)0;
}


USHORT ImpDdeMgr::SendUnadvises(HCONV hConvServer,USHORT nFormat,BOOL bFree)
{
    USHORT nTransFound = 0;
    BOOL bCallApp = (BOOL)(!(nTransactFilter & CBF_FAIL_ADVISES));

    // wenn wir weder loeschen noch die App benachrichtigen sollen,
    // koennen wir gleich wieder returnen
    if( !hConvServer || ( !bFree && !bCallApp ) )
        return 0;

    ImpHCONV* pConvSrv = pConvTable;
    pConvSrv += (USHORT)hConvServer;
    HSZ hszTopic = pConvSrv->hszTopic;

    Transaction* pTrans = pTransTable;
    pTrans++;
    USHORT nCurTransId = 1;
    USHORT nCurTransactions = pData->nCurTransCount;
    while( nCurTransactions && nCurTransId < pData->nMaxTransCount )
    {
        if( pTrans->hConvOwner )
            nCurTransactions--;
        if( pTrans->hConvOwner == hConvServer &&
            (pTrans->nType & XTYP_ADVREQ) )
        {
            if( !nFormat || (nFormat == pTrans->nFormat) )
            {
                nTransFound++;
                if( bCallApp )
                {
                    //WRITELOG("SendUnadvises:Notifying App")
                    Callback( XTYP_ADVSTOP, pTrans->nFormat, hConvServer,
                        hszTopic, pTrans->hszItem, 0,0,0 );
                }
                if( bFree )
                    FreeTransaction( pData, (ULONG)nCurTransId );
            }
        }
        nCurTransId++;
        pTrans++;
    }
    return nTransFound;
}



HCONV ImpDdeMgr::CheckIncoming( ImpWndProcParams* pParams, ULONG nTransMask,
    HSZ& rhszItem )
{
//  ////WRITELOG("CheckIncoming")
    rhszItem = 0;
    DDESTRUCT* pInDDEData = (DDESTRUCT*)(pParams->nPar2);
    if( !pInDDEData )
    {
        // ////WRITELOG("CheckIncoming:PDDESTRUCT==0")
        return (HCONV)0;
    }

    HWND hWndThis = pParams->hWndReceiver;
    HWND hWndClient = (HWND)pParams->nPar1;

    BOOL bReject = (BOOL)(nTransactFilter & nTransMask);
    HCONV hConv;
    if( !bReject )
        hConv = GetConvHandle( pData, hWndThis, hWndClient );
    if ( bReject || !hConv )
        return (HCONV)0;

    rhszItem = DdeCreateStringHandle(
        ((char*)(pInDDEData)+pInDDEData->offszItemName), 850 );

    // ////WRITELOG("CheckIncoming:OK");
    return hConv;
}


MRESULT ImpDdeMgr::DdeAdvise( ImpWndProcParams* pParams )
{
    ////WRITELOG("DdeAdvise:Received")
    HSZ hszItem;
    HCONV hConv           = CheckIncoming(pParams, CBF_FAIL_ADVISES, hszItem);
    DDESTRUCT* pInDDEData = (DDESTRUCT*)(pParams->nPar2);
    HWND hWndThis         = pParams->hWndReceiver;
    HWND hWndClient       = (HWND)pParams->nPar1;
    if( !hConv )
    {
        ////WRITELOG("DdeAdvise:Conversation not found")
        pInDDEData->fsStatus &= (~DDE_FACK);
        MyWinDdePostMsg(hWndClient,hWndThis,WM_DDE_ACK,pInDDEData,DDEPM_RETRY);
        DdeFreeStringHandle( hszItem );
        return (MRESULT)0;
    }

    Transaction* pTrans = pTransTable;
    ImpHCONV* pConv = pConvTable;
    pConv += (USHORT)hConv;

    // existiert schon ein Link auf Topic/Item/Format-Vektor ?

    ULONG nTransId=GetTransaction(pData,hConv,hszItem,pInDDEData->usFormat);
    if( nTransId )
    {
        ////WRITELOG("DdeAdvise:Transaction already exists")
        pTrans += (USHORT)nTransId;
        // ist es eine AdviseLoop ?
        USHORT nTempType = pTrans->nType;
        nTempType &= (~XTYPF_MASK);
        if( nTempType == XTYP_ADVREQ )
        {
            // Flags der laufenden Advise-Loop aktualisieren
            ////WRITELOG("DdeAdvise:Adjusting Advise-Params")
            pTrans->nType = XTYP_ADVREQ;
            if( pInDDEData->fsStatus & DDE_FNODATA )
                pTrans->nType |= XTYPF_NODATA;
            if( pInDDEData->fsStatus & DDE_FACKREQ )
                pTrans->nType |= XTYPF_ACKREQ;
            pInDDEData->fsStatus |= DDE_FACK;
            MyWinDdePostMsg(hWndClient,hWndThis,WM_DDE_ACK,pInDDEData,DDEPM_RETRY);
            DdeFreeStringHandle( hszItem );
            return (MRESULT)0;
        }
        else if( nTempType != XTYP_ADVSTART )
        {
            ////WRITELOG("DdeAdvise:Not a advise transaction")
            pInDDEData->fsStatus &= (~DDE_FACK);
            MyWinDdePostMsg(hWndClient,hWndThis,WM_DDE_ACK,pInDDEData,DDEPM_RETRY);
            DdeFreeStringHandle( hszItem );
            return (MRESULT)0;
        }
    }

    if( !nTransId )
    {
        ////WRITELOG("DdeAdvise:Creating Transaction")
        ////WRITESTATUS("DdeAdvise:Creating Transaction")
        nTransId = CreateTransaction( pData, hConv, hszItem,
            pInDDEData->usFormat, XTYP_ADVREQ );
        ////WRITESTATUS("DdeAdvise:Created Transaction")
    }
    if( nTransId )
    {
        pTrans = pTransTable;
        pTrans += (USHORT)nTransId;
        if( pInDDEData->fsStatus & DDE_FNODATA )
            pTrans->nType |= XTYPF_NODATA;
        if( pInDDEData->fsStatus & DDE_FACKREQ )
            pTrans->nType |= XTYPF_ACKREQ;
    }
    else
    {
        ////WRITELOG("DdeAdvise:Cannot create Transaction")
        pInDDEData->fsStatus &= (~DDE_FACK);
        MyWinDdePostMsg(hWndClient,hWndThis,WM_DDE_ACK,pInDDEData,DDEPM_RETRY);
        DdeFreeStringHandle( hszItem );
        return (MRESULT)0;
    }

    ////WRITELOG("DdeAdvise:Calling Server")

    if ( Callback( XTYP_ADVSTART, pInDDEData->usFormat,
        hConv, pConv->hszTopic, hszItem, (HDDEDATA)0, 0, 0 ) )
    {
        //
        // ServerApp erlaubt AdviseLoop
        //
        ////WRITELOG("DdeAdvise:Advise loop established")
        pInDDEData->fsStatus |= DDE_FACK;
        MyWinDdePostMsg(hWndClient,hWndThis,WM_DDE_ACK,pInDDEData,DDEPM_RETRY);
    }
    else
    {
        ////WRITELOG("DdeAdvise:Advise loop not established")
        FreeTransaction( pData, nTransId );
        pInDDEData->fsStatus &= (~DDE_FACK); // DDE_FNOTPROCESSED;
        MyWinDdePostMsg(hWndClient,hWndThis,WM_DDE_ACK,pInDDEData,DDEPM_RETRY);
    }
    ////WRITESTATUS("DdeAdvise:End")
    ////WRITELOG("DdeAdvise:End")
    DdeFreeStringHandle( hszItem );
    return (MRESULT)0;
}

MRESULT ImpDdeMgr::DdeData( ImpWndProcParams* pParams )
{
    WRITELOG("DdeData:Received")
    HSZ hszItem;
    HCONV hConv           = CheckIncoming(pParams, 0, hszItem);
    HWND hWndThis         = pParams->hWndReceiver;
    HWND hWndClient       = (HWND)pParams->nPar1;
    DDESTRUCT* pInDDEData = (DDESTRUCT*)(pParams->nPar2);

    BOOL bSendAck;
    if( pInDDEData && (pInDDEData->fsStatus & DDE_FACKREQ ))
    {
        WRITELOG("DdeData: Ackn requested")
        bSendAck = TRUE;
    }
    else
    {
        WRITELOG("DdeData: Ackn not requested")
        bSendAck = FALSE;
    }

    ULONG nTransId = GetTransaction(pData,hConv,hszItem,pInDDEData->usFormat);
    if( !nTransId )
    {
        WRITELOG("DdeData:Transaction not found")
        WRITEDATA(pInDDEData)
        if( bSendAck )
        {
            WRITELOG("DdeData: Posting Ackn")
            pInDDEData->fsStatus &= (~DDE_FACK); // NOTPROCESSED;
            MyWinDdePostMsg(hWndClient,hWndThis,WM_DDE_ACK,pInDDEData,DDEPM_RETRY);
        }
        else
        {
            MyDosFreeMem( pInDDEData,"DdeData" );
        }
        return (MRESULT)0;
    }

    BOOL bThisIsSync = (BOOL)( bInSyncTrans && nTransId == nSyncTransId );

    // pruefen, ob die Transaktion abgeschlossen ist.
    Transaction* pTrans = pTransTable;
    pTrans += (USHORT)nTransId;

    if( pTrans->nConvst == XST_WAITING_ACK )
    {
        // dieser Fall kann eintreten, wenn ein Server innerhalb
        // einer WM_DDE_ADVISE-Msg. oder bevor beim Client das
        // Ack eintrifft, Advise-Daten sendet.
        WRITELOG("DdeData:Ignoring unexpected data")
        if( bSendAck )
        {
            WRITELOG("DdeData: Posting Ackn")
            pInDDEData->fsStatus &= (~DDE_FACK); // NOTPROCESSED;
            MyWinDdePostMsg(hWndClient,hWndThis,WM_DDE_ACK,pInDDEData,DDEPM_RETRY);
        }
        else
        {
            MyDosFreeMem( pInDDEData,"DdeData" );
        }
        return (MRESULT)0;
    }

    ImpHCONV* pConv = pConvTable;
    pConv  += (USHORT)hConv;

    USHORT nType = pTrans->nType;
    nType &= (~XTYPF_MASK);
    BOOL bNotAdviseLoop = (BOOL)(nType != (XTYP_ADVREQ & (~XTYPF_NOBLOCK)));
    if( !bThisIsSync )
    {
        // WRITELOG("DdeData:Is async transaction")
        if( bNotAdviseLoop )
        {
            // WRITELOG("DdeData:Transaction completed -> calling client")
            Callback( XTYP_XACT_COMPLETE, pInDDEData->usFormat, hConv,
                pConv->hszTopic, hszItem, pInDDEData, nTransId, 0 );
            // WRITELOG("DdeData:Freeing transaction")
            FreeTransaction( pData, nTransId );
        }
        else
        {
            WRITELOG("DdeData:Advise-Loop -> calling client")
            HDDEDATA pToSend = pInDDEData;
            if( pTrans->nType & XTYPF_NODATA )
            {
                pToSend = 0;
                // WRITELOG("DdeData:Is warm link")
            }
            Callback( XTYP_ADVDATA, pInDDEData->usFormat, hConv,
                pConv->hszTopic, hszItem, pToSend, nTransId, 0 );
        }
        if( bSendAck )
        {
            WRITELOG("DdeData: Posting Ackn")
            pInDDEData->fsStatus = DDE_FACK;
            MyWinDdePostMsg(hWndClient,hWndThis,WM_DDE_ACK,pInDDEData,DDEPM_RETRY);
        }
        else
            MyDosFreeMem( pInDDEData,"DdeData" );
    }
    else  // synchrone Transaktion (Datenhandle nicht freigeben!)
    {
        // WRITELOG("DdeData:Is sync transaction")
        hSyncResponseData = pInDDEData;
        nSyncResponseMsg = WM_DDE_DATA;
        if( bSendAck )
        {
            pInDDEData->fsStatus |= DDE_FACK;
            WRITELOG("DdeData: Posting Ackn")
            MyWinDdePostMsg(hWndClient,hWndThis,WM_DDE_ACK,pInDDEData,
                DDEPM_RETRY | DDEPM_NOFREE );
        }
    }

    DdeFreeStringHandle( hszItem );
    // WRITELOG("DdeData:End")
    return (MRESULT)0;
}

MRESULT ImpDdeMgr::DdeExecute( ImpWndProcParams* pParams )
{
    ////WRITELOG("DdeExecute:Received")
    DDESTRUCT* pInDDEData = (DDESTRUCT*)(pParams->nPar2);
    HSZ hszItem;
    HCONV hConv = CheckIncoming(pParams, 0, hszItem);
    BOOL bSuccess = FALSE;
    ImpHCONV* pConv = pConvTable;
    pConv += (USHORT)hConv;
    if ( hConv && !(nTransactFilter & CBF_FAIL_EXECUTES) && pInDDEData )
    {
        if ( Callback( XTYP_EXECUTE, pInDDEData->usFormat, hConv,
            pConv->hszTopic, hszItem, pInDDEData, 0, 0 )
                == (DDESTRUCT*)DDE_FACK )
            bSuccess = TRUE;
    }
    else
    {
        ////WRITELOG("DdeExecute:Not processed")
    }
    if( pInDDEData )
    {
        if( bSuccess )
            pInDDEData->fsStatus |= DDE_FACK;
        else
            pInDDEData->fsStatus &= (~DDE_FACK);
        MyWinDdePostMsg( pConv->hWndPartner, pConv->hWndThis, WM_DDE_ACK,
            pInDDEData, DDEPM_RETRY );
    }
    DdeFreeStringHandle( hszItem );
    return (MRESULT)0;
}

HCONV ImpDdeMgr::ConnectWithClient( HWND hWndClient,
    HSZ hszPartner, HSZ hszService, HSZ hszTopic, BOOL bSameInst,
    DDEINIT* pDDEData, CONVCONTEXT* pCC )
{
    ////WRITELOG("ConnectWithClient:Start")
    HWND hWndSrv = CreateConversationWnd();
    IncConversationWndRefCount( hWndSrv );
    HCONV hConv  = CreateConvHandle( pData, pidThis, hWndSrv, hWndClient,
                    hszPartner, hszService, hszTopic );
    if(!hConv )
        return 0;
    BOOL bFreeDdeData = FALSE;
    if( !pDDEData )
    {
        bFreeDdeData = TRUE;
        pDDEData = CreateDDEInitData( hWndClient,hszService,hszTopic, pCC );
        PID pid; TID tid;
        WinQueryWindowProcess( hWndClient, &pid, &tid );
        DosGiveSharedMem( pDDEData, pid, PAG_READ | PAG_WRITE);
    }
    HAB hAB = WinQueryAnchorBlock( hWndSrv );
    WinGetLastError( hAB ); // fehlercode zuruecksetzen
    WinSendMsg(hWndClient,WM_DDE_INITIATEACK,(MPARAM)hWndSrv,(MPARAM)pDDEData);
    if( WinGetLastError( hAB ) )
    {
        // ////WRITELOG("DdeConnectWithClient:Client died")
        if( bFreeDdeData )
        {
            MyDosFreeMem( pDDEData,"ConnectWithClient" );
        }
        FreeConvHandle( pData, hConv );
        return (HCONV)0;
    }

    if( !(nTransactFilter & CBF_SKIP_CONNECT_CONFIRMS) )
    {
        Callback( XTYP_CONNECT_CONFIRM, 0, hConv, hszTopic, hszService,
            0, 0, (ULONG)bSameInst );
    }

    if( bFreeDdeData )
    {
        MyDosFreeMem( pDDEData,"ConnectWithClient" );
    }
    // HCONV der PartnerApp suchen & bei uns eintragen
    ImpHCONV* pConv = pConvTable;
    pConv += (USHORT)hConv;
    pConv->hConvPartner = GetConvHandle( pData, hWndClient, hWndSrv );
    pConv->nStatus = ST_CONNECTED;
    //WRITESTATUS("Server:Connected with client")
    //WRITELOG("ConnectWithClient:End")
    return hConv;
}

MRESULT ImpDdeMgr::DdeInitiate( ImpWndProcParams* pParams )
{
    ////WRITELOG("DdeInitiate:Received")
    HWND hWndClient = (HWND)(pParams->nPar1);
//  BOOL bSameInst = IsSameInstance( hWndClient );
    BOOL bSameInst = (BOOL)(hWndClient==hWndServer);
    DDEINIT* pDDEData = (DDEINIT*)pParams->nPar2;

    if ( ( nTransactFilter & (CBF_FAIL_CONNECTIONS | APPCMD_CLIENTONLY)) ||
         (( nTransactFilter & CBF_FAIL_SELFCONNECTIONS) && bSameInst )
    )
    {
        MyDosFreeMem( pDDEData,"DdeInitiate" );
        return (MRESULT)FALSE;   // narda
    }

    HSZ hszService = (HSZ)0;
    if( *(pDDEData->pszAppName) != '\0' )
    {
        hszService = DdeCreateStringHandle( pDDEData->pszAppName, 850 );
        ////WRITELOG(pDDEData->pszAppName);
    }
    HSZ hszTopic = (HSZ)0;
    if( *(pDDEData->pszTopic) != '\0' )
    {
        hszTopic = DdeCreateStringHandle( pDDEData->pszTopic, 850 );
        ////WRITELOG(pDDEData->pszTopic);
    }
    HSZ hszPartner = GetAppName( hWndClient );

    // nur weitermachen, wenn Service registriert oder
    // Service-Name-Filtering ausgeschaltet.
    if( !bServFilterOn || GetService(hszService) )
    {
        // XTYP_CONNECT-Transaktionen erfolgen nur mit
        // Services & Topics ungleich 0!
        if( hszService && hszTopic )
        {
            if( IsConvHandleAvailable(pData) && Callback( XTYP_CONNECT,
                 0, 0, hszTopic,hszService, 0, 0, (ULONG)bSameInst))
            {
                // App erlaubt Verbindung mit Client
                ConnectWithClient( hWndClient, hszPartner,
                    hszService, hszTopic, bSameInst, pDDEData );
            }
        }
        else
        {
            // ** Wildcard-Connect **
            ////WRITELOG("DdeInitiate:Wildconnect")
            // vom Server eine Liste aller Service/Topic-Paare anfordern
            CONVCONTEXT* pCC=(CONVCONTEXT*)(pDDEData+pDDEData->offConvContext);
            DDESTRUCT* hList = Callback( XTYP_WILDCONNECT, 0, (HCONV)0,
                hszTopic,hszService, (HDDEDATA)0, (ULONG)pCC, (ULONG)bSameInst );
            if( hList )
            {
                HSZPAIR* pPairs = (HSZPAIR*)((char*)hList+hList->offabData);
                while( pPairs->hszSvc )
                {
                    ////WRITELOG("DdeInitiate:Wildconnect.Connecting")
                    ConnectWithClient( hWndClient, hszPartner,
                        pPairs->hszSvc, pPairs->hszTopic, bSameInst, 0, pCC);
                    // Stringhandles gehoeren der App! (nicht free-en)
                    pPairs++;
                }
                DdeFreeDataHandle( hList );
            }
        }
    }
    DdeFreeStringHandle( hszTopic );
    DdeFreeStringHandle( hszService );
    DdeFreeStringHandle( hszPartner );
    MyDosFreeMem( pDDEData,"DdeInitiate" );

    return (MRESULT)TRUE;
}

MRESULT ImpDdeMgr::DdeInitiateAck( ImpWndProcParams* pParams )
{
    DDEINIT* pDDEData = (DDEINIT*)(pParams->nPar2);

    if( !bListConnect && hCurConv )
    {
        MyDosFreeMem( pDDEData,"DdeInitiateAck" );
        WinPostMsg( hWndServer, WM_DDE_TERMINATE, (MPARAM)hWndServer, 0 );
        return (MRESULT)FALSE;
    }

    HWND hWndThis = pParams->hWndReceiver;
    // Referenz-Count unseres Client-Windows inkrementieren
    IncConversationWndRefCount( hWndThis );

    HWND hWndSrv        = (HWND)(pParams->nPar1);
    HSZ hszService      = DdeCreateStringHandle( pDDEData->pszAppName, 850 );
    HSZ hszTopic        = DdeCreateStringHandle( pDDEData->pszTopic, 850 );
    HSZ hszPartnerApp   = GetAppName( hWndSrv );

    hCurConv = CreateConvHandle( pData, pidThis, hWndThis, hWndSrv,
                hszPartnerApp, hszService, hszTopic, 0 );

    ImpHCONV* pConv = pConvTable;
    pConv += (USHORT)hCurConv;

    // HCONV der PartnerApp suchen & bei uns eintragen
    pConv->hConvPartner = GetConvHandle( pData, hWndSrv, hWndThis );
    // nicht asserten, da ja non-ddeml-Partner moeglich
    // DBG_ASSERT(pConv->hConvPartner,"DDE:Partner not found");
    pConv->nStatus = ST_CONNECTED | ST_CLIENT;

    if( bListConnect )
    {
        ////WRITELOG("DdeInitiateAck:ListConnect/Connecting hConvs")
        // Status setzen & verketten
        pConv->hConvList = hCurListId;
        pConv->nPrevHCONV = nPrevConv;
        pConv->nStatus |= ST_INLIST;
        if( nPrevConv )
        {
            pConv = pConvTable;
            pConv += nPrevConv;
            pConv->nNextHCONV = (USHORT)hCurConv;
        }
        nPrevConv = (USHORT)hCurConv;
    }

    DdeFreeStringHandle( hszService );
    DdeFreeStringHandle( hszTopic );
    DdeFreeStringHandle( hszPartnerApp );
    MyDosFreeMem( pDDEData,"DdeInitiateAck" );
    ////WRITESTATUS("After DdeInitiateAck")
    ////WRITELOG("DdeInitiateAck:End")
    return (MRESULT)TRUE;
}

MRESULT ImpDdeMgr::DdePoke( ImpWndProcParams* pParams )
{
    HSZ hszItem = 0;
    DDESTRUCT* pInDDEData = (DDESTRUCT*)(pParams->nPar2);
    HCONV hConv = CheckIncoming( pParams, CBF_FAIL_REQUESTS, hszItem );
    BOOL bSuccess =FALSE;
    ImpHCONV* pConv = pConvTable;
    pConv += (USHORT)hConv;
    if ( hConv && !(nTransactFilter & CBF_FAIL_POKES) && pInDDEData )
    {
        if( Callback( XTYP_POKE, pInDDEData->usFormat, hConv,
            pConv->hszTopic, hszItem, pInDDEData, 0, 0 )
                == (DDESTRUCT*)DDE_FACK )
            bSuccess = TRUE;
    }
    if( pInDDEData )
    {
        if( bSuccess )
            pInDDEData->fsStatus |= DDE_FACK;
        else
            pInDDEData->fsStatus &= (~DDE_FACK);

        MyWinDdePostMsg( pConv->hWndPartner, pConv->hWndThis, WM_DDE_ACK,
            pInDDEData, DDEPM_RETRY );
    }
    DdeFreeStringHandle( hszItem );
    return (MRESULT)0;
}

MRESULT ImpDdeMgr::DdeRequest( ImpWndProcParams* pParams )
{
    HSZ hszItem = 0;
    DDESTRUCT* pInDDEData = (DDESTRUCT*)(pParams->nPar2);
    if( pInDDEData )
        // ist fuer Requests nicht definiert
        pInDDEData->fsStatus = 0;
    HCONV hConv = CheckIncoming( pParams, CBF_FAIL_REQUESTS, hszItem );
    HWND hWndThis = pParams->hWndReceiver;
    HWND hWndClient = (HWND)pParams->nPar1;
    if( hConv )
    {
        ImpHCONV* pConv = pConvTable;
        pConv += (USHORT)hConv;

        DDESTRUCT* pOutDDEData = Callback( XTYP_REQUEST, pInDDEData->usFormat,
            hConv, pConv->hszTopic, hszItem, (HDDEDATA)0, 0, 0 );

        if ( !pOutDDEData )
        {
            pInDDEData->fsStatus &= (~DDE_FACK);
            MyWinDdePostMsg(hWndClient,hWndThis,WM_DDE_ACK,pInDDEData,DDEPM_RETRY);
        }
        else
        {
            MyDosFreeMem( pInDDEData,"DdeRequest" );
            pOutDDEData->fsStatus |= DDE_FRESPONSE;
            MyWinDdePostMsg(hWndClient,hWndThis,WM_DDE_DATA,pOutDDEData,DDEPM_RETRY);
        }
    }
    else
    {
        pInDDEData->fsStatus &= (~DDE_FACK);
        MyWinDdePostMsg(hWndClient,hWndThis,WM_DDE_ACK,pInDDEData,DDEPM_RETRY);
    }

    DdeFreeStringHandle( hszItem );

    return (MRESULT)0;
}


MRESULT ImpDdeMgr::DdeUnadvise( ImpWndProcParams* pParams )
{
    HSZ hszItem;
    HCONV hConv           = CheckIncoming( pParams, 0, hszItem );
    DDESTRUCT* pInDDEData = (DDESTRUCT*)(pParams->nPar2);
    HWND hWndThis         = pParams->hWndReceiver;
    HWND hWndClient       = (HWND)pParams->nPar1;
    USHORT nClosedTransactions = 0;
    if( hConv )
    {
        USHORT nFormat = pInDDEData->usFormat;
        // alle Transaktionen des HCONVs loeschen ?
        if( !hszItem )
        {
            // App benachrichtigen & Transaktionen loeschen
            nClosedTransactions = SendUnadvises( hConv, nFormat, TRUE );
        }
        else
        {
            ULONG nTransId = GetTransaction(pData, hConv, hszItem, nFormat);
            if( nTransId )
            {
                ImpHCONV* pConv = pConvTable;
                pConv += (USHORT)hConv;
                nClosedTransactions = 1;
                if( !(nTransactFilter & CBF_FAIL_ADVISES) )
                    Callback( XTYP_ADVSTOP, nFormat, hConv,
                        pConv->hszTopic, hszItem, 0, 0, 0 );
                if( !pConv->hConvPartner )
                    FreeTransaction( pData, nTransId );
            }
#if defined(OV_DEBUG)
            else
            {
                WRITELOG("DdeUnadvise:Transaction not found")
            }
#endif
        }
    }
#if defined(OV_DEBUG)
    else
    {
       WRITELOG("DdeUnadvise:Conversation not found")
    }
#endif

    if( !nClosedTransactions )
        pInDDEData->fsStatus &= (~DDE_FACK);
    else
        pInDDEData->fsStatus |= DDE_FACK;

    MyWinDdePostMsg(hWndClient,hWndThis,WM_DDE_ACK,pInDDEData,DDEPM_RETRY);
    DdeFreeStringHandle( hszItem );
    return (MRESULT)0;
}

BOOL ImpDdeMgr::WaitTransState( Transaction* pTrans, ULONG nTransId,
    USHORT nNewState, ULONG nTimeout )
{
    ////WRITELOG("WaitTransState:Start")
    ImpHCONV* pConv = pConvTable;
    pConv += pTrans->hConvOwner;
    HAB hAB = WinQueryAnchorBlock( pConv->hWndThis );
    ULONG nTimerId = WinStartTimer( hAB, 0, 0, 50 );
    QMSG aQueueMsg;

    BOOL bContinue = TRUE;
    while( bContinue )
    {
        if( WinGetMsg( hAB, &aQueueMsg, 0, 0, 0 ))
        {
            WinDispatchMsg( hAB, &aQueueMsg );
            if( (!WinIsWindow( hAB, pConv->hWndPartner)) ||
                (pTrans->nConvst == nNewState) )
            {
                bContinue = FALSE;
            }
        }
        else
            bContinue = FALSE;
    }

    WinStopTimer( hAB, 0, nTimerId );
    ////WRITELOG("WaitTransState:End")
    return TRUE;
}

}

/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
