allegro-cl archives 1996-9-10 | home index prev H thread prev K thread next J next L |
From: tom (Tom McClure) Subject: Re: [pcspr3863] Allegro and Netscape via DDE Date: 1996-9-10 16:47 Steven-- You wrote: I tried the code for t.poke and receive-value, but got no results. This works perhaps for transaction type XTYP_POKE, but I need to do a XTYP_REQUEST and therefore the code for t.request ... I know, that you won't support the dde-methods, and maybe it is the best, when you send me the whole code of the dde implementation... Enclosed please find the DDE code in its entirety. -tom Tom McClure - <franz.com at tomj> - Franz Inc., 1995 University Ave, Suite 275 (510) 548-3600, FAX 548-8253 - Berkeley, CA 94704-1072 ACL FAQs: www.franz.com/Support/ or ftp.franz.com/pub/*faq **Please** cc <franz.com at pc-support> on all mail related to this matter. Be sure to include tracking number [pcspr3863] in the subject line. ;; copyright (c) 1990-1996 Franz Inc, Berkeley, CA ;; ;; The software, data and information contained herein are proprietary ;; to, and comprise valuable trade secrets of, Franz, Inc. They are ;; given in confidence by Franz, Inc. pursuant to a written license ;; agreement, and may be stored and used only in accordance with the terms ;; of such license. ;; ;; Restricted Rights Legend ;; ------------------------ ;; Use, duplication, and disclosure of the software, data and information ;; contained herein by any agency, department or entity of the U.S. ;; Government are subject to restrictions of Restricted Rights for ;; Commercial Software developed at private expense as specified in ;; DOD FAR Supplement 52.227-7013 (c) (1) (ii), as applicable. ;; ;; ;; >>> loader.lsp ;; loads the rest of the DDE source files ;; copyright (c) 1994 Franz Inc, Berkeley, CA ;; All rights reserved. ;; ;; Permission is granted only to any individual or institution which has ;; current Allegro CL license(s) to use, copy, or modify this software, ;; provided any reproduction or distribution of binary versions of this ;; software are compiled with a licensed Allegro CL, and provided ;; that this complete copyright and permission notice is maintained, intact, ;; in all copies and supporting documentation. ;; ;; Franz Incorporated provides this software "as is" without ;; express or implied warranty. ;; ;; Restricted Rights Legend ;; ------------------------ ;; Use, duplication, and disclosure of the software, data and information ;; contained herein by any agency, department or entity of the U.S. ;; Government are subject to restrictions of Restricted Rights for ;; Commercial Software developed at private expense as specified in FAR ;; 52.227-19 or DOD FAR Supplement 252 52.227-7013 (c) (1) (ii), as ;; applicable. (in-package :user) (dolist (filename (list "defpack.lsp" "utils.lsp" "main.lsp" )) (print filename) (load (namestring (merge-pathnames filename *load-pathname*)) :verbose nil :print nil)) ;; <<< loader.lsp ;; >>> defpack.lsp ;; define the package for the DDE facility ;; copyright (c) 1994 Franz Inc, Berkeley, CA ;; All rights reserved. ;; ;; Permission is granted only to any individual or institution which has ;; current Allegro CL license(s) to use, copy, or modify this software, ;; provided any reproduction or distribution of binary versions of this ;; software are compiled with a licensed Allegro CL, and provided ;; that this complete copyright and permission notice is maintained, intact, ;; in all copies and supporting documentation. ;; ;; Franz Incorporated provides this software "as is" without ;; express or implied warranty. ;; ;; Restricted Rights Legend ;; ------------------------ ;; Use, duplication, and disclosure of the software, data and information ;; contained herein by any agency, department or entity of the U.S. ;; Government are subject to restrictions of Restricted Rights for ;; Commercial Software developed at private expense as specified in FAR ;; 52.227-19 or DOD FAR Supplement 252 52.227-7013 (c) (1) (ii), as ;; applicable. (unless (find-package :dde) (make-package :dde)) (in-package :dde) (provide :dde) (defpackage :dde (:use :common-lisp :ct) (:export *server-active-p* *service-name* *service-topics* *sysitems* *active-server-ports* *active-client-ports* *case-sensitive-dde* open-server close-server close-dde client-port port-name port-application port-topic open-port close-port port-open-p send-request answer-request send-command execute-command send-value receive-value post-advice receive-advice list-to-tabbed-string )) ;; <<< defpack.lsp ;; >>> utils.lsp ;; miscellaneous utilities for use by the DDE facility ;; copyright (c) 1994 Franz Inc, Berkeley, CA ;; All rights reserved. ;; ;; Permission is granted only to any individual or institution which has ;; current Allegro CL license(s) to use, copy, or modify this software, ;; provided any reproduction or distribution of binary versions of this ;; software are compiled with a licensed Allegro CL, and provided ;; that this complete copyright and permission notice is maintained, intact, ;; in all copies and supporting documentation. ;; ;; Franz Incorporated provides this software "as is" without ;; express or implied warranty. ;; ;; Restricted Rights Legend ;; ------------------------ ;; Use, duplication, and disclosure of the software, data and information ;; contained herein by any agency, department or entity of the U.S. ;; Government are subject to restrictions of Restricted Rights for ;; Commercial Software developed at private expense as specified in FAR ;; 52.227-19 or DOD FAR Supplement 252 52.227-7013 (c) (1) (ii), as ;; applicable. ;; Changelog ;; <1> cheetham 3/16/95 ;; add an option for case-sensitive strings to see if that fixes the ;; problem in [pcspr2020] ;; <2> cheetham 6/30/95 ;; make dde-warn go through the condition system, though we ;; lose the "DDE" prefix at the beginning of the message ;; <3> cheetham 3/14/96 ;; default *case-sensitive-dde* to NIL rather than t, to solve the ;; problem where the OS always passes "SYSTEM" as "System". If a user ;; actually needs the case-sensitivity, they can set the variable. ;; Remember from [pcspr3263] that once a string handle is allocated ;; for "Foo", then string handles later allocated by the OS for "foo", ;; "FOO", or "foO" will be the same handle to "Foo"! So if an app ;; DOES require case-sensitivity, you have to hope that no one has ;; allocated a needed string in a different case! Also, the string ;; "System" is apparently already allocated in the string handle table ;; by the OS, and so even if someone sends us "SYSTEM" ;; or "system" what we get is always "System", and hence our server ;; topic "SYSTEM" cannot be connected to if our server is running with ;; *case-sensitive-dde* set to non-NIL! (in-package :dde) ;; ----------------------------------------- ;; variables and constants ;; ID for this lisp process (defvar *app-id* nil) ;; The service-name that we establish for our server with open-server (defvar *service-name* :allegro) ;; The allowed topics with which clients can connect to our server ;; A null list indicates that any topic is allowed ;; A NIL within the list indicates that the null string topic is allowed (defparameter *service-topics* '(nil :system :eval)) (defparameter *sysitems* '(:sysitems :topics :help)) ;; Whether we have established this lisp process as a DDE server (defvar *server-active-p* nil) (defvar *active-server-ports* nil) (defvar *active-client-ports* nil) (defparameter *null-string-handle* (ct:callocate win:hsz :initial-value 0)) (defparameter null-character <) at #\Control-> (defparameter *buffer-size* 256) (defparameter *buffer* (make-string *buffer-size*)) (defvar *dde-keys* nil) (defparameter *initial-dde-keys* '(:allegro :command-result :eval)) (defparameter *string-handle-to-symbol* (make-hash-table :size 16)) (defconstant null-char-string (make-string 1 :initial-element (int-char 0))) (defconstant crlf (format nil "~c~c" #\newline #\linefeed)) ;; ------------------------------------------------- ;; Automate the creation and management of "string handles" for each ;; application, topic, and item name. The user can simply pass ;; keyword symbols or strings for these, and one string handle ;; will be maintained and used for each one. If a string is used, ;; a keyword is still created for mapping from the lisp symbol to ;; the string handle via the symbol's plist. Mapping is done in ;; the other direction (from string handle to lisp keyword) via ;; a hash table. (defparameter *case-sensitive-dde* nil ;; <1><3> "Set this to t if you're using DDE with an application that requires case-sensitive strings. If NIL, the symbols that lisp uses for these strings may print more nicely") (defmethod get-string-handle ((string string)) (get-string-handle (intern (if *case-sensitive-dde* ;; <1> string (string-upcase string)) (symbol-package :start)))) (defmethod get-string-handle ((symbol symbol)) (or (getf (symbol-plist symbol) :string-handle) (make-string-handle symbol))) (defun make-string-handle (symbol) (let ((string-handle (win:ddecreatestringhandle *app-id* (if *case-sensitive-dde* ;; <1> (symbol-name symbol) (string-upcase (symbol-name symbol))) win:cp_winansi))) ;; not unicode (setf (get symbol :string-handle) string-handle) (push symbol *dde-keys*) (setf (gethash (handle-value win:hsz string-handle) *string-handle-to-symbol*) symbol) string-handle)) (defun string-handle-to-symbol (string-handle) (and (not (null-handle-p win:hsz string-handle)) (or (find-symbol-for-string-handle string-handle) (make-symbol-for-string-handle string-handle)))) (defun find-symbol-for-string-handle (string-handle) ;; Find the symbol for string handle that we created (gethash (handle-value win:hsz string-handle) *string-handle-to-symbol*)) (defun make-symbol-for-string-handle (string-handle) ;; I guess this is needed only if we want the server to ;; report what strings clients are passing to it which the ;; server itself hasn't defined (as a server normally does ;; ahead of time) (let* ((length (win:ddequerystring *app-id* string-handle ct:hnull 0 win:cp_winansi)) (buffer (make-string length)) symbol) (win:ddequerystring *app-id* string-handle buffer (1+ length) win:cp_winansi) (setq symbol (intern (if *case-sensitive-dde* ;; <1> buffer (string-upcase buffer)) (symbol-package :start))) (setf (gethash (handle-value win:hsz string-handle) *string-handle-to-symbol*) symbol) symbol)) (defun release-string-handles () (let ((handle (ccallocate win:hsz))) (maphash #'(lambda (string-handle-value symbol) (setf (handle-value win:hsz handle) string-handle-value) (win:ddefreestringhandle *app-id* handle)) *string-handle-to-symbol*) (clrhash *string-handle-to-symbol*))) ;; ------------------------------------ (defun hconv-to-server-port (hconv) ;; Find our lisp port object given its port handle (if (null-handle-p win:hconv hconv) nil (dolist (c *active-server-ports* hconv) (when (handle= win:hconv hconv (port-handle c)) (return c))))) (defun hconv-to-client-port (hconv) (if (null-handle-p win:hconv hconv) nil (dolist (c *active-client-ports* hconv) (when (handle= win:hconv hconv (port-handle c)) (return c))))) ;; ----------------------------------- ;; classes (defclass dde-port () ;; A DDE conversation ((name :initform nil :initarg :name :accessor port-name) (topic :initform nil :initarg :topic :accessor port-topic) (handle :initform nil :initarg :handle :accessor port-handle) )) (defmethod object-name ((object dde-port)) (port-name object)) (defclass server-port (dde-port) ;; A conversation where this lisp is the DDE server ;; Allow the client to maintain a current package between commands ((package :initform *package*) ;; The value returned by the previous execute (result :initform nil) ;; Items for which this port currently has ;; hot or warm links established ;; Apparently this is just for our general information, since ;; ddepostadvise will request data only where the links are ;; established anyway (hot-links :initform nil :accessor hot-links) (warm-links :initform nil :accessor warm-links) )) (defclass client-port (dde-port) ;; A conversation where this lisp is the DDE client ((application :initform nil :initarg :application :accessor port-application) )) (defmethod initialize-instance ((conv client-port) &rest initargs) (unless (getf initargs :name) (setf (port-name conv) (gensym-sequential-name :client))) (call-next-method)) (defun port-open-p (port) (port-handle port)) (defmethod print-object ((conv client-port) stream) (if (port-open-p conv) (format stream "#<Port ~a (Topic ~a of ~a)>" (port-name conv) (port-topic conv) (port-application conv) ) (format stream "#<CLOSED port ~a (Topic ~a of ~a)>" (port-name conv) (port-topic conv) (port-application conv) ))) (defmethod print-object ((conv server-port) stream) (if (port-handle conv) (format stream "#<port ~a (Topic ~a)>" (port-name conv) (port-topic conv) ) (format stream "#<CLOSED port ~a>" (port-name conv)))) ;; ----------------------------------- ;; Utility functions (defun convert-returned-dde-string (string) (if (position #\tab string) (tabbed-string-to-list string) (crlf-string-to-list string))) #+acl3.0 (defun tabbed-string-to-list (string) (delimited-string-to-list string #\tab)) #-acl3.0 (defun tabbed-string-to-list (string) ;; Separate items that are returned by DDE servers as ;; a string with TAB between successive items, such as Word for Windows (do* ((s string (subseq s (1+ index))) (index (position #\tab s) (position #\tab s)) (list (list (subseq s 0 index)) (nconc list (list (subseq s 0 index))))) ((null index) list))) #+acl3.0 (defun list-to-tabbed-string (list) (list-to-delimited-string list #\tab)) #-acl3.0 (defun list-to-tabbed-string (list) (let ((format-string "~{~a~^x~}")) (setf (elt format-string 6) #\tab) ;; must be a better way (format nil format-string list))) #+acl3.0 (defun crlf-string-to-list (string) (delimited-string-to-list string crlf)) #-acl3.0 (defun crlf-string-to-list (string) ;; Separate items that are returned by DDE servers as ;; a string with CRLF between successive items, as Program Manager (do* ((s string (subseq s (+ index 2))) (index (search crlf s) (search crlf s)) (list (list (subseq s 0 index)) (nconc list (list (subseq s 0 index))))) ((null index) list))) (defun dde-warning (format-string &rest format-args) ;; <2> (apply #'warn format-string format-args)) #+old ;; 6/30/95 (defun dde-warning (format-string &rest format-args) (apply #'format *error-output* (concatenate 'string "~%;; DDE Warning: " format-string) format-args)) (defun ensure-buffer-size (length) (unless (> (length *buffer*) length) (setq *buffer* (make-string (setq *buffer-size* (1+ length)))))) (defun string-from-buffer (length) (subseq *buffer* 0 (min length (or (position null-character *buffer*) most-positive-fixnum)))) ;; <<< utils.lsp ;; >>> main.lsp ;; Generalized clossified DDE code ;; copyright (c) 1994 Franz Inc, Berkeley, CA ;; All rights reserved. ;; ;; Permission is granted only to any individual or institution which has ;; current Allegro CL license(s) to use, copy, or modify this software, ;; provided any reproduction or distribution of binary versions of this ;; software are compiled with a licensed Allegro CL, and provided ;; that this complete copyright and permission notice is maintained, intact, ;; in all copies and supporting documentation. ;; ;; Franz Incorporated provides this software "as is" without ;; express or implied warranty. ;; ;; Restricted Rights Legend ;; ------------------------ ;; Use, duplication, and disclosure of the software, data and information ;; contained herein by any agency, department or entity of the U.S. ;; Government are subject to restrictions of Restricted Rights for ;; Commercial Software developed at private expense as specified in FAR ;; 52.227-19 or DOD FAR Supplement 252 52.227-7013 (c) (1) (ii), as ;; applicable. ;; Changelog ;; <1> cheetham 1/4/95 ;; new code to handle DDEPoke on the client side with send-value ;; and the server side with receive value. Also combine the ;; calls to ddeclienttransaction into one place ;; <2> cheetham 3/14/96 ;; where the callback function had returned t or NIL to messages from ;; a DDE client, instead return TRUE (1) or FALSE (0). I think that ;; t and NIL had actually been converted properly already (even though ;; the return value is not a boolean and is sometimes integers such ;; as win:dde_fack), but this makes it clearer exactly what is being ;; returned. I had thought that this may be the problem in [pcspr3263], ;; but that turned out to be a case-sensitivity problem. (in-package :dde) ;; ------------------------------------------------------ ;; Exported client functions (defun open-port (port) (ensure-dde-open) (when (port-handle port) (dde-warning "~a is already open" port) (return-from open-port nil)) (let* ((application (port-application port)) (topic (port-topic port)) (application-handle (if application (get-string-handle application) *null-string-handle*)) (topic-handle (if topic (get-string-handle topic) *null-string-handle*)) (hconv (win:ddeconnect *app-id* application-handle topic-handle ct:hnull)) error-code) (cond ((null-handle-p win:hconv hconv) (setq error-code (win:ddegetlasterror *app-id*)) (case error-code (#.win:dmlerr_no_conv_established (dde-warning "There is no application ~s running ~ that has a topic named ~s" application topic)) (t (error "DDE client connection failed to topic ~a of ~ application ~a. Error code = ~a" topic application ;; ??? Interpret these error codes error-code))) nil) (t (setf (port-handle port) hconv) (push port *active-client-ports*) port)))) (defun close-port (client-port) (let ((hconv (port-handle client-port))) (cond (hconv (win:ddedisconnect hconv) (setf (port-handle client-port) nil) (setq *active-client-ports* (delete client-port *active-client-ports*)) t) (t (dde-warning "Client ~a is already closed" (port-name client-port)) nil)))) (defun send-command (client-port command-string &key (timeout 1000)) ;; Does a DDE Execute (client-transaction :command client-port nil command-string timeout)) (defun send-request (client-port item &key (link :cold)(timeout 1000)) ;; Does a DDE Request (or starts or stops asking for Advice) ;; LINK can be one of (:cold :warm :hot :stop) (let ((global-buffer-handle (client-transaction link client-port item nil timeout)) length) (cond ((and global-buffer-handle (not (null-handle-p hconv global-buffer-handle))) (setq length (win:ddegetdata global-buffer-handle ct:hnull 0 0)) (ensure-buffer-size length) (win:ddegetdata global-buffer-handle *buffer* *buffer-size* 0) (and (plusp length) (convert-returned-dde-string (string-from-buffer length))) ) (t (lisp-warning "The DDE server did not respond the the ~ request for item ~s of topic ~s" item (port-topic client-port)) nil)))) (defun send-value (client-port item value-string ;; <1> &key (timeout 1000)) ;; Does a DDE Poke (let ((result (client-transaction :value client-port item value-string timeout))) (and (not (null-handle-p hconv result)) (handle-value hconv result)))) (defun client-transaction (transaction-type client-port item string timeout) (let* ((hconv (port-handle client-port)) (length 0) (item-handle nil) (result nil) ) (cond ((and hconv (not (null-handle-p hconv hconv))) (when item (setq item-handle (get-string-handle item))) (when string (setq length (length string)) (ensure-buffer-size length) (setf (subseq *buffer* 0 length) string) (setf (elt *buffer* length) null-character)) (setq result (win:ddeclienttransaction (if string *buffer* ct:hnull) ;; lpbData (the data) (if string (1+ length) 0) ;; cbData (length of data) hconv ;; conversation handle (if item item-handle *null-string-handle*) ;; hszItem ;; uFmt (data format) (if (eq transaction-type :command) 0 win:cf_text) ;; uType (type of transaction) (case transaction-type (:command win:xtyp_execute) (:value win:xtyp_poke) ;; <1> (:cold win:xtyp_request) ;; Four "request" types (:hot win:xtyp_advstart) (:warm (logior win:xtyp_advstart win:xtypf_nodata)) (:stop win:xtyp_advstop) ) timeout ;; timeout in milliseconds ct:hnull ;; ignore the return code )) result) (t (dde-warning "Client port ~s is not open" client-port) nil)))) (defmethod receive-advice ((port dde-port) topic item string) ;; Client receives advice from an earlier send-request with ;; a link type of :warm or :hot. ;; Define this method to receive intermittent updates. t) ;; ------------------------------------------------------ ;; Exported server functions (defun open-server (&key (name *service-name*) (topics *service-topics*)) ;; Establish this lisp process as a DDE server. A client can connect ;; to us using the service name and topics established here (ensure-dde-open) (cond (*server-active-p* (dde-warning "DDE Server is already active") nil) (t (win:ddenameservice *app-id* (get-string-handle name) *null-string-handle* win:dns_register) (setq *service-name* name) (setq *service-topics* topics) (setq *server-active-p* t) t))) (defun close-server () (cond ((and *app-id* *server-active-p*) (win:ddenameservice *app-id* (get-string-handle *service-name*) *null-string-handle* win:dns_unregister) (setq *active-server-ports* nil) (setq *server-active-p* nil) t) (t (dde-warning "DDE server already closed") nil))) ;; Modify this default method to do your own interpretation of a client ;; command, which is a string passed by a client issuing a DDE EXECUTE (defmethod execute-command (topic command-string) (format nil "No execute-command method supplied for topic ~s" topic)) ;; For the special topic :eval, evaluate the command string as a ;; lisp form. Note that this won't work in a runtime lisp since it ;; calls eval, which invokes the compiler (defmethod execute-command ((topic (eql :eval)) command-string) (let ((*read-tolerant* t) ) (eval (read-from-string command-string)))) ;; Modify this default method to return a string according to the ;; keyword arguments for the topic and item passed by a ;; client's DDE REQUEST message (defmethod answer-request (topic item command-result) "") ;; For the special topic :eval, return the value of the symbol named ;; by the item argument, except in the current package (defmethod answer-request ((topic (eql :eval)) item command-result) (format nil "~s" (symbol-value (intern (symbol-name item) *package*)))) ;; For the special item :command-result, return the value that was returned ;; by the most recent execute-command method invoked for this client (defmethod answer-request ((topic (eql :eval))(item (eql :command-result)) command-result) (format nil "~s" command-result)) ;; Some standard DDE topics and items to respond to (defmethod answer-request ((topic (eql :system))(item (eql :sysitems)) command-result) (list-to-tabbed-string *sysitems*)) (defmethod answer-request ((topic (eql :system))(item (eql :topics)) command-result) (list-to-tabbed-string *service-topics*)) (defmethod answer-request ((topic (eql :system))(item (eql :help)) command-result) "Send a DDE request for the HELP item of other topics for info on those topics") (defmethod answer-request ((topic (eql :eval))(item (eql :help)) command-result) #.(format nil "Send a DDE Execute using this EVAL topic ~ to evaluate an arbitrary lisp form. To get the value returned by that ~ form, send a DDE Request using this EVAL topic with the item ~ COMMAND-RESULT. To retrieve the value of any lisp symbol, send it as ~ the item in a DDE Request using this EVAL topic.")) ;; Call this function whenever an item for which this lisp server handles ;; hot or warm links has changed. This will result in answer-request ;; being invoked for any items for which hot or warm links are currently ;; established (defun post-advice (topic item) (win:DDEPostAdvise *app-id* (get-string-handle topic) (get-string-handle item))) ;; Modify this default method to do your own interpretation of a ;; value that is sent (poked) by a client. This default method ;; interprets the item as the name of a symbol in the current package, ;; and sets that symbol to a value read from the value-string (defmethod receive-value (topic item value-string) ;; <1> (let* ((*read-tolerant* t)) (set (intern (symbol-name item) *package*) (eval (read-from-string value-string))))) ;; ------------------------------------ ;; DDE initialization and clean-up (defun ensure-dde-open () (unless *app-id* ;; dde already initialized ;; Initialize the DDE library (let* ((scratch-long (ccallocate (:long 1))) (init-return (progn (setf (cref (:long 1) scratch-long 0) 0) (win:ddeinitialize scratch-long (ct:get-callback-procinst 'dde-callback) (logior win:appclass_standard #+old win:cbf_fail_pokes ;; <1> ;; ??? I guess we should only allow self-connections ;; for testing #+later win:cbf_fail_selfconnections win:cbf_skip_registrations win:cbf_skip_unregistrations) 0)))) ;; DDE initialization errors (case init-return (#.win:dmlerr_no_error (setq *app-id* (ct:cref (:long 1) scratch-long 0)) t) (#.win:dmlerr_invalidparameter (error "DML: Invalid Parameter")) (#.win:dmlerr_dll_usage (error "DML: Dll Usage")) (#.win:dmlerr_sys_error (error "DML: Sys Error")) (t (error "DML return code ~a" init-return)))) ;; Be sure to clean up resources at lisp exit (pushnew 'close-dde acl::*system-exit-fns*) ;; initialize the standard string-handles up front for efficiency (dolist (key *initial-dde-keys*) (make-string-handle key)) (setq *active-server-ports* nil) (setq *active-client-ports* nil) )) (defun close-dde (&rest ignore) (declare (ignore ignore)) ;; Passed as a lisp exit function (cond (*app-id* (when *server-active-p* (close-server)) (dolist (conv *active-client-ports*) (close-port conv)) (release-string-handles) (win:ddeuninitialize *app-id*) (setq *app-id* nil) t) (t (dde-warning "DDE already closed") nil))) ;; ---------------------------------------- ;; The DDE callback and handlers (ct:defun-callback dde-callback ((utype win:uint) (ufmt win:uint) (hconv win:hconv) (hsz1 win:hsz) (hsz2 win:hsz) (hdata win:hddedata) (dwdata1 :long) (dwdata2 :long)) (case utype (#.win:xtyp_connect (t.connect (string-handle-to-symbol hsz2) (string-handle-to-symbol hsz1) dwdata1)) (#.win:xtyp_connect_confirm (t.connect-confirm (string-handle-to-symbol hsz2) (string-handle-to-symbol hsz1) hconv)) (#.win:xtyp_disconnect (t.disconnect (hconv-to-server-port hconv))) (#.win:xtyp_error (t.error (hconv-to-server-port hconv) dwdata1)) ;; Server receives a command to execute (#.win:xtyp_execute (t.execute (hconv-to-server-port hconv) ;; port (string-handle-to-symbol hsz1) ;; topic hdata)) ;; command string ;; Server receives a poked value (#.win:xtyp_poke ;; <1> (t.poke (hconv-to-server-port hconv) ;; port ufmt ;; data format (string-handle-to-symbol hsz1) ;; topic (string-handle-to-symbol hsz2) ;; item hdata)) ;; value string ;; Server receives a request to answer (a cold link), ;; or the server's call to post-advice causes an advreq here ((#.win:xtyp_request #.win:xtyp_advreq) (t.request (hconv-to-server-port hconv) ;; port ufmt ;; data format (string-handle-to-symbol hsz1) ;; topic (string-handle-to-symbol hsz2))) ;; item ;; Server receives a request to start advice (a hot link) (#.win:xtyp_advstart (t.advstart (hconv-to-server-port hconv) ;; port ufmt ;; data format (string-handle-to-symbol hsz1) ;; topic (string-handle-to-symbol hsz2) ;; item :hot)) ;; Server receives a request to start advice (a warm link) (#.(logior win:xtyp_advstart win:xtypf_nodata) (t.advstart (hconv-to-server-port hconv) ;; port ufmt ;; data format (string-handle-to-symbol hsz1) ;; topic (string-handle-to-symbol hsz2) ;; item :warm)) ;; Server receives a request to stop a hot or warm link (#. win:xtyp_advstop (t.advstop (hconv-to-server-port hconv) ;; port ufmt ;; data format (string-handle-to-symbol hsz1) ;; topic (string-handle-to-symbol hsz2))) ;; item ;; Client receives advice for a warm or hot link (#.win:xtyp_advdata (t.advdata (hconv-to-client-port hconv) ;; port (string-handle-to-symbol hsz1) ;; topic (string-handle-to-symbol hsz2) ;; item hdata)) ;; arbitray data (#.win:xtyp_wildconnect (t.wildconnect (string-handle-to-symbol hsz2) (string-handle-to-symbol hsz1) dwdata1)) (t (t.unknown hconv utype ufmt hsz1 hsz2 hdata dwdata1 dwdata1)))) (defmethod t.wildconnect (application topic context) (cond ((and (eq application *service-name*) (or (null *service-topics*) (member topic *service-topics* :test #'eq))) (lisp-message "Accepting wild connection to application ~s and topic ~s" application topic) t) (t (dde-warning "Refusing wild connection to application ~s and topic ~s" application topic) nil))) (defmethod t.connect (application topic context) ;; Allow a connection to our lisp's service-name if one of ;; our valid topics is passed (cond ((and (eq application *service-name*) (or (null *service-topics*) (member topic *service-topics* :test #'eq))) (lisp-message "Accepting connection to application ~s and topic ~s" application topic) true) ;; <2> (t ;; Refuse connections that request other topics (dde-warning "Refusing connection to application ~s and topic ~s" application topic) false))) ;; <2> (defmethod t.connect-confirm (application topic hconv) (let ((port (make-instance 'server-port :name (gensym-sequential-name :server) :topic topic :handle hconv)) ) (push port *active-server-ports*) (lisp-message "DDE: Port ~s connection confirmed" port))) (defmethod t.disconnect (port) ;; default method for unknown clients (dde-warning "DDE: Unexpected disconnect transaction for port ~s" port)) (defmethod t.disconnect ((port dde-port)) (lisp-message "~s disconnected" port) (setq *active-server-ports* (delete port *active-server-ports*))) (defun data-buffer-to-handle (string id) (let* ((length (length string)) (hdata (win:ddecreatedatahandle *app-id* ct:hnull (1+ length) 0 id win:cf_text 0))) (win:ddeadddata hdata string length 0) (win:ddeadddata hdata null-char-string 1 length) hdata)) (defmethod t.error (port code) (dde-warning "DDE: Error transaction code ~s for port ~s" code port)) (defmethod t.execute (port topic hdata) ;; default method is to refuse the execute (dde-warning "DDE: Execute refused for unknown port") win:dde_fnotprocessed) (defmethod t.execute ((port dde-port) topic hdata) ;; but if we have a port going, we do it (let ((length (win:ddegetdata hdata ct:hnull 0 0)) ; length of data lisp-string result) (ensure-buffer-size length) (win:ddegetdata hdata *buffer* length 0) (let ((*package* (slot-value port 'package))) (setq lisp-string (string-from-buffer length)) (setq result (execute-command topic lisp-string)) (setf (slot-value port 'result) result) (setf (slot-value port 'package) *package*) ) (lisp-message "DDE: Command ~s returns ~s" lisp-string result) ) win:dde_fack) (defmethod t.advdata (port topic item hdata) (dde-warning "DDE: Advice received for unknown port ~s" port) win:dde_fnotprocessed) (defmethod t.advdata ((port dde-port) topic item hdata) ;; Client receives advice from the server, resulting from an ;; earlier send-request for a hot or warm link (let ((length (win:ddegetdata hdata ct:hnull 0 0)) ; length of data lisp-string advice) (ensure-buffer-size length) (win:ddegetdata hdata *buffer* length 0) (cond ((plusp length) ;; Hot link (receive-advice port topic item (setq advice (convert-returned-dde-string (string-from-buffer length)))) (lisp-message "DDE: Hot advice received for ~ application ~a topic ~a item ~a --> ~s" (port-application port) topic item advice) ) (t (receive-advice port topic item nil) (lisp-message "DDE: Warm advice received for ~ application ~a topic ~a item ~a" (port-application port) topic item)))) win:dde_fack) (defmethod t.advstart ((port dde-port) ufmt topic item type) ;; Server receives a request to begin sending advice (a hot or warm link) (if (eq type :hot) (push item (hot-links port)) (push item (warm-links port))) true) ;; <2> (defmethod t.advstop ((port dde-port) ufmt topic item) ;; Server receives a request to stop sending advice (a hot or warm link) (delete item (hot-links port)) (delete item (warm-links port)) true) ;; <2> (defmethod t.request ((port dde-port) ufmt topic item) ;; Server receives a standalone request (cold link) (let* ((*package* (slot-value port 'package)) (buffer (answer-request topic item ;; For the special :command-result item, always return the value that ;; was returned by the last execute command (slot-value port 'result))) hdata) (cond ((stringp buffer) (setq hdata (data-buffer-to-handle buffer (get-string-handle item))) (lisp-message "DDE: Request ~s returns ~s" item buffer) (handle-value win:hddedata hdata)) (t (dde-warning "DDE: Non-string returned by ~ answer-request method; returning NULL handle to the client") nil)))) (defmethod t.poke ((port dde-port) ufmt topic item hdata) ;; <1> ;; Server receives a poked value (let ((length (win:ddegetdata hdata ct:hnull 0 0)) ; length of data (*package* (slot-value port 'package)) lisp-string result) (ensure-buffer-size length) (win:ddegetdata hdata *buffer* length 0) (setq lisp-string (string-from-buffer length)) (setq result (receive-value topic item lisp-string)) (cond (result (lisp-message "DDE: Accepted value for topic ~s ~ item ~s new value ~s" topic item lisp-string) win:dde_fack) (t (lisp-warning "DDE: Rejected value for topic ~s ~ item ~s new value ~s" topic item lisp-string) win:dde_fnotprocessed)))) (defmethod t.unknown (port utype ufmt hsz1 hsz2 hdata dwdata1 dwdata) (dde-warning "DDE: Unexpected transaction ~s for ~s~%" utype port)) #.(export 'dde::free-item :dde) (defun free-item (item-string-or-symbol) (let* ((symbol (if (stringp item-string-or-symbol) (find-symbol (if *case-sensitive-dde* item-string-or-symbol (string-upcase item-string-or-symbol)) (symbol-package :start)) item-string-or-symbol)) handle) (unless symbol (error "Tried to free an unused DDE item string")) (setq handle (get symbol :string-handle)) (when handle (win:ddefreestringhandle *app-id* handle) (remprop symbol :string-handle)) (unintern symbol (symbol-package symbol)))) ;; <<< main.lsp |