Leaked source code of windows server 2003
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

799 lines
31 KiB

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