home *** CD-ROM | disk | FTP | other *** search
/ Chip 2002 March / Chip_2002-03_cd1.bin / tema / pegasus / download / w32-401.exe / WINPMDDE.TXT < prev    next >
Text File  |  1999-08-09  |  29KB  |  737 lines

  1. Pegasus Mail for Windows DDE (Dynamic Data Exchange) Interface
  2. Copyright (c) 1997-1999, David Harris, All Rights Reserved.
  3. ----------------------------------------------------------------
  4.  
  5. Revision history:
  6. * WinPMail v3.11: added IDENTITY and VOLATILE keywords to MESSAGE topic.
  7. * WinPMail v3.12: added RICHTEXT keyword to MESSAGE topic, and STATUS
  8.   keyword to ENVIRONMENT topic
  9.  
  10. Pegasus Mail v2.54 and later incorporates support for Windows DDE to allow
  11. interprocess communication. This document describes the topics supported by
  12. WinPMail and the syntax of the commands sent to them. It is not a tutorial
  13. on DDE, and assumes that you either understand how to use DDE, or are using
  14. an environment (such as Microsoft Word's WordBasic language) that
  15. simplifies the process of using DDE commands.
  16.  
  17. One of the most powerful features of DDE is that it allows transparent
  18. communication between 16-bit and 32-bit applications: so, by using DDE, you
  19. can communicate with either the 16- or the 32-bit version of WinPMail
  20. without having to be worried about the differences between the two.
  21.  
  22. Pegasus Mail's DDE interface has been designed to be as simple to use as
  23. possible, and depends on simple strings for commands. It should be possible
  24. to use DDE to interact with Pegasus Mail from almost any environment.
  25.  
  26.  
  27.  
  28. Service and Topic Names
  29. -----------------------
  30.  
  31. Under DDE, a "service name" is the name of an application that can accept
  32. DDE connections: when you connect to a service application, you specify a
  33. particular set of commands or operations in which you are interested - this
  34. set of commands is known as a "topic". Pegasus Mail uses the service name
  35. "WinPMail" and exports the following topics:
  36.  
  37.    "System"        The standard DDE system topic
  38.    "Environment"   Accepts DDE Requests (transactions of type XTYP_REQUEST)
  39.                    and returns information about the environment of the
  40.                    running copy of WinPMail, such as directories and
  41.                    usernames.
  42.    "Message"       Accepts DDE Poke commands to create and send e-mail
  43.                    messages. A DDE Client can either create and manipulate
  44.                    standard Pegasus Mail message editor windows, filling
  45.                    in whichever blanks are required, or it can create
  46.                    messages without a window.
  47.    "TCP"           Provides access via DDE Poke commands to WinPMail's
  48.                    TCP/IP-based mail services.
  49.  
  50.  
  51.  
  52. The "Environment" topic
  53. -----------------------
  54.  
  55. The "Environment" DDE topic allows client applications to obtain
  56. information about the running copy of Pegasus Mail. To use this command,
  57. send a DDE Request command (a transaction of type XTYP_REQUEST) the the
  58. "Environment" topic with the "item" parameter set to the environment item
  59. you wish to retrieve, selected from the following list:
  60.  
  61.    USER           - Returns the name of the user running WinPMail 
  62.    HOMEBOX        - Returns the full path to the user's home mail directory
  63.    NEWBOX         - Returns the full path to the user's new mail directory
  64.    MODE           - Returns either "Standalone" or "Network"
  65.    VERSION        - Returns the version of WinPMail, expressed as a four
  66.                     digit hexadecimal number ("0254" for v2.54) followed
  67.                     by a space and either "16" or "32" to indicate the
  68.                     version of WinPMail that is running.  
  69.    BASEDIR        - The directory from which WinPMail was run
  70.    TCP            - If WinPMail's TCP/IP services are available, returns
  71.                     the path to the WINSOCK.DLL in use, otherwise returns
  72.                     the single character "N".
  73.    NEWMAIL        - Returns the number of mail messages in the user's new
  74.                     mail folder.
  75.    STATUS         - Returns the status of the last mail delivery operation
  76.  
  77. -- Example: -----------------------------------
  78.  
  79.    In WordBasic (MS-Word 7) the following Macro opens a connection to
  80.    WinPMail, retrieves the current version, and displays it:
  81.  
  82.       Sub MAIN
  83.       channel = DDEInitiate("WinPMail", "Environment")
  84.       a$ = DDERequest$(channel, "version")
  85.       DDETerminate channel
  86.       MsgBox a$
  87.       End Sub
  88.  
  89.    A longer example in C is shown as Appendix A: in this document.
  90.  
  91.  
  92.  
  93. The "Message" topic
  94. -------------------
  95.  
  96. Client applications use the "Message" topic to create and send electronic
  97. mail messages using WinPMail. Once a connection has been established to the
  98. "Message" topic, the client Pokes data at WinPMail (using XTYP_POKE
  99. transactions). The string parameter in the Poke command should be set to
  100. "Message" - other values may be added in future. The data itself contains
  101. the commands and parameters.
  102.  
  103. The following commands can be Poked as data to WinPMail:
  104.  
  105.    New : <"Message"> or <"Window">
  106.       If the parameter is "Window", then WinPMail creates a standard
  107.       message editing window, and subsequent message commands will change
  108.       the controls and settings in that window. If the parameter is
  109.       "Message", then a data structure representing the message is
  110.       allocated internally and subsequent message commands will fill in the
  111.       fields within that structure.
  112.  
  113.    Defaults : Y
  114.       Tells WinPMail to apply the user's regular message setting default
  115.       values to the message. This command is only valid when the
  116.       "New: message" command has been sent - it has no effect on message
  117.       editing windows created using "New: window", since they always use
  118.       default values automatically.
  119.  
  120.    To : <address> [, <address> ...]
  121.       Fills in the "To" field for the message or window. The parameter may
  122.       be any address WinPMail would normally accept, and you may include
  123.       more than one address by separating them with commas. The same rules
  124.       apply to the Cc: and Bcc: fields.
  125.  
  126.    Cc : <address> [, <address> ...]
  127.       Fills in the "Cc:" (Carbon Copy) field for the message.
  128.  
  129.    Bcc : <address> [, <address> ...]
  130.       Fills in the "Bcc:" (Blind Carbon Copy) field for the message.
  131.  
  132.    Subject : <string>
  133.       Fills in the subject field of the message.
  134.  
  135.    Reply-to : <address>
  136.       Sets the optional reply address for the message.
  137.  
  138.    Copyself : <'Y'> or <'N'>
  139.       Selects whether or not a copy-to-self should be made of the message.
  140.  
  141.    Confirm-delivery : <'Y'> or <'N'>
  142.       Selects whether or not to request confirmation of delivery
  143.  
  144.    Confirm-reading : <'Y'> or <'N'>
  145.       Selects whether or not to request confirmation of reading
  146.  
  147.    Urgent : <'Y'> or <'N'>
  148.       Selects whether or not the message should be marked as "urgent"
  149.       
  150.    Encrypted : <encryptor-name><,><passphrase><,><flags>
  151.       Chooses an encryption method for the message. "Encryptor-name" is
  152.       the tagname of the encryptor you wish to use (use "PM-BUILTIN" to
  153.       select Pegasus Mail's built-in encryptor). You can find the tagname
  154.       for an encryptor in the "Form tagname" line of the encryptor's .FFF
  155.       file. "passphrase" is the password for the message. If it contains
  156.       a comma, then it must be quoted using " and ". "flags" is either
  157.       1 to encrypt the message, 2 to sign the message, or 3 to encrypt
  158.       and sign the message.
  159.  
  160.    Signature : <number>
  161.       Chooses the signature you wish to attach to the message. "number"
  162.       is 1 - 9, corresponding to the signature sets shown in WinPMail's
  163.       Tools | Signatures preferences page, or 0 to disable signatures.
  164.  
  165.    Mime : <'Y'> or <'N'>
  166.       Indicates whether or not MIME support should be turned on for
  167.       this message. MIME support affects the way attachments and
  168.       international characters are handled by WinPMail. We strongly
  169.       recommend that you turn MIME on wherever possible.
  170.  
  171.    Attach : <path><,><filename><,><attachment-type><,><encoding>
  172.       Attach a file to the message. "path" should be the full path
  173.       to the file - do not assume that the current directory in your
  174.       client application will be the same as the current directory in
  175.       Pegasus Mail. "filename" is the name Pegasus Mail should include
  176.       in the message as the real name of the file (you will typically
  177.       use this when you have to create a temporary file but want to
  178.       send it under the original file's name). If "filename" is "-",
  179.       WinPMail will automatically use the filename portion of "path".
  180.       "attachment-type" should be textual information describing the
  181.       attachment; it must not contain spaces. If you set this field to
  182.       a single dash ("-"), WinPMail will use its internal routines to
  183.       try to work out the most appropriate type information for the
  184.       file. "encoding" is used to select the type of encoding for the
  185.       attachment: we strongly recommend that you set this value to 0,
  186.       which will allow WinPMail to choose the most appropriate encoding
  187.       for you. Possible values are:
  188.          0 - Pegasus Mail decides.
  189.          1 - No encoding - the file is not altered (local mail only)
  190.          2 - ASCII encoding - the file is normalised to CR/LF line
  191.              endings and is not otherwise encoded in transit.
  192.          3 - UUencoding - the file is uuencoded
  193.          4 - BinHex - the file is transformed using the BinHex 4.0 method.
  194.          5 - MIME - the file is transformed using the MIME BASE64 method.
  195.  
  196.    File : <filename>
  197.       Indicates the name of a file containing the text WinPMail should
  198.       use as the body of the message. Note that this file is NOT an
  199.       attachment - it is expected to be plain text.
  200.  
  201.    Data : <String>
  202.       Adds "string" to the body of the message as a single line. Poke
  203.       this command repeatedly to build up a message line by line.
  204.       WinPMail automatically adds the CR/LF termination to each line.
  205.       Poking this command with no <string> parameter will print a blank
  206.       line in the message (but the ':' must still be present).
  207.  
  208.    Identity : <identity_name>
  209.       Sets the identity Pegasus Mail should use when processing this
  210.       message. For more information on Identities, see the Pegasus Mail
  211.       help file. You can specify any valid identity name as the parameter
  212.       to this command. The default is the user's current identity.
  213.  
  214.    Volatile : <'Y'> or <'N'>         (Default is 'Y')
  215.       (Only meaningful when using Pegasus Mail's built-in SMTP mailer).
  216.       Setting this flag to 'Y' tells Pegasus Mail that the message should
  217.       be written straight into Final Form in the queue when it is sent.
  218.       If you want your message to be reloadable from Pegasus Mail's
  219.       "Review queued mail" option, you should set this to 'N'. If your
  220.       message contains attachments that you plan to delete once the
  221.       message has been sent, you must not set this field to 'N'.
  222.  
  223.    Richtext : <'Y'> or <'N'>         (Default is 'N')
  224.       Set the "rich text" flag in the message.
  225.  
  226.    Send
  227.       This command sends the message. Note that it has no parameters and
  228.       no ':'. It has no effect when "Window" was used as the parameter to
  229.       the "New:" command.
  230.  
  231.    Restore
  232.       This command brings WinPMail to the top and activates the Message
  233.       Editing Window created using the "New: Window" command. Note that it
  234.       has no parameters and no ':'. For messages created using the
  235.       "New: Message" command, this command will simply bring WinPMail to
  236.       the top without taking any other action.
  237.  
  238.  
  239. -- Example: -----------------------------------
  240.  
  241.    In WordBasic (MS-Word 7) the following Macro opens a connection to
  242.    WinPMail, and sends a message with no window.
  243.  
  244.       Sub MAIN
  245.       channel = DDEInitiate("WinPMail", "Message")
  246.       DDEPoke channel, "Message", "New: Message"
  247.       DDEPoke channel, "Message", "Defaults: Y"
  248.       DDEPoke channel, "Message", "To: David"
  249.       DDEPoke channel, "Message", "Subject: Test from Word"
  250.       DDEPoke channel, "Message", "Data: Hi there!"
  251.       DDEPoke channel, "Message", "Send"
  252.       DDETerminate channel
  253.       End Sub
  254.  
  255.  
  256.  
  257. The "TCP" topic
  258. ---------------
  259.  
  260. The commands in this topic allow clients to control Pegasus Mail's
  261. built-in TCP/IP-based mail protocols. Before using these commands, a
  262. client should usually use the "TCP" request to the "Environment" topic to
  263. determine whether or not TCP/IP services are enabled and available.
  264. Clients interact with this topic by Poking data at it in a similar manner
  265. to that used to access the "Message" topic. The following commands can be
  266. poked at this topic:
  267.  
  268.    Get
  269.       Tells Pegasus Mail to download mail using the current TCP/IP
  270.       settings defined for the program.
  271.  
  272.    Send
  273.       Tells Pegasus Mail to send any messages currently waiting in
  274.       the queue to be sent out.
  275.  
  276.    Both
  277.       Tells Pegasus Mail to check for new mail, then to send any messages
  278.       currently waiting in the queue, in that order.
  279.  
  280.    Restore
  281.       Brings Pegasus Mail to the front and gives it focus.
  282.  
  283.  
  284. -- Example: -----------------------------------
  285.  
  286.    In WordBasic (MS-Word 7) the following Macro opens a connection to
  287.    WinPMail, tells it to download new mail, then brings it to the front:
  288.  
  289.       Sub MAIN
  290.       channel = DDEInitiate("WinPMail", "TCP")
  291.       DDEPoke channel, "TCP", "Get"
  292.       DDEPoke channel, "TCP", "Restore"
  293.       DDETerminate channel
  294.       End Sub
  295.  
  296.  
  297.  
  298. Pegasus Mail and the Windows Registry
  299. -------------------------------------
  300.  
  301. Starting with v2.54, WinPMail updates the Windows registry with a certain
  302. amount of information each time it is run. DDE Client Applications can use
  303. this registry information to work out how to find a copy of Pegasus Mail to
  304. run if none is active, and what commandline is appropriate.
  305.  
  306. The 32-bit version of WinPMail creates the following keys:
  307.  
  308.    HKEY_CURRENT_USER\Software\Pegasus Mail\Version
  309.    HKEY_CURRENT_USER\Software\Pegasus Mail\BaseDir
  310.    HKEY_CURRENT_USER\Software\Pegasus Mail\Command
  311.  
  312. The "version" key contains the WinPMail version, expressed in exactly the
  313. same way as the return from the DDE "Environment" topic's "Version"
  314. request. The "BaseDir" key contains the directory where the WinPMail
  315. executable file is located, and the "command" key contains the full
  316. commandline that was used to invoke the most recently-run copy of Pegasus
  317. Mail.
  318.  
  319. Both the 16- and 32-bit versions of WinPMail create the following keys:
  320.  
  321.    HKEY_CLASSES_ROOT\Software\Pegasus Mail\Version
  322.    HKEY_CLASSES_ROOT\Software\Pegasus Mail\BaseDir
  323.    HKEY_CLASSES_ROOT\Software\Pegasus Mail\Command
  324.  
  325. These keys are formatted in exactly the same way as those shown above.
  326. 32-bit applications should always attempt to find the HKEY_CURRENT_USER
  327. keys before the HKEY_CLASSES_ROOT keys, since doing so ensures that
  328. multiple user configurations are respected under Windows 95 and NT.
  329.  
  330.  
  331.  
  332. Appendix A: Using DDE from C programs
  333. -------------------------------------
  334.  
  335. The source code shown here can be used as a model for interacting with
  336. any DDE-aware application. It presents a simple dialog with "Service",
  337. "Topic" and "Command" fields, request selector radio buttons that allow
  338. the user to select between XTYP_EXECUTE, XTYP_REQUEST and XTYP_POKE
  339. transactions, and three buttons - one to open/close a connection, a
  340. "quit" button, and a "Do it" button, that sends the command. If a
  341. connection is established using the "Open" button, then the "Do it"
  342. button will send the command to that connection, otherwise it will
  343. establish a connection, send the command, then close the connection.
  344.  
  345. This code was written for Borland C++ v4.52 and should be linked using
  346. Borland's BWCC.LIB or BWCC32.LIB in order to present the proper dialog
  347. appearance.
  348.  
  349.  
  350. --------------- DDECLI.C --------------------------------------------
  351. #include <windows.h>
  352. #include <windowsx.h>
  353. #include <stdlib.h>
  354. #include <string.h>
  355. #include <ddeml.h>
  356.  
  357. DWORD idInst = 0L;      // DDE instance identifier
  358. HINSTANCE hInstance;
  359. HCONV mconv;
  360. HSZ ghszServSrv;
  361. HSZ ghszServTop;
  362.  
  363. #pragma warn -par
  364. #pragma warn -use
  365.  
  366.  
  367. HDDEDATA EXPENTRY DDECallback (WORD wType,   // transaction type
  368.    WORD     wFmt,    // clipboard format
  369.    HCONV    hConv,   // handle of the conversation
  370.     HSZ      hsz1,    // handle of a string
  371.    HSZ      hsz2,    // handle of a string
  372.    HDDEDATA hData,   // handle of a global memory object
  373.    DWORD    dwData1, // transaction-specific data
  374.    DWORD    dwData2) // transaction-specific data
  375.    {
  376.    // Nothing need be done here...
  377.    return (HDDEDATA) NULL;
  378.    }
  379.  
  380.  
  381. BOOL SendShellCommand (DWORD idInst, char *service, char *topic, LPSTR lpCommand)
  382.    {
  383.    HSZ      hszServSrv;    // Service is "DDESERV"
  384.    HSZ      hszServTop;    // Topic is "MAIL"
  385.    HCONV    hconv;         // handle of conversation
  386.    int      nLen;          // length of command string
  387.    HDDEDATA hData;         // return value of DdeClientTransaction
  388.    DWORD    dwResult;      // result of transaction
  389.    BOOL     bResult=FALSE; // TRUE if this function is successful
  390.  
  391.    if (mconv == NULL)
  392.       {
  393.       // create string handle to service/topic
  394.       hszServSrv = DdeCreateStringHandle (idInst, service, CP_WINANSI);
  395.       hszServTop = DdeCreateStringHandle (idInst, topic, CP_WINANSI);
  396.  
  397.       // attempt to start conversation with server app
  398.        hconv = DdeConnect (idInst, hszServSrv, hszServTop, NULL);
  399.       }
  400.    else
  401.       hconv = mconv;
  402.  
  403.    if (hconv != NULL)
  404.       {
  405.       // get length of the command string
  406.       nLen = lstrlen ((LPSTR) lpCommand);
  407.  
  408.       // send command to server app
  409.       hData = DdeClientTransaction (
  410.          (LPBYTE) lpCommand, // data to pass
  411.          nLen + 1,           // length of data
  412.          hconv,              // handle of conversation
  413.          NULL,               // handle of name-string
  414.          CF_TEXT,            // clipboard format
  415.          XTYP_EXECUTE,       // transaction type
  416.          20000,              // timeout duration
  417.          &dwResult);         // points to transaction result
  418.  
  419.       if (hData)
  420.          bResult = TRUE;
  421.  
  422.       if (mconv == NULL)
  423.          // end conversation
  424.          DdeDisconnect (hconv);
  425.       }
  426.  
  427.    if (mconv == NULL)
  428.       {
  429.       // free service/topic string handle
  430.       DdeFreeStringHandle(idInst, hszServSrv);
  431.       DdeFreeStringHandle(idInst, hszServTop);
  432.       }
  433.  
  434.    if (bResult == FALSE)
  435.       MessageBox (NULL, "SendShellCommand failed", "DDE Client", MB_OK);
  436.  
  437.    return bResult;
  438.    }
  439.  
  440.  
  441. BOOL SendPoke (DWORD idInst, char *service, char *topic, LPSTR lpCommand)
  442.    {
  443.    HSZ      hszServSrv;    // Service is "DDESERV"
  444.    HSZ      hszServTop;    // Topic is "MAIL"
  445.    HCONV    hconv;         // handle of conversation
  446.    int      nLen;          // length of command string
  447.    HDDEDATA hData;         // return value of DdeClientTransaction
  448.    DWORD    dwResult;      // result of transaction
  449.    BOOL     bResult=FALSE; // TRUE if this function is successful
  450.  
  451.    if (mconv == NULL)
  452.       {
  453.       // create string handle to service/topic
  454.       hszServSrv = DdeCreateStringHandle (idInst, service, CP_WINANSI);
  455.       hszServTop = DdeCreateStringHandle (idInst, topic, CP_WINANSI);
  456.  
  457.       // attempt to start conversation with server app
  458.        hconv = DdeConnect (idInst, hszServSrv, hszServTop, NULL);
  459.       }
  460.    else
  461.       hconv = mconv;
  462.  
  463.    if (hconv != NULL)
  464.       {
  465.       // get length of the command string
  466.       nLen = lstrlen ((LPSTR) lpCommand);
  467.  
  468.       // send command to server app
  469.       hData = DdeClientTransaction (
  470.          (LPBYTE) lpCommand, // data to pass
  471.          nLen + 1,           // length of data
  472.          hconv,              // handle of conversation
  473.          hszServTop,         // handle of name-string
  474.          CF_TEXT,            // clipboard format
  475.          XTYP_POKE,          // transaction type
  476.          20000,              // timeout duration
  477.          &dwResult);         // points to transaction result
  478.  
  479.       if (hData)
  480.          bResult = TRUE;
  481.  
  482.       if (mconv == NULL)
  483.          // end conversation
  484.          DdeDisconnect (hconv);
  485.       }
  486.  
  487.    if (mconv == NULL)
  488.       {
  489.       // free service/topic string handle
  490.       DdeFreeStringHandle(idInst, hszServSrv);
  491.       DdeFreeStringHandle(idInst, hszServTop);
  492.       }
  493.  
  494.    if (bResult == FALSE)
  495.       MessageBox (NULL, "SendPoke failed", "DDE Client", MB_OK);
  496.  
  497.    return bResult;
  498.    }
  499.  
  500.  
  501. BOOL SendShellRequest (DWORD idInst, char *service, char *topic, char *cmd)
  502.    {
  503.    HSZ      hszServSrv;    // Service is "DDESERV"
  504.    HSZ      hszServTop;    // Topic is "MAIL"
  505.    HSZ      item;
  506.    HCONV    hconv;         // handle of conversation
  507.    int      nLen;          // length of command string
  508.    HDDEDATA hData;         // return value of DdeClientTransaction
  509.    DWORD    dwResult;      // result of transaction
  510.    BOOL     bResult=FALSE; // TRUE if this function is successful
  511.    char     *str;
  512.    DWORD    x;
  513.  
  514.    if (mconv == NULL)
  515.       {
  516.       // create string handle to service/topic
  517.       hszServSrv = DdeCreateStringHandle (idInst, service, CP_WINANSI);
  518.       hszServTop = DdeCreateStringHandle (idInst, topic, CP_WINANSI);
  519.  
  520.       // attempt to start conversation with server app
  521.        hconv = DdeConnect (idInst, hszServSrv, hszServTop, NULL);
  522.       }
  523.    else
  524.       hconv = mconv;
  525.  
  526.    if (hconv != NULL)
  527.       {
  528.       // send command to server app
  529.       item = DdeCreateStringHandle (idInst, cmd, CP_WINANSI);
  530.       hData = DdeClientTransaction (
  531.          NULL,               // data to pass
  532.          0,                  // length of data
  533.          hconv,              // handle of conversation
  534.          item,               // handle of name-string
  535.          CF_TEXT,            // clipboard format
  536.          XTYP_REQUEST,       // transaction type
  537.          20000,              // timeout duration
  538.          &dwResult);         // points to transaction result
  539.  
  540.       if (hData)
  541.          {
  542.          if ((str = (char *) DdeAccessData (hData, &x)) != NULL)
  543.             {
  544.             strcpy (cmd, str);
  545.             bResult = TRUE;
  546.             DdeUnaccessData (hData);
  547.             }
  548.  
  549.          DdeFreeDataHandle (hData);
  550.          }
  551.  
  552.       DdeFreeStringHandle (idInst, item);
  553.  
  554.       if (mconv == NULL)
  555.          // end conversation
  556.          DdeDisconnect (hconv);
  557.       }
  558.  
  559.    if (mconv == NULL)
  560.       {
  561.       // free service/topic string handle
  562.       DdeFreeStringHandle (idInst, hszServSrv);
  563.       DdeFreeStringHandle (idInst, hszServTop);
  564.       }
  565.  
  566.    return bResult;
  567.    }
  568.  
  569.  
  570. int FAR PASCAL _export dummy_proc (HWND hDialog, UINT wMsg,
  571.    WPARAM wParam, LPARAM lParam)
  572.    {
  573.    BOOL fProcessed = TRUE;
  574.    DWORD dwResult;
  575.    RECT r;
  576.    HWND hControl;
  577.    char buffer [256], service [80], topic [80];
  578.  
  579.    switch (wMsg)
  580.       {
  581.       case WM_INITDIALOG :
  582.          CheckRadioButton (hDialog, 104, 106, 104);
  583.          break;
  584.  
  585.       case WM_SETFOCUS :
  586.          SetFocus (GetDlgItem (hDialog, 101));
  587.          break;
  588.  
  589.       case WM_COMMAND :
  590.          if (GET_WM_COMMAND_ID(wParam, lParam) == IDCANCEL)
  591.             {
  592.             EndDialog (hDialog, IDCANCEL);
  593.             break;
  594.             }
  595.  
  596.          if (GET_WM_COMMAND_ID(wParam, lParam) == 120)
  597.             {
  598.             if (mconv == NULL)
  599.                {
  600.                GetDlgItemText (hDialog, 101, service, sizeof (service));
  601.                GetDlgItemText (hDialog, 102, topic, sizeof (topic));
  602.                ghszServSrv = DdeCreateStringHandle (idInst, service, CP_WINANSI);
  603.                ghszServTop = DdeCreateStringHandle (idInst, topic, CP_WINANSI);
  604.  
  605.                // attempt to start conversation with server app
  606.                 mconv = DdeConnect (idInst, ghszServSrv, ghszServTop, NULL);
  607.                if (mconv == NULL)
  608.                   MessageBox (NULL, "Connection failed!", "DDE Client", 
  609.                      MB_OK | MB_ICONEXCLAMATION);
  610.                else
  611.                   SetDlgItemText (hDialog, 120, "Close");
  612.                }
  613.             else
  614.                {
  615.                DdeDisconnect (mconv);
  616.                mconv = NULL;
  617.                DdeFreeStringHandle (idInst, ghszServSrv);
  618.                DdeFreeStringHandle (idInst, ghszServTop);
  619.                SetDlgItemText (hDialog, 120, "Open");
  620.                }
  621.             break;
  622.             }
  623.  
  624.          if (GET_WM_COMMAND_ID(wParam, lParam) == IDOK)
  625.             {
  626.             GetDlgItemText (hDialog, 101, service, sizeof (service));
  627.             GetDlgItemText (hDialog, 102, topic, sizeof (topic));
  628.             GetDlgItemText (hDialog, 103, buffer, sizeof (buffer));
  629.             hControl = GetDlgItem (hDialog, 107);
  630.             if (IsDlgButtonChecked (hDialog, 105))   //  Request
  631.                {
  632.                if (SendShellRequest (idInst, service, topic, buffer))
  633.                   {
  634.                   Edit_ReplaceSel (hControl, "Request successful:\r\n   ");
  635.                   Edit_ReplaceSel (hControl, buffer);
  636.                   Edit_ReplaceSel (hControl, "\r\n");
  637.                   }
  638.                else
  639.                   Edit_ReplaceSel (hControl, "Request failed.\r\n");
  640.                }
  641.             else if (IsDlgButtonChecked (hDialog, 104))
  642.                {
  643.                if (SendShellCommand (idInst, service, topic, buffer))
  644.                   Edit_ReplaceSel (hControl, "Command successful.\r\n");
  645.                else
  646.                   Edit_ReplaceSel (hControl, "Command failed.\r\n");
  647.                }
  648.             else
  649.                {
  650.                if (SendPoke (idInst, service, topic, buffer))
  651.                   Edit_ReplaceSel (hControl, "Poke successful.\r\n");
  652.                else
  653.                   Edit_ReplaceSel (hControl, "Poke failed.\r\n");
  654.                Edit_SetSel (GetDlgItem (hDialog, 103), 0, 999);
  655.                }
  656.             }
  657.          break;
  658.  
  659.       default:
  660.          fProcessed = FALSE;
  661.          break;
  662.       }
  663.  
  664.    return fProcessed;
  665.    }
  666.  
  667.  
  668. int PASCAL WinMain (HINSTANCE __hInstance, HINSTANCE hPrevInstance,
  669.    LPSTR lpszCmdLine, int nCmdShow)
  670.    {
  671.    MSG msg;
  672.    HWND hWndFrame;
  673.    WNDCLASS wc;
  674.    FARPROC DDEProc, dlgProc;
  675.  
  676.    if (hPrevInstance != NULL)
  677.       return 0;
  678.  
  679.    hInstance = __hInstance;
  680.  
  681.    // get proc instance for our DDEML callback
  682.    DDEProc = MakeProcInstance ((FARPROC) DDECallback, hInstance);
  683.  
  684.    // register this app with the DDEML
  685.    if (DdeInitialize (&idInst,   // receives instance ID
  686.       (PFNCALLBACK) DDEProc,     // address of callback function
  687.       APPCMD_CLIENTONLY,         // this is a client app
  688.       0L))                       // reserved
  689.       {
  690. #ifndef __FLAT__        //  FreeProcInstance is obsolete under Win32
  691.       FreeProcInstance (DDEProc);
  692. #endif
  693.       return FALSE;
  694.       }
  695.  
  696.    dlgProc = MakeProcInstance ((FARPROC) dummy_proc, hInstance);
  697.    DialogBox (hInstance, "CLIENT", NULL, dlgProc);
  698.    DdeUninitialize (idInst);
  699.    return 0;
  700.    }
  701.  
  702. --------------- DDECLI.DEF ----------------------------------------
  703. NAME           DDECLI
  704. DESCRIPTION    'DDE Test Client'
  705. EXETYPE        WINDOWS
  706. DATA           MOVEABLE MULTIPLE PRELOAD
  707. CODE           MOVEABLE DISCARDABLE
  708. HEAPSIZE       27500
  709. STACKSIZE      16500
  710.  
  711. --------------- DDECLI.RC -----------------------------------------
  712.  
  713. CLIENT DIALOG 180, 121, 246, 168
  714. STYLE WS_POPUP | WS_VISIBLE | WS_CAPTION | WS_SYSMENU | WS_THICKFRAME
  715. CLASS "bordlg"
  716. CAPTION "DDE Test Client"
  717. FONT 8, "MS Sans Serif"
  718. {
  719.  RTEXT "Service name:", -1, 15, 15, 60, 8
  720.  CONTROL "winpmail", 101, "EDIT", ES_AUTOHSCROLL | WS_BORDER | WS_TABSTOP, 83, 13, 102, 12
  721.  RTEXT "Topic name:", -1, 15, 30, 60, 8
  722.  CONTROL "message", 102, "EDIT", ES_AUTOHSCROLL | WS_BORDER | WS_TABSTOP, 83, 28, 102, 12
  723.  RTEXT "Command/request:", -1, 14, 45, 61, 8
  724.  EDITTEXT 103, 83, 43, 102, 12, ES_AUTOHSCROLL | WS_BORDER | WS_TABSTOP
  725.  RTEXT "Transaction type:", -1, 15, 65, 60, 8
  726.  CONTROL "Execute", 104, "BorRadio", BS_AUTORADIOBUTTON | WS_CHILD | WS_VISIBLE | WS_TABSTOP, 85, 64, 47, 10
  727.  CONTROL "Request", 105, "BorRadio", BS_AUTORADIOBUTTON | WS_CHILD | WS_VISIBLE | WS_TABSTOP, 133, 64, 43, 10
  728.  CONTROL "Poke", 106, "BorRadio", BS_AUTORADIOBUTTON | WS_CHILD | WS_VISIBLE | WS_TABSTOP, 184, 64, 32, 10
  729.  LTEXT "Results / Diagnostics", -1, 12, 82, 78, 8
  730.  EDITTEXT 107, 10, 92, 228, 69, ES_MULTILINE | ES_AUTOVSCROLL | ES_AUTOHSCROLL | WS_BORDER | WS_VSCROLL | WS_HSCROLL | WS_TABSTOP
  731.  DEFPUSHBUTTON "Do it", IDOK, 192, 12, 43, 14
  732.  PUSHBUTTON "Quit", IDCANCEL, 192, 42, 43, 14
  733.  CONTROL "", -1, "BorShade", BSS_GROUP | BSS_CAPTION | BSS_LEFT | WS_CHILD | WS_VISIBLE, -5, -7, 257, 180
  734.  PUSHBUTTON "Open", 120, 192, 27, 43, 14
  735. }
  736.  
  737.