Documenti di Didattica
Documenti di Professioni
Documenti di Cultura
0
Solutions for Microsoft® Visual FoxPro® Developers
I Got Rendered
Where?
Doug Hennig 9.0
Doug Hennig continues his discussion of VFP 9 report listeners by presenting a set
of classes that output the contents of a report run to a cursor and use that cursor to
provide a “live” report preview surface.
W
HEN you run a report in VFP 8 or earlier, the output is sort of a black
box: You have little control over the preview window, don’t have any
information about what got rendered where, and can’t provide a
Sample Issue
“live” preview surface (one in which click events can be trapped to perform
1 I Got Rendered Where?
some object-specific action). Doug Hennig
Over the past two months, I’ve discussed the new ReportListener class in
VFP 9 and how it can be used to control report output in ways that previously 7 Accessing Hotmail and MSN
Accounts in Visual FoxPro
weren’t possible. The ReportListener subclasses I’ve shown so far performed
Anatoliy Mogylevets
some type of visible output, such as HTML, reports with dynamic formatting,
and so forth. 14 Tips from the VFP Team
This month, the output from a listener won’t really go anywhere obvious The Microsoft Visual FoxPro Team
to the user; instead, it’s sent to a cursor so we can track what got rendered
15 The Kit Box: SET Things Right
where. Having this information provides all kinds of interesting uses, such as Andy Kramek and Marcia Akins
a live preview surface, a dynamically generated table of contents, the ability to
find text, conditionally highlighting certain report objects, and so on. 20 Sample Issue Downloads
DBFListener
DBFListener, contained in DBFListener.PRG, is a subclass of _ReportListener,
9.0 8.0 7.0 6.0
a report listener subclass defined in _ReportListener.VCX in the FFC
subdirectory of the VFP home directory. I discussed _ReportListener in my Applies to Applies to Applies to Applies to
VFP 9.0 VFP 8.0 VFP 7.0 VFP 6.0
February 2005 FoxTalk 2.0 column, “Listening to a Report.” Because it’s a
subclass of _ReportListener, DBFListener can be used either as the sole
listener for a report or as one of a chain of listeners, each of which
performs some function during the report run. The ListenerType property Applies to Accompanying files available online at
VFP 5.0 www.pinnaclepublishing.com
Continues on page 3
AD:
AGE
LL P IELD
FU
O NE
F
ST
In some cases, it’s possible to send and receive Hotmail through a long series of clicks in a browser window.
messages with Visual FoxPro code. In this article, Anatoliy Outlook Express can integrate Hotmail accounts, and
Mogylevets describes several Visual FoxPro classes that on the surface, OE’s handling of the e-mail account is
use Microsoft ServerXMLHTTP and XMLDOM objects to almost indistinguishable from a regular SMTP/POP3
implement WebDAV access to the Hotmail server. You’ll also account. Therefore, we could deduce that there’s a way for
learn some of the mysteries of HTTP requests, including a non-browser application to interact with the Hotmail
request headers. server. If you have a packet sniffing application installed
on your computer, it’s easy to see what’s going on when a
T
HE system requirements for what’s described in this Hotmail account is synchronized through Outlook Express.
article are Windows XP/2000/2003, Visual FoxPro A packet sniffer captures all TCP packets that leave or
version 8 or 9, Microsoft ServerXMLHTTP and enter your computer and can help you understand HTTP,
XMLDOM COM objects, and a Hotmail or MSN account. FTP, SMTP, POP3, and other protocols. I have a packet
Note that the functionality described here may not be sniffer installed on my computer, so I set it to the TCP
available for free Hotmail accounts. packet capturing mode and pressed the Send/Recv button
on the Outlook Express toolbar. Figure 1 shows the result.
Introduction As you can see, the local computer connects to port 80
Anyone can easily access a Hotmail account using on three remote servers consecutively. There’s the first
the standard MSN Web page, which has a very clue—this is the HTTP protocol. By the way, you may
straightforward interface. But, can you automate or “nslookup” the IP addresses to find out who they belong
integrate that access in a FoxPro application? You could, to. Let’s see what’s inside those TCP packets.
of course, host a browser control on a form, but to check Figure 2 shows that this is an HTTP request sent by
new e-mails or to send a message you’d still have to go the local computer to a remote server at services.msn.com.
Figure 1. Outlook Express exchanges TCP packets with the Figure 2. Outlook Express communicates with the Hotmail server
Hotmail server. using HTTP requests.
Figure 3. The Hotmail server answers the client’s HTTP request by Figure 4. The HTTP request a browser sends to retrieve the
sending a response comprised of headers and XML body. Google search page.
The object supports XML data exchange and has IF THIS.response != RESPONSE_OK
* failed to connect
“server” in its name—and it really works as a server by RETURN .F.
establishing a connection with Hotmail, redirecting, ENDIF
creating cookies, and encrypting and decrypting data. * load the response to xml parser
THIS.xmlGateway.Load(THIS.http.ResponseXml)
Another Microsoft object, XMLDOM, is used to parse RETURN .T.
the XML data received from the Hotmail server.
PROCEDURE SendPropfind(href, xml)
THIS.SendHttpRequest(PROPFIND, m.href,;
Visual FoxPro classes wrapping the Hotmail "Cache-Control: no-cache;" +;
"Content-Type: text/xml", m.xml)
WebDAV functionality
The class library described in this article contains four
classes inherited from the VFP Session class (see Table 1).
THIS.http = CREATEOBJECT(;
Figure 5. The first HTTP request the Hotmail client sends to the
"MSXML2.ServerXMLHTTP") Hotmail server when initiating a connection.
Table 1. The FoxPro class library that wraps the Hotmail WebDAV functionality.
Class Description
WebmailClient Connects to the Hotmail server and requests a list of folders. It has methods for sending HTTP requests. Knows how to clear the
Deleted Items folder. Knows how to send an e-mail. The FOLDERS property is a collection of WebmailFolder objects.
WebmailFolder Represents a Hotmail folder. The class has a method that retrieves the list of message envelopes stored in this folder. The
MESSAGES property is a collection of WebmailMessage objects.
WebmailMessage Represents a Hotmail message. Can retrieve the message, delete the message, or move it to another folder. The HEADERS property
is a collection of message headers. For simplicity, the object doesn’t parse the message body’s parts or attachments.
MessageHeader Represents a message header like From, To, Cc, Content-Type, Return-Path, and so on.
PROCEDURE GetFolders
* reads available folders and populates
* a collection of WebmailFolder objects
= ClearCollection(THIS.folders)
cFoldersHref = THIS.xmlGateway.;
SelectSingleNode(MSGFOLDERROOT_NODE).Text
THIS.SendPropfind(m.cFoldersHref, XML_FOLDERS)
IF THIS.response <> RESPONSE_OK
RETURN .F.
ENDIF
THIS.xmlFolders.Load(THIS.http.ResponseXml)
nFolderCount = THIS.xmlFolders.;
Figure 6. The header fields of the Hotmail server’s response. Note
the X-Dav-Error header.
Figure 7. The XML body of the Hotmail server’s response. This Figure 8. The Hotmail client sends this HTTP request to get the
particular response returns main URIs for the server and for the list of folders for the Hotmail account.
Hotmail account.
10 FoxTalk 2.0 Sample Issue www.pinnaclepublishing.com
SelectSingleNode(MULTISTATUS).; GetMessageEnvelopes method of the WebmailFolder class:
ChildNodes.Length
Retrieving a message
The WebmailMessage class includes the
GetMessageContent method that retrieves the entire
message. Unlike the previous requests, this one uses the
GET verb and has no XML body. The URI of the message
is passed in the request’s first line right after the verb, and
the body of the Hotmail server’s response contains the
raw message content. Here’s the GetMessageContent
method of the WebmailMessage class:
PROCEDURE GetMessageContent
= ClearCollection(THIS.headers)
LOCAL cResponseText, nPos
THIS.folder.webmail.SendHttpRequest("GET",;
THIS.href, "", "")
IF THIS.folder.webmail.response = RESPONSE_OK
Figure 9. The Hotmail client sends this HTTP request to get cResponseText =;
THIS.folder.webmail.http.ResponseText
the list of message envelopes in the INBOX folder of the nPos = AT(dCRLF, cResponseText)
Hotmail account. THIS.rawcontent = SUBSTR(cResponseText,;
In Outlook Express, you can see the raw content PROCEDURE MoveMessage(cDstHref)
of a message by clicking on its Properties | Details | LOCAL cHeaders
cHeaders = "Destination: " + cDstHref +;
Message Source. THIS.msgid + ";Allow-Rename: t"
The top part of the raw message contains various WITH THIS.folder.webmail
message parameters. The following part, the body of the .SendHttpRequest("MOVE",;
THIS.href, cHeaders, "")
message, may not be as simple as shown in Figure 10. RETURN (.response = RESPONSE_OK)
Very often the body includes several parts (plain text, ENDWITH
Figure 11. The HTTP request a Hotmail client sends to the Figure 12. The HTTP request a Hotmail client sends to the
Hotmail server to delete a message from the INBOX folder. Note Hotmail server to delete several messages from the INBOX folder.
that the request has no XML body. The XML body contains message IDs.
the new reporting features of VFP 9, which is now available PROCEDURE BeforeBand(nBandObjCode,nFRXRecno)
for purchase. ** Check on Page Footer **
IF nBandObjCode=7
? "Before Page "+TRANSFORM(THIS.PageNo)+ ;
ReportListener events and report debugging " is about to print."
ENDIF
The following code serves as an introduction to how you ENDPROC
can hook your own code into the events that fire in
PROCEDURE AfterBand(nBandObjCode,nFRXRecno)
ReportListener as you progress through a report run. ** Page Footer **
(This program is available in the download file for this IF DEBUGGING AND nBandObjCode=7 AND THIS.PageNo=10
? "Suspending..."
column as IntroListener.prg.) SUSPEND
When you run this code, just choose a report, then ? "Resuming..."
ENDIF
watch the screen and respond to WAIT WINDOWS to ENDPROC
see the contents of the listener’s command clauses
ENDDEFINE
collection, as well as screen output fired for each page in
the BeforeBand method. (Note how it checks the incoming Chaining reports in the VFP 9 report preview
nBandObjCode parameter to determine when we’re on The following code sample demonstrates VFP 9’s ability
the page footer band.) to chain reports in the preview window:
Finally, note that if you change the DEBUGGING
constant in the first line to .T. and then run a report with *This demonstrates how reports can be chained in the
at least 10 pages, the report run will break with a *preview now.
SUSPEND statement at page 10. You can then fire up #define ListenerPrint 0
#define ListenerPreview 1
the debugger and step through the code that’s executing, #define ListenerXML 4
as well as explore some interesting variables and #define ListenerHTML 5
objects. At any time you can RESUME and the report
run will continue. rep1 = GETFILE("FRX", "Pick the first report")
rep2 = GETFILE("FRX", "Pick the second report")
#DEFINE DEBUGGING .F. *See how the range is now respected by the preview:
REPORT FORM (Rep1) RANGE 1,4 ;
CLEAR
OBJECT TYPE ListenerPreview NOPAGEEJECT
LOCAL oListener AS ReportListener
REPORT FORM (Rep2) RANGE 1,4 ;
oListener = NEWOBJECT("MyListener")
OBJECT TYPE ListenerPreview NORESET
oListener.ListenerType=1 && Preview
REPORT FORM (GETFILE("FRX")) OBJECT oListener PREVIEW
Know a clever shortcut? Have an idea for an article for FoxTalk 2.0?
Visit www.pinnaclepublishing.com and click on “Write For Us” to submit your ideas.
14 FoxTalk 2.0 Sample Issue www.pinnaclepublishing.com
The Kit Box FoxTalk 2.0
A global solution to a local problem is generally a bad idea. area 1, you’re returned to work area 2 (which is empty)!
But just how bad it can really be? Andy Kramek and Marcia Look at Figure 1 and you’ll see that the value of the
Akins find out when they revisit some old code. The effects of variable lnSelect is showing 2, but the currently selected
some commands like SET EXACT and SET UDFPARMS are well work area (as shown by the datasession window) is
known, but some of the consequences of SET COMPATIBLE actually 1. I can’t find anything wrong and it’s crashing
are more surprising. One, in particular, caused Andy a real the code. This is driving me crazy.
problem recently.
Marcia: Well, that’s a short drive for you.
Andy: I think I’ve found a bug in VFP 9.0, but I really can’t
believe it because it seems so obvious that it could never Andy: Thank you. Now please tell me what’s going on
have slipped through testing. here. If I quit Visual FoxPro and restart, and then do the
exact same thing in the Command window, everything is
Marcia: What are the symptoms? just fine. But when I’m running the app I get what you see
in Figure 1. It’s as if SELECT() is returning the current
Andy: Well, I’m calling one of my standard functions to work area number + 1.
perform a lookup using exact matching. This function
saves the current work area (and some other relevant Marcia: Let’s approach this logically. If you’re getting two
settings) and restores the work area before returning to different behaviors depending on whether you’re running
the calling code. the application or are in the Command window, then it’s a
certainty that something in the application environment is
Marcia: Hang on. Why are you changing work areas causing the problem.
anyway? You can just use the SEEK() function and
reference the alias. Andy: Yes, but what could be doing this? I have no idea
where to start.
Andy: Ah, but the function checks to see whether an index
tag has been specified, and also whether a specified index Marcia: I generally start by looking in the Help file. Let’s
tag actually exists, and if not it uses a LOCATE instead see what it has to say about the SELECT() function, since
of a SEEK(). As you know, you can’t do a “LOCATE IN that’s where you’re seeing the problem. Maybe it’s
<alias>”. But that’s not the problem anyway. Here’s the changed in version 9.0.
relevant part of the code:
Andy: The Help file states that the SELECT() function
LOCAL lnSelect returns either the number of the currently selected work
*** Save current work area
lnSelect = SELECT() area or the highest-numbered unused work area. Passing
<rest of function code here>
*** Restore work area
a parameter of 0 gets the current work area and 1 returns
SELECT (lnSelect)
RETURN
Figure 1. It’s a bug!
SELECT() = 2, but
Marcia: That looks pretty straightforward, so what’s
Work Area = 1.
the problem?
Andy: Well, when this function gets called from inside the
application (it’s an old FP 2.6 app that’s being converted
to run under VFP 9.0), it returns to the wrong work area.
Andy: Exactly what I say. If you call the code from work
Marcia: That would certainly explain what you’re seeing. Marcia: The trouble is that SET COMPATIBLE affects all
What happens if you use SELECT(0) in your function sorts of things—not just the behavior of the SELECT()
instead of just SELECT()? function (which has caused you to waste a couple of
hours trying to find a non-existent bug). There are a
Andy: It works! Checking the settings inside the function, couple of other settings in Visual FoxPro that also
I find that COMPATIBLE is ON, and the Code References illustrate what I mean. For example, when you want to
tool (bless it!) shows that there is indeed a SET do an exact match for a string comparison, you might
COMPATIBLE DB4 in one of the procedures called consider including EXACT = ON in the code before
from the startup program (which is equivalent to SET the comparison. This is a global solution because it
COMPATIBLE ON)! So you’ve solved my problem—it’s affects all string comparisons in the current datasession
not a bug in VFP 9.0 after all. and, unless you restore the prior state, you’ll affect
not only the comparison you’re about to do, but all
Marcia: Glad I could help. Interestingly, there is a bug subsequent operations.
there, but it’s in the Help file and not in the code. Look at
Figure 2. Andy: I see what you mean. Debugging this could be a
nightmare because unless you happen to hit the line of
Andy: Yes, I see. When compatible is ON, the SELECT() code that turns EXACT on, you could run the application
function returns the lowest free work area number, not the for days and never see a problem.
Marcia: Parameters can be passed either by reference or Marcia: Now do you see what I mean by global solutions
by value. When a parameter is passed to a function or to local problems?
procedure by reference, any changes made to its value in
the called code are reflected in the original value in the Andy: Definitely! But we’ve drifted off the point a bit.
calling program. Conversely, when a parameter is passed Let’s get back to SET COMPATIBLE. What else does that
by value, the called code can change that value but the affect besides the SELECT() function?
value in the calling program remains unchanged.
Marcia: The Help file lists 32 entries (including one that
Andy: Yes, and I also know that Visual FoxPro interprets merely says “Menu commands” but doesn’t actually tell
the code being called by the mechanism by which us which ones). However, only about a dozen of them
parameters are passed. So, when the calling syntax looks seem immediately applicable to Visual FoxPro—the
like this: others are really concerned with FoxPro 2.x issues. But
the ones that are relevant include some really nasty
luRetVal = CallMyFunction( param1, param2 ) behavior changes.
Visual FoxPro treats this as a function call and passes the Andy: Oddly, there are a lot of commands mentioned
parameters by value. However, if the same code is called there that, in the Help file, make no reference to SET
like this: COMPATIBLE at all.
Know a clever shortcut? Have an idea for an article for FoxTalk 2.0?
Visit www.pinnaclepublishing.com and click on “Write For Us” to submit your ideas.
Pinnacle, A Division of Lawrence Ragan Communications, Inc. ▲ 800-493-4867 x.4209 or 312-960-4100 ▲ Fax 312-960-4106
For access to current and archive content and source code, log in at www.pinnaclepublishing.com.
Customer Service: POSTMASTER: Send address changes to Lawrence Ragan Communications, Inc., 316 N. Michigan
Ave., Suite 300, Chicago, IL 60601.
Phone: 800-493-4867 x.4209 or 312-960-4100
Fax: 312-960-4106 Copyright © 2005 by Lawrence Ragan Communications, Inc. All rights reserved. No part of this
periodical may be used or reproduced in any fashion whatsoever (except in the case of brief
Email: PinPub@Ragan.com quotations embodied in critical articles and reviews) without the prior written consent of
Lawrence Ragan Communications, Inc. Printed in the United States of America.
Advertising: RogerS@Ragan.com
Brand and product names are trademarks or registered trademarks of their respective
Editorial: FarionG@Ragan.com holders. Microsoft is a registered trademark of Microsoft Corporation. The Fox Head logo,
FoxBASE+, FoxPro, and Visual FoxPro are registered trademarks of Microsoft Corporation.
FoxTalk 2.0 is an independent publication not affiliated with Microsoft Corporation. Microsoft
Pinnacle Web Site: www.pinnaclepublishing.com Corporation is not responsible in any way for the editorial policy or other contents of
the publication.
Subscription rates This publication is intended as a general guide. It covers a highly technical and complex
subject and should not be used for making decisions concerning specific products or
applications. This publication is sold as is, without warranty of any kind, either express or
United States: One year (12 issues): $159; two years (24 issues): $278 implied, respecting the contents of this publication, including but not limited to implied
Other:* One year: $189; two years: $338 warranties for the publication, performance, quality, merchantability, or fitness for any particular
purpose. Lawrence Ragan Communications, Inc., shall not be liable to the purchaser or any
Single issue rate: other person or entity with respect to any liability, loss, or damage caused or alleged to
be caused directly or indirectly by this publication. Articles published in FoxTalk 2.0 reflect
$20 ($25 outside the United States)* the views of their authors; they may or may not reflect the view of Lawrence Ragan
Communications, Inc. Inclusion of advertising inserts does not constitute an endorsement
* Funds must be in U.S. currency. by Lawrence Ragan Communications, Inc., or FoxTalk 2.0.