-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy patharticle-1.html
190 lines (166 loc) · 17.1 KB
/
article-1.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
---
article: 1
title: "How a non-windowed component can receive messages from Windows"
summary: "Sometimes we need a non-windowed component to receive Windows messages. Here's how and why."
meta-title: "Use Delphi Pascal to receive Windows messages in a non-windowed component | How to"
meta-desc: "An article that describes how enable a non-windowed Delphi Pascal component to receive Windows message by using a hidden window created with a call to AllocateHWnd."
index: true
redirect_from:
- /articles/1
---
<section id="intro">
<h2>Why do it?</h2>
<p>Sometimes we need a non-windowed component (i.e. one that isn't derived from <var>TWinControl</var>) to receive Windows messages. To receive messages the component needs a window handle, but a non-windowed component hasn't got one!</p>
<p>This article is about how to enable such a component to use a hidden window to receive messages.</p>
</section>
<section id="detail">
<h2>How it's done</h2>
<p>The Delphi library function <var>AllocateHWnd</var> is used to create a hidden window for us and the related <var>DeallocateHWnd</var> disposes of the window when we've finished with it.</p>
<p>A hidden window requires a window procedure. <var>AllocateHWnd</var> enables us to use a method as a window procedure where Windows normally requires a <var>stdcall</var> function. We pass a reference to the required method to <var>AllocateHWnd</var> and it takes care of the problem of registering the method as a window procedure for us. Inside the registered method we handle the messages we are interested in and hand the rest off to Windows using the <var>DefWindowProc</var> API call.</p>
<p><span class="figureref">Listing 2</span> below provides a skeleton of how to use <var>AllocateHWnd</var>. First though, <span class="figureref">Listing 1</span> shows an outline definition for our component class:</p>
<div class="frame">
<div class="code-pascal">
<pre class="line"><span class="linenum"> 1</span><span class="kwd">type</span></pre>
<pre
class="line"><span class="linenum"> 2</span><span class="space"> </span><span class="comment">{ Our class derived from TComponent </span></pre>
<pre
class="line"><span class="linenum"> 3</span><span class="comment"> or another ancestor class }</span></pre>
<pre
class="line"><span class="linenum"> 4</span><span class="space"> </span><span class="ident">TMyClass</span><span class="space"> </span><span class="sym">=</span><span class="space"> </span><span class="kwd">class</span><span class="sym">(</span><span class="ident">TComponent</span><span class="sym">)</span></pre>
<pre
class="line"><span class="linenum"> 5</span><span class="space"> </span><span class="kwd">private</span></pre>
<pre
class="line"><span class="linenum"> 6</span><span class="space"> </span><span class="ident">fHWnd</span><span class="sym">:</span><span class="space"> </span><span class="ident">HWND</span><span class="sym">;</span></pre>
<pre
class="line"><span class="linenum"> 7</span><span class="space"> </span><span class="comment">{ field to store the window handle }</span></pre>
<pre
class="line"><span class="linenum"> 8</span><span class="space"> </span><span class="sym">..</span><span class="sym">.</span></pre>
<pre
class="line"><span class="linenum"> 9</span><span class="space"> </span><span class="kwd">protected</span></pre>
<pre
class="line"><span class="linenum"> 10</span><span class="space"> </span><span class="kwd">procedure</span><span class="space"> </span><span class="ident">WndMethod</span><span class="sym">(</span><span class="kwd">var</span><span class="space"> </span><span class="ident">Msg</span><span class="sym">:</span><span class="space"> </span><span class="ident">TMessage</span><span class="sym">)</span><span class="sym">;</span><span class="space"> </span><span class="kwd">virtual</span><span class="sym">;</span></pre>
<pre
class="line"><span class="linenum"> 11</span><span class="space"> </span><span class="comment">{ window proc - called by Windows to handle </span></pre>
<pre
class="line"><span class="linenum"> 12</span><span class="comment"> messages passed to our hidden window }</span></pre>
<pre
class="line"><span class="linenum"> 13</span><span class="space"> </span><span class="sym">..</span><span class="sym">.</span></pre>
<pre
class="line"><span class="linenum"> 14</span><span class="space"> </span><span class="kwd">public</span></pre>
<pre
class="line"><span class="linenum"> 15</span><span class="space"> </span><span class="kwd">constructor</span><span class="space"> </span><span class="ident">Create</span><span class="sym">(</span><span class="ident">AOwner</span><span class="sym">:</span><span class="space"> </span><span class="ident">TComponent</span><span class="sym">)</span><span class="sym">;</span><span class="space"> </span><span class="kwd">override</span><span class="sym">;</span></pre>
<pre
class="line"><span class="linenum"> 16</span><span class="space"> </span><span class="comment">{ create hidden window here: store handle in fHWnd}</span></pre>
<pre
class="line"><span class="linenum"> 17</span><span class="space"> </span><span class="kwd">destructor</span><span class="space"> </span><span class="ident">Destroy</span><span class="sym">;</span><span class="space"> </span><span class="kwd">override</span><span class="sym">;</span></pre>
<pre
class="line"><span class="linenum"> 18</span><span class="space"> </span><span class="comment">{ free hidden window here }</span></pre>
<pre
class="line"><span class="linenum"> 19</span><span class="space"> </span><span class="sym">..</span><span class="sym">.</span></pre>
<pre
class="line"><span class="linenum"> 20</span><span class="space"> </span><span class="kwd">end</span><span class="sym">;</span></pre>
</div>
<div class="caption">
Listing 1
</div>
</div>
<p>And here are the implementation details:</p>
<div class="frame">
<div class="code-pascal">
<pre
class="line"><span class="linenum"> 1</span><span class="kwd">constructor</span><span class="space"> </span><span class="ident">TMyClass</span><span class="sym">.</span><span class="ident">Create</span><span class="sym">(</span><span class="ident">AOwner</span><span class="sym">:</span><span class="space"> </span><span class="ident">TComponent</span><span class="sym">)</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">inherited</span><span class="space"> </span><span class="ident">Create</span><span class="sym">(</span><span class="ident">AOwner</span><span class="sym">)</span><span class="sym">;</span></pre>
<pre
class="line"><span class="linenum"> 4</span><span class="space"> </span><span class="sym">..</span><span class="sym">.</span></pre>
<pre
class="line"><span class="linenum"> 5</span><span class="space"> </span><span class="comment">// Create hidden window using WndMethod as window proc</span></pre>
<pre
class="line"><span class="linenum"> 6</span><span class="space"> </span><span class="ident">fHWnd</span><span class="space"> </span><span class="sym">:=</span><span class="space"> </span><span class="ident">AllocateHWnd</span><span class="sym">(</span><span class="ident">WndMethod</span><span class="sym">)</span><span class="sym">;</span></pre>
<pre
class="line"><span class="linenum"> 7</span><span class="space"> </span><span class="sym">..</span><span class="sym">.</span></pre>
<pre
class="line"><span class="linenum"> 8</span><span class="kwd">end</span><span class="sym">;</span></pre>
<pre class="line"><span class="linenum"> 9</span></pre>
<pre
class="line"><span class="linenum"> 10</span><span class="kwd">destructor</span><span class="space"> </span><span class="ident">TMyClass</span><span class="sym">.</span><span class="ident">Destroy</span><span class="sym">;</span></pre>
<pre class="line"><span class="linenum"> 11</span><span class="kwd">begin</span></pre>
<pre
class="line"><span class="linenum"> 12</span><span class="space"> </span><span class="sym">..</span><span class="sym">.</span></pre>
<pre
class="line"><span class="linenum"> 13</span><span class="space"> </span><span class="comment">// Destroy hidden window</span></pre>
<pre
class="line"><span class="linenum"> 14</span><span class="space"> </span><span class="ident">DeallocateHWnd</span><span class="sym">(</span><span class="ident">fHWnd</span><span class="sym">)</span><span class="sym">;</span></pre>
<pre
class="line"><span class="linenum"> 15</span><span class="space"> </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">inherited</span><span class="space"> </span><span class="ident">Destroy</span><span class="sym">;</span></pre>
<pre
class="line"><span class="linenum"> 17</span><span class="kwd">end</span><span class="sym">;</span></pre>
<pre class="line"><span class="linenum"> 18</span></pre>
<pre
class="line"><span class="linenum"> 19</span><span class="kwd">procedure</span><span class="space"> </span><span class="ident">TMyClass</span><span class="sym">.</span><span class="ident">WndMethod</span><span class="sym">(</span><span class="kwd">var</span><span class="space"> </span><span class="ident">Msg</span><span class="space"> </span><span class="sym">:</span><span class="space"> </span><span class="ident">TMessage</span><span class="sym">)</span><span class="sym">;</span></pre>
<pre class="line"><span class="linenum"> 20</span><span class="kwd">var</span></pre>
<pre
class="line"><span class="linenum"> 21</span><span class="space"> </span><span class="ident">Handled</span><span class="sym">:</span><span class="space"> </span><span class="ident">Boolean</span><span class="sym">;</span></pre>
<pre class="line"><span class="linenum"> 22</span><span class="kwd">begin</span></pre>
<pre
class="line"><span class="linenum"> 23</span><span class="space"> </span><span class="comment">// Assume we handle message</span></pre>
<pre
class="line"><span class="linenum"> 24</span><span class="space"> </span><span class="ident">Handled</span><span class="space"> </span><span class="sym">:=</span><span class="space"> </span><span class="ident">True</span><span class="sym">;</span></pre>
<pre
class="line"><span class="linenum"> 25</span><span class="space"> </span><span class="kwd">case</span><span class="space"> </span><span class="ident">Msg</span><span class="sym">.</span><span class="ident">Msg</span><span class="space"> </span><span class="kwd">of</span></pre>
<pre
class="line"><span class="linenum"> 26</span><span class="space"> </span><span class="ident">WM_SOMETHING</span><span class="sym">:</span><span class="space"> </span><span class="ident">DoSomething</span><span class="sym">;</span></pre>
<pre
class="line"><span class="linenum"> 27</span><span class="space"> </span><span class="comment">// Code to handle a message</span></pre>
<pre
class="line"><span class="linenum"> 28</span><span class="space"> </span><span class="ident">WM_SOMETHINGELSE</span><span class="sym">:</span><span class="space"> </span><span class="ident">DoSomethingElse</span><span class="sym">;</span></pre>
<pre
class="line"><span class="linenum"> 29</span><span class="space"> </span><span class="comment">// Code to handle another message</span></pre>
<pre
class="line"><span class="linenum"> 30</span><span class="space"> </span><span class="comment">// Handle other messages here</span></pre>
<pre
class="line"><span class="linenum"> 31</span><span class="space"> </span><span class="kwd">else</span></pre>
<pre
class="line"><span class="linenum"> 32</span><span class="space"> </span><span class="comment">// We didn't handle message</span></pre>
<pre
class="line"><span class="linenum"> 33</span><span class="space"> </span><span class="ident">Handled</span><span class="space"> </span><span class="sym">:=</span><span class="space"> </span><span class="ident">False</span><span class="sym">;</span></pre>
<pre
class="line"><span class="linenum"> 34</span><span class="space"> </span><span class="kwd">end</span><span class="sym">;</span></pre>
<pre
class="line"><span class="linenum"> 35</span><span class="space"> </span><span class="kwd">if</span><span class="space"> </span><span class="ident">Handled</span><span class="space"> </span><span class="kwd">then</span></pre>
<pre
class="line"><span class="linenum"> 36</span><span class="space"> </span><span class="comment">// We handled message - record in message result</span></pre>
<pre
class="line"><span class="linenum"> 37</span><span class="space"> </span><span class="ident">Msg</span><span class="sym">.</span><span class="ident">Result</span><span class="space"> </span><span class="sym">:=</span><span class="space"> </span><span class="num">0</span></pre>
<pre
class="line"><span class="linenum"> 38</span><span class="space"> </span><span class="kwd">else</span></pre>
<pre
class="line"><span class="linenum"> 39</span><span class="space"> </span><span class="comment">// We didn't handle message</span></pre>
<pre
class="line"><span class="linenum"> 40</span><span class="space"> </span><span class="comment">// pass to DefWindowProc and record result</span></pre>
<pre
class="line"><span class="linenum"> 41</span><span class="space"> </span><span class="ident">Msg</span><span class="sym">.</span><span class="ident">Result</span><span class="space"> </span><span class="sym">:=</span><span class="space"> </span><span class="ident">DefWindowProc</span><span class="sym">(</span><span class="ident">fHWnd</span><span class="sym">,</span><span class="space"> </span><span class="ident">Msg</span><span class="sym">.</span><span class="ident">Msg</span><span class="sym">,</span></pre>
<pre
class="line"><span class="linenum"> 42</span><span class="space"> </span><span class="ident">Msg</span><span class="sym">.</span><span class="ident">WParam</span><span class="sym">,</span><span class="space"> </span><span class="ident">Msg</span><span class="sym">.</span><span class="ident">LParam</span><span class="sym">)</span><span class="sym">;</span></pre>
<pre
class="line"><span class="linenum"> 43</span><span class="kwd">end</span><span class="sym">;</span></pre>
</div>
<div class="caption">
Listing 2
</div>
</div>
<p>Of course, we could just use the Windows API to create a window the hard way and provide a windows procedure. But it is much more difficult to use a method as a window procedure if we do it this way. The cleverness of <var>AllocateHWnd</var> is that (a) it creates the hidden window for us and (b) it allows us to use a method, rather than a simple procedure, as the window procedure. Obviously, a method is more useful since it has access to the class' private data.</p>
</section>
<section id="example">
<h2>A Real World Example</h2>
<p>While there is no demo code to accompany this article, my <a href="/software/cbview">Clipboard Viewer Component</a> is a non-visual component that uses the hidden window techniques described here. The window receives Windows messages that provide information about changes to the clipboard.</p>
<p>Feel free to check it out.</p>
</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, then you can report them using this website's <a href="https://github.com/delphidabbler/delphidabbler.github.io/issues">Issues page</a>. Make sure you mention that the issue relates to "Article #1".</p>
</section>