home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
BUG 4
/
BUGCD1997_05.BIN
/
aplic
/
clip4win
/
clip4win.exe
/
C4W30E.HUF
/
SOURCE
/
DDE.PRG
< prev
next >
Wrap
Text File
|
1996-03-06
|
20KB
|
632 lines
////////////////////////////
//
// Clip-4-Win sample source
//
// Copyright (C) 1993 Skelton Software, Kendal Cottage, Hillam, Leeds, UK.
// All Rights Reserved.
//
// Written by: John M. Skelton, Jan. 93.
//
// Compile: dde /n /w
// Link: /se:600 dde,,,clip4win,clip4win.def
//
////////////////////////////
#define WIN_WANT_CLIPBOARD
#include "windows.ch"
#include "dde.ch"
#define CR chr(13)
#define MAKELONG(low, high) (low + (high * 65536))
#define MAKELPARAM(low, high) (low + (high * 65536))
#define GMEM_DDESHARE 8192 // magic number for Windows
#define GMEM_MOVEABLE 2 // magic number for Windows
#ifndef LIB_ONLY // for clip4win.lib use
static cAppName := "Clip-4-Win"
static hWnd, hInst, hPrevInst, nCmdShow
static cText := ""
function main()
local hMenu, nEvent
set scoreboard off
setcolor("n/w,w/n,w,w,w/n")
hWnd = WinSetup(cAppName, "Clip-4-Win Demo")
hMenu = MenuSetup()
HideCaret(hWnd)
do while .t.
do while (nEvent := ChkEvent()) == EVENT_NONE
// "background" processing could go here
enddo
HandleEvent(nEvent)
do case
case nEvent == EVENT_QUIT
DoExit()
endcase
enddo
return nil
procedure DoAbout()
MessageBox( , "Demo written by John Skelton", "About", ;
MB_ICONEXCLAMATION + MB_OK)
return
procedure DoExit()
quit
return
procedure DoServers()
static nX := 20, nY := 50
local nEvent, aDDE := {}, hWndProgMan, hWndPMClient
local hWndServer, hWndClient, hItem, i, s := "", hDC, aTM
// Broadcast to all DDE servers, asking for information (just to show how)
// (Note: Program Manager does not reply, for no apparent reason)
aDDE = DDEGetTopics(hWnd)
//MessageBox( , nstr(len(aDDE)), "DDEGetTopics length", MB_OK)
for i = 1 to len(aDDE)
s += aDDE[i, 1] + "|" + aDDE[i, 2] + CR
next i
MessageBox( , s, "DDE Servers and Topics", MB_OK)
return
procedure DoProgMan()
static nX := 20, nY := 50
local nEvent, aDDE := {}, hWndProgMan, hWndPMClient
local hWndServer, hWndClient, hItem, i, s := "", hDC, aTM
// Arrange to talk to Program Manager.
//
// Note: Both App and Topic need to be PROGMAN, rather surprisingly.
hWndPMClient = WinNew(cAppName, "DDE Client", nX += 40, nY += 60, 150, 100)
hWndProgMan = DDEStart(hWndPMClient, "PROGMAN", "PROGMAN")
if hWndProgMan != nil
// Program Manager responded, and the conversation is on-going
// let's get a Group list
// (Which arrives asynchronously, just to make things a pain)
AddHandler(hWndPMClient, {|nEvent| DDEPMgrEvent(nEvent, hWndPMClient, hWndProgMan)})
DDERequest(hWndProgMan, hWndPMClient, CF_TEXT)
else
// no response
DestroyWindow(hWndPMClient)
endif
return
procedure DDEPMgrEvent(nEvent, hWnd, hWndServer)
static lTerm := .f.
local nMsg := _LastMsg() // Windows message
local cData
if nEvent == EVENT_OTHER
// MessageBox( , nstr(nMsg), "DDEPMgrEvent", MB_OK)
endif
do case
case nMsg == WM_DDE_DATA
// here's the data
cData = DDEGetData()
MessageBox( , cData, "DDEPMgrEvent: WM_DDE_DATA", MB_OK)
// this example now terminates the conversation
lTerm = .t.
PostMessage(hWndServer, WM_DDE_TERMINATE, hWnd, 0)
case nMsg == WM_DDE_ACK
// must be a negative ack., but shouldn't happen at all
case nMsg == WM_DDE_TERMINATE
if !lTerm
// server wants to terminate, confirm by posting the msg back
PostMessage(hWndServer, WM_DDE_TERMINATE, hWnd, 0)
// else this is the reply from the server, confirming termination
endif
lTerm = .f.
DestroyWindow(hWnd)
endcase
return
// This shows how to use DDE Advise, with the SDK sample DDE server
// (if you have it).
procedure DoSDK()
static nX := 20, nY := 50
local nEvent, aDDE := {}, hWndProgMan, hWndPMClient
local hWndServer, hWndClient, hItem, i, s := "", hDC, aTM
// Arrange to talk to SDK sample server.
hWndClient = WinNew(cAppName, "DDE Client", nX += 40, nY += 60, 150, 100)
hWndServer = DDEStart(hWndClient, "Server", "File1")
if hWndServer != nil
// server responded, and the conversation is on-going
// let's get an item
// (which arrives asynchronously, just to make things a pain)
AddHandler(hWndClient, {|nEvent| DDEEvent(nEvent, hWndClient, hWndServer)})
DDEAdvise(hWndServer, hWndClient, CF_TEXT, "Item1")
else
// no response
DestroyWindow(hWndClient)
MessageBox( , "SDK Server not responding", "ERROR", MB_OK)
endif
return
procedure DDEEvent(nEvent, hWnd, hWndServer)
//static aConv := {} // active conversations
static lTerm := .f.
static nChg := 0
local nMsg := _LastMsg() // Windows message
local cData
if nEvent == EVENT_OTHER
// MessageBox( , nstr(nMsg), "DDE Event", MB_OK)
endif
do case
case nMsg == WM_DDE_DATA
// here's the data
cData = DDEGetData()
MessageBox( , cData, "DDE Event: WM_DDE_DATA", MB_OK)
if ++nChg > 2
DDEUnAdvise(hWndServer, hWnd)
endif
// // this example now terminates the conversation
// lTerm = .t.
// PostMessage(hWndServer, WM_DDE_TERMINATE, hWnd, 0)
case nMsg == WM_DDE_ACK
// may be a negative ack., but shouldn't happen
case nMsg == WM_CLOSE
// MessageBox( , "", "DDE Event: WM_CLOSE", MB_OK)
#define WM_QUIT 18
case nMsg == WM_QUIT
// MessageBox( , "", "DDE Event: WM_QUIT", MB_OK)
case nMsg == WM_DDE_TERMINATE
if !lTerm
// server wants to terminate, confirm by posting the msg back
if IsWindow(hWndServer)
PostMessage(hWndServer, WM_DDE_TERMINATE, hWnd, 0)
// else
// MessageBox( , "!IsWindow(hWndServer)", "DDE Event", MB_OK)
endif
// else this is the reply from the server, confirming termination
endif
lTerm = .f.
DestroyWindow(hWnd)
endcase
return
#endif // !LIB_ONLY
// Get all DDE Topics for cApp, or all Apps and Topics (if cApp is NIL)
function DDEGetTopics(hWnd, cApp)
// you may prefer to change nAppAtom to hAppAtom (or not)
local nAppAtom := iif(cApp == nil, 0, GlobalAddAtom(cApp)) // 0 means all
local nEvent, aDDE := {}, hWndServer, hServer, hTopic, cServer, cTopic
// Broadcast to all DDE servers (apps), asking for information
SendMessage(-1, WM_DDE_INITIATE, hWnd, nAppAtom)
// any replies come straight back, and will be in the event queue
// Process the replies (Shell is usually there, but PROGMAN does not
// reply even though you'd expect it to do so)
do while (nEvent := ChkEvent()) != EVENT_NONE
if _LastMsg() == WM_DDE_ACK .and. _LasthWnd() == hWnd
hWndServer = _LastwParam() // hWnd of server
hServer = _LastLolParam() // server atom
hTopic = _LastHilParam() // topic atom
cServer = GlobalGetAtomName(hServer)
cTopic = GlobalGetAtomName(hTopic)
aadd(aDDE, {cServer, cTopic, hWndServer})
// we're supposed to delete the atoms, even though we didn't
// create them...
GlobalDeleteAtom(hServer)
GlobalDeleteAtom(hTopic)
// it is not clear whether the following PostMessage()
// is required, not required, or redundant
PostMessage(hWndServer, WM_DDE_TERMINATE, hWnd, 0)
// MessageBox( , cServer + ":" + cTopic, "DDE Server:Topic", MB_OK)
loop
endif
HandleEvent(nEvent)
enddo
// all the replies have been processed
if nAppAtom != 0
GlobalDeleteAtom(nAppAtom) // clean up, if we added the atom
endif
return aDDE
// Start a DDE conversation from window hWnd to cApp about cTopic
function DDEStart(hWnd, cApp, cTopic)
local nAppAtom := iif(cApp == nil, 0, GlobalAddAtom(cApp)) // 0 means all
local nTopicAtom := iif(cTopic == nil, 0, GlobalAddAtom(cTopic)) // 0 means all
local nEvent, hWndServer, hServer, hTopic, cServer
// Broadcast to the DDE server (app), to start a conversation
SendMessage(-1, WM_DDE_INITIATE, hWnd, MAKELONG(nAppAtom, nTopicAtom))
// any replies come straight back, and will be in the event queue
//
// process the replies (we only want ONE, of course)
do while (nEvent := ChkEvent()) != EVENT_NONE
if _LastMsg() == WM_DDE_ACK .and. _LasthWnd() == hWnd
if hWndServer != nil
MessageBox( , ltrim(str(hWndServer)), "DDEStart: another reply", MB_OK)
endif
hWndServer = _LastwParam() // hWnd of server
hServer = _LastLolParam() // server atom
hTopic = _LastHilParam() // topic atom
// cServer = GlobalGetAtomName(hServer)
// cTopic = GlobalGetAtomName(hTopic)
GlobalDeleteAtom(hServer)
GlobalDeleteAtom(hTopic)
// MessageBox( , cServer + ":" + cTopic, "DDE Server:Topic", MB_OK)
loop
endif
HandleEvent(nEvent)
enddo
// all the replies have been processed
if nAppAtom != 0
GlobalDeleteAtom(nAppAtom) // clean up, if we added the atom
endif
if nTopicAtom != 0
GlobalDeleteAtom(nTopicAtom) // clean up, if we added the atom
endif
return hWndServer
// Post a message to stop the DDE conversation to hWndServer from hWndClient
procedure DDEStop(hWndServer, hWndClient)
PostMessage(hWndServer, WM_DDE_TERMINATE, hWndClient, 0)
return
// Request the current value of item cItem in format nFmt
// (the data comes back with a WM_DDE_DATA message).
procedure DDERequest(hWndServer, hWndClient, nFmt, cItem)
local nAtom := iif(cItem == nil, 0, ; // 0 means all or default
GlobalAddAtom(cItem))
if nFmt == nil
nFmt = CF_TEXT
endif
PostMessage(hWndServer, WM_DDE_REQUEST, hWndClient, MAKELPARAM(nFmt, nAtom))
return
// Get the DDE data corresponding to a WM_DDE_DATA message
// (which must be the most recent message).
//
// hWndServer defaults to the sender of the WM_DDE_DATA message,
// hWndClient defaults to the receiver of the WM_DDE_DATA message.
function DDEGetData(hWndServer, hWndClient)
local hData, cData, nAtom, cAtom, nFlags, nFmt
hData = _LastLolParam()
nAtom = _LastHilParam()
cAtom = iif(nAtom == 0, "<none>", GlobalGetAtomName(nAtom))
cData = GlobalData(hData)
nFlags = c4w_bin2i(cData)
nFmt = c4w_bin2i(substr(cData, 3, 2))
//MessageBox( , nstr(nFlags) + nstr(nFmt), "DDE Data flags + fmt", MB_OK)
cData = substr(cData, 5)
//MessageBox( , cAtom + " = " + cData, "DDE Atom + Data", MB_OK)
if c4w_and(nFlags, 32768) != 0
// ack requested
if hWndServer == nil
hWndServer = _LastwParam()
endif
if hWndClient == nil
hWndClient = _LasthWnd()
endif
PostMessage(hWndServer, WM_DDE_ACK, hWndClient, MAKELONG(32768, nAtom))
endif
if c4w_and(nFlags, 8192) != 0
// need to release the data
GlobalFree(hData)
endif
return cData
// Modified from a function kindly donated by Hugh Lokey.
//
// This function does a lot of grunt work. It issues the DDE
// EXECUTE commands and if a reply is required waits for the reply.
//
// Calling conventions:
//
// hServerWnd - handle/channel of DDE server. Obtained
// from DDEStart() command.
// hClientWnd - handle of client window.
// cItem - Execute command item.
// lReply - .T. if a reply is required, .F. if not, defaults to .F.
// (the reply is in the form of a WM_DDE_DATA message).
// NOTE: All servers are supposed to send a positive/negative
// acknowledgement in the form of a WM_DDE_ACK message, so this
// is waited for in any case. lReply determines whether the
// WM_DDE_ACK is also followed by WM_DDE_DATA.
// cMsg - Required response from DDE server.
// cExit - an optional string the server might send to say it is
// shutting down (you'd really expect WM_DDE_TERMINATE).
// Advanced use only:
// =================
// For a true "generic" DDEExecute() you would NOT do any message
// handling, just send the command and handle what happens elsewhere.
// The easiest way to do this would be to remove lReply, cMsg and cExit,
// delete the DO WHILE .T. loop, and also delete the GlobalFree().
// Returning the hExecute would allow you to keep track of it so it
// could be GlobalFree()'ed later.
// You'd probably want to use something like a state machine to control
// each step of the DDE conversation. If you do this, you need to keep
// in mind that YOU MUST free the global memory allocated by the
// GlobalAlloc() call after you have processed the results of the command.
// If you really want, you can leave out the HandleEvent()
// and restrict the user to not being able to do anything funny
// while the command is being processed by the DDE server.
FUNCTION DDEExecute(hServerWnd, hClientWnd, cItem, lReply, cMsg, cExit)
LOCAL hExecute := GlobalAlloc(GMEM_MOVEABLE+GMEM_DDESHARE, ;
LEN(cItem)+1)
LOCAL nMsg, nEvent, cData
LOCAL nStatus
GlobalData(hExecute,cItem+CHR(0))
PostMessage(hServerWnd,;
WM_DDE_EXECUTE,;
hClientWnd,;
MAKELPARAM(0, hExecute))
if lReply == nil
lReply := .f.
endif
DO WHILE .T.
DO WHILE (nEvent := ChkEvent()) != EVENT_OTHER
HandleEvent(nEvent)
ENDDO
nMsg := _LastMsg()
IF nMsg = WM_DDE_ACK
nStatus := _LastLolParam()
if c4w_and(nStatus, 32768) == 0
EXIT // server rejected the WM_DDE_EXECUTE
endif
IF !lReply
EXIT // seen the ACK, and don't want DATA
ENDIF
ENDIF
IF nMsg = WM_DDE_DATA
cData := DDEGetData(hServerWnd,hClientWnd)
IF cMsg == nil .or. UPPER(cData) = UPPER(cMsg)
EXIT
ENDIF
ENDIF
IF nMsg = WM_DDE_TERMINATE .OR. cData == cExit
DDEStop(hServerWnd,hClientWnd)
EXIT
ENDIF
ENDDO
GlobalFree(hExecute)
RETURN nil
// Post data to a server. Any well-behaved server will reply with
// a WM_DDE_ACK message containing an indication of whether the server
// accepted the WM_DDE_POKE.
//
// nFmt is the format of the data (default CF_TEXT).
// cItem identifies the data.
// cData is the data itself.
// lRelease should be .t. if the server should release the memory allocated
// to contain the cData, .f. if this function should release it
// (default is .t.).
// lWait can be .f. to NOT wait for the server's WM_DDE_ACK message
// (default is .t., because the server should respond).
//
// Returns:
// if lWait was .f., handle to memory
// otherwise, .t. if server accepted the POKE, .f. if it rejected it
function DDEPoke(hWndServer, hWndClient, nFmt, cItem, cData, lRelease, lWait)
local nAtom := iif(cItem == nil, 0, GlobalAddAtom(cItem))
local nFlags, hData, nEvent, nMsg, nStatus, xRet
if nFmt == nil
nFmt = CF_TEXT
endif
if lRelease == nil
lRelease = .t.
endif
nFlags = iif(lRelease, 8192, 0)
// need hData for DDEPOKE structure
hData = GlobalAlloc(GMEM_MOVEABLE + GMEM_DDESHARE, 4 + len(cData))
GlobalData(hData, c4w_i2bin(nFlags) + c4w_i2bin(nFmt) + cData)
//MessageBox( , nstr(hWndServer) + nstr(hWndClient) + nstr(hData) + nstr(nAtom), "srv cli data atom", MB_OK)
PostMessage(hWndServer, WM_DDE_POKE, hWndClient, MAKELPARAM(hData, nAtom))
if lWait == .f.
xRet = hData
else
DO WHILE .T.
DO WHILE (nEvent := ChkEvent()) != EVENT_OTHER
HandleEvent(nEvent)
ENDDO
nMsg := _LastMsg()
IF nMsg = WM_DDE_ACK
nStatus := _LastLolParam()
if c4w_and(nStatus, 32768) == 0
xRet = .f. // server rejected the WM_DDE_EXECUTE
GlobalFree(hData)
else
xRet = .t. // normal exit
if lRelease
GlobalFree(hData)
endif
endif
GlobalDeleteAtom(_LastHilParam())
EXIT
ENDIF
IF nMsg = WM_DDE_TERMINATE
DDEStop(hWndServer,hWndClient)
GlobalFree(hData)
EXIT
ENDIF
ENDDO
endif
RETURN xRet // .t. or .f. or hData
// Ask to be kept informed of changes to the item cItem; the server will
// automatically send WM_DDE_DATA messages whenever the data changes.
// Usually you will not specify lDeferUpd or lAckReq.
// If lDeferUpd is .t., these data messages will not contain the actual
// data, which must be requested when it is actually needed.
// If lAckReq is .t., these data messages sent by the server will specify
// that a WM_DDE_ACK message is wanted.
procedure DDEAdvise(hWndServer, hWndClient, nFmt, cItem, lDeferUpd, lAckReq)
local nAtom := iif(cItem == nil, 0, GlobalAddAtom(cItem))
local nFlags, hOptions
if nFmt == nil
nFmt = CF_TEXT
endif
nFlags = iif(lDeferUpd != nil .and. lDeferUpd, 16384, 0)
nFlags += iif(lAckReq != nil .and. lAckReq, 32768, 0)
// need hData for DDEADVISE struct
hOptions = GlobalAlloc(GMEM_MOVEABLE + GMEM_DDESHARE, 4)
GlobalData(hOptions, c4w_i2bin(nFlags) + c4w_i2bin(nFmt))
//MessageBox( , nstr(hWndServer) + nstr(hWndClient) + nstr(hOptions) + nstr(nAtom), "srv cli opt atom", MB_OK)
PostMessage(hWndServer, WM_DDE_ADVISE, hWndClient, MAKELPARAM(hOptions, nAtom))
return
// Turn off being advised about changes to data item(s)
procedure DDEUnAdvise(hWndServer, hWndClient, nFmt, cItem)
local nAtom := iif(cItem == nil, 0, GlobalAddAtom(cItem)) // 0 means all
if nFmt == nil
nFmt = 0 // means all formats
endif
PostMessage(hWndServer, WM_DDE_UNADVISE, hWndClient, MAKELPARAM(nFmt, nAtom))
return
#ifndef LIB_ONLY // for clip4win.lib use
function nstr(n)
return alltrim(str(n)) + " "
function asString(x)
local v := valtype(x)
do case
case v == "C"
case v == "N"
return nstr(x)
case v == "L"
if x
return ".T."
else
return ".F."
endif
case v == "D"
return "date"
case v == "U"
return "NIL"
case v $ "AOB"
return ""
otherwise
return ""
end case
return x
function MenuSetup()
local hWnd := SelectWindow(), hMenu, hPopupMenu
if (hMenu := GetMenu(hWnd)) != nil
DestroyMenu(hMenu)
endif
// do new one (forget old value)
hMenu = CreateMenu()
hPopupMenu = CreatePopupMenu()
AppendMenu(hMenu, "dde", MF_ENABLED + MF_POPUP, "&DDE", hPopupMenu)
AppendMenu(hPopupMenu, "servers", MF_ENABLED + MF_STRING, "&All Servers", {|| DoServers()})
AppendMenu(hPopupMenu, "progman", MF_ENABLED + MF_STRING, "&ProgMan Groups", {|| DoProgMan()})
AppendMenu(hPopupMenu, "sdk", MF_ENABLED + MF_STRING, "&SDK", {|| DoSDK()})
AppendMenu(hPopupMenu, "", MF_SEPARATOR)
AppendMenu(hPopupMenu, "exit", MF_ENABLED + MF_STRING, "E&xit", {|| DoExit() })
hPopupMenu = CreatePopupMenu()
AppendMenu(hMenu, "help", MF_ENABLED + MF_POPUP, "&Help", hPopupMenu)
AppendMenu(hPopupMenu, "about", MF_ENABLED + MF_STRING, "&About", {|| DoAbout()})
SetMenu(hWnd, hMenu)
return hMenu
// function AddHandler(hWnd, bAction) // --> nId (for use with DelHandler)
// see: evhand.prg
// procedure DelHandler(nId)
// see: evhand.prg
// procedure HandleEvent(nEvent)
// see: evhand.prg
function WinNew(cAppName, cTitle, nX, nY, nWidth, nHeight)
local hWin, hInst, nCmdShow
hInst = _GetInstance()
nCmdShow = _GetnCmdShow()
hWin = CreateWindow(cAppName, ; // window class
cTitle, ; // caption for title bar
WS_OVERLAPPEDWINDOW,; // window style
nX, ; // x co-ordinate
nY, ; // y co-ordinate
nWidth, ; // width
nHeight, ; // height
hWnd, ; // hWnd of parent
0, ; // hMenu of menu (none yet)
hInst) // our own app instance
if hWin == 0
// probably out of resources
MessageBox( , "Can't create window", "Error", MB_ICONEXCLAMATION + MB_OK)
return nil
endif
HideCaret(hWin)
// make sure it's displayed ...
ShowWindow(hWin, nCmdShow)
// ... and up to date
UpdateWindow(hWin)
return hWin
#endif // !LIB_ONLY