This week we released version 4.22b of the GoFiler line of products, and with the release came new Legato functions to control Internet Explorer through OLE. Note that in future releases of Legato we will be adding further OLE functionality. The concepts used in this blog can be applied to any other OLE automation project.
Now you may be asking, “What is OLE?” OLE or (Object Linking and Embedding) was a technology developed by Microsoft to allow one application to incorporate data from other applications. For example, a Word document could have an embedded Excel spreadsheet or vice versa. For our purposes, we will use the OLE interface to control Internet Explorer through Legato much like how Word could control Excel to render a spreadsheet for use in a document.
You might be thinking, “David, why use OLE? If we want the content of a webpage we could use HTTPGetString or HTTPPost.” While we could, keep in mind those functions return the contents of a page as the server presented it. What if the page we want to view has dynamic content generated through Javascript? We would need to run the Javascript ourselves to update the page. That sounds like a lot of work. We could have a browser load the page and give us the updated contents.
Consider the SEC’s XBRL viewer. When you click a presentation on the left, the contents on the right change. This is done using Javascript. Every time you click a presentation, a request is made the SEC’s web server for the information in that presentation. Javascript then takes the result of that request and replaces the page contents with the request result. The end product makes for a nicer user experience as the entire XBRL report doesn’t need to be loaded just to display a single presentation.
Before we go any further I am going to give a quick overview of the OLE functions in Legato. As always the Legato documentation has more information if needed. To create an OLE object, we need to use the OLEGetObject function. This function can take either an OLE CLSID or a predefined string. For now, we can use the value “Internet Explorer” to get an Internet Explorer OLE object. Like many Legato objects, an OLE object can be closed using the CloseHandle function.
Now that we have an OLE object, we need to interact with it. For Internet Explorer there are several functions we can use:
int = OLEIEGet ( handle hOLE, string uri );
int = OLEIEPost ( handle hOLE, string uri, string query );
int = OLEIEPostFile ( handle hOLE, string uri, string query, string data, int size, [string field] );
string = OLEIERead ( handle hOLE );
int = OLEIEIsBusy ( handle hOLE );
int = OLEIEShow ( handle hOLE );
int = OLEIEClose ( handle hOLE );
The first three functions can be used to GET or POST to a website. These functions operate asynchronously. This means that they will return immediately while Internet Explorer is likely still loading the page in the background. Because of this we need the fifth function, the OLEIEIsBusy function. This function simply returns true if Internet Explorer is still loading a page. As stated above, Javascript can edit the page after it is loaded so the OLEIEIsBusy function does not guarantee that Javascript is done loading content but rather that Internet Explorer has loaded the page enough for user interaction. If we want to read the resulting page information we can use the OLEIERead function. Please note that if Internet Explorer is still loading a page, you will likely still receive the contents of the last page (including the about:blank or home page for the user). Lastly, there is the OLEIEShow function, which displays Internet Explorer to the user, and the OLEIEClose function, which will close Internet Explorer. If the OLEIEShow function is never called, Internet Explorer will close automatically when the OLE object is closed.
Now that we’ve discussed Legato OLE functions, I’ve written a simple script to illustrate the difference between the HTTPGetString function and OLE automation. This script gets an XBRL viewer page using the HTTPGetString function and then again using OLE and compares the contents of the results:
string get_report_div(string html);
void main() {
handle hIE;
string page;
string res;
page = "https://www.sec.gov/cgi-bin/viewer?action=view&cik=1518520&accession_number=0001213900-18-004605&xbrl_type=v#";
// Get Page using HTTPGetFile
res = HTTPGetString(page);
res = get_report_div(res);
AddMessage("Direct Download: %d bytes, %s", GetStringLength(res), TrailStringAfter(res, 64));
// Get Page Using IE
hIE = OLEGetObject("Internet Explorer");
OLEIEGet(hIE, page);
while (OLEIEIsBusy(hIE)) {
Sleep(5000);
}
res = OLEIERead(hIE);
res = get_report_div(res);
AddMessage("Internet Explorer: %d bytes, %s", GetStringLength(res), TrailStringAfter(res, 64));
}
string get_report_div(string html) {
handle hSGML;
string res;
string tag;
hSGML = SGMLCreate();
SGMLSetString(hSGML, html);
tag = SGMLNextElement(hSGML);
while (tag != "") {
if (FindInString(tag, "<body") == 0) {
break;
}
tag = SGMLNextElement(hSGML);
}
while (tag != "") {
if (tag == "<div id=\"reportDiv\">") {
return SGMLFindClosingElement(hSGML, SP_FCE_CODE_AS_IS);
}
tag = SGMLNextElement(hSGML);
}
return "";
}
The output from the script looks like this:
Direct Download: 0 bytes,
Internet Explorer: 29282 bytes, <title></title><link href="report.css" rel="stylesheet" ...
The script gets the same page using the two different methods. It then employs the get_report_div function to get the content of the DIV tag with an id of “reportDiv”. When using the HTTPGetString function, the contents of the report division are empty. When we use Internet Explorer through OLE, the report division has contents.
While this does illustrate how the contents of pages can differ, there is another use for Internet Explorer automation that we can discuss. That is taking the user to a webpage with information already set. An example of this is the “Login to EDGAR System” menu function within GoFiler. We could launch the page using the RunProgram function. That will open the webpage, but it is limited to the GET verb. If we want to use POST or set something up for the user, we can use OLE automation.
For example, what if our operators also use a CRM database while working to track time or other work flow data? We could add a menu option in GoFiler that would log the user into the database and maybe even browse to the correct customer if the data was available in the active project. This is less development than adding an interface to the CRM system in GoFiler and less prone to user error since the user is entering less information each job.
As you can see, OLE is another powerful tool you can add to your Legato toolbox. With it we can take the user to webpages that require post data or read the content of dynamically generated pages. Whether it is to simplify the user experience or to reduce development time, Legato can help out!
David Theis has been developing software for Windows operating systems for over fifteen years. He has a Bachelor of Sciences in Computer Science from the Rochester Institute of Technology and co-founded Novaworks in 2006. He is the Vice President of Development and is one of the primary developers of GoFiler, a financial reporting software package designed to create and file EDGAR XML, HTML, and XBRL documents to the U.S. Securities and Exchange Commission. |
Additional Resources
Novaworks’ Legato Resources
Legato Script Developers LinkedIn Group
Primer: An Introduction to Legato