I recently needed to embed several PDF files in our executable so we didn’t have to ship them individually with application and keep track of updating them separately. The original plan was to try to do something similar to what pcSoft does with the DLLs in the executable, where they “self-extract” with the first run. However I ran into a few issues.
1. FExtractResource() looked promising but is only for Mobile
2. You can embed and use images (PNG, etc) easily. just referencing their names for using DLoadImage, but it doesn’t work with PDFs
3. Embedding the PDF in the EXE is easy, but since there is not a native function to extract them, it would require a couple of different Windows API calls to handle, which started making my head hurt.
I finally discovered that there is a little known feature of HF Classic files that lets you embed them in your executable and then the standard Hxxxx commands will access the files just like as if the HF was on disk.
So what I did was create a HF table that has a binary memo where I store the PDF. I then embed that HF table in my EXE. Then I use HReadxxxx commands to get the PDFs.
The last challenge was to use ShellExecute to display the PDF in acrobat etc. the PDF has to be on disk, so I created my own PDF viewer in WD, so that I could display the PDFs without ever writing them to disk.
So let’s take a look at the demo application I put together for all of this.
This is a simple screen that displays a browse of our HF table that has the binary memo in it. The first two buttons allow you to add either a file, or an entire directory of PDFs to the HF table. This includes storing the PDF in a binary memo field. The Delete button is fairly self explanatory. The refresh button updates the binary memo using the current PDF located in the original location recorded when the file was first added. This is useful for updating the HF table after the PDFs have been updated. As I mentioned this system was created for distributing PDF help files with an executable, so with each release of the executable some of the help files are updated to cover new features, so its a simple matter to run the refresh button before generating the executable so that the latest PDFs will be included. Finally the view button is viewing the PDF using a “native” PDF viewer I created. Note: this viewer is actually displaying the PDF stored in the HF binary memo, not the physical file on the drive.
The layout for the HF table is very simple.
First there is a autonumbered id field, which all your tables should have after hearing Andy and I harp on that for years! Then the filename, and location are in separate fields. You could store them as one field if desired, but in my production application we are referring to the PDFs by their name so I need it as an independent field. And last is a Binary memo field to store the actual contents of the PDF file. This is a HF Classic table.
Now for the code. First a bit of project code.
If you read the help (which I KNOW you do!), you will discover that by default WinDev looks for HF classic files embed in the EXE first and then on disk, but for the sake of the developer that comes after me, I wanted to make this a little more obvious. So if we are in TestMode(), the Green Go button in the IDE, then I want to use the HF from the disk. That means that when I run via the GO mode I am updating the physical HF table. In my production application I have slightly different logic to handle this as I have a small help application that is only on the developers machine for updating the HF table.
The real magic happens when we are not in test mode, the hWDL constant says to look the the HF table embed in the library either your a WDL file or the EXE file. As mentioned this is actually the default access mode for HF classic tables, and if it doesn’t find it in embed it will search for it on the disk, so you have to be careful that you have really accomplished what you think you have when testing. We will discuss that more latter.
Let’s look at adding PDFs to the HF table. The code behind the Add Directory looks like this. The code behind Add a file is just a simpler version of this that doesn’t need to step through a directory so we will only look at the Add Directory code.
Line 1 turns on the hourglass, in case this is a large directory that could take a few moments to process. Line 2 opens the standard windows dialog to select a directory. If a directory was selected the real work begins with line 4 which uses the fListFile function to get a carriage return separated list of all PDF files in the directory. If there were PDF files in the directory the Line 6 begins looping through the list and adding them to the HF table. If you haven’t seen a For Loop like this before spend a few minutes studying it and reading the help files as it can be very handy. It takes a string of values separated by a character in this case a carriage return (CR) it places them into a single string (tmpFullFileName) one at a time with each pass of the Loop.
Line 8 and 9 use a few handy string functions from WX to separate the FileName from the Path.
Line 10 checks to see if there is already a record with this file name and if so it updates the record in lines 11-13, otherwise a new record is added via lines 15-18. The only line that we should really need to take a look at in that code is line 12 and 17. What they are doing is using another great WX function (fLoadBuffer) to load the physical PDF into the binary memo. This could be any physical file, so you could use this to store word docs, other applications, whatever you might need store inside your HF table.
When the Loop as completed Line 23 refreshes the table display, then we give the user a message to let them know it is done and turn off the hour glass.
At this point we have our physical HF table with the PDF stored in the binary memo. Before we take a look at my PDF viewer, lets look at how to embed this HF table in the EXE we are going to make.
I am sure there are several ways to do this via the IDE, but I tend to use the Project Explorer for most things. Notice in the project explorer there is a “Other” folder, anything that we add to this folder will be incorporated into our executable as a resource.
This opens the Windows file picker and lets you select files to add, and you can see in this screen shot that I have selected the 3 files that make up our HF table, the actual file (FIC), the Index (NDX) , and the Memo (MMO).
Afterwards the files are shown in my project explorer
Now we can generate the EXE
This takes you through several screens, I will assume you have made a WinDev executable before, so I will only mention a few items from this screens. First notice on the content of the EXE our HF files are shown.
For simplicity of this demonstration, and because I have UAC turned off on my system, I will set the directory of the data files to reside with the executable.
Also to make this demonstration easier, I will include the DLLs in the EXE
After a few more screens we have an executable. And if you run the executable and View one of the PDFs it will be displaying the PDF from the Binary Memo from the HF table that is embed in the EXE. If you don’t believe me and want to verify this, you can move the EXE to a different directory and even delete the HF table and the PDFs, and the EXE will still perform fine and display the records.
NOTE: a “downside” to this is that the HF tables that are included in your EXE are read only, you can not update them. Which when you think about it makes a lot of sense, as it would be fairly dangerous writing new data inside an EXE file while you are running it!!! This does provide yet another way to tell that we are actually using the embed HF table, if we try to add a file, we will get an error like this
Which again tells us the file is in read only mode because it is embed.
So that is all there is to embedding a HF table inside your Executable, and I could end the article now, but you know I always have to give you some bonus material. In our production app we of course wanted the user to be able to view the PDF files, originally when we were distributing physical PDF files we would use ShellExecute to accomplish that using whatever PDF viewer they had installed, the only problem with that is the files have to physically exists on the drive, which sort of defeats the purpose of our cool new ability. So I decide to create my own PDF viewer to display the PDF from the Binary memo field without ever writing the PDF to disk.
The code behind the view button is pretty simple. If a record is selected in the table, it performs an HReadSeekFirst to get the record and then calls my PDFViewer window pass the binary memo as a parameter.
The viewer is a simple window with a toolbar of useful buttons, and an image control.
The display mode of the Image is Homothetic without enlargement and the Anchoring is set to expand both by width and height.
The initialization code of the window, moves the passed binary memo into the Image control, and sets the zoom mode to fit by width.
The InitTittleBarandPageNumber procedure set the caption to the display both the current page # and the total number of pages. It also has some logic to enable/disable the page up / down buttons based on what page is being displayed. This is done as a procedure so that it can be called from all of the controls in the toolbar.
If you were expecting some fancy, complicated image/PDF manipulation code next then you haven’t been paying attention to me going on and on about how easy WX makes my life!!
Here is the code from the 3 controls that change which page is displayed. The Page up and down buttons and the page number edit control. As you see all they do is change the PageNumber property of the image control and then call the InitTittleBarandPageNumber function to change the caption and enable/disable the page up/down buttons.
So there must be some complicated code behind the zoom buttons at least, right? WRONG!!! If it seems hard in WX you are probably doing it wrong!!! Here is the code for all five zoom buttons.
Again all they are doing is changing the value of the Zoom property, it can’t get much easier than that can it?
And that is the all of the code of my “native” PDF viewer. Nothing fancy, but it sure is handy!.
Hopefully this has inspired you and got you thinking about how you could use this technique in your projects. Along with distributing PDF help files like we are doing, some thoughts that come to mind is perhaps distributing images that you want somewhat protected and don’t want the user to have direct access to the image files. You could also create a electronic catalog of products, that could be distributed as a single EXE file, even if your actually catalog uses several HF tables. Perhaps this could be used for some type of high end individual licensing of executable prior to them being shipped. And I am sure there are even more imaginative ways to use this technique, so be sure to come back and let us know what you used it for!!!
And as a final bonus, here is a link where you can download this entire project! http://thenextage.com/opensource/PDFHelp.zip
Uncle Pete’s Corner is webinar series on all things WX, to watch the watch the Uncle Pete’s corner webinar that is the companion to this article, and many other WX related webinars go to the WinDev US YouTube Channel and be sure to also join the WxLive – US Communityon Google+ to get notices about upcoming webinars. On the Google+ community you will find a active group developers discussing all things WX.