/[rdesktop]/sourceforge.net/trunk/seamlessrdp/ServerExe/HookDll/hookdll.cpp
This is repository of my old source code which isn't updated any more. Go to git.rot13.org for current projects!
ViewVC logotype

Annotation of /sourceforge.net/trunk/seamlessrdp/ServerExe/HookDll/hookdll.cpp

Parent Directory Parent Directory | Revision Log Revision Log


Revision 996 - (hide annotations)
Mon Aug 29 09:11:50 2005 UTC (18 years, 9 months ago) by astrand
File size: 17922 byte(s)
Removed handling of WM_WINDOWPOSCHANGING.

1 astrand 930 //
2 astrand 938 // Copyright (C) 2004-2005 Martin Wickett
3 astrand 930 //
4    
5     #include "hookdll.h"
6     #include <windows.h>
7     #include <winuser.h>
8 astrand 995 #include <stdio.h>
9 astrand 930
10     #include "wtsapi32.h"
11     #include "Cchannel.h"
12    
13 astrand 993 #define DLL_EXPORT extern "C" __declspec(dllexport)
14 astrand 930
15     // Shared DATA
16     #pragma data_seg ( "SHAREDDATA" )
17    
18 astrand 937 // this is the total number of processes this dll is currently attached to
19 astrand 933 int iInstanceCount = 0;
20     HWND hWnd = 0;
21 astrand 930
22     #pragma data_seg ()
23    
24     #pragma comment(linker, "/section:SHAREDDATA,rws")
25    
26 astrand 933 bool bHooked = false;
27     bool bHooked2 = false;
28     bool bHooked3 = false;
29 astrand 994 HHOOK hhook = 0; //cbt
30     HHOOK hhook2 = 0; //shell
31     HHOOK hhook3 = 0; //wnd proc
32 astrand 933 HINSTANCE hInst = 0;
33     HANDLE m_vcHandle = 0;
34 astrand 930
35 astrand 994 BOOL APIENTRY DllMain( HINSTANCE hinstDLL, DWORD ul_reason_for_call, LPVOID lpReserved )
36 astrand 930 {
37 astrand 993 switch ( ul_reason_for_call ) {
38     case DLL_PROCESS_ATTACH: {
39 astrand 933 // remember our instance handle
40     hInst = hinstDLL;
41     ++iInstanceCount;
42     OpenVirtualChannel();
43     break;
44     }
45 astrand 993
46     case DLL_THREAD_ATTACH:
47 astrand 933 break;
48 astrand 993 case DLL_THREAD_DETACH:
49 astrand 933 break;
50 astrand 993 case DLL_PROCESS_DETACH: {
51 astrand 933 --iInstanceCount;
52     CloseVirtualChannel();
53     }
54     break;
55 astrand 930 }
56 astrand 993
57 astrand 930 return TRUE;
58     }
59    
60 astrand 993 LRESULT CALLBACK CallWndProc( int nCode, WPARAM wParam, LPARAM lParam )
61 astrand 930 {
62 astrand 993 if ( nCode < 0 ) {
63     return CallNextHookEx( hhook3, nCode, wParam, lParam );
64 astrand 933 }
65 astrand 993
66 astrand 933 PCHAR buffer = NULL;
67 astrand 993 char windowTitle[ 150 ] = { ""
68     };
69 astrand 933 HWND windowHandle = NULL;
70 astrand 994 HWND windowHandle2 = NULL;
71 astrand 993 char result[ 255 ] = { ""
72     };
73     char strWindowId[ 25 ];
74     char type[ 25 ];
75    
76 astrand 933 LONG b, t, l, r;
77 astrand 994 char strX[ 5 ];
78     char strY[ 5 ];
79     char strW[ 5 ];
80     char strH[ 5 ];
81 astrand 933 RECT rect;
82 astrand 993
83     CWPSTRUCT *details = ( CWPSTRUCT * ) lParam;
84 astrand 995 CREATESTRUCT *cs = ( CREATESTRUCT * ) details->lParam;
85     LONG dwStyle = GetWindowLong( details->hwnd, GWL_STYLE );
86 astrand 993
87     switch ( details->message ) {
88     case WM_SIZING:
89 astrand 994 windowHandle = details->hwnd;
90     //get win name
91     GetWindowText( windowHandle, windowTitle, 150 );
92    
93     //get an id for it
94     itoa( ( int ) windowHandle, strWindowId, 10 );
95    
96     //get coords
97     GetWindowRect( windowHandle, &rect );
98     b = rect.bottom;
99     t = rect.top;
100     l = rect.left;
101     r = rect.right;
102     ltoa( b - t, strH, 10 );
103     ltoa( t, strY, 10 );
104     ltoa( r - l, strW, 10 );
105     ltoa( l, strX, 10 );
106    
107     ////setup return string
108     strcat( result, "MSG=CALLWNDPROC_WM_SIZING;OP=6;" );
109     strcat( result, "ID=" );
110     strcat( result, strWindowId );
111     strcat( result, ";" );
112     strcat( result, "TITLE=" );
113     strcat( result, windowTitle );
114     strcat( result, ";" );
115     strcat( result, "X=" );
116     strcat( result, strX );
117     strcat( result, ";" );
118     strcat( result, "Y=" );
119     strcat( result, strY );
120     strcat( result, ";" );
121     strcat( result, "H=" );
122     strcat( result, strH );
123     strcat( result, ";" );
124     strcat( result, "W=" );
125     strcat( result, strW );
126     strcat( result, "." );
127    
128     buffer = result;
129    
130     break;
131 astrand 993 case WM_MOVING:
132    
133 astrand 933 windowHandle = details->hwnd;
134     //get win name
135 astrand 993 GetWindowText( windowHandle, windowTitle, 150 );
136    
137 astrand 933 //get an id for it
138 astrand 993 itoa( ( int ) windowHandle, strWindowId, 10 );
139    
140 astrand 933 //get coords
141 astrand 993 GetWindowRect( windowHandle, &rect );
142 astrand 933 b = rect.bottom;
143     t = rect.top;
144     l = rect.left;
145     r = rect.right;
146 astrand 994 ltoa( b - t, strH, 10 );
147     ltoa( t, strY, 10 );
148     ltoa( r - l, strW, 10 );
149     ltoa( l, strX, 10 );
150 astrand 993
151 astrand 933 ////setup return string
152 astrand 994 strcat( result, "MSG=CALLWNDPROC_WM_MOVING;OP=2;" );
153 astrand 993 strcat( result, "ID=" );
154     strcat( result, strWindowId );
155     strcat( result, ";" );
156     strcat( result, "TITLE=" );
157     strcat( result, windowTitle );
158     strcat( result, ";" );
159 astrand 994 strcat( result, "X=" );
160     strcat( result, strX );
161 astrand 993 strcat( result, ";" );
162 astrand 994 strcat( result, "Y=" );
163     strcat( result, strY );
164     strcat( result, ";" );
165     strcat( result, "H=" );
166     strcat( result, strH );
167     strcat( result, ";" );
168     strcat( result, "W=" );
169     strcat( result, strW );
170     strcat( result, "." );
171 astrand 993
172 astrand 933 buffer = result;
173 astrand 993
174 astrand 933 break;
175 astrand 993
176 astrand 994 case WM_WINDOWPOSCHANGED:
177    
178     windowHandle = details->hwnd;
179     //windowHandle2 = details->hwndInsertAfter;
180     //get win name
181     GetWindowText( windowHandle, windowTitle, 150 );
182    
183     //get an id for it
184     itoa( ( int ) windowHandle, strWindowId, 10 );
185    
186     //get coords
187     GetWindowRect( windowHandle, &rect );
188     b = rect.bottom;
189     t = rect.top;
190     l = rect.left;
191     r = rect.right;
192     ltoa( b - t, strH, 10 );
193     ltoa( t, strY, 10 );
194     ltoa( r - l, strW, 10 );
195     ltoa( l, strX, 10 );
196    
197     ////setup return string
198     strcat( result, "MSG=CALLWNDPROC_WM_WINDOWPOSCHANGED;OP=8;" );
199     strcat( result, "ID=" );
200     strcat( result, strWindowId );
201     strcat( result, ";" );
202     strcat( result, "TITLE=" );
203     strcat( result, windowTitle );
204     strcat( result, ";" );
205     strcat( result, "X=" );
206     strcat( result, strX );
207     strcat( result, ";" );
208     strcat( result, "Y=" );
209     strcat( result, strY );
210     strcat( result, ";" );
211     strcat( result, "H=" );
212     strcat( result, strH );
213     strcat( result, ";" );
214     strcat( result, "W=" );
215     strcat( result, strW );
216     strcat( result, "." );
217    
218     buffer = result;
219    
220     break;
221    
222 astrand 995
223     case WM_CREATE:
224     if ( cs->style & WS_DLGFRAME ) {
225     sprintf( result, "DEBUG:WM_CREATE:%dx%d at %d,%d, title=%s, menu=%p, window=%p, WS_BORDER=%d, WS_DLGFRAME=%d, WS_POPUP=%d",
226     cs->cx, cs->cy, cs->x, cs->y, cs->lpszName, cs->hMenu, details->hwnd,
227     cs->style & WS_BORDER, cs->style & WS_DLGFRAME,
228     cs->style & WS_POPUP );
229     buffer = result;
230     }
231    
232     break;
233    
234    
235     case WM_DESTROY:
236     if ( dwStyle & WS_DLGFRAME ) {
237     sprintf( result, "WM_DESTROY:%p", details->hwnd );
238     buffer = result;
239     }
240    
241     break;
242    
243 astrand 993 default:
244 astrand 933 break;
245     }
246 astrand 993
247     if ( ChannelIsOpen() ) {
248     if ( buffer != NULL ) {
249     WriteToChannel( buffer );
250 astrand 933 }
251     }
252 astrand 993
253     return CallNextHookEx( hhook3, nCode, wParam, lParam );
254 astrand 930 }
255    
256 astrand 993 LRESULT CALLBACK CbtProc( int nCode, WPARAM wParam, LPARAM lParam )
257 astrand 930 {
258 astrand 993 if ( nCode < 0 ) {
259     return CallNextHookEx( hhook, nCode, wParam, lParam );
260 astrand 933 }
261 astrand 993
262    
263 astrand 933 PCHAR buffer = NULL;
264 astrand 993
265    
266     char windowTitle[ 150 ] = { ""
267     };
268 astrand 933 HWND windowHandle = NULL;
269 astrand 993 char result[ 255 ] = { ""
270     };
271     char strWindowId[ 25 ];
272     char type[ 25 ];
273    
274    
275 astrand 933 LONG b, t, l, r;
276 astrand 994 char strW[ 5 ];
277     char strY[ 5 ];
278     char strX[ 5 ];
279     char strH[ 5 ];
280 astrand 933 RECT rect;
281 astrand 993
282 astrand 994 int i = 0; //tmp
283 astrand 993
284     switch ( nCode ) {
285     case HCBT_MINMAX:
286    
287     windowHandle = ( HWND ) wParam;
288 astrand 933 //get win name
289 astrand 993 GetWindowText( windowHandle, windowTitle, 150 );
290    
291 astrand 933 //get an id for it
292 astrand 993 itoa( ( int ) windowHandle, strWindowId, 10 );
293    
294 astrand 933 //get operation type(min,max). if max, do not clip at all,if min use window's previous coords
295     //codes are:
296 astrand 993
297 astrand 937 // SW_HIDE= 0 SW_SHOWNORMAL=1 SW_NORMAL=1 SW_SHOWMINIMIZED=2 SW_SHOWMAXIMIZED=3 SW_MAXIMIZE=3
298     // SW_SHOWNOACTIVATE=4 SW_SHOW=5 SW_MINIMIZE=6 SW_SHOWMINNOACTIVE=7 SW_SHOWNA=8 SW_RESTORE=9
299     // SW_SHOWDEFAULT=10 SW_FORCEMINIMIZE=11 SW_MAX=11
300 astrand 993
301     itoa( ( int ) lParam, type, 10 );
302    
303 astrand 933 //get coords
304 astrand 993 GetWindowRect( windowHandle, &rect );
305 astrand 933 b = rect.bottom;
306     t = rect.top;
307     l = rect.left;
308     r = rect.right;
309 astrand 994 ltoa( b - t, strW, 10 );
310     ltoa( t, strY, 10 );
311     ltoa( r - l, strH, 10 );
312     ltoa( l, strX, 10 );
313 astrand 993
314 astrand 933 //get name
315 astrand 993 GetWindowText( windowHandle, windowTitle, 150 );
316    
317 astrand 933 ////setup return string
318 astrand 994 strcat( result, "MSG=HCBT_MINMAX;OP=4;" );
319    
320     // SW_SHOWNOACTIVATE=4 SW_SHOW=5 SW_MINIMIZE=6 SW_SHOWMINNOACTIVE=7 SW_SHOWNA=8 SW_RESTORE=9
321     // SW_SHOWDEFAULT=10 SW_FORCEMINIMIZE=11 SW_MAX=11
322 astrand 993 strcat( result, "ID=" );
323     strcat( result, strWindowId );
324     strcat( result, ";" );
325     strcat( result, "TITLE=" );
326     strcat( result, windowTitle );
327     strcat( result, ";" );
328 astrand 994 strcat( result, "X=" );
329     strcat( result, strX );
330 astrand 993 strcat( result, ";" );
331 astrand 994 strcat( result, "Y=" );
332     strcat( result, strY );
333     strcat( result, ";" );
334     strcat( result, "H=" );
335     strcat( result, strH );
336     strcat( result, ";" );
337     strcat( result, "W=" );
338     strcat( result, strW );
339     strcat( result, ";" );
340 astrand 993 strcat( result, "TYPE=" );
341     strcat( result, type );
342 astrand 994 strcat( result, "." );
343 astrand 993
344 astrand 933 //-------------------------------------------------------------------------------------------------
345     // code to prevent minimising windows (can be removed once minimise has been implemented)
346 astrand 994 //i = (int)lParam;
347 astrand 933 //if (i==0 || i==2 || i==6 || i==7 || i==8 || i==11)
348 astrand 994 //if ( i==2 || i==6 )
349     //{
350     // MessageBox(0,"Minimising windows is not allowed in this version. Sorry!","TS Window Clipper", MB_OK);
351     // return 1;
352     //}
353 astrand 933 //-----------------------------------------------------------------------------------------
354 astrand 993
355 astrand 933 //-------------------------------------------------------------------------------------------------
356     // code to prevent maximising windows (can be removed once maximise has been implemented)
357 astrand 994 //i = (int)lParam;
358 astrand 933 //if (i==3 || i==9 || i==11)
359 astrand 994 //if (i==3 || i==11)
360     //{
361     // MessageBox(0,"Maximising windows is not allowed in this version. Sorry!","TS Window Clipper", MB_OK);
362     // return 1;
363     //}
364 astrand 933 //-----------------------------------------------------------------------------------------
365 astrand 993
366 astrand 933 buffer = result;
367 astrand 993
368 astrand 933 break;
369 astrand 993
370     case HCBT_MOVESIZE:
371    
372     windowHandle = ( HWND ) wParam;
373 astrand 933 //get win name
374 astrand 993 GetWindowText( windowHandle, windowTitle, 150 );
375    
376 astrand 933 //get an id for it
377 astrand 993 itoa( ( int ) windowHandle, strWindowId, 10 );
378    
379 astrand 933 //get coords
380 astrand 993 GetWindowRect( windowHandle, &rect );
381 astrand 933 b = rect.bottom;
382     t = rect.top;
383     l = rect.left;
384     r = rect.right;
385 astrand 994 ltoa( b - t, strH, 10 );
386     ltoa( t, strY, 10 );
387     ltoa( r - l, strW, 10 );
388     ltoa( l, strX, 10 );
389 astrand 993
390 astrand 933 //get name
391 astrand 993 GetWindowText( windowHandle, windowTitle, 150 );
392    
393 astrand 933 ////setup return string
394 astrand 994 strcat( result, "MSG=HCBT_MOVESIZE;OP=5;" );
395 astrand 993 strcat( result, "ID=" );
396     strcat( result, strWindowId );
397     strcat( result, ";" );
398     strcat( result, "TITLE=" );
399     strcat( result, windowTitle );
400     strcat( result, ";" );
401 astrand 994 strcat( result, "X=" );
402     strcat( result, strX );
403 astrand 993 strcat( result, ";" );
404 astrand 994 strcat( result, "Y=" );
405     strcat( result, strY );
406     strcat( result, ";" );
407     strcat( result, "H=" );
408     strcat( result, strH );
409     strcat( result, ";" );
410     strcat( result, "W=" );
411     strcat( result, strW );
412     strcat( result, "." );
413 astrand 993
414 astrand 933 buffer = result;
415 astrand 993
416 astrand 933 break;
417 astrand 993 case HCBT_SETFOCUS:
418 astrand 933 //buffer = "HCBT_SETFOCUS";
419     //not needed yet
420     break;
421 astrand 993 default:
422 astrand 933 break;
423     }
424 astrand 993
425     if ( ChannelIsOpen() ) {
426     if ( buffer != NULL ) {
427     WriteToChannel( buffer );
428 astrand 933 }
429     }
430 astrand 993
431     return CallNextHookEx( hhook, nCode, wParam, lParam );
432 astrand 930 }
433    
434    
435 astrand 993 LRESULT CALLBACK ShellProc( int nCode, WPARAM wParam, LPARAM lParam )
436 astrand 930 {
437 astrand 993 if ( nCode < 0 ) {
438     return CallNextHookEx( hhook, nCode, wParam, lParam );
439 astrand 933 }
440 astrand 993
441     if ( ChannelIsOpen() ) {
442 astrand 933 PCHAR buffer = NULL;
443 astrand 993
444     char windowTitle[ 150 ] = { ""
445     };
446 astrand 933 HWND windowHandle = NULL;
447 astrand 993 char result[ 255 ] = { ""
448     };
449     char strWindowId[ 25 ];
450 astrand 933 LONG b, t, l, r;
451 astrand 994 char strW[ 5 ];
452     char strY[ 5 ];
453     char strX[ 5 ];
454     char strH[ 5 ];
455 astrand 933 RECT rect;
456 astrand 993
457     switch ( nCode ) {
458     case HSHELL_WINDOWCREATED:
459    
460 astrand 933 //get window id
461 astrand 993 windowHandle = ( HWND ) wParam;
462     itoa( ( int ) windowHandle, strWindowId, 10 );
463    
464 astrand 933 //get coords
465 astrand 993 GetWindowRect( windowHandle, &rect );
466 astrand 933 b = rect.bottom;
467     t = rect.top;
468     l = rect.left;
469     r = rect.right;
470 astrand 994 ltoa( b - t, strH, 10 );
471     ltoa( t, strY, 10 );
472     ltoa( r - l, strW, 10 );
473     ltoa( l, strX, 10 );
474 astrand 993
475 astrand 933 //get name
476 astrand 993 GetWindowText( windowHandle, windowTitle, 150 );
477    
478 astrand 933 ////setup return string
479 astrand 994 strcat( result, "MSG=HSHELL_WINDOWCREATED;OP=0;" );
480 astrand 993 strcat( result, "ID=" );
481     strcat( result, strWindowId );
482     strcat( result, ";" );
483     strcat( result, "TITLE=" );
484     strcat( result, windowTitle );
485     strcat( result, ";" );
486 astrand 994 strcat( result, "X=" );
487     strcat( result, strX );
488 astrand 993 strcat( result, ";" );
489 astrand 994 strcat( result, "Y=" );
490     strcat( result, strY );
491     strcat( result, ";" );
492     strcat( result, "H=" );
493     strcat( result, strH );
494     strcat( result, ";" );
495     strcat( result, "W=" );
496     strcat( result, strW );
497     strcat( result, "." );
498 astrand 993
499 astrand 933 buffer = result;
500 astrand 993
501 astrand 933 break;
502 astrand 993
503     case HSHELL_WINDOWDESTROYED:
504    
505 astrand 933 //get window id
506 astrand 993 windowHandle = ( HWND ) wParam;
507     itoa( ( int ) windowHandle, strWindowId, 10 );
508    
509 astrand 994 //get coords
510     GetWindowRect( windowHandle, &rect );
511     b = rect.bottom;
512     t = rect.top;
513     l = rect.left;
514     r = rect.right;
515     ltoa( b - t, strH, 10 );
516     ltoa( t, strY, 10 );
517     ltoa( r - l, strW, 10 );
518     ltoa( l, strX, 10 );
519    
520 astrand 933 //get name
521 astrand 993 GetWindowText( windowHandle, windowTitle, 150 );
522    
523 astrand 933 ////setup return string
524 astrand 994 strcat( result, "MSG=HSHELL_WINDOWDESTROYED;OP=1;" );
525 astrand 993 strcat( result, "ID=" );
526     strcat( result, strWindowId );
527     strcat( result, ";" );
528     strcat( result, "TITLE=" );
529     strcat( result, windowTitle );
530     strcat( result, ";" );
531 astrand 994 strcat( result, "X=" );
532     strcat( result, strX );
533     strcat( result, ";" );
534     strcat( result, "Y=" );
535     strcat( result, strY );
536     strcat( result, ";" );
537     strcat( result, "H=" );
538     strcat( result, strH );
539     strcat( result, ";" );
540     strcat( result, "W=" );
541     strcat( result, strW );
542     strcat( result, "." );
543 astrand 993
544 astrand 933 buffer = result;
545 astrand 993
546 astrand 933 break;
547 astrand 993 default:
548 astrand 933 break;
549     }
550 astrand 993
551     if ( buffer != NULL ) {
552     WriteToChannel( buffer );
553 astrand 933 }
554     }
555 astrand 993
556     return CallNextHookEx( hhook, nCode, wParam, lParam );
557 astrand 930 }
558    
559 astrand 993 DLL_EXPORT void SetCbtHook( void )
560 astrand 930 {
561 astrand 993 if ( !bHooked ) {
562 astrand 994 hhook = SetWindowsHookEx( WH_CBT, ( HOOKPROC ) CbtProc, hInst, ( DWORD ) NULL );
563 astrand 933 bHooked = true;
564     }
565 astrand 993
566     if ( !bHooked2 ) {
567 astrand 994 hhook2 = SetWindowsHookEx( WH_SHELL, ( HOOKPROC ) ShellProc, hInst, ( DWORD ) NULL );
568 astrand 933 bHooked2 = true;
569     }
570 astrand 993
571     if ( !bHooked3 ) {
572 astrand 994 hhook3 = SetWindowsHookEx( WH_CALLWNDPROC, ( HOOKPROC ) CallWndProc, hInst, ( DWORD ) NULL );
573 astrand 933 bHooked3 = true;
574     }
575 astrand 930 }
576    
577 astrand 993 DLL_EXPORT void RemoveCbtHook( void )
578 astrand 930 {
579 astrand 993 if ( bHooked ) {
580     UnhookWindowsHookEx( hhook );
581 astrand 933 bHooked = false;
582     }
583 astrand 993
584     if ( bHooked2 ) {
585     UnhookWindowsHookEx( hhook2 );
586 astrand 933 bHooked2 = false;
587     }
588 astrand 993
589     if ( bHooked3 ) {
590     UnhookWindowsHookEx( hhook3 );
591 astrand 933 bHooked3 = false;
592     }
593 astrand 930 }
594    
595     DLL_EXPORT int GetInstanceCount()
596     {
597 astrand 933 return iInstanceCount;
598 astrand 930 }
599    
600     int OpenVirtualChannel()
601     {
602 astrand 994 m_vcHandle = WTSVirtualChannelOpen( WTS_CURRENT_SERVER_HANDLE, WTS_CURRENT_SESSION, CHANNELNAME );
603    
604 astrand 993 if ( m_vcHandle == NULL ) {
605 astrand 933 return 0;
606 astrand 937 } else {
607 astrand 933 return 1;
608     }
609 astrand 930 }
610    
611     int CloseVirtualChannel()
612     {
613 astrand 993 BOOL result = WTSVirtualChannelClose( m_vcHandle );
614    
615 astrand 933 m_vcHandle = NULL;
616 astrand 993
617     if ( result ) {
618 astrand 933 return 1;
619 astrand 937 } else {
620 astrand 933 return 0;
621     }
622 astrand 930 }
623    
624     int ChannelIsOpen()
625     {
626 astrand 993 if ( m_vcHandle == NULL ) {
627 astrand 933 return 0;
628 astrand 937 } else {
629 astrand 933 return 1;
630     }
631 astrand 930 }
632    
633 astrand 993 int WriteToChannel( PCHAR buffer )
634 astrand 930 {
635 astrand 933 PULONG bytesRead = 0;
636     PULONG pBytesWritten = 0;
637 astrand 993
638 astrand 994 BOOL result = WTSVirtualChannelWrite( m_vcHandle, buffer, ( ULONG ) strlen( buffer ), pBytesWritten );
639    
640 astrand 993 if ( result ) {
641 astrand 933 return 1;
642 astrand 937 } else {
643 astrand 933 return 0;
644     }
645 astrand 930 }

  ViewVC Help
Powered by ViewVC 1.1.26