1 | unit VA508ScreenReaderDLLLinker;
|
---|
2 |
|
---|
3 | interface
|
---|
4 |
|
---|
5 | { TODO -oJeremy Merrill -c508 :Add ability to handle multiple instances / multiple appliations to JAWS at the same time -
|
---|
6 | will need to use Application.MainForm handle approach, probably need to use different
|
---|
7 | registry keys with handle in registry key name. JAWS has a GetAppMainWindow command
|
---|
8 | to get the handle. Will need a cleanup command in delphi to make sure we don't leave
|
---|
9 | junk in the registry - probably search running apps, and if the main form's handle isn't in
|
---|
10 | the registry, delete entries. }
|
---|
11 | uses
|
---|
12 | Windows, SysUtils, Forms, Classes, VA508AccessibilityConst;
|
---|
13 |
|
---|
14 | {$I 'VA508ScreenReaderDLLStandard.inc'}
|
---|
15 |
|
---|
16 | // Returns true if a link to a screen reader was successful. The first link that
|
---|
17 | // is established causes searching to stop.
|
---|
18 | // Searches for .SR files in this order:
|
---|
19 | // 1) Current machine's Program Files directory
|
---|
20 | // 2) \Program Files directory on drive where app resides,
|
---|
21 | // if it's different than the current machine's program files directory
|
---|
22 | // 3) The directory the application was run from.
|
---|
23 |
|
---|
24 | function ScreenReaderDLLsExist: boolean;
|
---|
25 | function IsScreenReaderSupported(Unload: Boolean): boolean;
|
---|
26 | function InitializeScreenReaderLink: boolean;
|
---|
27 | procedure CloseScreenReaderLink;
|
---|
28 |
|
---|
29 |
|
---|
30 | type
|
---|
31 | TVA508RegisterCustomBehaviorProc = procedure(BehaviorType: integer; Before, After: PChar); stdcall;
|
---|
32 | TVA508SpeakTextProc = procedure(Text: PChar); stdcall;
|
---|
33 | TVA508IsRunningFunc = function(HighVersion, LowVersion: Word): BOOL; stdcall;
|
---|
34 | TVA508ConfigChangePending = function: boolean; stdcall;
|
---|
35 | TVA508ComponentDataProc = procedure (WindowHandle: HWND;
|
---|
36 | DataStatus: LongInt = DATA_NONE;
|
---|
37 | Caption: PChar = nil;
|
---|
38 | Value: PChar = nil;
|
---|
39 | Data: PChar = nil;
|
---|
40 | ControlType: PChar = nil;
|
---|
41 | State: PChar = nil;
|
---|
42 | Instructions: PChar = nil;
|
---|
43 | ItemInstructions: PChar = nil); stdcall;
|
---|
44 | var
|
---|
45 | SRSpeakText: TVA508SpeakTextProc = nil;
|
---|
46 | SRIsRunning: TVA508IsRunningFunc = nil;
|
---|
47 | SRRegisterCustomBehavior: TVA508RegisterCustomBehaviorProc = nil;
|
---|
48 | SRComponentData: TVA508ComponentDataProc = nil;
|
---|
49 | SRConfigChangePending: TVA508ConfigChangePending = nil;
|
---|
50 | ValidSRFiles: TStringList = nil;
|
---|
51 | ExecuteFind: boolean = TRUE;
|
---|
52 | DoInitialize: boolean = TRUE;
|
---|
53 | InitializeResult: boolean = FALSE;
|
---|
54 |
|
---|
55 | implementation
|
---|
56 |
|
---|
57 | uses VAUtils, VA508AccessibilityRouter, VA508AccessibilityManager;
|
---|
58 |
|
---|
59 | const
|
---|
60 | ScreenReaderFileExtension = '.SR';
|
---|
61 | ScreenReaderCommonFilesDir = 'VistA\Common Files\';
|
---|
62 | ScreenReaderSearchSpec = '*' + ScreenReaderFileExtension;
|
---|
63 | {$WARNINGS OFF} // Ignore platform specific code warning
|
---|
64 | BadFile = faHidden or faSysFile or faDirectory or faSymLink;
|
---|
65 | {$WARNINGS ON}
|
---|
66 |
|
---|
67 | {$REGION 'Initialize Proc Definition'}
|
---|
68 | type
|
---|
69 | TVA508InitializeProc = function(CallBackProc: TComponentDataRequestProc): BOOL; stdcall;
|
---|
70 | const
|
---|
71 | TVA508InitializeProcName = 'Initialize';
|
---|
72 | var
|
---|
73 | SRInitialize: TVA508InitializeProc = nil;
|
---|
74 |
|
---|
75 | function Initialize(ComponentCallBackProc: TComponentDataRequestProc): BOOL; stdcall;
|
---|
76 | {$HINTS OFF} // Ignore unused variable hint
|
---|
77 | var
|
---|
78 | CompileVerification: TVA508InitializeProc;
|
---|
79 | begin
|
---|
80 | CompileVerification := Initialize;
|
---|
81 | Result := FALSE;
|
---|
82 | end;
|
---|
83 | {$HINTS ON}
|
---|
84 | {$ENDREGION}
|
---|
85 |
|
---|
86 | {$REGION 'ShutDown Proc Definition'}
|
---|
87 | type
|
---|
88 | TVA508ShutDownProc = procedure; stdcall;
|
---|
89 | const
|
---|
90 | TVA508ShutDownProcName = 'ShutDown';
|
---|
91 | var
|
---|
92 | SRShutDown: TVA508ShutDownProc = nil;
|
---|
93 |
|
---|
94 | procedure ShutDown; stdcall;
|
---|
95 | {$HINTS OFF} // Ignore unused variable hint
|
---|
96 | var
|
---|
97 | CompileVerification: TVA508ShutDownProc;
|
---|
98 | begin
|
---|
99 | CompileVerification := ShutDown;
|
---|
100 | end;
|
---|
101 | {$HINTS ON}
|
---|
102 | {$ENDREGION}
|
---|
103 |
|
---|
104 | {$REGION 'RegisterCustomBehavior Proc Definition'}
|
---|
105 | const
|
---|
106 | TVA508RegisterCustomBehaviorProcName = 'RegisterCustomBehavior';
|
---|
107 |
|
---|
108 | procedure RegisterCustomBehavior(BehaviorType: integer; Before, After: PChar); stdcall;
|
---|
109 | {$HINTS OFF} // Ignore unused variable hint
|
---|
110 | var
|
---|
111 | CompileVerification: TVA508RegisterCustomBehaviorProc;
|
---|
112 | begin
|
---|
113 | CompileVerification := RegisterCustomBehavior;
|
---|
114 | end;
|
---|
115 | {$HINTS ON}
|
---|
116 | {$ENDREGION}
|
---|
117 |
|
---|
118 | {$REGION 'ComponentData Proc Definition'}
|
---|
119 | const
|
---|
120 | TVA508ComponentDataProcName = 'ComponentData';
|
---|
121 |
|
---|
122 | procedure ComponentData(WindowHandle: HWND;
|
---|
123 | DataStatus: LongInt = DATA_NONE;
|
---|
124 | Caption: PChar = nil;
|
---|
125 | Value: PChar = nil;
|
---|
126 | Data: PChar = nil;
|
---|
127 | ControlType: PChar = nil;
|
---|
128 | State: PChar = nil;
|
---|
129 | Instructions: PChar = nil;
|
---|
130 | ItemInstructions: PChar = nil); stdcall;
|
---|
131 |
|
---|
132 | {$HINTS OFF} // Ignore unused variable hint
|
---|
133 | var
|
---|
134 | CompileVerification: TVA508ComponentDataProc;
|
---|
135 | begin
|
---|
136 | CompileVerification := ComponentData;
|
---|
137 | end;
|
---|
138 | {$HINTS ON}
|
---|
139 | {$ENDREGION}
|
---|
140 |
|
---|
141 | {$REGION 'SpeakText Proc Definition'}
|
---|
142 | const
|
---|
143 | TVA508SpeakTextProcName = 'SpeakText';
|
---|
144 |
|
---|
145 | procedure SpeakText(Text: PChar); stdcall;
|
---|
146 | {$HINTS OFF} // Ignore unused variable hint
|
---|
147 | var
|
---|
148 | CompileVerification: TVA508SpeakTextProc;
|
---|
149 | begin
|
---|
150 | CompileVerification := SpeakText;
|
---|
151 | end;
|
---|
152 | {$HINTS ON}
|
---|
153 | {$ENDREGION}
|
---|
154 |
|
---|
155 | {$REGION 'IsRunning Proc Definition'}
|
---|
156 | const
|
---|
157 | TVA508IsRunningFuncName = 'IsRunning';
|
---|
158 |
|
---|
159 | function IsRunning(HighVersion, LowVersion: Word): BOOL; stdcall;
|
---|
160 | {$HINTS OFF} // Ignore unused variable hint
|
---|
161 | var
|
---|
162 | CompileVerification: TVA508IsRunningFunc;
|
---|
163 | begin
|
---|
164 | CompileVerification := IsRunning;
|
---|
165 | Result := FALSE; // avoid compiler warning...
|
---|
166 | end;
|
---|
167 | {$HINTS ON}
|
---|
168 | {$ENDREGION}
|
---|
169 |
|
---|
170 | {$REGION 'ConfigChangePending Proc Definition'}
|
---|
171 | const
|
---|
172 | TVA508ConfigChangePendingName = 'ConfigChangePending';
|
---|
173 |
|
---|
174 | function ConfigChangePending: boolean; stdcall;
|
---|
175 | {$HINTS OFF} // Ignore unused variable hint
|
---|
176 | var
|
---|
177 | CompileVerification: TVA508ConfigChangePending;
|
---|
178 | begin
|
---|
179 | CompileVerification := ConfigChangePending;
|
---|
180 | Result := FALSE; // avoid compiler warning...
|
---|
181 | end;
|
---|
182 | {$HINTS ON}
|
---|
183 | {$ENDREGION}
|
---|
184 |
|
---|
185 | var
|
---|
186 | DLLHandle: THandle = 0;
|
---|
187 |
|
---|
188 | procedure ClearProcPointers;
|
---|
189 | begin
|
---|
190 | SRInitialize := nil;
|
---|
191 | SRShutDown := nil;
|
---|
192 | SRRegisterCustomBehavior := nil;
|
---|
193 | SRSpeakText := nil;
|
---|
194 | SRIsRunning := nil;
|
---|
195 | SRComponentData := nil;
|
---|
196 | SRConfigChangePending := nil;
|
---|
197 | DoInitialize := FALSE;
|
---|
198 | InitializeResult := FALSE;
|
---|
199 | end;
|
---|
200 |
|
---|
201 | function InitializeScreenReaderLink: boolean;
|
---|
202 | begin
|
---|
203 | if DoInitialize then
|
---|
204 | begin
|
---|
205 | InitializeResult := SRInitialize(ComponentDataRequested);
|
---|
206 | DoInitialize := FALSE;
|
---|
207 | if not InitializeResult then
|
---|
208 | CloseScreenReaderLink;
|
---|
209 | end;
|
---|
210 | Result := InitializeResult;
|
---|
211 | end;
|
---|
212 |
|
---|
213 | procedure CloseScreenReaderLink;
|
---|
214 | begin
|
---|
215 | if DLLHandle > HINSTANCE_ERROR then
|
---|
216 | begin
|
---|
217 | SRShutDown;
|
---|
218 | FreeLibrary(DLLHandle);
|
---|
219 | DLLHandle := 0;
|
---|
220 | ClearProcPointers;
|
---|
221 | end;
|
---|
222 | end;
|
---|
223 |
|
---|
224 | procedure LoadScreenReader(index: integer);
|
---|
225 | var
|
---|
226 | FileName: string;
|
---|
227 | begin
|
---|
228 | FileName := ValidSRFiles[index];
|
---|
229 | DLLHandle := LoadLibrary(PChar(FileName));
|
---|
230 | if DLLHandle > HINSTANCE_ERROR then
|
---|
231 | begin
|
---|
232 | SRInitialize := GetProcAddress(DLLHandle, TVA508InitializeProcName);
|
---|
233 | SRShutDown := GetProcAddress(DLLHandle, TVA508ShutDownProcName);
|
---|
234 | SRRegisterCustomBehavior := GetProcAddress(DLLHandle, TVA508RegisterCustomBehaviorProcName);
|
---|
235 | SRSpeakText := GetProcAddress(DLLHandle, TVA508SpeakTextProcName);
|
---|
236 | SRIsRunning := GetProcAddress(DLLHandle, TVA508IsRunningFuncName);
|
---|
237 | SRComponentData := GetProcAddress(DLLHandle, TVA508ComponentDataProcName);
|
---|
238 | SRConfigChangePending := GetProcAddress(DLLHandle, TVA508ConfigChangePendingName);
|
---|
239 | DoInitialize := TRUE;
|
---|
240 | end;
|
---|
241 | end;
|
---|
242 |
|
---|
243 | function CheckRunning(Unload: boolean; HighVersion, LowVersion: integer): boolean;
|
---|
244 | begin
|
---|
245 | // Calling IsRunning this way, instead of setting ok to it's result,
|
---|
246 | // prevents ok from begin converted to a LongBool at compile time
|
---|
247 | if assigned(SRIsRunning) and SRIsRunning(HighVersion, LowVersion) then
|
---|
248 | Result := TRUE
|
---|
249 | else
|
---|
250 | Result := FALSE;
|
---|
251 | if Unload and (DLLHandle > HINSTANCE_ERROR)then
|
---|
252 | begin
|
---|
253 | FreeLibrary(DLLHandle);
|
---|
254 | DLLHandle := 0;
|
---|
255 | end;
|
---|
256 | end;
|
---|
257 |
|
---|
258 |
|
---|
259 | procedure FindScreenReaders;
|
---|
260 | var
|
---|
261 | ok: boolean;
|
---|
262 |
|
---|
263 | procedure CheckProcs;
|
---|
264 | begin
|
---|
265 | SRInitialize := GetProcAddress(DLLHandle, TVA508InitializeProcName);
|
---|
266 | ok := assigned(SRInitialize);
|
---|
267 | if ok then
|
---|
268 | begin
|
---|
269 | SRShutDown := GetProcAddress(DLLHandle, TVA508ShutDownProcName);
|
---|
270 | ok := assigned(SRShutDown);
|
---|
271 | if ok then
|
---|
272 | begin
|
---|
273 | SRRegisterCustomBehavior := GetProcAddress(DLLHandle, TVA508RegisterCustomBehaviorProcName);
|
---|
274 | ok := assigned(SRRegisterCustomBehavior);
|
---|
275 | if ok then
|
---|
276 | begin
|
---|
277 | SRSpeakText := GetProcAddress(DLLHandle, TVA508SpeakTextProcName);
|
---|
278 | ok := assigned(SRSpeakText);
|
---|
279 | if ok then
|
---|
280 | begin
|
---|
281 | SRIsRunning := GetProcAddress(DLLHandle, TVA508IsRunningFuncName);
|
---|
282 | ok := assigned(SRIsRunning);
|
---|
283 | if ok then
|
---|
284 | begin
|
---|
285 | SRComponentData := GetProcAddress(DLLHandle, TVA508ComponentDataProcName);
|
---|
286 | ok := assigned(SRComponentData);
|
---|
287 | if ok then
|
---|
288 | begin
|
---|
289 | SRConfigChangePending := GetProcAddress(DLLHandle, TVA508ConfigChangePendingName);
|
---|
290 | ok := assigned(SRConfigChangePending);
|
---|
291 | end;
|
---|
292 | end;
|
---|
293 | end;
|
---|
294 | end;
|
---|
295 | end;
|
---|
296 | end;
|
---|
297 | ClearProcPointers;
|
---|
298 | end;
|
---|
299 |
|
---|
300 | procedure CheckFile(FileName: string);
|
---|
301 | var
|
---|
302 | idx: integer;
|
---|
303 | begin
|
---|
304 | DLLHandle := 0;
|
---|
305 | ok := FileExists(FileName);
|
---|
306 | if ok then
|
---|
307 | begin
|
---|
308 | ok := FALSE;
|
---|
309 | idx := ValidSRFiles.IndexOf(FileName);
|
---|
310 | if idx < 0 then
|
---|
311 | begin
|
---|
312 | DLLHandle := LoadLibrary(PChar(FileName));
|
---|
313 | if DLLHandle > HINSTANCE_ERROR then
|
---|
314 | begin
|
---|
315 | try
|
---|
316 | CheckProcs;
|
---|
317 | if ok then
|
---|
318 | ValidSRFiles.Add(FileName)
|
---|
319 | finally
|
---|
320 | FreeLibrary(DLLHandle);
|
---|
321 | DLLHandle := 0;
|
---|
322 | end;
|
---|
323 | end;
|
---|
324 | end;
|
---|
325 | end
|
---|
326 | end;
|
---|
327 |
|
---|
328 | procedure ScanScreenReaders(dir: string; addCommonFilesPath: boolean = true);
|
---|
329 | var
|
---|
330 | SR: TSearchRec;
|
---|
331 | Done: integer;
|
---|
332 | RootDir: string;
|
---|
333 | begin
|
---|
334 | if dir = '' then exit;
|
---|
335 | RootDir := AppendBackSlash(dir);
|
---|
336 | if addCommonFilesPath then
|
---|
337 | RootDir := RootDir + ScreenReaderCommonFilesDir;
|
---|
338 | Done := FindFirst(RootDir + ScreenReaderSearchSpec, faAnyFile, SR);
|
---|
339 | try
|
---|
340 | while Done = 0 do
|
---|
341 | begin
|
---|
342 | if((SR.Attr and BadFile) = 0) and (CompareText(ExtractFileExt(SR.Name), ScreenReaderFileExtension) = 0) then
|
---|
343 | begin
|
---|
344 | CheckFile(RootDir + SR.Name);
|
---|
345 | end;
|
---|
346 | Done := FindNext(SR);
|
---|
347 | end;
|
---|
348 | finally
|
---|
349 | FindClose(SR);
|
---|
350 | end;
|
---|
351 | end;
|
---|
352 |
|
---|
353 | begin
|
---|
354 | if ExecuteFind then
|
---|
355 | begin
|
---|
356 | if not assigned(ValidSRFiles) then
|
---|
357 | ValidSRFiles := TStringList.Create;
|
---|
358 | ScanScreenReaders(GetProgramFilesPath);
|
---|
359 | if not ok then
|
---|
360 | ScanScreenReaders(GetAlternateProgramFilesPath);
|
---|
361 | if not ok then
|
---|
362 | ScanScreenReaders(ExtractFilePath(Application.ExeName), FALSE);
|
---|
363 | ExecuteFind := FALSE;
|
---|
364 | end;
|
---|
365 | end;
|
---|
366 |
|
---|
367 | function ScreenReaderDLLsExist: boolean;
|
---|
368 | begin
|
---|
369 | FindScreenReaders;
|
---|
370 | Result := (ValidSRFiles.Count > 0);
|
---|
371 | end;
|
---|
372 |
|
---|
373 | function IsScreenReaderSupported(Unload: Boolean): boolean;
|
---|
374 | var
|
---|
375 | i: integer;
|
---|
376 | HighVersion, LowVersion: integer;
|
---|
377 | begin
|
---|
378 | Result := FALSE;
|
---|
379 | FindScreenReaders;
|
---|
380 | VersionStringSplit(VA508AccessibilityManagerVersion, HighVersion, LowVersion);
|
---|
381 | for I := 0 to ValidSRFiles.Count - 1 do
|
---|
382 | begin
|
---|
383 | LoadScreenReader(i);
|
---|
384 | Result := CheckRunning(Unload, HighVersion, LowVersion);
|
---|
385 | if Result then exit;
|
---|
386 | if not Unload then
|
---|
387 | begin
|
---|
388 | FreeLibrary(DLLHandle);
|
---|
389 | DLLHandle := 0;
|
---|
390 | end;
|
---|
391 | end;
|
---|
392 | end;
|
---|
393 |
|
---|
394 | initialization
|
---|
395 |
|
---|
396 | finalization
|
---|
397 | CloseScreenReaderLink;
|
---|
398 | if assigned(ValidSRFiles) then
|
---|
399 | FreeAndNil(ValidSRFiles);
|
---|
400 |
|
---|
401 | end.
|
---|