-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy patharticle-10.html
499 lines (346 loc) · 39.2 KB
/
article-10.html
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
---
article: 10
title: "How to create and use HTML resource files"
summary: "A review of the techniques used to create HTML resources for embedding in a program, along with a discussion of Internet Explorer's use of the res:// protocol to access and display embedded HTML."
meta-title: "Use Delphi Pascal to create and use HTML resource files | How to"
meta-desc: "An article explaining how to use Delphi Pascal to create and use HTML resource files suitable for embedding in a program or DLL for use with Internet Explorer's res:// protocol."
index: true
redirect_from:
- /articles/10
---
<section id="contents">
<h2>Contents</h2>
<nav>
<div class="well">
<ul>
<li><a href="#intro">Introduction</a></li>
<li><a href="#access">Accessing HTML resources from the browser</a></li>
<li><a href="#understand">Understanding the res:// protocol</a>
<ul>
<li><a href="#understand-overview">Overview</a></li>
<li><a href="#understand-resid">About resource identifiers</a></li>
<li><a href="#understand-safeurl">Making res:// protocol URLs safe</a></li>
</ul>
</li>
<li>
<a href="#generate">Generating res:// protocol URLs from Delphi</a>
<ul>
<li><a href="#generate-overview">Overview</a></li>
<li><a href="#generate-resid">Resource identifiers</a></li>
<li><a href="#generate-safeurl">URL encoding</a></li>
<li><a href="#generate-buildurl">Building the URL</a></li>
<li><a href="#generate-rthtml">Defining RT_HTML</a></li>
</ul>
</li>
<li>
<a href="#create">How to create HTML resources</a>
<ul>
<li><a href="#create-source">Creating resource file source code</a></li>
<li><a href="#create-better">Using better resource names</a></li>
</ul>
</li>
<li><a href="#conclusion">Conclusion</a></li>
<li><a href="#demo">Demo program</a></li>
<li><a href="#bibliography">Further reading</a></li>
<li><a href="#feedback">Feedback</a></li>
</ul>
</div>
</nav>
</section>
<section id="intro">
<h2>Introduction</h2>
<p><a href="/articles/article-2">Article #2</a> noted the benefits of embedding data within your program's executable file or within a DLL. Often this is preferable to supplying a lot of separate support files that the user can accidentally alter or delete. The same principle applies to HTML and related files displayed by your application in a <var>TWebBrowser</var> control.</p>
<p>In fact Microsoft® have encouraged this by creating the <code>res://</code> protocol. This is used by <var>TWebBrowser</var> and Internet Explorer to access HTML etc. files that are stored in a module's resources. This protocol makes it very easy to navigate to HTML resources using the <var>TWebBrowser</var> control.</p>
</section>
<section id="access">
<h2>Accessing HTML resources from the browser</h2>
<p>When displaying data from embedded resources we usually have to do some work – we have to read the data out of resources and display it in a suitable control (see <a href="./article-3">article #3</a>). However, with <var>TWebBrowser</var>, Microsoft® has made things easier for us. All we have to do is specify where to find the resource by using the special <code>res://</code> protocol URL. The browser will extract the data from the resource and display it as it would a conventional file.</p>
<p>This approach works in both the <var>TWebBrowser</var> control and by typing the <code>res://</code> protocol URLs directly into the <em>Internet Explorer</em> location bar.</p>
<aside>
<div class="callout callout-warning">
<h4>No query strings</h4>
<p>The <code>res://</code> protocol doesn't interpret query strings that are appended to the URL. Pity!</p>
</div>
</aside>
<p>By way of an example, the following screenshot shows Internet Explorer displaying one of the web pages included in the resources of this article's <a href="#demo">demo program</a>.</p>
<div class="frame">
<div>
<img class="scale center-block" src="{{ site.data.core.articles-images-base-url}}/10.png" alt="IE showing a HTML resource from the demo program" title="IE showing a HTML resource from the demo program" />
</div>
<div class="caption">Image 1</div>
</div>
<p>To replicate this you must first compile the <a href="#demo">demo program</a> then enter the following URL in <em>Internet Explorer</em>'s location bar:</p>
<pre class="indent pre-scrollable kbd">res://[Path to Demo]\Article10.exe/INDEX_PAGE</pre>
<p>Where <kbd>[Path to Demo]</kbd> is the full path to the demo program. Strictly speaking you should "escape" any backslash, space, hash ("#") and colon characters in the path – see <a href="#understand-safeurl">Making res:// protocol URLs safe</a> below for details. If you copy the URL from the demo program it will already be escaped.</p>
<p>What is more, if a document is loaded from a <code>res://</code> URL, that URL becomes the browser's base URL. This means that any relative links in the document refer back into the resources. We can get the same effect by setting a document's <code><base></code> address to a <code>res://</code> URL.</p>
</section>
<section id="understand">
<h2>Understanding the res:// protocol</h2>
<h3 id="understand-overview">Overview</h3>
<p>Valid <code>res://</code> protocol URLs have the form:</p>
<p class="indent"><dfn>res://<strong>module</strong>[/<strong>restype</strong>]/<strong>resid</strong></dfn></p>
<p>where the segments of the URL have the following meanings:</p>
<dl>
<dt><dfn>module</dfn></dt>
<dd>The name of the module (DLL or executable program) containing the resource. The full path to the module is required, unless it is on the search path.</dd>
<dt><dfn>restype</dfn></dt>
<dd>An optional segment that specifies the resource type identifier. If the segment is omitted the resource type is assumed to be <var>RT_HTML</var>.</dd>
<dt><dfn>resid</dfn></dt>
<dd>The resource name identifier.</dd>
</dl>
<h3 id="understand-resid">About resource identifiers</h3>
<p>An attribute of Windows® resource names and resource types is that they can either be identified by a character string or by a numeric value.</p>
<p>When specifying resource identifiers in URLs we must obviously represent both kinds as strings, but we must also be able to distinguish between the two different kinds. Numeric identifiers are flagged by prepending a "#" character to the decimal value of the identifier. For example the <var>RT_HTML</var> resource type would be represented as "#23". String identifiers are represented literally.</p>
<h3 id="understand-safeurl">Making res:// protocol URLs safe</h3>
<p>We have to be careful when entering a <code>res://</code> protocol URL into the browser bar, or when embedding the URL as an attribute of a HTML tag such as the <code><a></code> tag. The URL must not include "special characters". Any such characters need to be replaced by URL escape characters.</p>
<aside>
<div class="callout callout-info">
<h4>URL escape characters</h4>
<p>These characters have the form <code>%HH</code> where <code>HH</code> is the character code in hexadecimal.</p>
</div>
</aside>
<p>In particular, since our <code>res://</code> protocol URL may contain a path to the module that holds the required resource, we need to escape any back-slashes and spaces in the path. Additionally, since numeric resource identifiers begin with a "#" character, and these have special meaning in a URL, it may be wise to escape these too.</p>
<p>For example this URL:</p>
<pre class="indent code pre-scrollable">res://C:\Program Files\MyModule.dll/#42</pre>
<p>would be escaped as:</p>
<pre class="indent code pre-scrollable">res://C%3A%5CProgram%20Files%5CMyModule.dll/%2342</pre>
<p>The escape codes used are: <code>%3A</code> (":"), <code>%5C</code> ("\"), <code>%20</code> (space) and <code>%23</code> ("#").</p>
</section>
<section id="generate">
<h2>Generating res:// protocol URLs from Delphi</h2>
<h3 id="generate-overview">Overview</h3>
<p>When developing an application that uses the web browser control, we may need to build <code>res://</code> protocol URLs programatically. In this section we present the code to do this.</p>
<h3 id="generate-resid">Resource identifiers</h3>
<p>Recall that resource names and resource types can be either strings or have a numeric value. In code both kinds of resource identifier are represented by a <var>PChar</var> value. When the identifier is a string the <var>PChar</var> value points to the required string of characters. For numeric identifiers the high order word of the pointer is zero and the loworder word contains the value. In a URL we know that the numeric indentifier is a series of digits preceded by a <code>#</code> symbol.</p>
<p>Our job is to develop a Delphi function that transforms a resource identifier referenced by a <var>PChar</var> into an appropriately formatted string for use in a URL. <span class="figureref">Listing 1</span> shows a solution:</p>
<div id="listing-1" class="frame">
<div class="code-pascal">
<pre class="line"><span class="linenum"> 1</span><span class="kwd">function</span><span class="space"> </span><span class="ident">FormatResNameOrType</span><span class="sym">(</span><span class="ident">ResID</span><span class="sym">:</span><span class="space"> </span><span class="ident">PChar</span><span class="sym">)</span><span class="sym">:</span><span class="space"> </span><span class="kwd">string</span><span class="sym">;</span></pre>
<pre class="line"><span class="linenum"> 2</span><span class="kwd">begin</span></pre>
<pre class="line"><span class="linenum"> 3</span><span class="space"> </span><span class="kwd">if</span><span class="space"> </span><span class="ident">HiWord</span><span class="sym">(</span><span class="ident">LongWord</span><span class="sym">(</span><span class="ident">ResID</span><span class="sym">)</span><span class="sym">)</span><span class="space"> </span><span class="sym">=</span><span class="space"> </span><span class="num">0</span><span class="space"> </span><span class="kwd">then</span></pre>
<pre class="line"><span class="linenum"> 4</span><span class="space"> </span><span class="comment">// high word = 0 => numeric resource id</span></pre>
<pre class="line"><span class="linenum"> 5</span><span class="space"> </span><span class="comment">// numeric value is stored in low word</span></pre>
<pre class="line"><span class="linenum"> 6</span><span class="space"> </span><span class="ident">Result</span><span class="space"> </span><span class="sym">:=</span><span class="space"> </span><span class="ident">Format</span><span class="sym">(</span><span class="str">'#%d'</span><span class="sym">,</span><span class="space"> </span><span class="sym">[</span><span class="ident">LoWord</span><span class="sym">(</span><span class="ident">LongWord</span><span class="sym">(</span><span class="ident">ResID</span><span class="sym">)</span><span class="sym">)</span><span class="sym">]</span><span class="sym">)</span></pre>
<pre class="line"><span class="linenum"> 7</span><span class="space"> </span><span class="kwd">else</span></pre>
<pre class="line"><span class="linenum"> 8</span><span class="space"> </span><span class="comment">// high word <> 0 => string value</span></pre>
<pre class="line"><span class="linenum"> 9</span><span class="space"> </span><span class="comment">// PChar is implicitly converted to string</span></pre>
<pre class="line"><span class="linenum"> 10</span><span class="space"> </span><span class="ident">Result</span><span class="space"> </span><span class="sym">:=</span><span class="space"> </span><span class="ident">ResID</span><span class="sym">;</span></pre>
<pre class="line"><span class="linenum"> 11</span><span class="kwd">end</span><span class="sym">;</span></pre>
</div>
<div class="caption">Listing 1</div>
</div>
<p>We simply test the resource identifier pointer's high word to see if it is zero. If so we return the value of the low word preceeded by <code>#</code>. Otherwise we return the resource identifier unchanged. Delphi silently converts the result to the compiler's default string type.</p>
<aside>
<div class="callout callout-info">
<h4>Using <var>PChar</var></h4>
<p><var>PChar</var> works here on any version of Delphi, regardless of whether Delphi defines it as <var>PAnsiChar</var> or <var>PWideChar</var>. This is because <var>FormatResNameOrType</var> checks the pointer itself, not the character it points to.</p>
<p class="alert alert-warning glyph">Don't try to use the wide char version in non-Unicode Delphis or the ANSI version on Unicode Delphis – always use the native <var>PChar</var>.</p>
</div>
</aside>
<h3 id="generate-safeurl">URL encoding</h3>
<p><a href="#understand-safeurl">Earlier</a> we discussed the need to escape special characters in a<code>res://</code> protocol URL. The simple function presented in <span class="figureref">Listing 2</span> takes the safest possible approach and escapes all characters that are neither alphanumeric nor one of "-", "_" or ".". Conditional compilation is used to test membership correctly depending on if an ANSI or Unicode version of Delphi is being used.</p>
<div id="listing-2" class="frame">
<div class="code-pascal">
<pre class="line"><span class="linenum"> 1</span><span class="kwd">function</span><span class="space"> </span><span class="ident">URLEncode</span><span class="sym">(</span><span class="kwd">const</span><span class="space"> </span><span class="ident">S</span><span class="sym">:</span><span class="space"> </span><span class="kwd">string</span><span class="sym">)</span><span class="sym">:</span><span class="space"> </span><span class="kwd">string</span><span class="sym">;</span></pre>
<pre class="line"><span class="linenum"> 2</span><span class="kwd">var</span></pre>
<pre class="line"><span class="linenum"> 3</span><span class="space"> </span><span class="ident">Idx</span><span class="sym">:</span><span class="space"> </span><span class="ident">Integer</span><span class="sym">;</span><span class="space"> </span><span class="comment">// loops thru characters in string</span></pre>
<pre class="line"><span class="linenum"> 4</span><span class="kwd">begin</span></pre>
<pre class="line"><span class="linenum"> 5</span><span class="space"> </span><span class="ident">Result</span><span class="space"> </span><span class="sym">:=</span><span class="space"> </span><span class="str">''</span><span class="sym">;</span></pre>
<pre class="line"><span class="linenum"> 6</span><span class="space"> </span><span class="kwd">for</span><span class="space"> </span><span class="ident">Idx</span><span class="space"> </span><span class="sym">:=</span><span class="space"> </span><span class="num">1</span><span class="space"> </span><span class="kwd">to</span><span class="space"> </span><span class="ident">Length</span><span class="sym">(</span><span class="ident">S</span><span class="sym">)</span><span class="space"> </span><span class="kwd">do</span></pre>
<pre class="line"><span class="linenum"> 7</span><span class="space"> </span><span class="kwd">begin</span></pre>
<pre class="line"><span class="linenum"> 8</span><span class="space"> </span><span class="preproc">{$IFDEF UNICODE}</span></pre>
<pre class="line"><span class="linenum"> 9</span><span class="space"> </span><span class="kwd">if</span><span class="space"> </span><span class="ident">CharInSet</span><span class="sym">(</span><span class="ident">S</span><span class="sym">[</span><span class="ident">Idx</span><span class="sym">]</span><span class="sym">,</span><span class="space"> </span><span class="sym">[</span><span class="str">'A'</span><span class="sym">..</span><span class="str">'Z'</span><span class="sym">,</span><span class="space"> </span><span class="str">'a'</span><span class="sym">..</span><span class="str">'z'</span><span class="sym">,</span><span class="space"> </span><span class="str">'0'</span><span class="sym">..</span><span class="str">'9'</span><span class="sym">,</span><span class="space"> </span><span class="str">'-'</span><span class="sym">,</span><span class="space"> </span><span class="str">'_'</span><span class="sym">,</span><span class="space"> </span><span class="str">'.'</span><span class="sym">]</span><span class="sym">)</span><span class="space"> </span><span class="kwd">then</span></pre>
<pre class="line"><span class="linenum"> 10</span><span class="space"> </span><span class="preproc">{$ELSE}</span></pre>
<pre class="line"><span class="linenum"> 11</span><span class="space"> </span><span class="kwd">if</span><span class="space"> </span><span class="ident">S</span><span class="sym">[</span><span class="ident">Idx</span><span class="sym">]</span><span class="space"> </span><span class="kwd">in</span><span class="space"> </span><span class="sym">[</span><span class="str">'A'</span><span class="sym">..</span><span class="str">'Z'</span><span class="sym">,</span><span class="space"> </span><span class="str">'a'</span><span class="sym">..</span><span class="str">'z'</span><span class="sym">,</span><span class="space"> </span><span class="str">'0'</span><span class="sym">..</span><span class="str">'9'</span><span class="sym">,</span><span class="space"> </span><span class="str">'-'</span><span class="sym">,</span><span class="space"> </span><span class="str">'_'</span><span class="sym">,</span><span class="space"> </span><span class="str">'.'</span><span class="sym">]</span><span class="space"> </span><span class="kwd">then</span></pre>
<pre class="line"><span class="linenum"> 12</span><span class="space"> </span><span class="preproc">{$ENDIF}</span></pre>
<pre class="line"><span class="linenum"> 13</span><span class="space"> </span><span class="ident">Result</span><span class="space"> </span><span class="sym">:=</span><span class="space"> </span><span class="ident">Result</span><span class="space"> </span><span class="sym">+</span><span class="space"> </span><span class="ident">S</span><span class="sym">[</span><span class="ident">Idx</span><span class="sym">]</span></pre>
<pre class="line"><span class="linenum"> 14</span><span class="space"> </span><span class="kwd">else</span></pre>
<pre class="line"><span class="linenum"> 15</span><span class="space"> </span><span class="ident">Result</span><span class="space"> </span><span class="sym">:=</span><span class="space"> </span><span class="ident">Result</span><span class="space"> </span><span class="sym">+</span><span class="space"> </span><span class="str">'%'</span><span class="space"> </span><span class="sym">+</span><span class="space"> </span><span class="ident">IntToHex</span><span class="sym">(</span><span class="ident">Ord</span><span class="sym">(</span><span class="ident">S</span><span class="sym">[</span><span class="ident">Idx</span><span class="sym">]</span><span class="sym">)</span><span class="sym">,</span><span class="space"> </span><span class="num">2</span><span class="sym">)</span><span class="sym">;</span></pre>
<pre class="line"><span class="linenum"> 16</span><span class="space"> </span><span class="kwd">end</span><span class="sym">;</span></pre>
<pre class="line"><span class="linenum"> 17</span><span class="kwd">end</span><span class="sym">;</span></pre>
</div>
<div class="caption">Listing 2</div>
</div>
<aside>
<div class="callout callout-info">
<h4>Library routine</h4>
<p>A more flexible version of this routine see <var>URLEncode</var> in the <a href="https://github.com/delphidabbler/code-snippets">Code Snippets Database</a>.</p>
</div>
</aside>
<h3 id="generate-buildurl">Building the URL</h3>
<p>We now move on to take a look at two overloaded functions that can create a <code>res://</code> protocol URL.</p>
<p>Both functions take a reference to a module, a resource name and an optional resource type as parameters. If the resource type parameter is omitted, or is nil, it is not included in the URL and a resource type of <var>RT_HTML</var> will be assumed.</p>
<p>The routines differ in how they handle the module component of the URL. The first function (<span class="figureref">Listing 3</span>) is passed the module file name as a string while the second routine (<span class="figureref">Listing 4</span>) takes a handle to the required module. First we will consider the function that accepts the module name as a string:</p>
<div id="listing-3" class="frame">
<div class="code-pascal">
<pre class="line"><span class="linenum"> 1</span><span class="kwd">function</span><span class="space"> </span><span class="ident">MakeResourceURL</span><span class="sym">(</span><span class="kwd">const</span><span class="space"> </span><span class="ident">ModuleName</span><span class="sym">:</span><span class="space"> </span><span class="kwd">string</span><span class="sym">;</span><span class="space"> </span><span class="kwd">const</span><span class="space"> </span><span class="ident">ResName</span><span class="sym">:</span><span class="space"> </span><span class="ident">PChar</span><span class="sym">;</span></pre>
<pre class="line"><span class="linenum"> 2</span><span class="space"> </span><span class="kwd">const</span><span class="space"> </span><span class="ident">ResType</span><span class="sym">:</span><span class="space"> </span><span class="ident">PChar</span><span class="space"> </span><span class="sym">=</span><span class="space"> </span><span class="kwd">nil</span><span class="sym">)</span><span class="sym">:</span><span class="space"> </span><span class="kwd">string</span><span class="sym">;</span><span class="space"> </span><span class="kwd">overload</span><span class="sym">;</span></pre>
<pre class="line"><span class="linenum"> 3</span><span class="kwd">begin</span></pre>
<pre class="line"><span class="linenum"> 4</span><span class="space"> </span><span class="ident">Assert</span><span class="sym">(</span><span class="ident">ModuleName</span><span class="space"> </span><span class="sym"><></span><span class="space"> </span><span class="str">''</span><span class="sym">)</span><span class="sym">;</span></pre>
<pre class="line"><span class="linenum"> 5</span><span class="space"> </span><span class="ident">Assert</span><span class="sym">(</span><span class="ident">Assigned</span><span class="sym">(</span><span class="ident">ResName</span><span class="sym">)</span><span class="sym">)</span><span class="sym">;</span></pre>
<pre class="line"><span class="linenum"> 6</span><span class="space"> </span><span class="ident">Result</span><span class="space"> </span><span class="sym">:=</span><span class="space"> </span><span class="str">'res://'</span><span class="space"> </span><span class="sym">+</span><span class="space"> </span><span class="ident">URLEncode</span><span class="sym">(</span><span class="ident">ModuleName</span><span class="sym">)</span><span class="sym">;</span></pre>
<pre class="line"><span class="linenum"> 7</span><span class="space"> </span><span class="kwd">if</span><span class="space"> </span><span class="ident">Assigned</span><span class="sym">(</span><span class="ident">ResType</span><span class="sym">)</span><span class="space"> </span><span class="kwd">then</span></pre>
<pre class="line"><span class="linenum"> 8</span><span class="space"> </span><span class="ident">Result</span><span class="space"> </span><span class="sym">:=</span><span class="space"> </span><span class="ident">Result</span><span class="space"> </span><span class="sym">+</span><span class="space"> </span><span class="str">'/'</span><span class="space"> </span><span class="sym">+</span><span class="space"> </span><span class="ident">URLEncode</span><span class="sym">(</span><span class="ident">FormatResNameOrType</span><span class="sym">(</span><span class="ident">ResType</span><span class="sym">)</span><span class="sym">)</span><span class="sym">;</span></pre>
<pre class="line"><span class="linenum"> 9</span><span class="space"> </span><span class="ident">Result</span><span class="space"> </span><span class="sym">:=</span><span class="space"> </span><span class="ident">Result</span><span class="space"> </span><span class="sym">+</span><span class="space"> </span><span class="str">'/'</span><span class="space"> </span><span class="sym">+</span><span class="space"> </span><span class="ident">URLEncode</span><span class="sym">(</span><span class="ident">FormatResNameOrType</span><span class="sym">(</span><span class="ident">ResName</span><span class="sym">)</span><span class="sym">)</span><span class="sym">;</span></pre>
<pre class="line"><span class="linenum"> 10</span><span class="kwd">end</span><span class="sym">;</span></pre>
</div>
<div class="caption">Listing 3</div>
</div>
<p>This function records the protocol name and appends the name of the module to it. It then appends the resource type if specified and finally adds the resource name. URL segments are separated by slash characters. Notice how we call <var><a href="#listing-1">FormatResNameOrType</a></var> to get the resource type and resource name identifiers and we use <var><a href="#listing-2">URLEncode</a></var> to ensure each segment of the URL is URL safe.</p>
<p>The second form of the function is for use with loaded modules. Such modules are identified by means of their module handle rather than by name, as shown in <span class="figureref">Listing 4</span>:</p>
<div id="listing-4" class="frame">
<div class="code-pascal">
<pre class="line"><span class="linenum"> 1</span><span class="kwd">function</span><span class="space"> </span><span class="ident">MakeResourceURL</span><span class="sym">(</span><span class="kwd">const</span><span class="space"> </span><span class="ident">Module</span><span class="sym">:</span><span class="space"> </span><span class="ident">HMODULE</span><span class="sym">;</span><span class="space"> </span><span class="kwd">const</span><span class="space"> </span><span class="ident">ResName</span><span class="sym">:</span><span class="space"> </span><span class="ident">PChar</span><span class="sym">;</span></pre>
<pre class="line"><span class="linenum"> 2</span><span class="space"> </span><span class="kwd">const</span><span class="space"> </span><span class="ident">ResType</span><span class="sym">:</span><span class="space"> </span><span class="ident">PChar</span><span class="space"> </span><span class="sym">=</span><span class="space"> </span><span class="kwd">nil</span><span class="sym">)</span><span class="sym">:</span><span class="space"> </span><span class="kwd">string</span><span class="sym">;</span><span class="space"> </span><span class="kwd">overload</span><span class="sym">;</span></pre>
<pre class="line"><span class="linenum"> 3</span><span class="kwd">begin</span></pre>
<pre class="line"><span class="linenum"> 4</span><span class="space"> </span><span class="ident">Result</span><span class="space"> </span><span class="sym">:=</span><span class="space"> </span><span class="ident">MakeResourceURL</span><span class="sym">(</span><span class="ident">GetModuleName</span><span class="sym">(</span><span class="ident">Module</span><span class="sym">)</span><span class="sym">,</span><span class="space"> </span><span class="ident">ResName</span><span class="sym">,</span><span class="space"> </span><span class="ident">ResType</span><span class="sym">)</span><span class="sym">;</span></pre>
<pre class="line"><span class="linenum"> 5</span><span class="kwd">end</span><span class="sym">;</span></pre>
</div>
<div class="caption">Listing 4</div>
</div>
<p>This function simply gets the name of the module by calling the <var>SysUtils</var> unit's <var>GetModuleName</var> function. It then calls the other overloaded function, passing it the module name.</p>
<h3 id="generate-rthtml">Defining RT_HTML</h3>
<p><var>RT_HTML</var> is a constant that denotes an HTML resource. Unlike other <var>RT_*</var> constants it is not defined in Delphi 7. If needed, the constant should be defined as follows:</p>
<div id="listing-5" class="frame">
<div class="code-pascal">
<pre class="line"><span class="linenum"> 1</span><span class="kwd">const</span></pre>
<pre class="line"><span class="linenum"> 2</span><span class="space"> </span><span class="ident">RT_HTML</span><span class="space"> </span><span class="sym">=</span><span class="space"> </span><span class="ident">MakeIntResource</span><span class="sym">(</span><span class="num">23</span><span class="sym">)</span><span class="sym">;</span></pre>
</div>
<div class="caption">Listing 5</div>
</div>
</section>
<section id="create">
<h2>How to create HTML resources</h2>
<h3 id="create-source">Creating resource file source code</h3>
<p>Although you can embed and access HTML and related files as <var>RCDATA</var> or any other user-defined resource type, it is recommended that they are stored as resource type #23 (<var>RT_HTML</var>). Any type of file that can be displayed in a browser can legitimately be included under this resource type. If you have access to a resource compiler, the easiest way to create a resource file containing HTML resources is as follows:</p>
<ol>
<li>
<p class="strong">Assemble the required assets</p>
<p>Gather together all the files you wish to include in the resource file in a convenient location.</p>
</li>
<li>
<p class="strong">Create a resource source file</p>
<p>Create the resource source (<code>.rc</code>) file. This file tells resource compiler which files to include in the resource and what resource names types to use for them. The compiler embeds the files in the resource file Here is an example source file:</p>
<div id="listing-6" class="frame">
<div class="code-pascal">
<pre class="line"><span class="comment">/* standard resource # for HTML */</span></pre>
<pre class="line"><span class="nothing">#define HTML 23</span></pre>
<pre class="line"> </pre>
<pre class="line"><span class="comment">/* include all resource from external files */</span></pre>
<pre class="line"><span class="nothing">TIPS_HTML HTML "tips.html"</span></pre>
<pre class="line"><span class="nothing">HELP_HTML HTML "help.html"</span></pre>
<pre class="line"><span class="nothing">LEFTARROW_GIF HTML "left-arrow.gif"</span></pre>
<pre class="line"><span class="nothing">RIGHTARROW_GIF HTML "right-arrow.gif"</span></pre>
<pre class="line"><span class="nothing">FILL_GIF HTML "fill.gif"</span></pre>
<pre class="line"><span class="nothing">HELP_GIF HTML "help.gif"</span></pre>
<pre class="line"><span class="nothing">TIPS_JS HTML "tips.js"</span></pre>
<pre class="line"><span class="nothing">STYLE_CSS HTML "style.css"</span></pre>
</div>
<div class="caption">Listing 6</div>
</div>
</li>
<li>
<p class="strong">Compile the source file</p>
<p>Use a resource compiler such as Borland's BRCC32 to compile the source file. The following command line instructs BRCC32 to compile a source file, <code>HTML.rc</code>, into a binary resource file named <code>HTML.res</code>:</p>
<pre class="pre-scrollable kbd">BRCC32 -fo"HTML.res" HTML.rc</pre>
<p>We assume that the source file is in the current directory and the compiler is on the path. If you don't have a suitable resource compiler then <code>.res</code> files can be generated programmatically – <a href="/articles/article-2">article #2</a> explains – or you can use my open source <a href="/software/htmlres">HTML resource file compiler</a>.</p>
</li>
<li>
<p class="strong">Link the resource file into your application or DLL.</p>
<p>In Delphi we link the resource by adding the following line to a project file or to a convenient unit. For the example given above we would use:</p>
<pre class="pre-scrollable samp">{$R HTML.res}</pre>
</li>
</ol>
<h3 id="create-better">Using better resource names</h3>
<p>You may find it helpful to name resources with file-like names. Doing this will make it easier to migrate an existing series of interlinked HTML files to resources (providing the files are all in the same directory). Why? because the existing local links in the file will still work after the move!</p>
<aside>
<div class="callout callout-info">
<h4>Windows does it!</h4>
<p>Several HTML resources in Windows DLLs are named in this way.</p>
</div>
</aside>
<p>For example, suppose you have an application that displays local files from a single directory in a <var>TWebBrowser</var> control. Assume the directory contains the following files:</p>
<ul>
<li><code>index.html</code></li>
<li><code>page1.html</code></li>
<li><code>page2.html</code></li>
<li><code>logo.gif</code></li>
</ul>
<p>Each HTML file displays the logo and so contains an <code><img></code> tag such as:</p>
<div id="listing-8" class="frame">
<pre class="source-body">...
<img src="logo.gif" ... />
...</pre>
<div class="caption">Listing 8</div>
</div>
<p>Additionally, <code>index.html</code> contains a relative link to both <code>page1.html</code> and <code>page2.html</code> thus:</p>
<div id="listing-9" class="frame">
<pre class="source-body">...
<a href="page1.html">Page 1</a><br />
<a href="page2.html">Page 2</a>
...</pre>
<div class="caption">Listing 9</div>
</div>
<p>You now decide to store the pages within your program's HTML resources and display them using the <code>res://</code> protocol. Suppose you decided to give an arbitrary name to each resource as follows:</p>
<div id="listing-10" class="frame">
<pre class="source-body">INDEX_PAGE 23 "index.html"
PAGE_1 23 "page1.html"
PAGE_2 23 "page2.html"
LOGO_GIF 23 "logo.gif"</pre>
<div class="caption">Listing 10</div>
</div>
<p>To get your program working again you will need to track down every link and change it to reference the new resource name rather than the old file name. For example, each of the logo <code><img></code> tags will have to be changed to:</p>
<div class="frame">
<pre class="source-body">...
<img src="LOGO_GIF" ... />
...</pre>
<div class="caption">Listing 11</div>
</div>
<p>This is obviously prone to error. A far better way would be to name the resources with the same name as the original file, i.e.:</p>
<div id="listing-12" class="frame">
<pre class="source-body">index.html 23 "index.html"
page1.html 23 "page1.html"
page2.html 23 "page2.html"
logo.gif 23 "logo.gif"</pre>
<div class="caption">Listing 12</div>
</div>
<p>Using this scheme all the original links will continue to work unchanged. Of course this technique only works if all the files are in the same directory.</p>
<div class="callout callout-danger">
<h4>Problems with BRCC32</h4>
<p>There's always a gotcha somewhere isn't there? The BRCC32 resource compiler that ships with Delphi refuses to compile any resource file that has dots in any of its resource names. This means that the naming scheme proposed above is useless for those of us who use BRCC32.</p>
<p>To get round the above problem I have written <a href="/software/htmlres">HTML resource compiler</a>, a command line application that can compile HTML resource files. This compiler automatically names each resource after its associated file.</p>
<p>I have never used the Microsoft® RC compiler so can't comment on whether or not it will work in this situation. If you know please please <a href="https://github.com/delphidabbler/delphidabbler.github.io/issues">open an issue</a> on GitHub <small>(GitHub account required)</small> and let me know so I can update the article.</p>
</div>
</section>
<section id="conclusion">
<h2>Conclusion</h2>
<p>In this article we have investigated how to use the Internet Explorer (and hence <var>TWebBrowser</var>) specific <code>res://</code> protocol to view HTML content stored in a module's resources. We first looked at how to access a HTML resource from Internet Explorer. We then examined the component parts of a <code>res://</code> protocol URL before showing how to generate such URLs from Delphi code. The article ended by demonstrating how to create HTML resource files and observed that naming HTML resources after the original file can at times be useful.</p>
</section>
<section id="demo">
<h2>Demo program</h2>
<p>A demo program to accompany this article can be found in the <code><a href="https://github.com/delphidabbler/article-demos">delphidabbler/article-demos</a></code> Git repository on GitHub.</p>
<p>You can view the code in the <code><a href="https://github.com/delphidabbler/article-demos/tree/master/article-10">article-10</a></code> sub-directory. Alternatively download a zip file containing all the demos by going to the repository's landing page and clicking the <em>Clone or download</em> button and selecting <em>Download ZIP</em>.</p>
<p>The demo can be used to test and exercise the code presented here. See the demo's <a href="https://github.com/delphidabbler/article-demos/blob/master/article-10/README.md">README.md</a> file for details.</p>
<div class="callout callout-info">
<p><span class="fa fa-code fa-x-pad-right fa-2x fa-pull-left fa-border text-muted"></span>This source code is merely a proof of concept and is intended only to illustrate this article. It is not designed for use in its current form in finished applications. The code is provided on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or implied.</p>
<p>The demo is open source. See the demo's <a href="https://github.com/delphidabbler/article-demos/blob/master/article-10/LICENSE.md">LICENSE.md</a> file for licensing details.</p>
</div>
<p>If you need further demo code, the <a href="/software/htmlres">HTML Resource Compiler</a> comes with sample HTML files and a Delphi project to create a DLL containing only <var>RT_HTML</var> resources. These resources can be displayed in Internet Explorer by using the <code>res://</code> protocol.</p>
</section>
<section id="bibliography">
<h2>Further reading</h2>
<p>The following articles on this site deal with resources in general:</p>
<ul>
<li><a href="/articles/article-2">How to store files inside an executable program</a>.</li>
<li><a href="/articles/article-3">How to read data embedded in your program's resources</a>.</li>
</ul>
</section>
<section id="feedback">
<h2>Feedback</h2>
<p>I hope you found this article useful.</p>
<p>If you have any observations, comments, or have found any errors there are two places you can report them.</p>
<ol class="wide">
<li>For anything to do with the article content, <em>but not the downloadable demo code</em>, please use this website's <a href="https://github.com/delphidabbler/delphidabbler.github.io/issues">Issues page</a> on GitHub. Make sure you mention that the issue relates to "article #10".</li>
<li>For bugs in the demo code see the <code>article-demo</code> project's <code><a href="https://github.com/delphidabbler/article-demos/blob/master/README.md#bug-reports">README.md</a></code> file for details of how to report them.</li>
</ol>
</section>