home *** CD-ROM | disk | FTP | other *** search
/ PC World 2005 June / PCWorld_2005-06_cd.bin / software / vyzkuste / firewally / firewally.exe / framework-2.3.exe / TPJ.pod < prev    next >
Text File  |  2002-07-08  |  32KB  |  800 lines

  1. =pod
  2.  
  3. =head1 NAME
  4.  
  5. The Perl Journal #10 - Win32::OLE by Jan Dubois
  6.  
  7. =head1 INTRODUCTION
  8.  
  9. Suppose you're composing a document with Microsoft Word. You want to
  10. include an Excel spreadsheet. You could save the spreadsheet in some
  11. image format that Word can understand, and import it into your
  12. document. But if the spreadsheet changes, your document will be out of
  13. date.
  14.  
  15. Microsoft's OLE (Object Linking and Embedding, pronounced "olay") lets
  16. one program use objects from another. In the above scenario, the
  17. spreadsheet is the object. As long as Excel makes that spreadsheet
  18. available as an OLE object, and Word knows to treat it like one, your
  19. document will always be current.
  20.  
  21. You can control OLE objects from Perl with the Win32::OLE module, and
  22. that's what this article is about. First, I'll show you how to "think
  23. OLE," which mostly involves a lot of jargon. Next, I'll show you the
  24. mechanics involved in using Win32::OLE. Then we'll go through a Perl
  25. program that uses OLE to manipulate Microsoft Excel, Microsoft Access,
  26. and Lotus Notes. Finally, I'll talk about Variants, an internal OLE
  27. data type.
  28.  
  29.  
  30. =head1 THE OLE MINDSET
  31.  
  32. When an application makes an OLE object available for other
  33. applications to use, that's called OLE I<automation>. The program
  34. using the object is called the I<controller>, and the application
  35. providing the object is called the I<server>. OLE automation is guided
  36. by the OLE Component Object Model (COM) which specifies how those
  37. objects must behave if they are to be used by other processes and
  38. machines.
  39.  
  40. There are two different types of OLE automation servers. I<In-process>
  41. servers are implemented as dynamic link libraries (DLLs) and run in
  42. the same process space as the controller. I<Out-of-process> servers
  43. are more interesting; they are standalone executables that exist as
  44. separate processes - possibly on a different computer.
  45.  
  46. The Win32::OLE module lets your Perl program act as an OLE
  47. controller. It allows Perl to be used in place of other languages like
  48. Visual Basic or Java to control OLE objects. This makes all OLE
  49. automation servers immediately available as Perl modules.
  50.  
  51. Don't confuse ActiveState OLE with Win32::OLE. ActiveState OLE is
  52. completely different, although future builds of ActiveState Perl (500
  53. and up) will work with Win32::OLE.
  54.  
  55. Objects can expose OLE methods, properties, and events to the outside
  56. world. Methods are functions that the controller can call to make the
  57. object do something; properties describe the state of the object; and
  58. events let the controller know about external events affecting the
  59. object, such as the user clicking on a button. Since events involve
  60. asynchronous communication with their objects, they require either
  61. threads or an event loop. They are not yet supported by the Win32::OLE
  62. module, and for the same reason ActiveX controls (OCXs) are currently
  63. unsupported as well.
  64.  
  65. =head1 WORKING WITH WIN32::OLE
  66.  
  67. The Win32::OLE module doesn't let your Perl program create OLE
  68. objects. What it does do is let your Perl program act like a remote
  69. control for other applications-it lets your program be an OLE
  70. controller. You can take an OLE object from another application
  71. (Access, Notes, Excel, or anything else that speaks OLE) and invoke
  72. its methods or manipulate its properties.
  73.  
  74. =head2 THE FIRST STEP: CREATING AN OLE SERVER OBJECT
  75.  
  76. First, we need to create a Perl object to represent the OLE
  77. server. This is a weird idea; what it amounts to is that if we want to
  78. control OLE objects produced by, say, Excel, we have to create a Perl
  79. object that represents Excel. So even though our program is an OLE
  80. controller, it'll contain objects that represent OLE servers.
  81.  
  82. You can create a new OLE I<server object> with C<< Win32::OLE->new >>.
  83. This takes a program ID (a human readable string like
  84. C<'Speech.VoiceText'>) and returns a server object:
  85.  
  86.   my $server = Win32::OLE->new('Excel.Application', 'Quit');
  87.  
  88. Some server objects (particularly those for Microsoft Office
  89. applications) don't automatically terminate when your program no
  90. longer needs them. They need some kind of Quit method, and that's just
  91. what our second argument is. It can be either a code reference or a
  92. method name to be invoked when the object is destroyed. This lets you
  93. ensure that objects will be properly cleaned up even when the Perl
  94. program dies abnormally.
  95.  
  96. To access a server object on a different computer, replace the first
  97. argument with a reference to a list of the server name and program ID:
  98.  
  99.   my $server = Win32::OLE->new(['foo.bar.com',
  100.                                 'Excel.Application']);
  101.  
  102. (To get the requisite permissions, you'll need to configure your
  103. security settings with F<DCOMCNFG.EXE>.)
  104.  
  105. You can also directly attach your program to an already running OLE
  106. server:
  107.  
  108.   my $server = Win32::OLE->GetActiveObject('Excel.Application');
  109.  
  110. This fails (returning C<undef>) if no server exists, or if the server
  111. refuses the connection for some reason. It is also possible to use a
  112. persistent object moniker (usually a filename) to start the associated
  113. server and load the object into memory:
  114.  
  115.   my $doc = Win32::OLE->GetObject("MyDocument.Doc");
  116.  
  117. =head2 METHOD CALLS
  118.  
  119. Once you've created one of these server objects, you need to call its
  120. methods to make the OLE objects sing and dance. OLE methods are
  121. invoked just like normal Perl object methods:
  122.  
  123.   $server->Foo(@Arguments);
  124.  
  125. This is a Perl method call - but it also triggers an OLE method call
  126. in the object. After your program executes this statement, the
  127. C<$server> object will execute its Foo() method. The available methods
  128. are typically documented in the application's I<object model>.
  129.  
  130. B<Parameters.> By default, all parameters are positional
  131. (e.g. C<foo($first, $second, $third)>) rather than named (e.g.
  132. C<< foo(-name => "Yogi", -title => "Coach") >>). The required parameters
  133. come first, followed by the optional parameters; if you need to
  134. provide a dummy value for an optional parameter, use undef.
  135.  
  136. Positional parameters get cumbersome if a method takes a lot of
  137. them. You can use named arguments instead if you go to a little extra
  138. trouble - when the last argument is a reference to a hash, the
  139. key/value pairs of the hash are treated as named parameters:
  140.  
  141.   $server->Foo($Pos1, $Pos2, {Name1 => $Value1,
  142.                               Name2 => $Value2});
  143.  
  144. B<Foreign Languages and Default Methods.> Sometimes OLE servers use
  145. method and property names that are specific to a non-English
  146. locale. That means they might have non-ASCII characters, which aren't
  147. allowed in Perl variable names. In German, you might see C<╓ffnen> used
  148. instead of C<Open>. In these cases, you can use the Invoke() method:
  149.  
  150.   $server->Invoke('╓ffnen', @Arguments);
  151.  
  152. This is necessary because C<< $Server->╓ffnen(@Arguments) >> is a syntax
  153. error in current versions of Perl.
  154.  
  155. =head2 PROPERTIES
  156.  
  157. As I said earlier, objects can expose three things to the outside
  158. world: methods, properties, and events. We've covered methods, and
  159. Win32::OLE can't handle events. That leaves properties. But as it
  160. turns out, properties and events are largely interchangeable. Most
  161. methods have corresponding properties, and vice versa.
  162.  
  163. An object's properties can be accessed with a hash reference:
  164.  
  165.   $server->{Bar} = $value;
  166.   $value = $server->{Bar};
  167.  
  168. This example sets and queries the value of the property named
  169. C<Bar>. You could also have called the object's Bar() method to
  170. achieve the same effect:
  171.  
  172.   $value = $server->Bar;
  173.  
  174. However, you can't write the first line as C<< $server->Bar = $value >>,
  175. because you can't assign to the return value of a method call. In
  176. Visual Basic, OLE automation distinguishes between assigning the name
  177. of an object and assigning its value:
  178.  
  179.   Set Object = OtherObject
  180.  
  181.   Let Value = Object
  182.  
  183. The C<Set> statement shown here makes C<Object> refer to the same object as
  184. C<OtherObject>. The C<Let> statement copies the value instead. (The value of
  185. an OLE object is what you get when you call the object's default
  186. method.
  187.  
  188. In Perl, saying C<< $server1 = $server2 >> always creates another reference,
  189. just like the C<Set> in Visual Basic. If you want to assign the value
  190. instead, use the valof() function:
  191.  
  192.   my $value = valof $server;
  193.  
  194. This is equivalent to
  195.  
  196.   my $value = $server->Invoke('');
  197.  
  198. =head2 SAMPLE APPLICATION
  199.  
  200. Let's look at how all of this might be used. In Listing: 1 you'll see
  201. F<T-Bond.pl>, a program that uses Win32::OLE for an almost-real world
  202. application.
  203.  
  204. The developer of this application, Mary Lynch, is a financial futures
  205. broker. Every afternoon, she connects to the Chicago Board of Trade
  206. (CBoT) web site at http://www.cbot.com and collects the time and sales
  207. information for U.S. T-bond futures. She wants her program to create a
  208. chart that depicts the data in 15-minute intervals, and then she wants
  209. to record the data in a database for later analysis. Then she wants
  210. her program to send mail to her clients.
  211.  
  212. Mary's program will use Microsoft Access as a database, Microsoft
  213. Excel to produce the chart, and Lotus Notes to send the mail. It will
  214. all be controlled from a single Perl program using OLE automation. In
  215. this section, we'll go through T-Bond. pl step by step so you can see
  216. how Win32::OLE lets you control these applications.
  217.  
  218. =head2 DOWNLOADING A WEB PAGE WITH LWP
  219.  
  220. However, Mary first needs to amass the raw T-bond data by having her
  221. Perl program automatically download and parse a web page. That's the
  222. perfect job for LWP, the libwww-perl bundle available on the CPAN. LWP
  223. has nothing to do with OLE. But this is a real-world application, and
  224. it's just what Mary needs to download her data from the Chicago Board
  225. of Trade.
  226.  
  227.   use LWP::Simple;
  228.   my $URL = 'http://www.cbot.com/mplex/quotes/tsfut';
  229.   my $text = get("$URL/tsf$Contract.htm");
  230.  
  231. She could also have used the Win32::Internet module:
  232.  
  233.   use Win32::Internet;
  234.   my $URL = 'http://www.cbot.com/mplex/quotes/tsfut';
  235.   my $text = $Win32::Internet->new->FetchURL("$URL/tsf$Contract.htm");
  236.  
  237. Mary wants to condense the ticker data into 15 minute bars. She's
  238. interested only in lines that look like this:
  239.  
  240.   03/12/1998 US 98Mar 12116 15:28:34 Open
  241.  
  242. A regular expression can be used to determine whether a line looks
  243. like this. If it does, the regex can split it up into individual
  244. fields. The price quoted above, C<12116>, really means 121 16/32, and
  245. needs to be converted to 121.5. The data is then condensed into 15
  246. minute intervals and only the first, last, highest, and lowest price
  247. during each interval are kept. The time series is stored in the array
  248. C<@Bars>. Each entry in C<@Bars> is a reference to a list of 5 elements:
  249. Time, Open, High, Low, and Close.
  250.  
  251.   foreach (split "\n", $text) {
  252.       # 03/12/1998 US 98Mar 12116 15:28:34 Open
  253.       my ($Date,$Price,$Hour,$Min,$Sec,$Ind) =
  254.            m|^\s*(\d+/\d+/\d+) # " 03/12/1998"
  255.               \s+US\s+\S+\s+(\d+) # " US 98Mar 12116"
  256.               \s+(\d+):(\d+):(\d+) # " 12:42:40"
  257.               \s*(.*)$|x; # " Ask"
  258.       next unless defined $Date;
  259.       $Day = $Date;
  260.  
  261.       # Convert from fractional to decimal format
  262.       $Price = int($Price/100) + ($Price%100)/32;
  263.  
  264.       # Round up time to next multiple of 15 minutes
  265.       my $NewTime = int(($Sec+$Min*60+$Hour*3600)/900+1)*900;
  266.       unless (defined $Time && $NewTime == $Time) {
  267.           push @Bars, [$hhmm, $Open, $High, $Low, $Close]
  268.                                             if defined $Time;
  269.           $Open = $High = $Low = $Close = undef;
  270.           $Time = $NewTime;
  271.           my $Hour = int($Time/3600);
  272.           $hhmm = sprintf "%02d:%02d", $Hour, $Time/60-$Hour*60;
  273.       }
  274.  
  275.       # Update 15 minute bar values
  276.       $Close = $Price;
  277.       $Open = $Price unless defined $Open;
  278.       $High = $Price unless defined $High && $High > $Price;
  279.       $Low = $Price unless defined $Low && $Low > $Price;
  280.   }
  281.  
  282.   die "No data found" unless defined $Time;
  283.   push @Bars, [$hhmm, $Open, $High, $Low, $Close];
  284.  
  285. =head2 MICROSOFT ACCESS
  286.  
  287. Now that Mary has her T-bond quotes, she's ready to use Win32::OLE to
  288. store them into a Microsoft Access database. This has the advantage
  289. that she can copy the database to her lap-top and work with it on her
  290. long New York commute. She's able to create an Access database as
  291. follows:
  292.  
  293.   use Win32::ODBC;
  294.   use Win32::OLE;
  295.  
  296.   # Include the constants for the Microsoft Access
  297.   # "Data Access Object".
  298.  
  299.   use Win32::OLE::Const 'Microsoft DAO';
  300.  
  301.   my $DSN      = 'T-Bonds';
  302.   my $Driver   = 'Microsoft Access Driver (*.mdb)';
  303.   my $Desc     = 'US T-Bond Quotes';
  304.   my $Dir      = 'i:\tmp\tpj';
  305.   my $File     = 'T-Bonds.mdb';
  306.   my $Fullname = "$Dir\\$File";
  307.  
  308.   # Remove old database and dataset name
  309.   unlink $Fullname if -f $Fullname;
  310.   Win32::ODBC::ConfigDSN(ODBC_REMOVE_DSN, $Driver, "DSN=$DSN")
  311.                          if Win32::ODBC::DataSources($DSN);
  312.  
  313.   # Create new database
  314.   my $Access = Win32::OLE->new('Access.Application', 'Quit');
  315.   my $Workspace = $Access->DBEngine->CreateWorkspace('', 'Admin', '');
  316.   my $Database = $Workspace->CreateDatabase($Fullname, dbLangGeneral);
  317.  
  318.   # Add new database name
  319.   Win32::ODBC::ConfigDSN(ODBC_ADD_DSN, $Driver,
  320.           "DSN=$DSN", "Description=$Desc", "DBQ=$Fullname",
  321.           "DEFAULTDIR=$Dir", "UID=", "PWD=");
  322.  
  323. This uses Win32::ODBC (described in TPJ #9) to remove and create
  324. F<T-Bonds.mdb>. This lets Mary use the same script on her workstation
  325. and on her laptop even when the database is stored in different
  326. locations on each. The program also uses Win32::OLE to make Microsoft
  327. Access create an empty database.
  328.  
  329. Every OLE server has some constants that your Perl program will need
  330. to use, made accessible by the Win32::OLE::Const module. For instance,
  331. to grab the Excel constants, say C<use Win32::OLE::Const 'Microsoft
  332. Excel'>.
  333.  
  334. In the above example, we imported the Data Access Object con-stants
  335. just so we could use C<dbLangGeneral>.
  336.  
  337. =head2 MICROSOFT EXCEL
  338.  
  339. Now Mary uses Win32::OLE a second time, to have Microsoft Excel create
  340. the chart shown below.
  341.  
  342.   Figure 1: T-Bond data generated by MicroSoft Excel via Win32::OLE
  343.  
  344.   # Start Excel and create new workbook with a single sheet
  345.   use Win32::OLE qw(in valof with);
  346.   use Win32::OLE::Const 'Microsoft Excel';
  347.   use Win32::OLE::NLS qw(:DEFAULT :LANG :SUBLANG);
  348.  
  349.   my $lgid = MAKELANGID(LANG_ENGLISH, SUBLANG_DEFAULT);
  350.   $Win32::OLE::LCID = MAKELCID($lgid);
  351.  
  352.   $Win32::OLE::Warn = 3;
  353.  
  354. Here, Mary sets the locale to American English, which lets her do
  355. things like use American date formats (e.g. C<"12-30-98"> rather than
  356. C<"30-12-98">) in her program. It will continue to work even when she's
  357. visiting one of her international customers and has to run this
  358. program on their computers.
  359.  
  360. The value of C<$Win32::OLE::Warn> determines what happens when an OLE
  361. error occurs. If it's 0, the error is ignored. If it's 2, or if it's 1
  362. and the script is running under C<-w>, the Win32::OLE module invokes
  363. C<Carp::carp()>. If C<$Win32::OLE::Warn> is set to 3, C<Carp::croak()>
  364. is invoked and the program dies immediately.
  365.  
  366. Now the data can be put into an Excel spreadsheet to produce the
  367. chart. The following section of the program launches Excel and creates
  368. a new workbook with a single worksheet. It puts the column titles
  369. ('Time', 'Open', 'High', 'Low', and 'Close') in a bold font on the
  370. first row of the sheet. The first column displays the timestamp in
  371. I<hh:mm> format; the next four display prices.
  372.  
  373.   my $Excel = Win32::OLE->new('Excel.Application', 'Quit');
  374.   $Excel->{SheetsInNewWorkbook} = 1;
  375.   my $Book = $Excel->Workbooks->Add;
  376.   my $Sheet = $Book->Worksheets(1);
  377.   $Sheet->{Name} = 'Candle';
  378.  
  379.   # Insert column titles
  380.   my $Range = $Sheet->Range("A1:E1");
  381.   $Range->{Value} = [qw(Time Open High Low Close)];
  382.   $Range->Font->{Bold} = 1;
  383.  
  384.   $Sheet->Columns("A:A")->{NumberFormat} = "h:mm";
  385.   # Open/High/Low/Close to be displayed in 32nds
  386.   $Sheet->Columns("B:E")->{NumberFormat} = "# ?/32";
  387.  
  388.   # Add 15 minute data to spreadsheet
  389.   print "Add data\n";
  390.   $Range = $Sheet->Range(sprintf "A2:E%d", 2+$#Bars);
  391.   $Range->{Value} = \@Bars;
  392.  
  393. The last statement shows how to pass arrays to OLE objects. The
  394. Win32::OLE module automatically translates each array reference to a
  395. C<SAFEARRAY>, the internal OLE array data type. This translation first
  396. determines the maximum nesting level used by the Perl array, and then
  397. creates a C<SAFEARRAY> of the same dimension. The C<@Bars> array
  398. already contains the data in the correct form for the spreadsheet:
  399.  
  400.   ([Time1, Open1, High1, Low1, Close1],
  401.   ...
  402.   [TimeN, OpenN, HighN, LowN, CloseN])
  403.  
  404. Now the table in the spreadsheet can be used to create a candle stick
  405. chart from our bars. Excel automatically chooses the time axis labels
  406. if they are selected before the chart is created:
  407.  
  408.   # Create candle stick chart as new object on worksheet
  409.   $Sheet->Range("A:E")->Select;
  410.  
  411.   my $Chart = $Book->Charts->Add;
  412.   $Chart->{ChartType} = xlStockOHLC;
  413.   $Chart->Location(xlLocationAsObject, $Sheet->{Name});
  414.   # Excel bug: the old $Chart is now invalid!
  415.   $Chart = $Excel->ActiveChart;
  416.  
  417. We can change the type of the chart from a separate sheet to a chart
  418. object on the spreadsheet page with the C<< $Chart->Location >>
  419. method. (This invalidates the chart object handle, which might be
  420. considered a bug in Excel.) Fortunately, this new chart is still the
  421. 'active' chart, so an object handle to it can be reclaimed simply by
  422. asking Excel.
  423.  
  424. At this point, our chart still needs a title, the legend is
  425. meaningless, and the axis has decimals instead of fractions. We can
  426. fix those with the following code:
  427.  
  428.   # Add title, remove legend
  429.   with($Chart, HasLegend => 0, HasTitle => 1);
  430.   $Chart->ChartTitle->Characters->{Text} = "US T-Bond";
  431.  
  432.   # Set up daily statistics
  433.   $Open  = $Bars[0][1];
  434.   $High  = $Sheet->Evaluate("MAX(C:C)");
  435.   $Low   = $Sheet->Evaluate("MIN(D:D)");
  436.   $Close = $Bars[$#Bars][4];
  437.  
  438. The with() function partially mimics the Visual Basic With statement,
  439. but allows only property assignments. It's a convenient shortcut for
  440. this:
  441.  
  442.   { # open new scope
  443.     my $Axis = $Chart->Axes(xlValue);
  444.     $Axis->{HasMajorGridlines} = 1;
  445.     $Axis->{HasMinorGridlines} = 1;
  446.     # etc ...
  447.   }
  448.  
  449. The C<$High> and C<$Low> for the day are needed to determine the
  450. minimum and maximum scaling levels. MIN and MAX are spreadsheet
  451. functions, and aren't automatically available as methods. However,
  452. Excel provides an Evaluate() method to calculate arbitrary spreadsheet
  453. functions, so we can use that.
  454.  
  455. We want the chart to show major gridlines at every fourth tick and
  456. minor gridlines at every second tick. The minimum and maximum are
  457. chosen to be whatever multiples of 1/16 we need to do that.
  458.  
  459.   # Change tickmark spacing from decimal to fractional
  460.   with($Chart->Axes(xlValue),
  461.       HasMajorGridlines => 1,
  462.       HasMinorGridlines => 1,
  463.       MajorUnit => 1/8,
  464.       MinorUnit => 1/16,
  465.       MinimumScale => int($Low*16)/16,
  466.       MaximumScale => int($High*16+1)/16
  467.   );
  468.  
  469.   # Fat candles with only 5% gaps
  470.   $Chart->ChartGroups(1)->{GapWidth} = 5;
  471.  
  472.   sub RGB { $_[0] | ($_[1] >> 8) | ($_[2] >> 16) }
  473.  
  474.   # White background with a solid border
  475.  
  476.   $Chart->PlotArea->Border->{LineStyle} = xlContinuous;
  477.   $Chart->PlotArea->Border->{Color} = RGB(0,0,0);
  478.   $Chart->PlotArea->Interior->{Color} = RGB(255,255,255);
  479.  
  480.   # Add 1 hour moving average of the Close series
  481.   my $MovAvg = $Chart->SeriesCollection(4)->Trendlines
  482.         ->Add({Type => xlMovingAvg, Period => 4});
  483.   $MovAvg->Border->{Color} = RGB(255,0,0);
  484.  
  485. Now the finished workbook can be saved to disk as
  486. F<i:\tmp\tpj\data.xls>. That file most likely still exists from when the
  487. program ran yesterday, so we'll remove it. (Otherwise, Excel would pop
  488. up a dialog with a warning, because the SaveAs() method doesn't like
  489. to overwrite files.)
  490.  
  491.  
  492.   # Save workbook to file my $Filename = 'i:\tmp\tpj\data.xls';
  493.   unlink $Filename if -f $Filename;
  494.   $Book->SaveAs($Filename);
  495.   $Book->Close;
  496.  
  497. =head2 ACTIVEX DATA OBJECTS
  498.  
  499. Mary stores the daily prices in her T-bonds database, keeping the data
  500. for the different contracts in separate tables. After creating an ADO
  501. (ActiveX Data Object) connection to the database, she tries to connect
  502. a record set to the table for the current contract. If this fails, she
  503. assumes that the table doesn't exists yet and tries to create it:
  504.  
  505.   use Win32::OLE::Const 'Microsoft ActiveX Data Objects';
  506.  
  507.   my $Connection = Win32::OLE->new('ADODB.Connection');
  508.   my $Recordset = Win32::OLE->new('ADODB.Recordset');
  509.   $Connection->Open('T-Bonds');
  510.  
  511.   # Open a record set for the table of this contract
  512.   {
  513.     local $Win32::OLE::Warn = 0;
  514.     $Recordset->Open($Contract, $Connection, adOpenKeyset,
  515.                          adLockOptimistic, adCmdTable);
  516.   }
  517.  
  518.   # Create table and index if it doesn't exist yet
  519.   if (Win32::OLE->LastError) {
  520.       $Connection->Execute(>>"SQL");
  521.         CREATE TABLE $Contract
  522.         (
  523.           Day DATETIME,
  524.           Open DOUBLE, High DOUBLE, Low DOUBLE, Close DOUBLE
  525.         )
  526.   SQL
  527.       $Connection->Execute(>>"SQL");
  528.         CREATE INDEX $Contract
  529.         ON $Contract (Day) WITH PRIMARY
  530.   SQL
  531.       $Recordset->Open($Contract, $Connection, adOpenKeyset,
  532.                                 adLockOptimistic, adCmdTable);
  533.   }
  534.  
  535. C<$Win32::OLE::Warn> is temporarily set to zero, so that if
  536. C<$Recordset->Open> fails, the failure will be recorded silently without
  537. terminating the program. C<Win32::OLE->LastError> shows whether the Open
  538. failed or not. C<LastError> returns the OLE error code in a numeric
  539. context and the OLE error message in a string context, just like
  540. Perl's C<$!> variable.
  541.  
  542. Now Mary can add today's data:
  543.  
  544.   # Add new record to table
  545.   use Win32::OLE::Variant;
  546.   $Win32::OLE::Variant::LCID = $Win32::OLE::LCID;
  547.  
  548.   my $Fields = [qw(Day Open High Low Close)];
  549.   my $Values = [Variant(VT_DATE, $Day),
  550.                 $Open, $High, $Low, $Close];
  551.  
  552. Mary uses the Win32::OLE::Variant module to store C<$Day> as a date
  553. instead of a mere string. She wants to make sure that it's stored as
  554. an American-style date, so in the third line shown here she sets the
  555. locale ID of the Win32::OLE::Variant module to match the Win32::OLE
  556. module. (C<$Win32::OLE::LCID> had been set earlier to English, since
  557. that's what the Chicago Board of Trade uses.)
  558.  
  559.   {
  560.       local $Win32::OLE::Warn = 0;
  561.       $Recordset->AddNew($Fields, $Values);
  562.   }
  563.  
  564.   # Replace existing record
  565.   if (Win32::OLE->LastError) {
  566.       $Recordset->CancelUpdate;
  567.       $Recordset->Close;
  568.       $Recordset->Open(>>"SQL",
  569.                        $Connection, adOpenDynamic);
  570.           SELECT * FROM $Contract
  571.           WHERE Day = #$Day#
  572.   SQL
  573.       $Recordset->Update($Fields, $Values);
  574.   }
  575.  
  576.   $Recordset->Close;
  577.   $Connection->Close;
  578.  
  579. The program expects to be able to add a new record to the table. It
  580. fails if a record for this date already exists, because the Day field
  581. is the primary index and therefore must be unique. If an error occurs,
  582. the update operation started by AddNew() must first be cancelled with
  583. C<< $Recordset->CancelUpdate >>; otherwise the record set won't close.
  584.  
  585. =head2 LOTUS NOTES
  586.  
  587. Now Mary can use Lotus Notes to mail updates to all her customers
  588. interested in the T-bond data. (Lotus Notes doesn't provide its
  589. constants in the OLE type library, so Mary had to determine them by
  590. playing around with LotusScript.) The actual task is quite simple: A
  591. Notes session must be started, the mail database must be opened and
  592. the mail message must be created. The body of the message is created
  593. as a rich text field, which lets her mix formatted text with object
  594. attachments.
  595.  
  596. In her program, Mary extracts the email addresses from her customer
  597. database and sends separate message to each. Here, we've simplified it
  598. somewhat.
  599.  
  600.   sub EMBED_ATTACHMENT {1454;}     # from LotusScript
  601.  
  602.   my $Notes = Win32::OLE->new('Notes.NotesSession');
  603.   my $Database = $Notes->GetDatabase('', '');
  604.   $Database->OpenMail;
  605.   my $Document = $Database->CreateDocument;
  606.  
  607.   $Document->{Form} = 'Memo';
  608.   $Document->{SendTo} = ['Jon Orwant >orwant@tpj.com>',
  609.                          'Jan Dubois >jan.dubois@ibm.net>'];
  610.   $Document->{Subject} = "US T-Bonds Chart for $Day";
  611.  
  612.   my $Body = $Document->CreateRichtextItem('Body');
  613.   $Body->AppendText(>>"EOT");
  614.   I\'ve attached the latest US T-Bond data and chart for $Day.
  615.   The daily statistics were:
  616.  
  617.   \tOpen\t$Open
  618.   \tHigh\t$High
  619.   \tLow\t$Low
  620.   \tClose\t$Close
  621.  
  622.   Kind regards,
  623.  
  624.   Mary
  625.   EOT
  626.  
  627.   $Body->EmbedObject(EMBED_ATTACHMENT, '', $Filename);
  628.  
  629.   $Document->Send(0);
  630.  
  631. =head1 VARIANTS
  632.  
  633. In this final section, I'll talk about Variants, which are the data
  634. types that you use to talk to OLE objects. We talked about this line
  635. earlier:
  636.  
  637.   my $Values = [Variant(VT_DATE, $Day),
  638.                 $Open, $High, $Low, $Close];
  639.  
  640. Here, the Variant() function creates a Variant object, of type C<VT_DATE>
  641. and with the value C<$Day>. Variants are similar in many ways to Perl
  642. scalars. Arguments to OLE methods are transparently converted from
  643. their internal Perl representation to Variants and back again by the
  644. Win32::OLE module.
  645.  
  646. OLE automation uses a generic C<VARIANT> data type to pass
  647. parameters. This data type contains type information in addition to
  648. the actual data value. Only the following data types are valid for OLE
  649. automation:
  650.  
  651.   B<Data Type     Meaning>
  652.   VT_EMPTY      Not specified
  653.   VT_NULL       Null
  654.   VT_I2         2 byte signed integer
  655.   VT_I4         4 byte signed integer
  656.   VT_R4         4 byte real
  657.   VT_R8         8 byte real
  658.   VT_CY         Currency
  659.   VT_DATE       Date
  660.   VT_BSTR       Unicode string
  661.   VT_DISPATCH   OLE automation interface
  662.   VT_ERROR      Error
  663.   VT_BOOL       Boolean
  664.   VT_VARIANT    (only valid with VT_BYREF)
  665.   VT_UNKNOWN    Generic COM interface
  666.   VT_UI1        Unsigned character
  667.  
  668. The following two flags can also be used:
  669.  
  670.   VT_ARRAY      Array of values
  671.   VT_BYREF      Pass by reference (instead of by value)
  672.  
  673. B<The Perl to Variant transformation.> The following conversions are
  674. performed automatically whenever a Perl value must be translated into
  675. a Variant:
  676.  
  677.   Perl value                  Variant
  678.   Integer values              VT_I4
  679.   Real values                 VT_R8
  680.   Strings                     VT_BSTR
  681.   undef                       VT_ERROR (DISP_E_PARAMNOTFOUND)
  682.   Array reference             VT_VARIANT | VT_ARRAY
  683.   Win32::OLE object           VT_DISPATCH
  684.   Win32::OLE::Variant object  Type of the Variant object
  685.  
  686. What if your Perl value is a list of lists? Those can be irregularly
  687. shaped in Perl; that is, the subsidiary lists needn't have the same
  688. number of elements. In this case, the structure will be converted to a
  689. "rectangular" C<SAFEARRAY> of Variants, with unused slots set to
  690. C<VT_EMPTY>. Consider this Perl 2-D array:
  691.  
  692.   [ ["Perl" ],            # one element
  693.     [1, 3.1215, undef]    # three elements
  694.   ]
  695.  
  696. This will be translated to a 2 by 3 C<SAFEARRAY> that looks like this:
  697.  
  698.   VT_BSTR("Perl") VT_EMPTY      VT_EMPTY
  699.   VT_I4(1) VT_R8(3.1415)        VT_ERROR(DISP_E_PARAMNOTFOUND)
  700.  
  701. B<The Variant To Perl Transformation.> Automatic conversion from Variants
  702. to Perl values happens as follows:
  703.  
  704.   Variant                Perl value
  705.   VT_BOOL, VT_ERROR      Integer
  706.   VT_UI1, VT_I2, VT_I4   Integer
  707.   VT_R4, VT_R8           Float value
  708.   VT_BSTR                String
  709.   VT_DISPATCH            Win32::OLE object
  710.  
  711. B<The Win32::OLE::Variant module.> This module provides access to the
  712. Variant data type, which gives you more control over how these
  713. arguments to OLE methods are encoded. (This is rarely necessary if you
  714. have a good grasp of the default conversion rules.) A Variant object
  715. can be created with the C<< Win32::OLE::Variant->new >> method or the
  716. equivalent Variant() function:
  717.  
  718.   use Win32::OLE::Variant;
  719.   my $var1 = Win32::OLE::Variant->new(VT_DATE, 'Jan 1,1970');
  720.   my $var2 = Variant(VT_BSTR, 'This is an Unicode string');
  721.  
  722. Several methods let you inspect and manipulate Variant objects: The
  723. Type() and Value() methods return the variant type and value; the As()
  724. method returns the value after converting it to a different variant
  725. type; ChangeType() coerces the Variant into a different type; and
  726. Unicode() returns the value of a Variant object as an object of the
  727. Unicode::String class.
  728.  
  729. These conversions are more interesting if they can be applied directly
  730. to the return value of an OLE method call without first mutilating the
  731. value with default conversions. This is possible with the following
  732. trick:
  733.  
  734.   my $RetVal = Variant(VT_EMPTY, undef);
  735.   $Object->Dispatch($Method, $RetVal, @Arguments);
  736.  
  737. Normally, you wouldn't call Dispatch() directly; it's executed
  738. implicitly by either AUTOLOAD() or Invoke(). If Dispatch() realizes
  739. that the return value is already a Win32::OLE::Variant object, the
  740. return value is not translated into a Perl representation but rather
  741. copied verbatim into the Variant object.
  742.  
  743. Whenever a Win32::OLE::Variant object is used in a numeric or string
  744. context it is automatically converted into the corresponding format.
  745.  
  746.   printf "Number: %f and String: %s\n",
  747.          $Var, $Var;
  748.  
  749. This is equivalent to:
  750.  
  751.   printf "Number: %f and String: %s\n",
  752.          $Var->As(VT_R8), $Var->As(VT_BSTR);
  753.  
  754. For methods that modify their arguments, you need to use the C<VT_BYREF>
  755. flag. This lets you create number and string Variants that can be
  756. modified by OLE methods. Here, Corel's GetSize() method takes two
  757. integers and stores the C<x> and C<y> dimensions in them:
  758.  
  759.   my $x = Variant( VT_I4 | VT_BYREF, 0);
  760.   my $y = Variant( VT_I4 | VT_BYREF, 0);
  761.   $Corel->GetSize($x, $y);
  762.  
  763. C<VT_BYREF> support for other Variant types might appear in future
  764. releases of Win32::OLE.
  765.  
  766. =head1 FURTHER INFORMATION
  767.  
  768. =head2 DOCUMENTATION AND EXAMPLE CODE
  769.  
  770. More information about the OLE modules can be found in the
  771. documentation bundled with Win32::OLE. The distribution also contains
  772. other code samples.
  773.  
  774. The object model for Microsoft Office applications can be found in the
  775. Visual Basic Reference for Microsoft Access, Excel, Word, or
  776. PowerPoint. These help files are not installed by default, but they
  777. can be added later by rerunning F<setup.exe> and choosing I<custom
  778. setup>. The object model for Microsoft Outlook can be found on the
  779. Microsoft Office Developer Forum at:
  780. http://www.microsoft.com/OutlookDev/.
  781.  
  782. Information about the LotusScript object model can be found at:
  783. http://www.lotus.com/products/lotusscript.nsf.
  784.  
  785. =head2 OLE AUTOMATION ON OTHER PLATFORMS
  786.  
  787. Microsoft also makes OLE technology available for the Mac. DCOM is
  788. already included in Windows NT 4.0 and can be downloaded for Windows
  789. 95. MVS and some Unix systems can use EntireX to get OLE
  790. functionality; see
  791. http://www.softwareag.com/corporat/solutions/entirex/entirex.htm.
  792.  
  793. =head1 COPYRIGHT
  794.  
  795. Copyright 1998 I<The Perl Journal>. http://www.tpj.com
  796.  
  797. This article originally appeared in I<The Perl Journal> #10.  It
  798. appears courtesy of Jon Orwant and I<The Perl Journal>.  This document
  799. may be distributed under the same terms as Perl itself.
  800.