home *** CD-ROM | disk | FTP | other *** search
/ Microsoft Programmer's Library 1.3 / Microsoft-Programers-Library-v1.3.iso / sampcode / os2sdk / os2sdk12 / ddeml / ddeml.c next >
Encoding:
C/C++ Source or Header  |  1990-06-22  |  98.9 KB  |  2,815 lines

  1. /****************************** Module Header ******************************\
  2. * Module Name: DDE.C
  3. *
  4. * DDE Manager main module - Contains all exported Dde functions.
  5. *
  6. * Created: 12/12/88 Sanford Staab
  7. *
  8. * Copyright (c) 1988, 1989  Microsoft Corporation
  9. * 4/5/89        sanfords        removed need for hwndFrame registration parameter
  10. * 6/5/90        sanfords        Fixed callbacks so they are blocked during
  11. *                               timeouts.
  12. *                               Fixed SendDDEInit allocation bug.
  13. *                               Added hApp to ConvInfo structure.
  14. *                               Allowed QueryConvInfo() to work on server hConvs.
  15. *                               Added FindFrame() to provide an hApp for Server
  16. *                               hConvs.
  17. * 6/14/90       sanfords        Altered hDatas so they will work when shared
  18. *                               between threads of the same process.  Also
  19. *                               added optimization to only have the hsz
  20. *                               in the hData for local conversations.
  21. * 6/21/90       sanfords        Renamed APIs to Dde....
  22. *                               Finished DdeAppNameServer() implementation.
  23. *                       
  24. \***************************************************************************/
  25.  
  26. #include "ddemlp.h" 
  27. #include "version.h"
  28.  
  29. /****** Globals *******/
  30.  
  31. HMODULE hmodDmg = 0;        /* initialized by LoadProc on DLL initialization */
  32. PFNWP lpfnFrameWndProc = 0;             /* system Frame window procedure */
  33.  
  34. HHEAP hheapDmg = 0;                     /* main DLL heap */
  35.  
  36. PHATOMTBL aAtbls;
  37. USHORT cAtbls = 0;
  38. USHORT iAtblCurrent = 0;
  39. USHORT   cMonitor = 0;
  40. DOSFSRSEM FSRSemDmg;                    /* used to protect globals */
  41. USHORT cAtoms = 0;                      /* for debugging! */
  42.  
  43. PAPPINFO pAppInfoList = NULL;           /* registered thread data list */
  44. USHORT usHugeShift;                     /* for huge segment support */
  45. USHORT usHugeAdd;                       /* for huge segment support */
  46. COUNTRYCODE syscc;
  47.  
  48. BOOL fInSubset(PCQDATA pcqd, ULONG afCmd);
  49.  
  50. /* PUBDOC START *\
  51. DLL overview:
  52.  
  53. This DLL supports standard DDE communication on behalf of its client
  54. applications.  It is compatable with most existing DDE programs.  The
  55. API interface features object-like abstractions that allow its implementation
  56. on and across a variety of platforms.
  57.  
  58. Main features:
  59.  
  60.   * HSZ manager:    Allows more efficient handling of numerous character
  61.                     strings without the limitations of the atom manager.
  62.                     
  63.   * HDATA manager:  Data container objects allow easy filling, accessing
  64.                     and reuse of huge amounts of data and allow a
  65.                     server to efficiently support several clients.
  66.                     
  67.   * Controled initiates: Supports client to multi-server initiates and
  68.                     allows a client application to pick and choose which
  69.                     conversations to keep.
  70.  
  71.   * Debugging support: Allows monitoring applications to be easily written
  72.                     and provides readable error messages.  
  73.  
  74.   * Huge Segment support: Exports a useful huge segment copy function and
  75.                     properly handles huge data transfers.
  76.  
  77.   * Synchronous communication:  Clients may use a very simple form of
  78.                     transaction processing that requires little application
  79.                     support.
  80.  
  81.   * Asynchronous communication:  Clients may opt to use queued transfers
  82.                     freeing them to do other tasks in parellel with DDE.
  83.  
  84.   * Callback control:  Applications may selectively suspend DLL callbacks
  85.                     to themselves when in time critical sections.  Selected
  86.                     conversations can be blocked while being serviced so
  87.                     others can be processed by the same thread.
  88.  
  89.   * registration notification: All applications using this DLL are notified
  90.                     whenever any other application registers or unregisters
  91.                     itself with this DLL.  This enables nameserver support
  92.                     for DDE.
  93.  
  94.   * multiple DDE entity support:  An application can register itself with
  95.                     any number of application names and can change what
  96.                     application names it responds to at any time.  Each
  97.                     registered thread is a seperate DDE entity.
  98.  
  99.   * advise loop control:  Advise loops are tracked by the DLL, however
  100.                     server applications have complete control over
  101.                     advise loop initiation.
  102.  
  103.   * network agent support: Special agent applications can register with this
  104.                     DLL to represent multiple applications on other machines
  105.                     or platforms.
  106.                     
  107. API specifications:
  108.  
  109. Callback function:
  110.     
  111. EXPENTRY Callback(
  112. HCONV hConv,        // holds server conversation handle in most cases
  113. HSZ hszTopic,       // holds topic hsz for transaction
  114. HSZ hszItem,        // holds item or application hsz for transaction
  115. USHORT usFormat,    // holds data format when applicable
  116. USHORT usType,      // holds transaction type code
  117. HDMGDATA hDmgData); // holds incomming data in most cases
  118.  
  119. This is the definition of the data call-back function that an
  120. application must export so that the DDE can initiate interaction
  121. with the application when necessary.  This function is refered to in
  122. the DdeInitialize() call.
  123.  
  124. The application callback function is very much like a PM window 
  125. procedure for DDE.  The usType specifies the type of transaction being 
  126. done.  By ANDing this parameter with XCLASS_MASK and comparing the 
  127. result with the XCLASS_ constants, the transaction return type 
  128. expected can be classified.
  129.  
  130. XTYP_ constants also may contain the XTYPF_NOBLOCK flag.  The presence
  131. of this flag indicates that the CBR_BLOCK return value from the callback
  132. will not be honored by the DDE.  (see DdeEnableCallback() for more
  133. information on this concept.)
  134.  
  135. The various transactions are explained below:
  136.  
  137.  
  138. -----XCLASS_NOTIFICATION class:
  139.     These are strictly notification messages to an application.  The return
  140.     value is ignored except in the case of CBR_BLOCK. (if the notification
  141.     is blockable)
  142.  
  143. XTYP_RTNPKT
  144.  
  145.    This transaction is sent to agent applications.  hDmgData contains a 
  146.    packet to send to the agent who's handle is in the hszItem parameter.  
  147.  
  148. XTYP_REGISTER
  149.  
  150.    Another server name has just been registered with the DLL application 
  151.    name server.  hszItem is set to the application name being 
  152.    registered.  If this is NULL, an application has registered itself as 
  153.    WILD.  hDmgData is set to the application handle that registered.
  154.    This is not blockable by CBR_BLOCK because no hConv is 
  155.    associated with it.  Only if all callbacks are disabled is this 
  156.    transaction blocked.
  157.  
  158. XTYP_UNREGISTER
  159.  
  160.    Another server application has just unregistered a name with this 
  161.    DLL.  hszItem is set to the application name being unregistered.
  162.    hDmgData is set to the app handle of the unregistering application.
  163.    This is not blockable by CBR_BLOCK because no hConv is associated 
  164.    with it.  Only if all callbacks are disabled is this transaction 
  165.    blocked.
  166.  
  167. XTYP_INIT_CONFIRM
  168.  
  169.    Sent to let a server know that a conversation on application hszItem 
  170.    and Topic hszTopic has been established on hConv.  hConv uniquely 
  171.    identifies this conversation from the server's prospective.  This 
  172.    call cannot be blocked because it is part of the DDE initiate 
  173.    sequence.  This callback is generated by the results of XTYP_INIT and 
  174.    XTYP_WILDINIT callbacks.  
  175.  
  176. XTYP_TERM
  177.  
  178.    This is a notification telling a server application that a 
  179.    conversation has been terminated.  hConv is set to identify which 
  180.    conversation was terminated.  
  181.  
  182. XTYP_ADVSTOP
  183.  
  184.    This notifies a server that an advise loop is stopping.  hszTopic, 
  185.    hszItem, and usFormat identify the advise loop within hConv.  
  186.  
  187. XTYP_XFERCOMPLETE
  188.  
  189.    This notifictaion is sent to a client when an asynchronous data 
  190.    DdeClientXfer() transaction is completed.  hDmgData is the client 
  191.    queue ID of the completed transaction.  hConv is the client 
  192.    conversation handle.  
  193.     
  194. XTYP_MONITOR
  195.  
  196.    This notifies an app registered as DMGCMD_MONITOR of DDE data that is 
  197.    being transmitted.  hDmgData contains a text string representing the 
  198.    transaction suitable for printing on a terminal, file, or window.  
  199.    Note that this monitors ALL DDE communication and may be extensive.  
  200.    This call cannot be delayed by disabled callbacks.  This transaction 
  201.    is not blockable.  
  202.  
  203. XTYP_ACK
  204.  
  205.    This notifies a server that it has received an acknowledge from data 
  206.    it has sent a client.  The hConv, topic, item, and format are set 
  207.    apropriately.  LOUSHORT(hDmgData) will contain the dde flags from the 
  208.    ack.  
  209.  
  210. -----XCLASS_DATA class:
  211.  
  212.    Transactions in this class are expected to return an HDMGDATA or 0 as 
  213.    apropriate.  
  214.  
  215. XTYP_PKT
  216.  
  217.    This transaction is sent to agent applications.  hDmgData contains a 
  218.    packet to send.  hszItem contains the agent handle to send the data 
  219.    to.  hConv is set to the associated conversation handle.  The return 
  220.    packet received from the partner agent should be returned.  This 
  221.    call is blockable.
  222.  
  223.    If blocked, the conversation will be unblocked and processed by 
  224.    DdeProcessPkt() when the return packet is received.  It is a good 
  225.    idea for the agent to remember the hConv parameter in case the return 
  226.    packet does not arrive within a reasonable amount of time via 
  227.    XTYP_RTNPKT.  If an agent determines that it wishes to kill a 
  228.    conversation, it should call DdeDisconnect().  
  229.  
  230. XTYP_REQUEST
  231. XTYP_ADVREQ
  232.  
  233.    Data is being requested from a server application.  The function 
  234.    should create a hDmgData using the DdePutData() function and 
  235.    return it.  XTYP_ADVREQ origonates from a DdePostAdvise() call 
  236.    while XTYP_REQUEST origonates from a client data request.  
  237.  
  238. XTYP_WILDINIT
  239.  
  240.    This is asking a DDE server permission to make multiple connections 
  241.    with a specific client.
  242.  
  243.    hszItem may be the application name the client is requesting or it 
  244.    may be NULL indicating a wild application name.  hszTopic may be the 
  245.    Topic requested or NULL indicating a wild topic.  If not NULL, 
  246.    hDmgData contains a CONVCONTEXT structure.  All other parameters are 
  247.    0 or NULL.
  248.  
  249.    For local initiates, (initiates with the server application itself) 
  250.    The server should return a 0 terminated (ie hszApp = hszTopic = 0) 
  251.    array of HSZPAIR structures using DdePutData() and 
  252.    DdeAddData().  Each hsz pair represents an app/topic the server 
  253.    wishes to support.  Each created conversation will result in an 
  254.    XTYP_INIT_CONFIRM notification.  If 0 is returned, no connections are 
  255.    made with the requesting client.  This call is made even if callbacks 
  256.    are disabled due the the synchronous nature of DDE initiates.  This 
  257.    callback cannot be blocked by returning CBR_BLOCK.  
  258.  
  259.    Agent applications may also process this transaction as a 
  260.    representative initiate.  The agent is expected to package this 
  261.    transaction using DdeCreateInitPkt() and broadcast it to all 
  262.    apropriate agents along its communication channel.  It then must 
  263.    collect the return packets and for each packet and call 
  264.    DdeProcessPkt().  Representative initiates are synchronous in that 
  265.    the agent cannot return from this callback until all initiate return 
  266.    packets are returned.  Representative initiates do NOT result in any 
  267.    XTYP_INIT_CONFIRM notifications to the agent.  The agent is then free 
  268.    to process this transaction for local connections as described.  
  269.  
  270. -----XCLASS_BOOL class:
  271.  
  272.    Transactions in this class expect a BOOL return of TRUE or FALSE.  
  273.  
  274. XTYP_INIT
  275.  
  276.    This is a query asking a DDE server permission to connect to a 
  277.    specific client.  hszItem is set to the Application name.  hszTopic 
  278.    is set to the topic name.  hDmgData if not NULL, contains CONVCONTEXT 
  279.    data.  All other parameters are 0 or NULL.  A TRUE return value 
  280.    allows the Dde to start up a server on the app/topic specified.  
  281.    This will result in an XTYP_INIT_CONFIRM callback.  This call is made 
  282.    even if callbacks are disabled due the the synchronous nature of DDE 
  283.    initiates.  
  284.     
  285.    Agent applications may process this transaction as a representative 
  286.    transaction.  The agent is expected to package this transaction using 
  287.    DdeCreateInitPkt() and broadcast it to all apropriate agents along 
  288.    its communication channel.  It then must collect the return packets 
  289.    and for each packet call DdeProcessPkt().  This will NOT result in 
  290.    any XTYP_INIT_CONFIRM notifications to the agent.  The agent is then 
  291.    free to process this transaction for local connections.  
  292.  
  293.     A FALSE return implies no local initiate permissions are granted.
  294.  
  295. XTYP_ADVSTART
  296.  
  297.    This transaction requests permission to start a DDE advise loop with 
  298.    a server.  The hszTopic, hszItem, and usFormat identify the advise 
  299.    loop.  If FALSE is returned, the advise loop will not be started.  
  300.  
  301. -----XCLASS_FLAGS Class:
  302.  
  303.    Transactions in this class have hDmgData set.  An application should 
  304.    use the DLL Data functions to extract the data from hDmgData.  The 
  305.    return value should be the DDE fsStatus flags the app desires to 
  306.    return to the client application.  If DDE_FACK is set, DDE_FBUSY and 
  307.    DDE_FNOTPROCESSED are ignored.  
  308.  
  309.    The only flags the Dde expects to be returned are:
  310.         DDE_FACK
  311.         DDE_FBUSY
  312.         DDE_FNOTPROCESSED
  313.         any DDE_APPSTATUS bits
  314.         
  315.    All other bits will be stripped out by the Dde before sending an 
  316.    ack message.  
  317.  
  318.    A 0 return is equivalent to DDE_NOTPROCESSED.  
  319.  
  320. XTYP_EXEC
  321.  
  322.    hDmgData contains an execute string from a client.  hConv, hszTopic, 
  323.    hszItem are set.  If the WM_DDE_EXECUTE message received had the same 
  324.    string for the itemname as for the data, hszItem will be 0L.  This 
  325.    provides for EXCEL EXECUTE compatibility without requireing the 
  326.    creation of an HSZ for the data string.  Applications are advised to 
  327.    ignore the hszItem parameter for execute transactions since newer DDE 
  328.    specifications ignore this value.  
  329.   
  330. XTYP_POKE
  331.  
  332.    Similar to XTYP_EXEC but hDmgData contains data poked to the server.  
  333.   
  334. XTYP_ADVDATA - advise data for a client!
  335.  
  336.    Note that XTYP_ADVDATA is for advise loop data intended for the 
  337.    CLIENT not the server.  If the advise loop in progress is of the 
  338.    NODATA type, hDmgData will be 0.  
  339.  
  340. -----Agent Transfers:
  341.  
  342.    A DDE agent application is one which registers itself with the 
  343.    DMGCMD_AGENT flag.  Agent applications represent any number of other 
  344.    applications across its communications channel.  Agent applications 
  345.    are only allowed to communicate locally with other non-agent 
  346.    applications.  This prevents communication loops from forming across 
  347.    communication channels.  Any number of agents may register with the 
  348.    DLL but each agent should represent a different communication 
  349.    channel, one which is orthogonal to all other agents.  It is the 
  350.    users responsability to only start up orthogonal agents.  
  351.  
  352.    Agents are responsible for handling and updating any DDE nameservers 
  353.    associated with their communicaton channels.  Since agent 
  354.    applications can converse directly with non-agent applications, they 
  355.    can set up advise loops on the SysTopic/Topics items of local 
  356.    applications to update the nameserver for the communication channel 
  357.    if they wish to support DDE topics.  This may be impossible with some 
  358.    DDE applications which either do not support the SysTopic/Topics item 
  359.    or which have an unenumerable set of topics they support.  For 
  360.    application name servers the DdeAppNameServer() function is 
  361.    provided to give agents a local application name server from which to 
  362.    draw on.  
  363.  
  364.    In general, an agent administers two classes of conversations.  One 
  365.    is direct conversations with itself and local non-agent applications.  
  366.    These transactions would be handled by the agent exactly like any 
  367.    non-agent application would handle them.  
  368.  
  369.    The other class is representative conversations which the agent 
  370.    passes over its communication channel.  In general, representative 
  371.    conversation callbacks to the agent are only XTYP_PKT or XTYP_RTNPKT 
  372.    type transactions or specially handled initiate transactions.  
  373.  
  374.    Agent applications are responsible for providing a unique ULONG agent 
  375.    handle for every agent it is communicating with on its communication 
  376.    channel.  The handle is provided by the agent whenever it calls 
  377.    DdeProcessPkt().  Agent handles need not be global to the channel 
  378.    since only the agent that created the handle will be expected to use 
  379.    it.  Agent handles should not change over the life of a conversation.
  380.  
  381.    Should it be necessary to allow agents to alter or convert packet 
  382.    data, the format of the packets can be documented later.  
  383.  
  384.    To show how an agent would handle its callback function, the 
  385.    following pseudo code is offered as a model: 
  386.  
  387. ReceivePkt(pBits, cb, hAgentFrom)    \\ gets called when a packet arrives 
  388. {
  389.     hPkt = DdePutData(pBits, cb, 0, 0, 0, 0);
  390.     if (hDmgData = ProcessPkt(hPkt, hAgentFrom))
  391.         PassPkt(hDmgData, hAgentFrom);  \\ agent function to send pkt 
  392. }
  393.     
  394. AgentCallback(hConv, hszTopic, hszItem, usFormat, usType, hDmgData)
  395. {
  396.     switch (usType) {
  397.     case XTYP_INIT:
  398.     case XTYP_WILDINIT:
  399.         \\
  400.         \\ process representative initiates
  401.         \\
  402.         QueryInterestedAgents(hszApp, hszTopic, pAgents);
  403.         hDmgData = DdeCreateInitPkt(hszTopic, hszItem, hDmgData);
  404.         BroadcastPkt(hDmgData, pAgents);
  405.         \\
  406.         \\ agent blocks here till all are in or timeout.
  407.         \\ Packets get sent to ReceivePkt()
  408.         \\
  409.         CollectRtnPkts(pAgents); 
  410.         \\  
  411.         \\ now agent does his own processing of local inits.
  412.         \\ retval == 0 if not interested.
  413.         \\
  414.         return(retval);
  415.         break;
  416.  
  417.     case XTYP_RTNPKT:
  418.         RemoveFromDeadCheckQ(hConv);
  419.         PassPkt(hDmgData, hszItem); \\ hDmgData==Pkt, hszItem==hAgentTo 
  420.         return(0);
  421.         break;
  422.         
  423.     case XTYP_PKT:
  424.         if (FindIgnoreList(hConv) { \\ was this unblocked due to no rtn pkt? 
  425.             RemoveFromIgnoreList(hConv);
  426.             return(0);  \\ rtn pkt failure.
  427.         }
  428.         if (!PassPkt(hDmgData, hszItem))  \\ hDmgData==Pkt, hszItem==hAgentTo 
  429.             return(0);  \\ packet send failure. 
  430.         AddToDeadCheckQ(hConv, timenow());
  431.         return(CBR_BLOCK);  \\ will be unblocked and handled by ProcessPkt() 
  432.         break;
  433.  
  434.     case XTYP_REGISTER:
  435.     case XTYP_UNREGISTER:
  436.         \\
  437.         \\ agent updates its communications name server.
  438.         \\
  439.         return(0);
  440.         break;
  441.  
  442.     default:
  443.         \\
  444.         \\ the rest would reference local conversatoins that the agent
  445.         \\ is maintaining.
  446.         \\
  447.         break;
  448.     }
  449. }
  450.  
  451. UnblockDeadTransaction(hConv) \\ called when no rtn pkt for hConv has been
  452.                               \\ received for a long time. 
  453. {
  454.     RemoveFromDeadCheckQ(hConv);
  455.     AddToIgnoreList(hConv);
  456.     DdeEnableCallback(CBK_ENABLE, hConv);
  457. }
  458.  
  459. \* PUBDOC END */
  460.  
  461.  
  462. /***************************** Public  Function ****************************\
  463. * PUBDOC START
  464. * USHORT EXPENTRY DdeInitialize(pfnCallback, afCmd, ulRes)
  465. * PFNCALLBACK pfnCallback;  // address to application callback function
  466. * ULONG afCmd;              // registration command flags
  467. * ULONG ulRes;              // currently reserved, must be 0L.
  468. *     This API is used to initialize the DDEML for an application thread.
  469. *     afCmd - is a set of DMGCMD_ flags for special initialization instructions.
  470. *     DMGCMD_AGENT
  471. *         The registering application represents more than one DDE application.
  472. *         Agents are never allowed to establish a conversation with
  473. *         another agent.  See Agent Transactions.
  474. *     DMGCMD_MONITOR
  475. *
  476. *         This defines the registered application as a DDE transaction 
  477. *         monitor.  This is primarily used for debugging DDE 
  478. *         applications.  A monitoring application will have its Callback 
  479. *         function called every time a DDE message is sent.
  480. *         This flag is exclusive of all others.  No other flags should be
  481. *         or'ed in with this one.
  482. *    DMGCMD_CLIENTONLY
  483. *        This should be specified when the application only intends to
  484. *        be a DDE client.  This reduces the resource consumption of the DLL.
  485. *        
  486. *     Registration is on a per-thread basis.  Thus a multi-threaded application
  487. *     could register several threads as seperate DDE entities.  
  488. *     returns any applicable DMGERR_ error code or 0 on success.
  489. *
  490. *     Most other DLL APIs will fail if the calling thread has not called this.
  491. * PUBDOC END
  492. * Registration causes the following windows to be created:
  493. * HWND_OBJECT
  494. *   hwndDmg(s)
  495. *       hwndClient(s)
  496. *   hwndTopicServer(s)
  497. *       hwndServer(s)
  498. *   hwndMonitor(s)
  499. * HWND_DESKTOP
  500. *   hwndFrame(s)
  501. *   See api.doc file for usage info.
  502. * History:
  503. *   Created     12/14/88    Sanfords
  504. \***************************************************************************/
  505. USHORT EXPENTRY DdeInitialize(pfnCallback, afCmd, ulRes)
  506. PFNCALLBACK pfnCallback;
  507. ULONG afCmd;
  508. ULONG ulRes; 
  509. {
  510.     if (ulRes != 0L || CheckSel(SELECTOROF(pfnCallback)) == 0)
  511.         return(DMGERR_INVALIDPARAMETER);
  512.  
  513.     return(Register(pfnCallback, afCmd, ulRes, FALSE));
  514. }
  515.  
  516.  
  517.  
  518. USHORT EXPENTRY Register(pfnCallback, afCmd, ulRes, f32bit)
  519. PFNCALLBACK pfnCallback;
  520. ULONG afCmd;
  521. ULONG ulRes;
  522. BOOL f32bit;    /* set if calling app is a 32bit app. */
  523. {
  524.     BOOL        fInit;
  525.     PAPPINFO    pai = 0L, paiT;
  526.     PIDINFO     pidInfo;
  527.     USHORT      usRet = DMGERR_PMWIN_ERROR;
  528.     ULONG       ctlFlags;
  529.     CLASSINFO   ci;
  530.     USHORT      cb;
  531.  
  532.     UNUSED ulRes;
  533.  
  534.     SemEnter();
  535.     if (fInit = (hheapDmg == 0L)) {
  536.         /*
  537.          * First time only
  538.          */
  539.         syscc.codepage = syscc.country = 0;
  540.         DosGetCtryInfo(sizeof(COUNTRYCODE), &syscc, (PCOUNTRYINFO)&syscc, &cb);
  541.         if (DosGetHugeShift(&usHugeShift))
  542.             goto Abort;
  543.         usHugeAdd = (1 << usHugeShift) - 1;
  544.         if (!(hheapDmg = MyCreateHeap(0, 4096, 0, 0, 0, HEAPFLAGS)))
  545.             goto Abort;
  546.         if (!WinQueryClassInfo(DMGHAB, WC_FRAME, &ci))
  547.             goto Abort;
  548.         lpfnFrameWndProc = ci.pfnWindowProc;
  549.         if (!AddAtomTable(TRUE)) 
  550.             goto Abort;
  551.     } else {
  552.         
  553.         if ((pai = GetCurrentAppInfo(FALSE)) != NULL) {
  554.             /*
  555.              * re-registration
  556.              */
  557.             return(DMGERR_DLL_USAGE);
  558.         }
  559.         
  560.         /*
  561.          * share the main heap with this process.
  562.          */
  563.         if (DosGetSeg(SELECTOROF(hheapDmg))) {
  564.             SemLeave();
  565.             return(DMGERR_PMWIN_ERROR);
  566.         }
  567.     }
  568.  
  569.         
  570.     if (DosGetPID(&pidInfo))
  571.         goto Abort;
  572.  
  573.     if (!(pai = (PAPPINFO)FarAllocMem(hheapDmg, sizeof(APPINFO))))
  574.         goto Abort;
  575.         
  576.     if (!(pai->hheapApp = MyCreateHeap(0, 4096, 0, 0, 0, HEAPFLAGS))) {
  577.         FarFreeMem(hheapDmg, pai, sizeof(APPINFO));
  578.         pai = 0L;
  579.         goto Abort;
  580.     }
  581.  
  582.     pai->pAppNamePile = NULL;   /* responds to nothing */
  583.     pai->pSvrTopicList = CreateLst(pai->hheapApp, sizeof(HWNDHSZLI));
  584.     pai->pHDataPile = CreatePile(pai->hheapApp, sizeof(HDMGDATA), 8);
  585.     pai->afCmd = (USHORT)afCmd | (f32bit ? DMGCMD_32BIT : 0);
  586.     pai->hwndDmg =
  587.     pai->hwndFrame =
  588.     pai->hwndMonitor =
  589.     pai->hwndTimer = 0;
  590.     pai->pid = pidInfo.pid;
  591.     pai->tid = pidInfo.tid;
  592.     pai->pfnCallback = pfnCallback;
  593.     pai->cInCallback = 0;
  594.     pai->LastError = DMGERR_NO_ERROR;
  595.     pai->fEnableCB = TRUE;
  596.     pai->plstCB = CreateLst(pai->hheapApp, sizeof(CBLI));
  597.     pai->plstCBExceptions = NULL;
  598.  
  599.     /*
  600.      * make nextThread link.
  601.      */
  602.     paiT = pAppInfoList;
  603.     while (paiT && paiT->pid != pai->pid) {
  604.         paiT = paiT->next;
  605.     }
  606.     pai->nextThread = paiT; /* paiT is NULL or of the same process */
  607.     
  608.     if (paiT) {
  609.         while (paiT->nextThread->tid != pai->nextThread->tid) {
  610.             paiT = paiT->nextThread;
  611.         }
  612.         paiT->nextThread = pai;
  613.     } else {
  614.         /*
  615.          * We must reregister each class for each process that invokes this
  616.          * DLL because we can't register public classes unless we are the
  617.          * shell.
  618.          * Since pai->nextThread is NULL, this is a new process.
  619.          */
  620.         WinRegisterClass(0, SZCLIENTCLASS, ClientWndProc, 0L, 4);
  621.         WinRegisterClass(0, SZSERVERCLASS, ServerWndProc, 0L, 4);
  622.         WinRegisterClass(0, SZDMGCLASS, DmgWndProc, 0L, 4);
  623.         WinRegisterClass(0, SZDEFCLASS, WinDefWindowProc, 0L, 4);
  624.     }
  625.     
  626.     pai->next = pAppInfoList;
  627.     pAppInfoList = pai;
  628.     
  629.     if ((pai->hwndDmg = WinCreateWindow(HWND_OBJECT, SZDMGCLASS, "", 0L,
  630.             0, 0, 0, 0, (HWND)NULL, HWND_BOTTOM, WID_APPROOT, 0L, 0L)) == 0L) {
  631.         goto Abort;
  632.     }
  633.  
  634.     if (pai->afCmd & DMGCMD_MONITOR) {
  635.         WinRegisterClass(0, SZMONITORCLASS, MonitorWndProc, 0L, 4);
  636.         if ((pai->hwndMonitor = WinCreateWindow(HWND_OBJECT, SZMONITORCLASS, NULL,
  637.                 0L, 0, 0, 0, 0, (HWND)NULL, HWND_BOTTOM, WID_MONITOR, 0L, 0L))
  638.                 == 0L) {
  639.             goto Abort;
  640.         }
  641.         if (++cMonitor) {
  642.             WinSetHook(DMGHAB, NULL, HK_INPUT, (PFN)DdePostHookProc, hmodDmg);
  643.             WinSetHook(DMGHAB, NULL, HK_SENDMSG, (PFN)DdeSendHookProc, hmodDmg);
  644.         }
  645.     }
  646.  
  647.     /*
  648.      * create an invisible top-level frame for initiates. (if server ok)
  649.      */
  650.     usRet = DMGERR_PMWIN_ERROR;
  651.     if (!(afCmd & DMGCMD_CLIENTONLY)) {
  652.         ctlFlags = 0;
  653.         if ((pai->hwndFrame = WinCreateStdWindow(HWND_DESKTOP, 0L, &ctlFlags,
  654.                 (PSZ)NULL, "", 0L, (HMODULE)NULL, 0, (PHWND)NULL)) == (HWND)NULL)
  655.             goto Abort;
  656.         WinSubclassWindow(pai->hwndFrame, subframeWndProc);
  657.     }
  658.  
  659.     DosExitList(EXLST_ADD, (PFNEXITLIST)ExlstAbort);
  660.  
  661.     SemLeave();
  662.  
  663.     return(DMGERR_NO_ERROR);
  664.  
  665. Abort:
  666.     SemLeave();
  667.  
  668.     if (pai)
  669.         DdeUninitialize();
  670.     else if (fInit && hheapDmg)
  671.         hheapDmg = MyDestroyHeap(hheapDmg);
  672.     return(usRet);
  673. }
  674.  
  675.  
  676. /***************************** Public  Function ****************************\
  677. * PUBDOC START
  678. * BOOL EXPENTRY DdeUninitialize(void);
  679. *     This uninitializes an application thread from the DDEML.
  680. *     All DLL resources associated with the application are destroyed.
  681. *     Most other APIs will fail if called after this API by the same thread.
  682. *
  683. * PUBDOC END
  684. *
  685. * History:
  686. *   Created     12/14/88    Sanfords
  687. \***************************************************************************/
  688. BOOL EXPENTRY DdeUninitialize()
  689. {
  690.     PAPPINFO pai, paiT;
  691.     PMYDDES pmyddes;
  692.     PIDINFO pi;
  693.  
  694.     if ((pai = GetCurrentAppInfo(TRUE)) == NULL)
  695.         return(FALSE);
  696.  
  697.     DosExitList(EXLST_REMOVE, (PFNEXITLIST)ExlstAbort);
  698.     
  699.     /*
  700.      * get us out of the semaphore!
  701.      * !!! NOTE: semaphore tid id -1 during exitlist processing!
  702.      */
  703.     DosGetPID(&pi);
  704.     if (FSRSemDmg.cUsage > 0 && FSRSemDmg.pid == pi.pid &&
  705.             (FSRSemDmg.tid == pi.tid || FSRSemDmg.tid == -1)) {
  706.         while (FSRSemDmg.cUsage) {
  707.             SemLeave();
  708.         }
  709.     }
  710.     
  711.     if (pai->hwndTimer)
  712.         WinSendMsg(pai->hwndTimer, WM_TIMER, MPFROMSHORT(TID_ABORT), 0L);
  713.  
  714.     if (pai->hwndMonitor) {
  715.         DestroyWindow(pai->hwndMonitor);
  716.         if (!--cMonitor) {
  717.             WinReleaseHook(DMGHAB, NULL, HK_INPUT, (PFN)DdePostHookProc, hmodDmg);
  718.             WinReleaseHook(DMGHAB, NULL, HK_SENDMSG, (PFN)DdeSendHookProc, hmodDmg);
  719.         }
  720.     }
  721.  
  722.     /*
  723.      * inform others of DeRegistration
  724.      */
  725.     if (pai->pAppNamePile != NULL) 
  726.         DdeAppNameServer(NULL, ANS_UNREGISTER);
  727.         
  728.     UnlinkAppInfo(pai);
  729.  
  730.     DestroyWindow(pai->hwndDmg);
  731.     DestroyWindow(pai->hwndFrame);
  732.     DestroyHwndHszList(pai->pSvrTopicList);
  733.     while (PopPileSubitem(pai->pHDataPile, (PBYTE)&pmyddes)) {
  734.         if (CheckSel(SELECTOROF(pmyddes)) > sizeof(MYDDES) &&
  735.                 pmyddes->magic == MYDDESMAGIC &&
  736.                 pmyddes->pai == pai) {
  737.             pmyddes->fs &= ~HDATA_APPOWNED;
  738.         }
  739.         FreeData(pmyddes, pai);
  740.     }
  741.     DestroyPile(pai->pHDataPile);
  742.     if (pai->nextThread) {
  743.         paiT = pai;
  744.         while (paiT->nextThread != pai) {
  745.             paiT = paiT->nextThread;
  746.         } 
  747.         paiT->nextThread = pai->nextThread;
  748.         if (paiT->nextThread == paiT) {
  749.             paiT->nextThread = NULL;
  750.         }
  751.     }
  752.     MyDestroyHeap(pai->hheapApp);
  753.     
  754.     DestroyPile(pai->pAppNamePile);
  755.     FarFreeMem(hheapDmg, (PBYTE)pai, sizeof(APPINFO));
  756.  
  757.     if (pAppInfoList == NULL) {     /* last guy out? - turn the lights out. */
  758.         while (cAtbls--)
  759.             WinDestroyAtomTable(aAtbls[cAtbls]);
  760.         hheapDmg = MyDestroyHeap(hheapDmg);
  761.     } else 
  762.         DosFreeSeg(SELECTOROF(hheapDmg));
  763.  
  764.     SemCheckOut();
  765.  
  766.     return(TRUE);
  767. }
  768.  
  769.  
  770.  
  771. /***************************** Public  Function ****************************\
  772. * PUBDOC START
  773. * HCONVLIST EXPENTRY DdeBeginEnumServers(
  774. * HSZ hszAppName,    // app name to connect to, NULL is wild.                   
  775. * HSZ hszTopic,      // topic name to connect to, NULL is wild.                 
  776. * HCONV hConvList,   // previous hConvList for reenumeration, NULL for initial. 
  777. * PCONVCONTEXT pCC,  // language info or NULL for system default.               
  778. * HAPP hApp);        // target application handle or NULL for broadcast init.   
  779. *
  780. *   hszAppName - the DDE application name to connect to - may be 0 for wild.
  781. *   hszTopic - the DDE topic name to connect to - may be 0 for wild.
  782. *   hConvList - The conversation list handle to use for reenumeration.
  783. *       If this is 0, a new hConvList is created.
  784. *   pCC - pointer to CONVCONTEXT structure which provides conversation
  785. *       information needed for international support.  All DDEFMT_TEXT
  786. *       strings within any conversation started by this call should use
  787. *       the codepage referenced in this structure.
  788. *       If NULL is given, the current system values are used.
  789. *   hApp - if not NULL, this directs initiates to only be sent to hApp.
  790. *
  791. *       This routine connects all available conversations on the given 
  792. *       app/topic pair.  Hsz values of 0 indicate wild names.  On reenumeration
  793. *       old hConv's are kept and any new ones created are added to the
  794. *       list.  Duplicate connections are avoided where possible.  A
  795. *       duplicate connection is one which is to the same process and
  796. *       thread on the same application/topic names.  If hApp is provided,
  797. *       initiates are given only to that application.
  798. *       Reenumeration is primarily intended as a response
  799. *       to registration of a new app name to the system.  Reenumeration
  800. *       also removes any terminated conversations from the list.
  801. *
  802. *   returns NULL on failure, hConvList on success.
  803. *
  804. * PUBDOC END
  805. *
  806. * History:
  807. *   Created     12/14/88    Sanfords
  808. \***************************************************************************/
  809. HCONVLIST EXPENTRY DdeBeginEnumServers(hszAppName, hszTopic, hConvList,
  810.         pCC, hApp)
  811. HSZ hszAppName;     
  812. HSZ hszTopic;       
  813. HWND hConvList;     
  814. PCONVCONTEXT pCC;   
  815. HAPP hApp;          
  816. {
  817.     PAPPINFO            pai;
  818.     HCONV               hConv, hConvNext, hConvNew;
  819.     HCONVLIST           hConvListNew;
  820.     PCLIENTINFO         pciOld, pciNew;
  821.     PID                 pidOld, pidNew;
  822.     TID                 tidOld, tidNew;
  823.  
  824.     if ((pai = GetCurrentAppInfo(TRUE)) == 0)
  825.         return(0);
  826.  
  827.     /*
  828.      * destroy any dead old clients
  829.      */
  830.     if (hConvList) {
  831.         hConv = WinQueryWindow(hConvList, QW_TOP, FALSE);
  832.         while (hConv != NULL) {
  833.             hConvNext = WinQueryWindow(hConv, QW_NEXT, FALSE);
  834.             if (!((USHORT)WinSendMsg(hConv, UM_QUERY, MPFROMSHORT(Q_STATUS), 0L) &
  835.                     ST_CONNECTED))
  836.                 WinDestroyWindow(hConv);
  837.             hConv = hConvNext;
  838.         }
  839.     }
  840.     
  841.     if ((hConvListNew = WinCreateWindow(pai->hwndDmg, SZDEFCLASS, "", 0L,
  842.             0, 0, 0, 0, (HWND)NULL, HWND_BOTTOM, WID_CLROOT, 0L, 0L)) == NULL) {
  843.         pai->LastError = DMGERR_PMWIN_ERROR;
  844.         return(0L);
  845.     }
  846.     
  847.     hConvNew = GetDDEClientWindow(hConvListNew, (HWND)hApp, NULL, hszAppName,
  848.             hszTopic, pCC);
  849.  
  850.     /*
  851.      * If no new hConvs created, quit now.
  852.      */
  853.     if (hConvNew == NULL) {
  854.         if (hConvList && WinQueryWindow(hConvList, QW_TOP, FALSE) == NULL) {
  855.             DestroyWindow(hConvList);
  856.             hConvList = NULL;
  857.         }
  858.         if (hConvList == NULL)
  859.             pai->LastError = DMGERR_NO_CONV_ESTABLISHED;
  860.         return(hConvList);
  861.     }
  862.  
  863.     /*
  864.      * remove any new ones that duplicate old existing ones
  865.      */
  866.     if (hConvList && (hConv = WinQueryWindow(hConvList, QW_TOP, FALSE))) {
  867.         while (hConv) {
  868.             hConvNext = WinQueryWindow(hConv, QW_NEXT, FALSE);
  869.             pciOld = (PCLIENTINFO)WinQueryWindowULong(hConv, QWL_USER);
  870.             if (!WinIsWindow(DMGHAB, pciOld->ci.hwndPartner)) {
  871.                 WinDestroyWindow(hConv);
  872.                 hConv = hConvNext;
  873.                 continue;
  874.             }
  875.             WinQueryWindowProcess(pciOld->ci.hwndPartner, &pidOld, &tidOld);
  876.             /*
  877.              * destroy any new clients that are duplicates of the old ones.
  878.              */
  879.             hConvNew = WinQueryWindow(hConvListNew, QW_TOP, FALSE);
  880.             while (hConvNew) {
  881.                 hConvNext = WinQueryWindow(hConvNew, QW_NEXT, FALSE);
  882.                 pciNew = (PCLIENTINFO)WinQueryWindowULong(hConvNew, QWL_USER);
  883.                 WinQueryWindowProcess(pciNew->ci.hwndPartner, &pidNew, &tidNew);
  884.                 if (pciOld->ci.hszServerApp == pciNew->ci.hszServerApp &&
  885.                         pciOld->ci.hszTopic == pciNew->ci.hszTopic &&
  886.                         pidOld == pidNew &&
  887.                         tidOld == tidNew) {
  888.                     /*
  889.                      * assume same app, same topic, same process, same thread
  890.                      * is a duplicate.
  891.                      */
  892.                     WinDestroyWindow(hConvNew);
  893.                 }
  894.                 hConvNew = hConvNext;
  895.             }
  896.             /*
  897.              * move the unique old client to the new list
  898.              */
  899.             WinSetParent(hConv, hConvListNew, FALSE);
  900.             hConv = hConvNext;
  901.         }
  902.         WinDestroyWindow(hConvList);
  903.     }
  904.     
  905.     /*
  906.      * If none are left, fail because no conversations were established.
  907.      */
  908.     if (WinQueryWindow(hConvListNew, QW_TOP, FALSE) == NULL) {
  909.         DestroyWindow(hConvListNew);
  910.         pai->LastError = DMGERR_NO_CONV_ESTABLISHED;
  911.         return(NULL);
  912.     } else {
  913.         return(hConvListNew);
  914.     }
  915. }
  916.  
  917.  
  918.  
  919. /***************************** Public  Function ****************************\
  920. * PUBDOC START
  921. * HCONV EXPENTRY DdeGetNextServer(
  922. * HCONVLIST hConvList,  // conversation list being traversed
  923. * HCONV hConvPrev)      // previous conversation extracted or NULL for first
  924. *
  925. * hConvList - handle of conversation list returned by DdeBeginEnumServers().
  926. * hConvPrev - previous hConv returned by this API or 0 to start from the top
  927. *   of hConvList.
  928. *
  929. * This API returns the next conversation handle associated with hConvList.
  930. * A 0 is returned if hConvPrev was the last conversation or if hConvList
  931. * has no active conversations within it.
  932. *
  933. * PUBDOC END
  934. *
  935. * History:
  936. *   Created     12/14/88    Sanfords
  937. \***************************************************************************/
  938. HCONV EXPENTRY DdeGetNextServer(hConvList, hConvPrev)
  939. HCONVLIST hConvList;
  940. HCONV hConvPrev;
  941. {
  942.     if (!WinIsWindow(DMGHAB, hConvList))
  943.         return(NULL);
  944.     if (hConvPrev == NULL)
  945.         return(WinQueryWindow(hConvList, QW_TOP, FALSE));
  946.     else
  947.         return(WinQueryWindow(hConvPrev, QW_NEXT, FALSE));
  948. }
  949.  
  950.  
  951.  
  952.  
  953. /***************************** Public  Function ****************************\
  954. * PUBDOC START
  955. * BOOL EXPENTRY DdeEndEnumServers(hConvList)
  956. * HCONVLIST hConvList;  // conversation list to destroy.
  957. *
  958. * hConvList - a conversation list handle returned by DdeBeginEnumServers().
  959. *
  960. * This API destroys hConvList and terminates all conversations associated
  961. * with it.  If an application wishes to save selected conversations within
  962. * hConvList, it should call DdeDisconnect() on all hConv's it does not
  963. * want to use and not call this API.
  964. *
  965. * PUBDOC END
  966. * History:
  967. *   Created     12/14/88    Sanfords
  968. \***************************************************************************/
  969. BOOL EXPENTRY DdeEndEnumServers(hConvList)
  970. HCONVLIST hConvList;
  971. {
  972.     PAPPINFO pai;
  973.     HCONV hConv, hConvNext;
  974.  
  975.     if ((pai = GetCurrentAppInfo(TRUE)) == NULL)
  976.         return(FALSE);
  977.     if (WinIsWindow(DMGHAB, hConvList)) {
  978.         hConv = WinQueryWindow(hConvList, QW_TOP, FALSE);
  979.         while (hConv != NULL) {
  980.             hConvNext = WinQueryWindow(hConv, QW_NEXT, FALSE);
  981.             DestroyWindow(hConv);
  982.             hConv = hConvNext;
  983.         }
  984.         DestroyWindow(hConvList);
  985.     }
  986.     return(TRUE);
  987. }
  988.  
  989.  
  990.  
  991. /***************************** Public  Function ****************************\
  992. * PUBDOC START
  993. * HCONV EXPENTRY DdeConnect(hszAppName, hszTopic, pCC, hApp)
  994. * HSZ hszAppName;   // app name to connect to, NULL is wild.
  995. * HSZ hszTopic;     // topic name to connect to, NULL is wild.
  996. * PCONVCONTEXT pCC; // language information or NULL for sys default.
  997. * HAPP hApp;        // target application or NULL for broadcast.
  998. *
  999. * hszAppName - DDE application name to connect to.
  1000. * hszTopic - DDE Topic name to connect to.
  1001. * pCC - CONVCONTEXT information pertinant to this conversation.
  1002. *       If NULL, the current system information is used.
  1003. * hApp - if not NULL, directs connection to a specific app.
  1004. *
  1005. * returns - the conversation handle of the connected conversation or 0 on error.
  1006. *
  1007. * This function allows the simpler aproach of allowing a client to
  1008. * talk to the first server it finds on a topic.  It is most efficient when
  1009. * an hApp is provided.
  1010. *
  1011. * PUBDOC END
  1012. *
  1013. * History:
  1014. *   Created     12/16/88    Sanfords
  1015. \***************************************************************************/
  1016. HCONV EXPENTRY DdeConnect(hszAppName, hszTopic, pCC, hApp)
  1017. HSZ hszAppName;
  1018. HSZ hszTopic;
  1019. PCONVCONTEXT pCC;
  1020. HAPP hApp;
  1021. {
  1022.     PAPPINFO pai;
  1023.     HCONV hConv;
  1024.  
  1025.     if ((pai = GetCurrentAppInfo(TRUE)) == NULL)
  1026.         return(0);
  1027.         
  1028.     hConv = GetDDEClientWindow(pai->hwndDmg, NULL, (HWND)hApp, hszAppName,
  1029.             hszTopic, pCC);
  1030.             
  1031.     if (hConv == 0)
  1032.         pai->LastError = DMGERR_NO_CONV_ESTABLISHED;
  1033.         
  1034.     return(hConv);
  1035. }
  1036.  
  1037.  
  1038.  
  1039. /***************************** Public  Function ****************************\
  1040. * PUBDOC START
  1041. * BOOL EXPENTRY DdeDisconnect(hConv)
  1042. * hConv; // conversation handle of conversation to terminate.
  1043. *
  1044. * This API terminates a conversation started by either DdeConnect() or
  1045. * DdeBeginEnumServers().  hConv becomes invalid after this call.
  1046. *
  1047. * If hConv is a server conversation, any transactions for that conversation
  1048. * found on the server callback queue will be deleted prior to terminating
  1049. * the conversation.
  1050. *
  1051. * If hConv is a client conversation, any transactions on the Client Queue
  1052. * are purged before termination.
  1053. *
  1054. * Note that client conversations that are terminated from the server end
  1055. * go into a dormant state but are still available so that DdeQueryConvInfo()
  1056. * can be used to determine why a conversation is not working.
  1057. * Server conversations will destroy themselves if terminated from a client.
  1058. *
  1059. * returns fSuccess
  1060. *
  1061. * PUBDOC END
  1062. * History:
  1063. *   Created     12/16/88    Sanfords
  1064. \***************************************************************************/
  1065. BOOL EXPENTRY DdeDisconnect(hConv)
  1066. HCONV hConv;
  1067. {
  1068.     PAPPINFO pai;
  1069.  
  1070.     if ((pai = GetCurrentAppInfo(TRUE)) == NULL)
  1071.         return(FALSE);
  1072.         
  1073.     if (!WinIsWindow(DMGHAB, hConv)) {
  1074.         pai->LastError = DMGERR_NO_CONV_ESTABLISHED;
  1075.         return(FALSE);
  1076.     }
  1077.     SemCheckOut();
  1078.     return((BOOL)WinSendMsg(hConv, UMCL_TERMINATE, 0L, 0L));
  1079. }
  1080.  
  1081.  
  1082.  
  1083. /***************************** Public  Function ****************************\
  1084. * PUBDOC START
  1085. * BOOL EXPENTRY DdeQueryConvInfo(hConv, pConvInfo, idXfer)
  1086. * HCONV hConv;          // conversation hand to get info on.
  1087. * PCONVINFO pConvInfo;  // structure to hold info.
  1088. * ULONG idXfer;         // transaction ID if async, QID_SYNC if not.
  1089. *
  1090. * hConv - conversation handle of a conversation to query.
  1091. * pConvInfo - pointer to CONVINFO structure.
  1092. * idXfer - Should be a QID_ constant or an ID returned from DdeCheckQueue().
  1093. *       if id is QID_SYNC, then the synchronous conversation state is returned.
  1094. *
  1095. * returns - fSuccess.  The CONVINFO structure is filled in with the
  1096. *     conversation's status on success.
  1097. *
  1098. * Note that a client conversation may have several transactions in progress
  1099. * at the same time.  idXfer is used to choose which transaction to refer to.
  1100. *
  1101. * hConv may be a client or server conversation handle.  Server conversation
  1102. * handles ignore idXfer.
  1103. *
  1104. * PUBDOC END
  1105. *
  1106. * History:
  1107. *   Created     12/14/88    Sanfords
  1108. \***************************************************************************/
  1109. BOOL EXPENTRY DdeQueryConvInfo(hConv, pConvInfo, idXfer)
  1110. HCONV hConv;
  1111. PCONVINFO pConvInfo;
  1112. ULONG idXfer;
  1113. {
  1114.     PCLIENTINFO pci;
  1115.     PAPPINFO pai;
  1116.     PXADATA pxad;
  1117.     PCQDATA pqd;
  1118.     BOOL fClient;
  1119.  
  1120.     if ((pai = GetCurrentAppInfo(FALSE)) == 0)
  1121.         return(FALSE);
  1122.  
  1123.     SemCheckOut();
  1124.  
  1125.     /*
  1126.      * note that pci may actually be a psi if fClient is false.  Since
  1127.      * the common info portions are identical, we don't care except
  1128.      * where data is extracted from non-common portions.
  1129.      */
  1130.     if (!WinIsWindow(DMGHAB, hConv) ||
  1131.             !(pci = (PCLIENTINFO)WinSendMsg(hConv, UM_QUERY, (MPARAM)Q_ALL, 0L)) ||
  1132.             !WinIsWindow(DMGHAB, pci->ci.hwndPartner)) {
  1133.         pai->LastError = DMGERR_NO_CONV_ESTABLISHED;
  1134.         return(FALSE);
  1135.     }
  1136.     
  1137.     fClient = (BOOL)WinSendMsg(hConv, UM_QUERY, (MPARAM)Q_CLIENT, 0L);
  1138.     
  1139.     if (idXfer == QID_SYNC || !fClient) {
  1140.         pxad = &pci->ci.xad;
  1141.     } else {
  1142.         if (pci->pQ != NULL &&
  1143.                 (pqd = (PCQDATA)Findqi(pci->pQ, idXfer))) {
  1144.             pxad = &pqd->xad;
  1145.         } else {
  1146.             pai->LastError = DMGERR_UNFOUND_QUEUE_ID;
  1147.             return(FALSE);
  1148.         }
  1149.     }
  1150.     SemEnter();
  1151.     pConvInfo->cb = sizeof(CONVINFO);
  1152.     pConvInfo->hConvPartner = IsDdeWindow(pci->ci.hwndPartner);
  1153.     pConvInfo->hszAppName = pci->ci.hszServerApp;
  1154.     pConvInfo->hszAppPartner = fClient ? pci->ci.hszServerApp : 0;
  1155.     pConvInfo->hszTopic = pci->ci.hszTopic;
  1156.     pConvInfo->hAgent = pci->ci.hAgent;
  1157.     pConvInfo->hApp = pci->ci.hwndFrame;
  1158.     if (fClient) {
  1159.         pConvInfo->hszItem = pxad->pXferInfo->hszItem;
  1160.         pConvInfo->usFmt = pxad->pXferInfo->usFmt;
  1161.         pConvInfo->usType = pxad->pXferInfo->usType;
  1162.         pConvInfo->usConvst = pxad->state;
  1163.         pConvInfo->LastError = pxad->LastError;
  1164.     } else {
  1165.         pConvInfo->hszItem = NULL;
  1166.         pConvInfo->usFmt = 0;
  1167.         pConvInfo->usType = 0;
  1168.         pConvInfo->usConvst = pci->ci.xad.state;
  1169.         pConvInfo->LastError = pci->ci.pai->LastError;
  1170.     }
  1171.     pConvInfo->usStatus = pci->ci.fs;
  1172.     pConvInfo->fsContext = pci->ci.cc.fsContext;
  1173.     pConvInfo->idCountry = pci->ci.cc.idCountry;
  1174.     pConvInfo->usCodepage = pci->ci.cc.usCodepage;
  1175.     SemLeave();
  1176.     return(TRUE);
  1177. }
  1178.  
  1179.  
  1180.  
  1181. /***************************** Public  Function ****************************\
  1182. * PUBDOC START
  1183. * BOOL EXPENTRY DdePostAdvise(hszTopic, hszItem)
  1184. * HSZ hszTopic;     // topic of changed data, NULL is all topics
  1185. * HSZ hszItem;      // item of changed data, NULL is all items
  1186. *
  1187. * Causes any clients who have advise loops running on the topic/item name
  1188. * specified to receive the apropriate data messages they require.
  1189. *
  1190. * This should be called by a server application anytime data associated with
  1191. * a particular topic/item changes.  This call results in XTYP_ADVREQ
  1192. * callbacks being generated.
  1193. *
  1194. * hszTopic and/or hszItem may be NULL if all topics or items are to be updated.
  1195. * This will result in callbacks for all active advise loops that fit the
  1196. * hszTopic/hszItem pair.
  1197. * The API is intended for SERVERS only!
  1198. *
  1199. * PUBDOC END
  1200. * History:
  1201. *   Created     12/16/88    Sanfords
  1202. \***************************************************************************/
  1203. BOOL EXPENTRY DdePostAdvise(hszTopic, hszItem)
  1204. HSZ hszTopic;
  1205. HSZ hszItem;
  1206. {
  1207.     PAPPINFO pai;
  1208.     HWND hwndTopic;
  1209.     HWND hwndSvr;
  1210.  
  1211.     // LATER - add wild hsz support
  1212.     
  1213.     if (((pai = GetCurrentAppInfo(TRUE)) == 0))
  1214.         return(FALSE);
  1215.         
  1216.     if (pai->afCmd & DMGCMD_CLIENTONLY) {
  1217.         pai->LastError = DMGERR_DLL_USAGE;
  1218.         return(FALSE);
  1219.     }
  1220.         
  1221.     if ((hwndTopic = HwndFromHsz((HSZ)hszTopic, pai->pSvrTopicList)) == 0)
  1222.         return(TRUE);
  1223.  
  1224.     hwndSvr = WinQueryWindow(hwndTopic, QW_TOP, FALSE);
  1225.     while (hwndSvr) {
  1226.         WinPostMsg(hwndSvr, UMSR_POSTADVISE, MPFROMSHORT(hszItem), 0L);
  1227.         hwndSvr = WinQueryWindow(hwndSvr, QW_NEXT, FALSE);
  1228.     }
  1229.  
  1230.     return(TRUE);
  1231. }
  1232.  
  1233.  
  1234. /***************************** Public  Function ****************************\
  1235. * PUBDOC START
  1236. * HDMGDATA EXPENTRY DdeClientXfer(pSrc, cb, hConv, hszItem, usFmt,
  1237. *         usType, ulTimeout, pidXfer)
  1238. * PBYTE pSrc;       // data source, or NULL for non-data cases.
  1239. * ULONG cb;         // data size or 0 for non-data cases.
  1240. * HCONV hConv;      // associated conversation handle
  1241. * HSZ hszItem;      // item of transaction
  1242. * USHORT usFmt;     // format for transaction
  1243. * USHORT usType;    // transaction type code
  1244. * ULONG ulTimeout;  // timeout for synchronous, TIMEOUT_ASSYNC otherwise.
  1245. * PULONG pidXfer;   // OUTPUT: assync transfer id, NULL for no output.
  1246. *
  1247. * This API initiates a transaction from a client to the server connected
  1248. * via the conversation specified by hConv.
  1249. * Currently usType may be:
  1250. *     XTYP_REQUEST
  1251. *     XTYP_POKE
  1252. *     XTYP_EXEC
  1253. *     XTYP_ADVSTART
  1254. *     XTYP_ADVSTART | XTYPF_NODATA
  1255. *     XTYP_ADVSTART | XTYPF_ACKREQ
  1256. *     XTYP_ADVSTART | XTYPF_NODATA | XTYPF_ACKREQ
  1257. *     XTYP_ADVSTOP
  1258. *
  1259. * ulTimeout specifies the maximum time to wait for a response in miliseconds
  1260. * and applies to synchronous transactions only.
  1261. * if ulTimeout is TIMEOUT_ASSYNC, then the transfer is asynchronous and
  1262. * pidXfer may point to where to place the client transaction queue item
  1263. * ID created by this request.
  1264. * pidXfer may be NULL if no ID is desired.
  1265. * If usType is XTYP_REQUEST, synchronous transfers return a valid hDmgData
  1266. * on success which holds the data received from the request.
  1267. *
  1268. * if usType is XTYP_EXEC and hszItem==NULL (wild) and usFmt==DDEFMT_TEXT,
  1269. * the item name will be changed to the same as the text data.
  1270. * This allows for EXCEL and porthole WINDOWS compatability
  1271. * for XTYP_EXEC transactions.  It is suggested that applications always set
  1272. * hszItem to NULL for XTYP_EXEC transactions and that servers ignore the
  1273. * hszItem perameter in their callback for execute transactions.
  1274. *
  1275. * returns hDmgData or ACK DDE flags on Success, 0 on failure.
  1276. *
  1277. * Note: the hDmgData passed in by this call is only valid for the duration
  1278. * of the callback.
  1279. * PUBDOC END
  1280. *
  1281. * History:
  1282. *   Created     12/14/88    Sanfords
  1283. \***************************************************************************/
  1284. HDMGDATA EXPENTRY DdeClientXfer(pSrc, cb, hConv, hszItem, usFmt,
  1285.         usType, ulTimeout, pidXfer)
  1286. PBYTE pSrc;
  1287. ULONG cb;
  1288. HCONV hConv;
  1289. HSZ hszItem;
  1290. USHORT usFmt;
  1291. USHORT usType;
  1292. ULONG ulTimeout;
  1293. PULONG pidXfer;
  1294. {
  1295.     PAPPINFO pai;
  1296.     XFERINFO xi;
  1297.     HDMGDATA hDmgData;
  1298.  
  1299.     if ((pai = GetCurrentAppInfo(TRUE)) == NULL)
  1300.         return(0);
  1301.  
  1302.     if (!WinIsWindow(DMGHAB, hConv)) {
  1303.         pai->LastError = DMGERR_NO_CONV_ESTABLISHED;
  1304.         return(0);
  1305.     }
  1306.     
  1307.     SemCheckOut();
  1308.     
  1309.     switch (usType) {
  1310.     case XTYP_REQUEST:
  1311.     case XTYP_POKE:
  1312.     case XTYP_EXEC:
  1313.     case XTYP_ADVSTART:
  1314.     case XTYP_ADVSTART | XTYPF_NODATA:
  1315.     case XTYP_ADVSTART | XTYPF_ACKREQ:
  1316.     case XTYP_ADVSTART | XTYPF_NODATA | XTYPF_ACKREQ:
  1317.     case XTYP_ADVSTOP:
  1318.         xi.pidXfer = pidXfer;
  1319.         xi.ulTimeout = ulTimeout;
  1320.         xi.usType = usType;
  1321.         xi.usFmt = usFmt;
  1322.         xi.hszItem = hszItem;
  1323.         xi.hConv = hConv;
  1324.         xi.cb = cb;
  1325.         xi.pData = pSrc;        
  1326.         hDmgData = (HDMGDATA)WinSendMsg(hConv, UMCL_XFER, (MPARAM)&xi, 0L);
  1327.         if (ulTimeout == TIMEOUT_ASYNC) {
  1328.             /*
  1329.              * Increment the count of hszItem incase the app frees it on
  1330.              * return.  This will be decremented when the client Queue
  1331.              * entry is removed.
  1332.              */
  1333.             IncHszCount(hszItem);
  1334.         }
  1335.         
  1336.         /*
  1337.          * add the hDmgData to the client's list of handles he needs
  1338.          * to eventually free.
  1339.          */
  1340.         if ((usType & XCLASS_DATA) && (CheckSel(SELECTOROF(hDmgData)))) {
  1341.             AddPileItem(pai->pHDataPile, (PBYTE)&hDmgData, CmpULONG);
  1342.             if (((PMYDDES)hDmgData)->magic == MYDDESMAGIC) {
  1343.                 ((PMYDDES)hDmgData)->fs |= HDATA_READONLY;
  1344.             }
  1345.         }
  1346.         
  1347.         return(hDmgData);
  1348.     }
  1349.     pai->LastError = DMGERR_INVALIDPARAMETER;
  1350.     return(0);
  1351. }
  1352.  
  1353.  
  1354.  
  1355. /***************************** Public  Function ****************************\
  1356. * PUBDOC START
  1357. * USHORT EXPENTRY DdeGetLastError(void)
  1358. *
  1359. * This API returns the most recent error registered by the DDE manager for
  1360. * the current thread.  This should be called anytime a DDE manager API
  1361. * returns in a failed state.
  1362. *
  1363. * returns an error code which corresponds to a DMGERR_ constant found in
  1364. * ddeml.h.  This error code may be passed on to DdePostError() to
  1365. * show the user the reason for the error or to DdeGetErrorString() to convert
  1366. * the error code into an apropriate string.
  1367. *
  1368. * PUBDOC END
  1369. *
  1370. * History:
  1371. *   Created     12/14/88    Sanfords
  1372. \***************************************************************************/
  1373. USHORT EXPENTRY DdeGetLastError(void)
  1374. {
  1375.     PAPPINFO pai;
  1376.     SHORT err = DMGERR_DLL_NOT_INITIALIZED;
  1377.  
  1378.     pai = GetCurrentAppInfo(FALSE);
  1379.  
  1380.     if (pai) {
  1381.         err = pai->LastError;
  1382.         pai->LastError = DMGERR_NO_ERROR;
  1383.     }
  1384.     return(err);
  1385. }
  1386.  
  1387.  
  1388. /***************************** Public  Function ****************************\
  1389. * PUBDOC START
  1390. * void EXPENTRY DdePostError(err)
  1391. * ULONG err;    // error code to post.
  1392. *
  1393. * This API puts up a message box describing the error who's code was
  1394. * passed in.
  1395. *
  1396. * PUBDOC END
  1397. *
  1398. * History:
  1399. *   Created     12/20/88    sanfords
  1400. \***************************************************************************/
  1401. void EXPENTRY DdePostError(err)
  1402. USHORT err;
  1403. {
  1404.     char szError[MAX_ERRSTR + 1];
  1405.  
  1406.     if (err < DMGERR_FIRST || err > DMGERR_LAST)
  1407.         return;
  1408.     WinLoadString(DMGHAB, hmodDmg, err, MAX_ERRSTR + 1, szError);
  1409.     WinMessageBox(HWND_DESKTOP, NULL, szError, SZERRCAPTION, 0,
  1410.             MB_OK | MB_ICONHAND | MB_SYSTEMMODAL);
  1411. }
  1412.  
  1413.  
  1414.  
  1415. /***************************** Public  Function ****************************\
  1416. * PUBDOC START
  1417. * USHORT EXPENTRY DdeGetErrorString(err, cbMax, psz)
  1418. * USHORT err;       // error code to convert
  1419. * USHORT cbMax;     // size of string buffer provided by caller
  1420. * PSZ psz;          // string buffer address.
  1421. *
  1422. * This function fills psz with the error string referenced by err of up
  1423. * to cbMax characters.  All error strings are <= MAX_ERRSTR in length.
  1424. * (not counting the NULL terminator.)
  1425. *
  1426. * returns length of copied string without NULL terminator or 0 on failure.
  1427. *
  1428. * PUBDOC END
  1429. *
  1430. * History:
  1431. *   Created     12/20/88    sanfords
  1432. \***************************************************************************/
  1433. USHORT EXPENTRY DdeGetErrorString(err, cbMax, psz)
  1434. USHORT err;
  1435. USHORT cbMax;
  1436. PSZ psz;
  1437. {
  1438.     if (err < DMGERR_FIRST || err > DMGERR_LAST)
  1439.         return(0);
  1440.     return(WinLoadString(DMGHAB, hmodDmg, err, cbMax, psz));
  1441. }
  1442.  
  1443.  
  1444.  
  1445. /*\
  1446. * HDATA stuff:
  1447. * Each thread has an hData list that contains all the hData's it has
  1448. * been given by the DLL.   This list indicates what hData's an app can
  1449. * and must eventually free.
  1450. * If an app has multiple threads registered, each threads pai's are linked
  1451. * via the nextThread pointer.  The links are circular so the TID
  1452. * should be used to know when all the lists are traversed.
  1453. * Each hData contains the following flags:
  1454. * HDATA_READONLY - set on any hData given to the DLL or created by the DLL.
  1455. *         This prevents AddData from working.
  1456. *         
  1457. * HDATA_APPOWNED - set at creation time by app so app will keep access
  1458. *         until it frees it or unregistration happens.
  1459. *         This prevents the DLL from freeing the hData before an app is
  1460. *         through with it.
  1461. * HDATA_APPFREEABLE - set at creation time if logged into thread list.
  1462. * Each hData also contains the pai of the thread that created the hData.
  1463. *         (set by PutData)  If APPOWNED is set, this identifies the
  1464. *         owner thread as well.
  1465. * General rules for apps:
  1466. *         hDatas from DLL calls are the apps responsibility to free and are
  1467. *                 only valid till passed back into the DLL or freed.
  1468. *         hDatas from callback calls are only valid during the callback.
  1469. *         hDatas created by an app but not APPOWNED are only valid till
  1470. *                 passed into the DLL.
  1471. *         hDatas created by an app that are APPOWNED are valid until
  1472. *                 the creating thread frees it or till that thread unregisters
  1473. *                 itself.
  1474. *         A process will not loose access to an hData untill all threads that
  1475. *                 have received the hData have freed it or passed it back to
  1476. *                 the DLL (via callback)
  1477. *         
  1478. * Register:                                                       DONE
  1479. *         creates hDataList for app/thread.
  1480. *         creates nextThread links if applicable.
  1481. * DdePutData:                                                  DONE
  1482. *         Only allows HDATA_APPOWNED flag
  1483. *         sets HDATA_APPFREEABLE flag
  1484. *         ...Falls into....
  1485. * PutData:                                                        DONE
  1486. *         Allocates selector
  1487. *         sets creator pai
  1488. *         sets HDATA_ flags specified
  1489. *         if (HDATA_APPFREEABLE)
  1490. *             adds to hDataList. 
  1491. * DdeAddData:                                                  DONE
  1492. *         fails if HDATA_READONLY or a non-dll type selector
  1493. * Callback:                                                       DONE
  1494. *         Entry:
  1495. *             for hData to callback:
  1496. *                 Sets HDATA_READONLY if hData valid DLL type selector.
  1497. *                 (does NOT add hData to thread list so he can't free it during
  1498. *                  the callback)
  1499. *                  
  1500. *         Exit:
  1501. *             for hData to callback:
  1502. *                 nothing.
  1503. *             for hData from callback:
  1504. *                 verifies creator == current
  1505. * DdeCreateInitPkt:                                            DONE
  1506. * DdeAppNameServer:                                            NOT COMPLETE
  1507. * DdeClientXfer:                                               DONE
  1508. * DdeCheckQ:                                                   DONE
  1509. *         if (valid selector)
  1510. *             add to thread list
  1511. *             if (valid DLL type selector)
  1512. *                 Set READONLY flag.
  1513. * ProcessPkt:                                                     NOT IMP.
  1514. *         for hData in:
  1515. *              After used, calls FreeData()
  1516. *         for hData out:
  1517. *              before return, adds hData to thread list
  1518. *         
  1519. * MyPostMsg:                                                      DONE
  1520. *         gives if target process != current
  1521. *         if target process is not a DLL process/thread, expand hszItem.
  1522. *         calls FreeData() on current process if MDPM_FREEHDATA is set.
  1523. *         
  1524. * DdeFreeData:                                                 DONE
  1525. *         if in thread list.
  1526. *             remove hData from list
  1527. *             if not in any thread lists for this process
  1528. *                 free data
  1529. *             return pass
  1530. *         else 
  1531. *             return fail
  1532. * FreeData:                                                       DONE
  1533. *         if (DLL type selector && HDATA_APPOWNED && creator == current)
  1534. *             exit
  1535. *         remove hData from thread list if found
  1536. *         if not in any thread lists for this process
  1537. *             free data
  1538. *         
  1539. * UnRegister:                                                     DONE
  1540. *         for each item in the hDataList:
  1541. *             clear HDATA_APPOWNED flag if dll type selector and owned by this
  1542. *                 thread.
  1543. *             FreeData()
  1544. *         destroy list
  1545. *         unlink from other thread lists
  1546. *         
  1547. \*/
  1548.  
  1549.  
  1550. /***************************** Public  Function ****************************\
  1551. * PUBDOC START
  1552. * HDMGDATA EXPENTRY DdePutData(pSrc, cb, cbOff, hszItem, usFmt, afCmd)
  1553. * PBYTE pSrc;   // address of data to place into data handle or NULL
  1554. * ULONG cb;     // amount of data to copy if pSrc is not NULL.
  1555. * ULONG cbOff;  // offset into data handle region to place pSrc data
  1556. * HSZ hszItem;  // item associated with this data
  1557. * USHORT usFmt; // format associated with this data
  1558. * USHORT afCmd; // HDATA_ flags.
  1559. * This api allows an application to create a hDmgData apropriate
  1560. * for return from its call-back function.
  1561. * The passed in data is stored into the hDmgData which is
  1562. * returned on success.  Any portions of the data handle not filled are
  1563. * undefined.  afCmd contains any of the HDATA_ constants described below:
  1564. *
  1565. * HDATA_APPOWNED
  1566. *   This declares the created data handle to be the responsability of
  1567. *   the application to free it.  Application owned data handles may
  1568. *   be given to the DLL multiple times.  This allows a server app to be
  1569. *   able to support many clients without having to recopy the data for
  1570. *   each request.  If this flag is not specified, the data handle becomes
  1571. *   invalid once passed to the DLL via any API or the callback function.
  1572. *
  1573. * NOTES:
  1574. *   If an application expects this data handle to hold >64K of data via
  1575. *   DdeAddData(), it should specify a cb + cbOff to be as large as
  1576. *   the object is expected to get via DdeAddData() calls to avoid
  1577. *   unnecessary data copying or reallocation by the DLL.
  1578. *
  1579. *   if psrc==NULL or cb == 0, no actual data copying takes place.
  1580. *   Data handles given to an application via the DdeClientXfer() or
  1581. *   DdeCheckQueue() functions are the responsability of the client
  1582. *   application to free and MUST NOT be returned from the callback
  1583. *   function as server data!  The DLL will only accept data handles
  1584. *   returned from the callback function that were created by the
  1585. *   called application.
  1586. *
  1587. * PUBDOC END
  1588. *
  1589. * History:
  1590. *   Created     12/14/88    Sanfords
  1591. \***************************************************************************/
  1592. HDMGDATA EXPENTRY DdePutData(pSrc, cb, cbOff, hszItem, usFmt, afCmd)
  1593. PBYTE pSrc;
  1594. ULONG cb;
  1595. ULONG cbOff;
  1596. HSZ hszItem;
  1597. USHORT usFmt;
  1598. USHORT afCmd;
  1599. {
  1600.     PAPPINFO pai;
  1601.  
  1602.     if (!(pai = GetCurrentAppInfo(FALSE)))
  1603.         return(0L);;
  1604.  
  1605.     if (afCmd & ~(HDATA_APPOWNED)) {
  1606.         pai->LastError = DMGERR_INVALIDPARAMETER;
  1607.         return(0L);
  1608.     }
  1609.  
  1610.     return(PutData(pSrc, cb, cbOff, hszItem, usFmt, afCmd | HDATA_APPFREEABLE, pai));
  1611. }
  1612.  
  1613.  
  1614. /***************************** Public  Function ****************************\
  1615. * PUBDOC START
  1616. * HDMGDATA EXPENTRY DdeAddData(hDmgData, pSrc, cb, cbOff)
  1617. * HDMGDATA hDmgData;    // data handle to add data to
  1618. * PBYTE pSrc;           // pointer to data to add (if NULL, no data copying)
  1619. * ULONG cb;             // size of data to add
  1620. * ULONG cbOff;          // offset within hData to place data
  1621. *
  1622. * This routine allows an application to add data to a hDmgData it had
  1623. * previously created using DdePutData().  The handle will be
  1624. * grown as necessary to support the data addition.  Data may be added
  1625. * to a data handle multiple times and in any order of data locations.
  1626. *
  1627. * Once a data handle is given to the DLL via return from the callback
  1628. * function or via a DLL API, it becomes readonly.  Further attempts to
  1629. * add data to the hData by any process will fail.
  1630. *
  1631. * This call will return 0 if it failed to allocate enough memory or if
  1632. * the data handle is readonly.  On success, the returned hDmgData should 
  1633. * replace the given hDmgData.  On failure, the hDmgData given is left
  1634. * in the state it was initially.
  1635. *
  1636. * NOTE: For huge segments, or segments expected to grow to greater than 
  1637. *    64K, it is best if DdePutData() be called with a cbOff + cb as 
  1638. *    large as maximally expected so as not to force reallocation from a 
  1639. *    normal to a huge segment in 16bit applications.  This also avoids 
  1640. *    the need of replaceing the hDmgData with the output each time
  1641. *    incase a new selector was needed to be allocated and copied to.  
  1642. * PUBDOC END
  1643. *
  1644. * History:
  1645. *   Created     9/17/89    Sanfords
  1646. \***************************************************************************/
  1647. HDMGDATA EXPENTRY DdeAddData(hDmgData, pSrc, cb, cbOff)
  1648. HDMGDATA hDmgData;
  1649. PBYTE pSrc;
  1650. ULONG cb;
  1651. ULONG cbOff;
  1652. {
  1653. #define pmyddes ((PMYDDES)hDmgData)
  1654. #define selIn (SELECTOROF(hDmgData))
  1655.     
  1656.     PAPPINFO pai;
  1657.     SEL sel;
  1658.     ULONG cbOffAbs;
  1659.  
  1660.     if (!(pai = GetCurrentAppInfo(FALSE)))
  1661.         return(0L);;
  1662.  
  1663.     if (!CheckSel(selIn) ||
  1664.             pmyddes->offszItemName != sizeof(MYDDES) ||
  1665.             pmyddes->magic != MYDDESMAGIC ||
  1666.             pmyddes->fs & HDATA_READONLY) {
  1667.         pai->LastError = DMGERR_INVALID_HDMGDATA;
  1668.         return(0L);
  1669.     }
  1670.     
  1671.     cbOffAbs = pmyddes->offabData + cbOff;
  1672.     if (pmyddes->cbData + pmyddes->offabData < cb + cbOffAbs) {
  1673.         /*
  1674.          * need to grow...
  1675.          */
  1676.         if (cbOffAbs + cb > 0xFFFFL) {
  1677.             /*
  1678.              * going to be huge...
  1679.              */
  1680.             if ((pmyddes->offabData + pmyddes->cbData < 0xFFFFL) ||
  1681.                     DosReallocHuge(HIUSHORT(cb + cbOffAbs),
  1682.                     LOUSHORT(cb + cbOffAbs), selIn)) {
  1683.                 /*
  1684.                  * Either we can't grow a huge seg or we need to make one.
  1685.                  */
  1686.                 if (DosAllocHuge(HIUSHORT(cb + cbOffAbs),
  1687.                         LOUSHORT(cb + cbOffAbs), &sel, 0, SEG_GIVEABLE)) {
  1688.                     pai->LastError = DMGERR_MEMORY_ERROR;
  1689.                     return(0);
  1690.                 }
  1691.                 CopyHugeBlock(hDmgData, MAKEP(sel, 0), pmyddes->cbData +
  1692.                         sizeof(MYDDES) + 1);
  1693.                 FindPileItem(pai->pHDataPile, CmpULONG, (PBYTE)&hDmgData,
  1694.                         FPI_DELETE);
  1695.                 DosFreeSeg(selIn);
  1696.                 hDmgData = MAKEP(sel, 0);
  1697.                 AddPileItem(pai->pHDataPile, (PBYTE)&hDmgData, NULL);
  1698.             }
  1699.         } else {
  1700.             /*
  1701.              * not going to be huge
  1702.              */
  1703.             if (DosReallocSeg((USHORT)(cb + cbOffAbs), selIn)) { 
  1704.                 pai->LastError = DMGERR_MEMORY_ERROR;
  1705.                 return(0L);
  1706.             }
  1707.         }
  1708.         pmyddes->cbData = cbOff + cb;
  1709.     }
  1710.     if (pSrc)
  1711.         CopyHugeBlock(pSrc, HugeOffset((PBYTE)hDmgData, cbOffAbs), cb);
  1712.     return(hDmgData);
  1713. #undef selIn    
  1714. #undef pmyddes    
  1715. }
  1716.  
  1717.  
  1718. /***************************** Public  Function ****************************\
  1719. * PUBDOC START
  1720. * ULONG EXPENTRY DdeGetData(hDmgData, pDst, cbMax, cbOff)
  1721. * HDMGDATA hDmgData;    // data handle to extract data from
  1722. * PBYTE pDst;           // destination for extracted data
  1723. * ULONG cbMax;          // destination buffer size
  1724. * ULONG cbOff;          // offset into data to start extraction
  1725. *
  1726. * This copies up to cbMax bytes of data contained in the hDmgData data handle
  1727. * at offset cbOff into application memory pointed to by pDst.
  1728. * If pDst == NULL, no copying is performed.
  1729. *
  1730. * returns the size of the data contained in the data handle remaining after
  1731. * cbOff or 0 if hDmgData is invalid or cbOff is too large.
  1732. *
  1733. * This API supports HUGE segments in 16 bit applications.
  1734. *
  1735. * PUBDOC END
  1736. *
  1737. * History:
  1738. *   Created     12/14/88    Sanfords
  1739. \***************************************************************************/
  1740. ULONG EXPENTRY DdeGetData(hDmgData, pDst, cbMax, cbOff)
  1741. HDMGDATA hDmgData;
  1742. PBYTE pDst;
  1743. ULONG cbMax;
  1744. ULONG cbOff;
  1745. {
  1746.     PAPPINFO pai;
  1747.  
  1748.     if ((pai = GetCurrentAppInfo(FALSE)) == NULL)
  1749.         return(0L);
  1750.  
  1751.     if (!CheckSel(SELECTOROF(hDmgData))) {
  1752.         pai->LastError = DMGERR_INVALID_HDMGDATA;
  1753.         return(0L);
  1754.     }
  1755.     if (cbOff >= ((PMYDDES)hDmgData)->cbData) {
  1756.         pai->LastError = DMGERR_INVALIDPARAMETER;
  1757.         return(0L);
  1758.     }
  1759.     cbMax = min(cbMax, ((PMYDDES)hDmgData)->cbData - cbOff);
  1760.     if (pDst == NULL) 
  1761.         return(((PMYDDES)hDmgData)->cbData - cbOff); 
  1762.     CopyHugeBlock(HugeOffset(DDES_PABDATA(hDmgData), cbOff), pDst, cbMax);
  1763.     return(cbMax);
  1764. }
  1765.  
  1766.  
  1767.  
  1768. /***************************** Public  Function ****************************\
  1769. * PUBDOC START
  1770. * PBYTE EXPENTRY DdeAccessData(hDmgData)
  1771. * HDMGDATA hDmgData;    // data handle to access
  1772. *
  1773. * This API returns a pointer to the data referenced by the data handle.
  1774. * The pointer returned becomes invalid once the data handle is freed
  1775. * or given to the DLL via callback return or API call.
  1776. *
  1777. * NOTE: applications MUST take care not to access beyond the limits of
  1778. * the data handle.  Only hDmgData's created by the application via
  1779. * a DdePutData() call may write to this memory prior to passing on
  1780. * to any DLL API or returning from the callback function.  Any hDmgData
  1781. * received from the DLL should be considered shared-readonly data and
  1782. * should be treated as such.  This applies whether the application owns
  1783. * the data handle or not.
  1784. *
  1785. * 0L is returned on error.
  1786. *
  1787. * PUBDOC END
  1788. *
  1789. * History:
  1790. *   Created     8/24/88    Sanfords
  1791. \***************************************************************************/
  1792. PBYTE EXPENTRY DdeAccessData(hDmgData)
  1793. HDMGDATA hDmgData;
  1794. {
  1795.     PAPPINFO pai;
  1796.     
  1797.     if ((pai = GetCurrentAppInfo(FALSE)) == NULL)
  1798.         return(0L);
  1799.     if (CheckSel(SELECTOROF(hDmgData))) {
  1800.         return(DDES_PABDATA(hDmgData));
  1801.     }
  1802.     pai->LastError = DMGERR_ACCESS_DENIED;
  1803.     return(0L);
  1804. }
  1805.  
  1806.  
  1807.  
  1808.  
  1809. /***************************** Public  Function ****************************\
  1810. * PUBDOC START
  1811. * BOOL EXPENTRY DdeFreeData(hDmgData)
  1812. * HDMGDATA hDmgData;        // data handle to destroy
  1813. *
  1814. * This routine should be called by an application wishing to release
  1815. * custody of an hDmgData it owns.
  1816. * An application owns any hDmgData
  1817. * it created with the HDATA_APPOWNED flag or an hDmgData given to
  1818. * it via DdeClientXfer(), DdeCheckQueue(), DdeAppNameServer(),
  1819. * DdeCreateInitPkt() or DdeProcessPkt().
  1820. *
  1821. * Returns fSuccess.  This function will fail if the hDmgData is not
  1822. * owned by the calling app.
  1823. *
  1824. * PUBDOC END
  1825. * History:
  1826. *   Created     12/14/88    Sanfords
  1827. *   6/12/90 sanfords    Fixed to work with non-DLL generated selectors
  1828. \***************************************************************************/
  1829. BOOL EXPENTRY DdeFreeData(hDmgData)
  1830. HDMGDATA hDmgData;
  1831. {
  1832.     PAPPINFO pai;
  1833.     USHORT cbSel;
  1834.     TID tid;
  1835.  
  1836.     if ((pai = GetCurrentAppInfo(FALSE)) == NULL)
  1837.         return(FALSE);
  1838.         
  1839.     cbSel = CheckSel(SELECTOROF(hDmgData));
  1840.     if (!cbSel) {
  1841.         pai->LastError = DMGERR_INVALID_HDMGDATA;
  1842.         return(FALSE);
  1843.     }
  1844.     SemEnter();
  1845.     /*
  1846.      * Apps can only free handles the DLL does not own or handles from external
  1847.      * non-DLL DDE apps.
  1848.      */
  1849.     if (!FindPileItem(pai->pHDataPile, CmpULONG, (PBYTE)&hDmgData, FPI_DELETE)) {
  1850.         pai->LastError = DMGERR_INVALID_HDMGDATA;
  1851.         SemLeave();
  1852.         return(FALSE);
  1853.     }    
  1854.             
  1855.     tid = pai->tid;
  1856.     do {
  1857.         if (FindPileItem(pai->pHDataPile, CmpULONG, (PBYTE)&hDmgData, FPI_COUNT)) {
  1858.             SemLeave();
  1859.             return(TRUE);
  1860.         }
  1861.         pai = pai->nextThread;
  1862.     } while (pai && pai->tid != tid);
  1863.     DosFreeSeg(SELECTOROF(hDmgData));
  1864.         
  1865.     SemLeave();
  1866.     return(TRUE);    
  1867. }
  1868.  
  1869.  
  1870.  
  1871. /***************************** Public  Function ****************************\
  1872. * PUBDOC START
  1873. * BOOL EXPENTRY DdeCopyBlock(pSrc, pDst, cb)
  1874. * PBYTE pSrc;   // source of copy
  1875. * PBYTE pDst;   // destination of copy
  1876. * ULONG cb;     // size in bytes of copy
  1877. *
  1878. * This copy utility can handle HUGE segments and can be used by any
  1879. * application as a copy utility.  This does not support overlapping huge
  1880. * copies.
  1881. *
  1882. * Returns fSuccess.
  1883. *
  1884. * PUBDOC END
  1885. * History:      1/1/89  created         sanfords
  1886. \***************************************************************************/
  1887. BOOL EXPENTRY DdeCopyBlock(pSrc, pDst, cb)
  1888. PBYTE pSrc;
  1889. PBYTE pDst;
  1890. ULONG cb;
  1891. {
  1892.     return(CopyHugeBlock(pSrc, pDst, cb));
  1893. }
  1894.  
  1895.  
  1896.  
  1897.  
  1898. /***************************************************************************\
  1899. * PUBDOC START
  1900. * HSZ management notes:
  1901. *
  1902. *   HSZs are used in this DLL to simplify string handling for applications
  1903. *   and for inter-process communication.  Since many applications use a
  1904. *   fixed set of Application/Topic/Item names, it is convenient to convert
  1905. *   them to HSZs and allow quick comparisons for lookups.  This also frees
  1906. *   the DLL up from having to constantly provide string buffers for copying
  1907. *   strings between itself and its clients.
  1908. *
  1909. *   HSZs are the same as atoms except they have no restrictions on length or
  1910. *   number and are 32 bit values.  They are case preserving and can be
  1911. *   compared directly for case sensitive comparisons or via DdeCmpHsz()
  1912. *   for case insensitive comparisons.
  1913. *
  1914. *   When an application creates an HSZ via DdeGetHsz() or increments its
  1915. *   count via DdeIncHszCount() it is essentially claiming the HSZ for
  1916. *   its own use.  On the other hand, when an application is given an
  1917. *   HSZ from the DLL via a callback, it is using another application's HSZ
  1918. *   and should not free that HSZ via DdeFreeHsz().
  1919. *
  1920. *   The DLL insures that during the callback any HSZs given will remain
  1921. *   valid for the duration of the callback.
  1922. *
  1923. *   If an application wishes to keep that HSZ to use for itself as a
  1924. *   standard for future comparisons, it should increment its count so that,
  1925. *   should the owning application free it, the HSZ will not become invalid.
  1926. *   This also prevents an HSZ from changing its value.  (ie, app A frees it
  1927. *   and then app B creates a new one that happens to use the same HSZ code,
  1928. *   then app C, which had the HSZ stored all along (but forgot to increment
  1929. *   its count) now is holding a handle to a different string.)
  1930. *
  1931. *   Applications may free HSZs they have created or incremented at any time
  1932. *   by calling DdeFreeHsz().
  1933. *
  1934. *   The DLL internally increments HSZ counts while in use so that they will
  1935. *   not be destroyed until both the DLL and all applications concerned are
  1936. *   through with them.
  1937. *
  1938. *   IT IS THE APPLICATIONS RESPONSIBILITY TO PROPERLY CREATE AND FREE HSZs!!
  1939. *
  1940. * PUBDOC END
  1941. \***************************************************************************/
  1942.  
  1943.  
  1944. /***************************** Public  Function ****************************\
  1945. * PUBDOC START
  1946. * HSZ EXPENTRY DdeGetHsz(psz, country, codepage)
  1947. * PSZ psz;          // string to HSZize.
  1948. * USHORT country;   // country ID to use in comparisons.
  1949. * USHORT codepage;  // codepage to use in comparisons.
  1950. *
  1951. * This routine returns a string handle to the psz passed in.
  1952. * 0 is returned on failure or for NULL strings.
  1953. *
  1954. * If country is 0, the default system country code is used.
  1955. * If codepage is 0, the default system codepage code is used.
  1956. *
  1957. * String handles are similar to atoms but without the 255
  1958. * character limit on strings.  String handles are case preserving.
  1959. * see DdeCmpHsz().
  1960. *
  1961. * String handles are consistant across all processes using the DLL.
  1962. *
  1963. * PUBDOC END
  1964. *
  1965. * History:      1/1/89 created         sanfords
  1966. \***************************************************************************/
  1967. HSZ EXPENTRY DdeGetHsz(psz, country, codepage)
  1968. PSZ psz;
  1969. USHORT country, codepage;
  1970. {
  1971.     PAPPINFO pai;
  1972.  
  1973.     if ((pai = GetCurrentAppInfo(FALSE)) == NULL)
  1974.         return(0);
  1975.  
  1976.     return(GetHsz(psz, country, codepage, TRUE));
  1977. }
  1978.  
  1979.  
  1980.  
  1981. /***************************** Public  Function ****************************\
  1982. * PUBDOC START
  1983. * BOOL EXPENTRY DdeFreeHsz(hsz)
  1984. * HSZ hsz;      // string handle to free
  1985. *
  1986. * This function decrements the usage count for the HSZ given and frees
  1987. * it if the count becomes 0.
  1988. *
  1989. * PUBDOC END
  1990. *
  1991. * History:      1/1/89 created         sanfords
  1992. \***************************************************************************/
  1993. BOOL EXPENTRY DdeFreeHsz(hsz)
  1994. HSZ hsz;
  1995. {
  1996.     PAPPINFO pai;
  1997.  
  1998.     if ((pai = GetCurrentAppInfo(FALSE)) == NULL)
  1999.         return(0);
  2000.     return(FreeHsz(hsz));
  2001. }
  2002.  
  2003.  
  2004.  
  2005. /***************************** Public  Function ****************************\
  2006. * PUBDOC START
  2007. * BOOL EXPENTRY DdeIncHszCount(hsz)
  2008. * HSZ hsz;  // string handle to increment.
  2009. *
  2010. * This function increments the usage count for the HSZ given.  This is
  2011. * useful when an application wishes to keep an HSZ given to it in its
  2012. * callback.
  2013. *
  2014. * PUBDOC END
  2015. *
  2016. * History:      1/1/89 created         sanfords
  2017. \***************************************************************************/
  2018. BOOL EXPENTRY DdeIncHszCount(hsz)
  2019. HSZ hsz;
  2020. {
  2021.     PAPPINFO pai;
  2022.  
  2023.     if ((pai = GetCurrentAppInfo(FALSE)) == NULL)
  2024.         return(0);
  2025.  
  2026.     return(IncHszCount(hsz));
  2027. }
  2028.  
  2029.  
  2030.  
  2031. /***************************** Public  Function ****************************\
  2032. * PUBDOC START
  2033. * USHORT EXPENTRY DdeGetHszString(hsz, psz, cchMax)
  2034. * HSZ hsz;      // string handle to extract string from
  2035. * PSZ psz;      // buffer for case-sensitive string
  2036. * ULONG cchMax; // buffer size.
  2037. *
  2038. * This API is the inverse of DdeGetHsz().  The actual length of the
  2039. * string (without NULL terminator) referenced by hsz is returned.
  2040. *
  2041. * if psz is NULL, no string is returned in psz.
  2042. * 0 is returned if hsz does not exist or is wild.
  2043. * If hsz is wild, psz will be set to a 0 length string.
  2044. *
  2045. * PUBDOC END
  2046. * History:      Created 5/10/89         sanfords
  2047. \***************************************************************************/
  2048. USHORT EXPENTRY DdeGetHszString(hsz, psz, cchMax)
  2049. HSZ hsz;
  2050. PSZ psz;
  2051. ULONG cchMax;
  2052. {
  2053.     if (psz) {
  2054.         if (hsz) {
  2055.             return(QueryHszName(hsz, psz, (USHORT)cchMax));
  2056.         } else {
  2057.             *psz = '\0';
  2058.             return(0);
  2059.         }
  2060.     } else if (hsz) {
  2061.         return(QueryHszLength(hsz));
  2062.     } else {
  2063.         return(0);
  2064.     }
  2065. }
  2066.  
  2067.  
  2068. /***************************** Public  Function ****************************\
  2069. * PUBDOC START
  2070. * SHORT EXPENTRY DdeCmpHsz(hsz1, hsz2)
  2071. * HSZ hsz1, hsz2;   // string handles to compare.
  2072. *
  2073. * This routine returns:
  2074. *   0  if hsz1 is of equal  rank to   hsz2
  2075. *   -2 if hsz1 is invalid.
  2076. *   -1 if hsz1 is of lower  rank than hsz2
  2077. *   1  if hsz1 is of higher rank than hsz2.
  2078. *   2  if hsz2 is invalid.
  2079. *
  2080. * Note that direct comparison of hszs (ie (hsz1 == hsz2)) is a case sensitive
  2081. * comparison.  This function performs a case insensitive comparison.  Thus
  2082. * different valued hszs may actually be equal.
  2083. * A ranking is provided for binary searching.
  2084. *
  2085. * PUBDOC END
  2086. \***************************************************************************/
  2087. SHORT EXPENTRY DdeCmpHsz(hsz1, hsz2)
  2088. HSZ hsz1, hsz2;
  2089. {
  2090.     return(CmpHsz(hsz1, hsz2));
  2091. }
  2092.  
  2093.  
  2094. /***************************** Public  Function ****************************\
  2095. * PUBDOC START
  2096. * ULONG EXPENTRY DdeCheckQueue(hConv, phDmgData, idXfer, afCmd)
  2097. * HCONV hConv;          // related convesation handle
  2098. * PHDMGDATA phDmgData;  // OUTPUT: for resultant data handle, or NULL
  2099. * ULONG idXfer;         // transaction ID or QID_NEWEST or QID_OLDEST
  2100. * ULONG afCmd;          // queue operation code.
  2101. *
  2102. *       This routine checks a client conversation's assynchronous 
  2103. *       transaction queue for queued transaction status.  This allows a 
  2104. *       client to extract data or monitor the status of any transaction 
  2105. *       previously started asynchronously.  (TIMEOUT_ASSYNC) hConv is 
  2106. *       the client conversation who's queue is being checked.  
  2107. *
  2108. *       If phDmgData is not NULL and the referenced item has data to 
  2109. *       return to the client, phDmgData is filled.  Returned hDmgDatas 
  2110. *       must be freed by the application.  phDmgData will be filled with 
  2111. *       0L if no return data is applicable or if the transaction is not 
  2112. *       complete.  
  2113. *
  2114. *       If the queue is not periodicly flushed by an application 
  2115. *       issueing asynchronous transactions the queue is automaticly
  2116. *       flushed as needed.  Oldest transactions are flushed first.  
  2117. *       DdeProcessPkt() and DdeDisconnect() and DdeEndEnumServers
  2118. *       remove items from this queue.  
  2119. *
  2120. *       idXfer is the transaction id returned by an asynchronous call to 
  2121. *       DdeClientXfer().
  2122. * afCmd = CQ_FLUSH - remove all items in the queue - return is fSuccess.
  2123. * afCmd = CQ_REMOVE - the item referenced is removed from the queue.
  2124. * afCmd = CQ_NEXT - references the idXfer AFTER (more recent than) the id 
  2125. *       given. 0 is returned if the ID given was the newest in the 
  2126. *       queue otherwise the next ID is returned.
  2127. * afCmd = CQ_PREV - references the idXfer BEFORE (less recent than) the id 
  2128. *       given. 0 is returned if the ID given was the oldest in the 
  2129. *       queue otherwise the previous ID is returned.
  2130. * afCmd = CQ_COUNT    - returns the number of entries in the queue.
  2131. *
  2132. *       By ORing in one of the following flags, the above flags can be 
  2133. *       made to reference the apropriate subset of queue entries: 
  2134. *
  2135. * afCmd = CQ_ACTIVEONLY - incomplete active transactions only.
  2136. * afCmd = CQ_COMPLETEDONLY - completed transactions only.
  2137. * afCmd = CQ_FAILEDONLY - transactions which had protocol violations or
  2138. *                         communication failures.
  2139. * afCmd = CQ_INACTIVEONLY - The complement of CQ_ACTIVEONLY which is the
  2140. *                           union of CQ_COMPLETEDONLY and CQ_FAILEDONLY.
  2141. * if phdmgdata = NULL, no hdmgdata is returned.
  2142. * if idXfer == QID_NEWEST, the top-most (most recent) entry in the 
  2143. *       queue is referenced.
  2144. * if idXfer == QID_OLDEST, the bottom-most (oldest) entry ID is 
  2145. *       referenced.
  2146. *
  2147. *       returns the ID of the item processed if it applies, or the count 
  2148. *       of items or fSuccess.  
  2149. *
  2150. *
  2151. * Standard usage examples:
  2152. *
  2153. *   To get the state of the oldest transaction:
  2154. *   id = DdeCheckQueue(hConv, &hDmgData, QID_OLDEST, 0)
  2155. *
  2156. *   To get and flush the next completed transaction data, if there is 
  2157. *       any:
  2158. *   id = DdeCheckQueue(hConv, &hDmgData, QID_OLDEST, CQ_REMOVE | 
  2159. *       CQ_INACTIVEONLY)
  2160. *
  2161. *   To flush all successfully completed transactions:
  2162. *   DdeCheckQueue(hConv, NULL, QID_OLDEST, CQ_FLUSH | CQ_COMPLETEDONLY)
  2163. *
  2164. *   To see if a specific transaction is complete, and if so, to get the
  2165. *   information and remove the transaction from the queue:
  2166. *   if (DdeCheckQueue(hConv, &hDmgData, id, CQ_REMOVE | CQ_INACTIVEONLY))
  2167. *       ProcessAndFreeData(hDmgData);
  2168. *       
  2169. * PUBDOC END
  2170. * History:      Created 6/6/89         sanfords
  2171. \***************************************************************************/
  2172. ULONG EXPENTRY DdeCheckQueue(hConv, phDmgData, idXfer, afCmd)
  2173. HCONV hConv;
  2174. PHDMGDATA phDmgData;
  2175. ULONG idXfer;
  2176. ULONG afCmd;
  2177. {
  2178.     PAPPINFO pai;
  2179.     PCLIENTINFO pci;
  2180.     PCQDATA pcqd, pcqdT, pcqdEnd;
  2181.     USHORT err;
  2182.     ULONG retVal = TRUE;
  2183.     int i;
  2184.  
  2185.     if ((pai = GetCurrentAppInfo(TRUE)) == 0)
  2186.         return(0);
  2187.  
  2188.     SemCheckOut();
  2189.     SemEnter();
  2190.     
  2191.     err = DMGERR_INVALIDPARAMETER;
  2192.     if (!WinIsWindow(DMGHAB, hConv) ||
  2193.             !(BOOL)WinSendMsg(hConv, UM_QUERY, (MPARAM)Q_CLIENT, 0L) ||
  2194.             idXfer == QID_SYNC) {
  2195.         goto failExit;
  2196.     }
  2197.     
  2198.     if (!(pci = (PCLIENTINFO)WinSendMsg(hConv, UM_QUERY, (MPARAM)Q_ALL, 0L))) 
  2199.         goto failExit;
  2200.     
  2201.     err = DMGERR_UNFOUND_QUEUE_ID;
  2202.     if ((pcqd = (PCQDATA)Findqi(pci->pQ, idXfer)) == NULL)
  2203.         goto failExit;
  2204.  
  2205.     /*
  2206.      * if referencing an end item, make sure it fits any subset flags.
  2207.      * If it doesn't we alter afCmd to force us to the first qualifying
  2208.      * entry in the correct direction.
  2209.      */
  2210.     if (!fInSubset(pcqd, afCmd)) {
  2211.         if (idXfer & (QID_OLDEST))
  2212.             afCmd |= CQ_NEXT;
  2213.         else if (idXfer & (QID_NEWEST))
  2214.             afCmd |= CQ_PREV;
  2215.         else if (!(afCmd & (CQ_NEXT | CQ_PREV | CQ_COUNT | CQ_FLUSH)))
  2216.             goto failExit;
  2217.     }
  2218.     
  2219.     if (afCmd & CQ_NEXT) {
  2220.         pcqdEnd = (PCQDATA)pci->pQ->pqiHead->next;
  2221.         if ((pcqd = (PCQDATA)pcqd->next) == pcqdEnd)
  2222.             goto failExit;
  2223.         while (!fInSubset(pcqd, afCmd)) {
  2224.             if ((pcqd = (PCQDATA)pcqd->next) == pcqdEnd)
  2225.                 goto failExit;
  2226.         }
  2227.     }
  2228.             
  2229.     if (afCmd & CQ_PREV) {
  2230.         pcqdEnd = (PCQDATA)pci->pQ->pqiHead;
  2231.         if ((pcqd = (PCQDATA)pcqd->prev) == pcqdEnd)
  2232.             goto failExit;
  2233.         while (!fInSubset(pcqd, afCmd)) {
  2234.             if ((pcqd = (PCQDATA)pcqd->prev) == pcqdEnd)
  2235.                 goto failExit;
  2236.         }
  2237.     }
  2238.     
  2239.     /*
  2240.      * pcqd now points to the apropriate entry
  2241.      */
  2242.  
  2243.     if (afCmd & CQ_COUNT) 
  2244.         retVal = 0;
  2245.     else
  2246.         retVal = MAKEID(pcqd);
  2247.  
  2248.     /*
  2249.      * Fill phDmgData if specified.
  2250.      */
  2251.     if (phDmgData != NULL)
  2252.         if ((pcqd->xad.state == CONVST_CONNECTED) &&
  2253.                 CheckSel(SELECTOROF(pcqd->xad.pddes))) {
  2254.             *phDmgData = pcqd->xad.pddes;
  2255.             AddPileItem(pai->pHDataPile, (PBYTE)phDmgData, CmpULONG);
  2256.             if (((PMYDDES)*phDmgData)->magic == MYDDESMAGIC) {
  2257.                 ((PMYDDES)*phDmgData)->fs |= HDATA_READONLY;
  2258.             }
  2259.         } else
  2260.             *phDmgData = NULL;
  2261.  
  2262.     /*
  2263.      * remove pcqd if apropriate.
  2264.      */   
  2265.     if (afCmd & (CQ_REMOVE | CQ_FLUSH)) {
  2266.         if (!FindPileItem(pai->pHDataPile, CmpULONG, (PBYTE)&pcqd->xad.pddes,
  2267.                 0))
  2268.             FreeData((PMYDDES)pcqd->xad.pddes, pai);
  2269.         /*
  2270.          * Decrement the use count we incremented when the client started
  2271.          * this transaction.
  2272.          */
  2273.         FreeHsz(pcqd->XferInfo.hszItem);
  2274.         Deleteqi(pci->pQ, MAKEID(pcqd));
  2275.     }
  2276.  
  2277.     /*
  2278.      * go through entire list and flush or count if specified.
  2279.      */
  2280.     if (afCmd & (CQ_FLUSH | CQ_COUNT)) {
  2281.         pcqd = (PCQDATA)pci->pQ->pqiHead;
  2282.         for (i = pci->pQ->cItems; i; i--) {
  2283.             if (fInSubset(pcqd, afCmd)) {
  2284.                 if (afCmd & CQ_COUNT) 
  2285.                     retVal++;
  2286.                 if (afCmd & CQ_FLUSH) {
  2287.                     pcqdT = (PCQDATA)pcqd->next;
  2288.                     /*
  2289.                      * Only free the data if not logged - ie the user never got a
  2290.                      * copy.
  2291.                      */
  2292.                     if (!FindPileItem(pci->ci.pai->pHDataPile, CmpULONG,
  2293.                             (PBYTE)&pcqd->xad.pddes, 0))
  2294.                         FreeData((PMYDDES)pcqd->xad.pddes, pai);
  2295.                     /*
  2296.                      * Decrement the use count we incremented when the client started
  2297.                      * this transaction.
  2298.                      */
  2299.                     FreeHsz(pcqd->XferInfo.hszItem);
  2300.                     Deleteqi(pci->pQ, MAKEID(pcqd));
  2301.                     pcqd = pcqdT;
  2302.                     continue;
  2303.                 }
  2304.             }
  2305.             pcqd = (PCQDATA)pcqd->next;
  2306.         }
  2307.     }
  2308.         
  2309.     SemLeave();
  2310.     SemCheckOut();
  2311.     return(retVal);
  2312.     
  2313. failExit:
  2314.     pai->LastError = err;
  2315.     SemLeave();
  2316.     SemCheckOut();
  2317.     return(0);
  2318. }
  2319.  
  2320.  
  2321.  
  2322. BOOL fInSubset(pcqd, afCmd)
  2323. PCQDATA pcqd;
  2324. ULONG afCmd;
  2325. {
  2326.     if (afCmd & CQ_ACTIVEONLY && (pcqd->xad.state <= CONVST_INIT1))
  2327.         return(FALSE);
  2328.  
  2329.     if (afCmd & CQ_INACTIVEONLY && (pcqd->xad.state > CONVST_INIT1))
  2330.         return(FALSE);
  2331.  
  2332.     if (afCmd & CQ_COMPLETEDONLY && (pcqd->xad.state != CONVST_CONNECTED))
  2333.         return(FALSE);
  2334.                             
  2335.     if (afCmd & CQ_FAILEDONLY && (pcqd->xad.state > CONVST_TERMINATED))
  2336.         return(FALSE);
  2337.     return(TRUE);
  2338. }
  2339.  
  2340.  
  2341.  
  2342. /***************************** Public  Function ****************************\
  2343. * PUBDOC START
  2344. * BOOL EXPENTRY DdeEnableCallback(hConv, fEnable)
  2345. * HCONV hConv;      // server conversation handle to enable/disable or NULL 
  2346. * BOOL fEnable;     // TRUE enables, FALSE disables callbacks.
  2347. *
  2348. * This routine is used by an application that does not want to be interrupted
  2349. * by DLL calls to its Callback function.  When callbacks are disabled,
  2350. * all non critical attempts to call the Callback function instead result in
  2351. * the call being placed on a server transaction queue until callbacks are
  2352. * reenabled.  An application should reenable callbacks in a timely manner
  2353. * to avoid causing synchronous clients to time out.
  2354. *
  2355. * note that DdeProcessPkt() has the side effect of unblocking and removing
  2356. * items from the server transaction queue when processing return packets.
  2357. * DdeDisconnect() and DdeEndEnumServers() have the side effect of removing
  2358. * any transactions for the disconnected conversation.
  2359. *
  2360. * If hConv is non-NULL, only the hConv conversation is affected.
  2361. * If hConv is NULL, all conversations are affected.
  2362. *
  2363. * Callbacks can also be disabled on return from the callback function by
  2364. * returning the constant CBR_BLOCK.  This has the same effect as
  2365. * calling DdeEnableCallback(hConv, FALSE) except that the callback
  2366. * returned from is placed back on the callback queue for later re-submission
  2367. * to the callback function when the conversations callbacks are reenabled.
  2368. * This allows a server that needs a long time to process a request to delay
  2369. * returning the result without blocking within the callback function.
  2370. * No callbacks are made within this function.
  2371. * Note:  Callback transactions that have XTYPF_NOBLOCK set in their usType
  2372. *       parameter cannot be blocked by CBR_BLOCK.
  2373. *       These callbacks are issued to the server regardless of the state of
  2374. *       callback enableing.
  2375. *
  2376. * fSuccess is returned.
  2377. *
  2378. * PUBDOC END
  2379. * History:      Created 6/6/89         sanfords
  2380. \***************************************************************************/
  2381. BOOL EXPENTRY DdeEnableCallback(hConv, fEnable)
  2382. HCONV hConv;
  2383. BOOL fEnable;
  2384. {
  2385.     PAPPINFO pai;
  2386.  
  2387.     if ((pai = GetCurrentAppInfo(TRUE)) == 0)
  2388.         return(FALSE);
  2389.  
  2390.     SemCheckOut();
  2391.     SemEnter();
  2392.     if (hConv == NULL) {
  2393.         pai->fEnableCB = fEnable = fEnable ? TRUE : FALSE;
  2394.         FlushLst(pai->plstCBExceptions);
  2395.     } else if (pai->fEnableCB != fEnable) {
  2396.         if (pai->plstCBExceptions == NULL) 
  2397.             pai->plstCBExceptions = CreateLst(pai->hheapApp, sizeof(HWNDLI));
  2398.         AddHwndList((HWND)hConv, pai->plstCBExceptions);
  2399.     }
  2400.     SemLeave();
  2401.     if (fEnable)
  2402.         WinPostMsg(pai->hwndDmg, UM_CHECKCBQ, (MPARAM)pai, 0L);
  2403.     return(TRUE);
  2404. }
  2405.  
  2406.  
  2407.  
  2408. /***************************** Public  Function ****************************\
  2409. * PUBDOC START
  2410. * HDMGDATA EXPENTRY DdeAppNameServer(hszApp, afCmd);
  2411. * HSZ hszApp;       // referenced app name
  2412. * USHORT afCmd;     // action code.
  2413. *
  2414. * Updates or queries the DDE DLL application name server.  The DDE name
  2415. * server acts as a filter for initiate messages on behalf of registered
  2416. * applications and serves as a way for an application to determine what
  2417. * other applications are available without the need for wild initiates.
  2418. * Any change in the name server results in XTYP_REGISTER or XTYP_UNREGISTER
  2419. * callbacks to all other applications.  Agents will not be allowed to
  2420. * know about any other agent registrations.
  2421. *
  2422. * afCmd:
  2423. *
  2424. *   ANS_REGISTER
  2425. *       Adds hszApp to the name server.  hszApp cannot be NULL.
  2426. *       All other applications will receive a registration notification
  2427. *       if their callback filters allow it.
  2428. *       fSuccess is returned.
  2429. *
  2430. *   ANS_UNREGISTER
  2431. *       Remove hszApp from the name server.  If hszApp==NULL the name server
  2432. *       is cleared for the calling application.
  2433. *       All other applications will receive a deregistration notification
  2434. *       if their callback filters allow it.
  2435. *       fSuccess is returned.
  2436. *
  2437. *   ANS_QUERYALLBUTME
  2438. *       Returns a zero terminated array of hszApps/hApp pairs registered with
  2439. *       the name server by all other applications.
  2440. *       (Agent applications do not see other agent registered names.)
  2441. *       If hszApp is set, only names matching* hszApp are placed into the list.
  2442. *       0 is returned if no applicable names were found.
  2443. *
  2444. *   ANS_QUERYMINE
  2445. *       Returns a zero terminated array of hszApps registered with the name
  2446. *       server by the calling application.  If hszApp is set, only names
  2447. *       matching hszApp are placed into the list.  0 is returned if no
  2448. *       applicable names were found.
  2449. *
  2450. *   The following flags may be ORed in with one of the above flags:
  2451. *
  2452. *   ANS_FILTERON
  2453. *       Turns on dde initiate callback filtering.  Only wild app names or
  2454. *       those matching a registered name are given to the application for
  2455. *       initiate requests or registration notifications.
  2456. *
  2457. *   ANS_FILTEROFF
  2458. *       Turns off dde initiate callback filtering.  All initiate requests
  2459. *       and registration notifications are passed onto the application.
  2460. *
  2461. * A 0 is returned on error.
  2462. * PUBDOC END
  2463. *
  2464. * History:
  2465. \***************************************************************************/
  2466. HDMGDATA EXPENTRY DdeAppNameServer(hszApp, afCmd)
  2467. HSZ hszApp;
  2468. USHORT afCmd;
  2469. {
  2470.     PAPPINFO pai, paiT;
  2471.     BOOL fAgent;
  2472.     HDMGDATA hData;
  2473.     HSZ hsz;
  2474.     
  2475.     if ((pai = GetCurrentAppInfo(TRUE)) == 0)
  2476.         return(FALSE);
  2477.  
  2478.     if (afCmd & ANS_FILTERON)
  2479.         pai->afCmd |= DMGCMD_FILTERINITS;
  2480.  
  2481.     if (afCmd & ANS_FILTEROFF) 
  2482.         pai->afCmd &= ~DMGCMD_FILTERINITS;
  2483.  
  2484.     
  2485.     if (afCmd & (ANS_REGISTER | ANS_UNREGISTER)) {
  2486.         
  2487.         if (pai->afCmd & DMGCMD_CLIENTONLY) {
  2488.             pai->LastError = DMGERR_DLL_USAGE;
  2489.             return(FALSE);
  2490.         }
  2491.     
  2492.         fAgent = pai->afCmd & DMGCMD_AGENT ? TRUE : FALSE;
  2493.         
  2494.         if (hszApp == NULL) {
  2495.             if (afCmd & ANS_REGISTER) {
  2496.                 /*
  2497.                  * registering NULL is not allowed!
  2498.                  */
  2499.                 pai->LastError = DMGERR_INVALIDPARAMETER;
  2500.                 return(FALSE);
  2501.             }
  2502.             /*
  2503.              * unregistering NULL is just like unregistering each
  2504.              * registered name.
  2505.              */
  2506.             while (PopPileSubitem(pai->pAppNamePile, (PBYTE)&hsz)) {
  2507.                 for (paiT = pAppInfoList; paiT; paiT = paiT->next) {
  2508.                     if (pai == paiT || (fAgent && (paiT->afCmd & DMGCMD_AGENT))) 
  2509.                         continue;
  2510.                     WinPostMsg(paiT->hwndFrame, UM_UNREGISTER, (MPARAM)hsz,
  2511.                             (MPARAM)pai->hwndFrame);
  2512.                 }
  2513.             }
  2514.             return(TRUE);
  2515.         }
  2516.             
  2517.         if (afCmd & ANS_REGISTER) {
  2518.             if (pai->pAppNamePile == NULL) 
  2519.                 pai->pAppNamePile = CreatePile(hheapDmg, sizeof(HSZ), 8);
  2520.             AddPileItem(pai->pAppNamePile, (PBYTE)&hszApp, NULL);
  2521.         } else
  2522.             FindPileItem(pai->pAppNamePile, CmpULONG, (PBYTE)&hszApp,
  2523.                     FPI_DELETE);
  2524.  
  2525.         for (paiT = pAppInfoList; paiT; paiT = paiT->next) {
  2526.             if (pai == paiT ||
  2527.                     (fAgent && (paiT->afCmd & DMGCMD_AGENT))) {
  2528.                 continue;
  2529.             }
  2530.             WinPostMsg(paiT->hwndFrame,
  2531.                     afCmd & ANS_REGISTER ? UM_REGISTER : UM_UNREGISTER,
  2532.                     (MPARAM)hszApp, (MPARAM)pai->hwndFrame);
  2533.         }
  2534.         return(TRUE);
  2535.     }
  2536.  
  2537.     if (afCmd & ANS_QUERYMINE) {
  2538.         hData = PutData(NULL, 10L * sizeof(HSZ),
  2539.                 0L, 0L, 0, HDATA_APPOWNED | HDATA_APPFREEABLE, pai);
  2540.         if (QueryAppNames(pai, hData, hszApp, 0L)) {
  2541.             return(hData);
  2542.         } else {
  2543.             DdeFreeData(hData);
  2544.             return(0L);
  2545.         }
  2546.     }
  2547.  
  2548.     if (afCmd & ANS_QUERYALLBUTME) {
  2549.         ULONG offAdd, offAddNew;
  2550.         
  2551.         hData = PutData(NULL, 10L * sizeof(HSZ),
  2552.                 0L, 0L, 0, HDATA_APPOWNED | HDATA_APPFREEABLE, pai);
  2553.         *((PHSZ)DDES_PABDATA((PDDESTRUCT)hData)) = 0L;
  2554.         SemEnter();
  2555.         paiT = pAppInfoList;
  2556.         offAdd = 0L;
  2557.         while (paiT) {
  2558.             if (paiT != pai &&
  2559.                     !(fAgent && (paiT->afCmd & DMGCMD_AGENT))) {
  2560.                 offAddNew = QueryAppNames(paiT, hData, hszApp, offAdd);
  2561.                 if (offAddNew == 0 && offAddNew < offAdd) {
  2562.                     /*
  2563.                      * memory error most likely.
  2564.                      */
  2565.                     SemLeave();
  2566.                     DdeFreeData(hData);
  2567.                     return(0L);
  2568.                 }
  2569.                 offAdd = offAddNew;
  2570.             }
  2571.             paiT = paiT->next;
  2572.         }
  2573.         SemLeave();
  2574.         return(hData);
  2575.     }
  2576.  
  2577.     return(0L);
  2578. }
  2579.  
  2580.  
  2581.  
  2582. /***************************** Public  Function ****************************\
  2583. * PUBDOC START
  2584. * HDMGDATA EXPENTRY DdeCreateInitPkt(
  2585. * HSZ hszApp,         // initiate app string
  2586. * HSZ hszTopic,       // initiate topic string
  2587. * HDMGDATA hDmgData)  // packet data containing language information.
  2588. *
  2589. * This routine is called by agent applications from within their callback
  2590. * functions which need to store transaction initiate data into a 
  2591. * network-portable packet.  On return, HDMGDATA contains the packeted 
  2592. * data.  The calling application must free this data handle when 
  2593. * through.  Agents are given access to the app string so that they
  2594. * can modify it if needed for their net protocol.
  2595. *
  2596. * PUBDOC END
  2597. \***************************************************************************/
  2598. HDMGDATA EXPENTRY DdeCreateInitPkt(hszApp, hszTopic, hDmgData)
  2599. HSZ hszApp;
  2600. HSZ hszTopic;
  2601. HDMGDATA hDmgData;
  2602. {
  2603.     PAPPINFO pai;
  2604.  
  2605.     hszApp;
  2606.     hszTopic;
  2607.     hDmgData;
  2608.  
  2609.     if ((pai = GetCurrentAppInfo(TRUE)) == NULL)
  2610.         return(0);
  2611.     /*
  2612.      * Add hDataRet to thread list
  2613.      */
  2614.     pai->LastError = DMGERR_NOT_IMPLEMENTED;
  2615. }
  2616.  
  2617.  
  2618. /***************************** Public  Function ****************************\
  2619. * PUBDOC START
  2620. * BOOL EXPENTRY DdeProcessPkt(
  2621. * HDMGDATA hPkt,        // packet from net to process
  2622. * ULONG hAgentFrom);    // foreign agent handle associated with this packet.
  2623. *
  2624. * This routine is called by agent applications which have received a packet
  2625. * from another agent.  This call performs what actions are requested by the
  2626. * hPkt.  If the particular transaction can be done synchronously, a return
  2627. * packet is returned.  If not, an XTYP_RTNPKT callback will eventually be
  2628. * sent to the agent and 0 is returned.  0 is also returned on error.
  2629. *
  2630. * hAgentFrom identifies the agent the packet came from.
  2631. *
  2632. * This call should NOT be made from within a callback.
  2633. *
  2634. * PUBDOC END
  2635. \***************************************************************************/
  2636. HDMGDATA EXPENTRY DdeProcessPkt(hPkt, hAgentFrom)
  2637. HDMGDATA hPkt;
  2638. ULONG hAgentFrom;
  2639. {
  2640.     PAPPINFO pai;
  2641.  
  2642.     hPkt;
  2643.     hAgentFrom;
  2644.     
  2645.     if ((pai = GetCurrentAppInfo(TRUE)) == NULL)
  2646.         return(0);
  2647.     pai->LastError = DMGERR_NOT_IMPLEMENTED;
  2648. }
  2649.  
  2650.  
  2651.  
  2652. /***************************** Public  Function ****************************\
  2653. * PUBDOC START
  2654. * ULONG EXPENTRY DdeQueryVersion()
  2655. *
  2656. * Returns the DLL version number: 0xjjmmuuppL
  2657. * jj = major release;
  2658. * mm = minor release;
  2659. * uu = update number;
  2660. * pp = platform ID; 1=OS/2, 2=DOS, 3=WINDOWS, 4=UNIX, 5=MAC, 6=SUN
  2661. *
  2662. * PUBDOC END
  2663. \***************************************************************************/
  2664. ULONG EXPENTRY DdeQueryVersion()
  2665. {
  2666.     return(MAKEULONG(MAKESHORT(1, rup),MAKESHORT(rmm, rmj)));
  2667. }
  2668.  
  2669.  
  2670.  
  2671. /* PUBDOC START
  2672.  * ----------------------- Platform specific APIs -----------------------
  2673.  * PUBDOC END
  2674.  */
  2675.  
  2676. /***************************** Public  Function ****************************\
  2677. * PUBDOC START
  2678. * HCONV EXPENTRY DdeConverseWithWindow(
  2679. * HWND hwnd,            // server window to converse with
  2680. * HSZ hszApp,           // app name to converse on
  2681. * HSZ hszTopic,         // topic name to converse on
  2682. * PCONVCONTEXT pCC)     // language information to converse on
  2683. * This creates a pre-initiated client conversation with the given hwnd. 
  2684. * The conversation is assumed to be on the given app, topic and context.
  2685. * This is useful for implementing such things as drag and drop DDE 
  2686. * protocols.  hszApp and hszTopic cannot be NULL.  See
  2687. * DdeCreateServerWindow().
  2688. *  
  2689. * PUBDOC END
  2690. \***************************************************************************/
  2691. HCONV EXPENTRY DdeConverseWithWindow(hwnd, hszApp, hszTopic, pCC)
  2692. HWND hwnd;     
  2693. HSZ hszApp, hszTopic;  
  2694. PCONVCONTEXT pCC;
  2695. {
  2696.     PAPPINFO pai;
  2697.  
  2698.     UNUSED hwnd;
  2699.     UNUSED hszApp;
  2700.     UNUSED hszTopic;
  2701.     UNUSED pCC;
  2702.     
  2703.     if ((pai = GetCurrentAppInfo(TRUE)) == NULL)
  2704.         return(0);
  2705.  
  2706.     if (QuerylatomLength((LATOM)hszApp) == 0 ||
  2707.             QuerylatomLength((LATOM)hszTopic) == 0) {
  2708.         pai->LastError = DMGERR_INVALIDPARAMETER;
  2709.         return(0);
  2710.     }
  2711.     
  2712. }
  2713.  
  2714.  
  2715. /***************************** Public  Function ****************************\
  2716. * PUBDOC START
  2717. * HWND DdeCreateServerWindow(hszApp, hszTopic, pCC)
  2718. * HSZ hszApp,       // app name to accept conversations on.
  2719. * HSZ hszTopic;     // topic name to accept conversations on.
  2720. * PCONVCONTEXT pCC; // language information to accept or NULL for sys default.
  2721. *    
  2722. * This creates a pre_initiated server DDE window for the caller.  The 
  2723. * server window does not know what client window it will be talking to.  As 
  2724. * soon as any DDE message is received by the DDE server window, its 
  2725. * companion client hwnd is remembered and the conversation is 'locked in'.  
  2726. * The hwnd of the server window is returned.  hszApp and hszTopic cannot 
  2727. * be NULL.  
  2728. *    
  2729. * Typically, the server app would call CreateServerWindow() and pass the 
  2730. * hwnd to some prospective client via some other transport mechanism.  The 
  2731. * client would then call ConverseWithWindow() with the hwnd it received 
  2732. * from the server and then begin DDE transactions using the HCONV returned.  
  2733. *    
  2734. * A server may pass this hwnd to many potential clients.  Only the first 
  2735. * one that responds would get 'locked in'.  The rest would be out of luck 
  2736. * and their API calls would fail.  The server window ignores messages that
  2737. * to not fit the app and topic given.
  2738. *  
  2739. * PUBDOC END
  2740. \****************************************************************************/
  2741. HWND EXPENTRY DdeCreateServerWindow(hszApp, hszTopic, pCC)
  2742. HSZ hszApp, hszTopic;
  2743. PCONVCONTEXT pCC;
  2744. {
  2745.     PAPPINFO pai;
  2746.     INITINFO ii;
  2747.     HWND hwndServer;
  2748.     
  2749.     hszApp;
  2750.     hszTopic;
  2751.     pCC;
  2752.     
  2753.     if ((pai = GetCurrentAppInfo(TRUE)) == NULL)
  2754.         return(0);
  2755.         
  2756.     if (QuerylatomLength((LATOM)hszApp) == 0 ||
  2757.             QuerylatomLength((LATOM)hszTopic) == 0) {
  2758.         pai->LastError = DMGERR_INVALIDPARAMETER;
  2759.         return(0);
  2760.     }
  2761.     
  2762.     if ((hwndServer = CreateServerWindow(pai, hszTopic)) == NULL)
  2763.         return(0);
  2764.  
  2765.     ii.hszAppName = hszApp;
  2766.     ii.hszTopic = hszTopic;
  2767.     ii.hwndSend =
  2768.     ii.hwndFrame = NULL;
  2769.     ii.pCC = pCC;
  2770.     WinSendMsg(hwndServer, UMSR_INITIATE, 0L, 0L);
  2771. }
  2772.