1 /* AbiSource Program Utilities
2 * Copyright (C) 2003 Tomas Frydrych <tomas@frydrych.uklinux.net>
3 *
4 * This program is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU General Public License
6 * as published by the Free Software Foundation; either version 2
7 * of the License, or (at your option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software
16 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
17 * 02110-1301 USA.
18 */
19
20 #include "ut_misc.h"
21 #include <windows.h>
22 #ifdef _MSC_VER
23 #include <winsock.h>
24 #else
25 #include <mswsock.h>
26 #endif
27 #ifndef __WINE__
28 #include <snmp.h>
29 #endif
30 #include <nb30.h>
31 #include <stdio.h>
32 #include "ut_debugmsg.h"
33 #include "ut_Win32Resources.rc2"
34 #include "ut_Win32Timer.h"
35 #include "ut_Win32LocaleString.h"
36
37 /*!
38 UT_gettimeofday() fills in the timeval structure with current
39 time; the platform implementation needs to be as accurate as
40 possible since this function is used in the UT_UUID class.
41
42 this implementation is from:
43 http://bugzilla.vovida.org/bugzilla/show_bug.cgi?id=751
44 */
UT_gettimeofday(struct timeval * tv)45 void UT_gettimeofday(struct timeval *tv)
46 {
47 FILETIME ft;
48 GetSystemTimeAsFileTime (&ft);
49
50 ULARGE_INTEGER _100ns = {{ft.dwLowDateTime,ft.dwHighDateTime}};
51
52 #ifndef __GNUC__
53 _100ns.QuadPart -= 0x19db1ded53e8000;
54 #else
55 _100ns.QuadPart -= 0x19db1ded53e8000ULL;
56 #endif
57 tv->tv_sec = long (_100ns.QuadPart / (10000 * 1000));
58 tv->tv_usec = (long) ((_100ns.LowPart % (DWORD) (10000 * 1000)) / 10);
59 }
60
61 #ifndef __WINE__
62 #define NO_MAC_ADDRESS
63 #endif
64 #ifdef NO_MAC_ADDRESS
65 typedef struct _ASTAT
66 {
67 ADAPTER_STATUS adapt;
68 NAME_BUFFER NameBuff [30];
69 }ASTAT, * PASTAT;
70
71 typedef BOOL(WINAPI * pSnmpExtensionInit) (IN DWORD dwTimeZeroReference,
72 OUT HANDLE * hPollForTrapEvent,
73 OUT AsnObjectIdentifier * supportedView);
74
75 typedef BOOL(WINAPI * pSnmpExtensionQuery) (IN BYTE requestType,
76 IN OUT RFC1157VarBindList * variableBindings,
77 OUT AsnInteger * errorStatus,
78 OUT AsnInteger * errorIndex);
79
80 typedef VOID (WINAPI * pSnmpUtilVarBindFree) (RFC1157VarBind *VarBind);
81
82 typedef SNMPAPI (WINAPI * pSnmpUtilOidNCmp )(AsnObjectIdentifier *ObjIdA,
83 AsnObjectIdentifier *ObjIdB,
84 UINT Len);
85
86 typedef SNMPAPI (WINAPI * pSnmpUtilOidCpy) (AsnObjectIdentifier *DestObjId,
87 AsnObjectIdentifier *SrcObjId);
88
89 typedef int (WINAPI * pWSAStartup) (WORD wVersionRequested,LPWSADATA lpWSAData);
90
91 typedef UCHAR (WINAPI * pNetbios) (PNCB pncb);
92
93 #endif
94 /*!
95 retrieve the 6-byte address of the network card; returns true on success
96 */
UT_getEthernetAddress(UT_EthernetAddress & A)97 bool UT_getEthernetAddress(UT_EthernetAddress &A)
98 {
99 #ifdef NO_MAC_ADDRESS
100 // the following code by James Marsh <James.Marsh@sandtechnology.com>
101 // was found at http://tangentsoft.net/wskfaq/examples/getmac-snmp.html
102 // I adjusted it, so all the libs are dynamically loaded and unloaded
103
104 HINSTANCE m_hWSInst = NULL;
105 m_hWSInst = LoadLibraryW(L"ws2_32.dll");
106 pWSAStartup m_WSAStartup = NULL;
107
108 if(m_hWSInst < (HINSTANCE) HINSTANCE_ERROR)
109 {
110 UT_DEBUGMSG(("UT_getEthernetAddress: could not load ws2_32.dll\n"));
111 m_hWSInst = NULL;
112 goto try_netbios;
113 }
114 else
115 {
116 m_WSAStartup = (pWSAStartup) GetProcAddress(m_hWSInst, "WSAStartup");
117 if(!m_WSAStartup)
118 {
119 UT_DEBUGMSG(("UT_getEthernetAddress: no WSAStartup\n"));
120 FreeLibrary(m_hWSInst);
121 }
122 }
123
124 {
125
126 WSADATA WinsockData;
127 if(m_WSAStartup(MAKEWORD(2, 0), &WinsockData) != 0)
128 {
129 UT_DEBUGMSG(("UT_getEthernetAddress: need Winsock 2.x!\n"));
130 FreeLibrary(m_hWSInst);
131 goto try_netbios;
132 }
133
134 HINSTANCE m_hSNMPInst = NULL;
135 pSnmpUtilVarBindFree m_SnmpUtilVarBindFree = NULL;
136 pSnmpUtilOidNCmp m_SnmpUtilOidNCmp = NULL;
137 pSnmpUtilOidCpy m_SnmpUtilOidCpy = NULL;
138
139
140 HINSTANCE m_hInst = NULL;
141 pSnmpExtensionInit m_Init = NULL;
142 pSnmpExtensionQuery m_Query = NULL;
143 HANDLE PollForTrapEvent;
144 AsnObjectIdentifier SupportedView;
145
146 UINT OID_ifEntryType[] = {1, 3, 6, 1, 2, 1, 2, 2, 1, 3};
147 UINT OID_ifEntryNum[] = {1, 3, 6, 1, 2, 1, 2, 1};
148 UINT OID_ipMACEntAddr[] = {1, 3, 6, 1, 2, 1, 2, 2, 1, 6};
149
150 AsnObjectIdentifier MIB_ifMACEntAddr = {sizeof(OID_ipMACEntAddr)/sizeof(UINT),
151 OID_ipMACEntAddr};
152
153 AsnObjectIdentifier MIB_ifEntryType = {sizeof(OID_ifEntryType)/sizeof(UINT),
154 OID_ifEntryType};
155
156 AsnObjectIdentifier MIB_ifEntryNum = {sizeof(OID_ifEntryNum)/sizeof(UINT),
157 OID_ifEntryNum};
158
159 RFC1157VarBindList varBindList;
160 RFC1157VarBind varBind[2];
161 AsnInteger errorStatus;
162 AsnInteger errorIndex;
163 AsnObjectIdentifier MIB_NULL = {0,0};
164
165 int ret;
166 int dtmp;
167 int j = 0;
168 bool bFound = false;
169
170 /* Load the SNMP dll and get the addresses of the functions
171 necessary */
172 m_hSNMPInst = LoadLibraryW(L"snmpapi.dll");
173 if(m_hSNMPInst < (HINSTANCE) HINSTANCE_ERROR)
174 {
175 UT_DEBUGMSG(("UT_getEthernetAddress: could not load snmpapi.dll\n"));
176 m_hSNMPInst = NULL;
177 goto try_netbios;
178 }
179
180 m_SnmpUtilVarBindFree = (pSnmpUtilVarBindFree) GetProcAddress(m_hSNMPInst,
181 "SnmpUtilVarBindFree");
182
183 m_SnmpUtilOidNCmp = (pSnmpUtilOidNCmp) GetProcAddress(m_hSNMPInst,
184 "SnmpUtilOidNCmp");
185
186 m_SnmpUtilOidCpy = (pSnmpUtilOidCpy) GetProcAddress(m_hSNMPInst,
187 "SnmpUtilOidCpy");
188
189
190 if(!m_SnmpUtilVarBindFree || !m_SnmpUtilOidNCmp || !m_SnmpUtilOidCpy)
191 {
192 UT_DEBUGMSG(("UT_getEthernetAddress (win32): could not load SNMP functions\n"));
193 FreeLibrary(m_hSNMPInst);
194 goto try_netbios;
195 }
196
197
198 // load the SNMP extension library
199 m_hInst = LoadLibraryW(L"inetmib1.dll");
200
201 if(m_hInst < (HINSTANCE) HINSTANCE_ERROR)
202 {
203 UT_DEBUGMSG(("UT_getEthernetAddress: could not load inetmib1.dll\n"));
204 m_hInst = NULL;
205 FreeLibrary(m_hSNMPInst);
206 goto try_netbios;
207 }
208
209 m_Init = (pSnmpExtensionInit) GetProcAddress(m_hInst, "SnmpExtensionInit");
210 m_Query = (pSnmpExtensionQuery) GetProcAddress(m_hInst, "SnmpExtensionQuery");
211
212 if(!m_Init || !m_Query)
213 {
214 UT_DEBUGMSG(("UT_getEthernetAddress (win32): could not load SNMP ext functions\n"));
215 FreeLibrary(m_hInst);
216 FreeLibrary(m_hSNMPInst);
217 goto try_netbios;
218 }
219
220 m_Init(GetTickCount(), &PollForTrapEvent, &SupportedView);
221
222 /* Initialize the variable list to be retrieved by m_Query */
223 varBindList.list = varBind;
224 varBind[0].name = MIB_NULL;
225 varBind[1].name = MIB_NULL;
226
227 /* Copy in the OID to find the number of entries in the
228 Inteface table */
229 varBindList.len = 1; /* Only retrieving one item */
230 m_SnmpUtilOidCpy(&varBind[0].name, &MIB_ifEntryNum);
231
232 ret = m_Query(ASN_RFC1157_GETNEXTREQUEST, &varBindList, &errorStatus, &errorIndex);
233
234 UT_DEBUGMSG(("UT_getEthernetAddress: adapters in this system : %i\n",
235 varBind[0].value.asnValue.number));
236
237 varBindList.len = 2;
238
239 /* Copy in the OID of ifType, the type of interface */
240 m_SnmpUtilOidCpy(&varBind[0].name, &MIB_ifEntryType);
241
242 /* Copy in the OID of ifPhysAddress, the address */
243 m_SnmpUtilOidCpy(&varBind[1].name, &MIB_ifMACEntAddr);
244
245 do {
246
247 /* Submit the query. Responses will be loaded into varBindList.
248 We can expect this call to succeed a # of times corresponding
249 to the # of adapters reported to be in the system */
250 ret = m_Query(ASN_RFC1157_GETNEXTREQUEST, &varBindList, &errorStatus, &errorIndex);
251
252 if(!ret)
253 {
254 ret = 1;
255 }
256 else
257 {
258 /* Confirm that the proper type has been returned */
259 ret = m_SnmpUtilOidNCmp(&varBind[0].name,
260 &MIB_ifEntryType,
261 MIB_ifEntryType.idLength);
262 }
263
264 if (!ret)
265 {
266 j++;
267 dtmp = varBind[0].value.asnValue.number;
268 UT_DEBUGMSG(("UT_getEthernetAddress (win32): Interface #%i type : %i\n"
269 , j, dtmp));
270
271 /* Type 6 describes ethernet interfaces */
272 if(dtmp == 6)
273 {
274 /* Confirm that we have an address here */
275 ret = m_SnmpUtilOidNCmp(&varBind[1].name,
276 &MIB_ifMACEntAddr,
277 MIB_ifMACEntAddr.idLength);
278
279 if((!ret) && (varBind[1].value.asnValue.address.stream != NULL))
280 {
281 if( (varBind[1].value.asnValue.address.stream[0] == 0x44)
282 && (varBind[1].value.asnValue.address.stream[1] == 0x45)
283 && (varBind[1].value.asnValue.address.stream[2] == 0x53)
284 && (varBind[1].value.asnValue.address.stream[3] == 0x54)
285 && (varBind[1].value.asnValue.address.stream[4] == 0x00))
286 {
287 /* Ignore all dial-up networking adapters */
288 UT_DEBUGMSG(("UT_getEthernetAddress: #%i is a DUN adapter\n", j));
289 continue;
290 }
291 if( (varBind[1].value.asnValue.address.stream[0] == 0x00)
292 && (varBind[1].value.asnValue.address.stream[1] == 0x00)
293 && (varBind[1].value.asnValue.address.stream[2] == 0x00)
294 && (varBind[1].value.asnValue.address.stream[3] == 0x00)
295 && (varBind[1].value.asnValue.address.stream[4] == 0x00)
296 && (varBind[1].value.asnValue.address.stream[5] == 0x00))
297 {
298
299 /* Ignore NULL addresses returned by other network
300 interfaces */
301 UT_DEBUGMSG(("UT_getEthernetAddress (win32): Interface #%i is "
302 "a NULL address\n", j));
303 continue;
304 }
305
306 for(UT_uint32 k = 0; k < 6; ++k)
307 A[k] = varBind[1].value.asnValue.address.stream[k];
308
309 UT_DEBUGMSG(("UT_getEthernetAddress : MAC Address of interface #%i: "
310 "%02x.%02x.%02x.%02x.%02x.%02x\n",
311 j,A[0],A[1],A[2],A[3],A[4],A[5]));
312
313 // we only need the first address we find ...
314 bFound = true;
315 break;
316 } // if ((!ret) && (varBind[1].
317 } // if (dtmp == 6)
318 } //if (!ret) # 2
319 }
320 while (!ret); /* Stop only on an error. An error will occur
321 when we go exhaust the list of interfaces to
322 be examined */
323
324 /* Free the bindings */
325 m_SnmpUtilVarBindFree(&varBind[0]);
326 m_SnmpUtilVarBindFree(&varBind[1]);
327
328 FreeLibrary(m_hInst);
329 FreeLibrary(m_hSNMPInst);
330 FreeLibrary(m_hWSInst);
331
332 if(bFound)
333 return true;
334 }
335
336 try_netbios:
337 // this is the method described in MS win32 docs; it has one
338 // fundamental shortcoming: not everyone uses netbios, so it is
339 // here as a fall back in case the previous method does not work
340 // I have adjusted the MS code so as to load and unload the dlls
341 // dynamically
342 {
343 UT_DEBUGMSG(("UT_getEthernetAddress: unable to use SNMP\n"));
344
345 HINSTANCE m_hInst;
346
347 m_hInst = LoadLibraryW(L"netapi32.dll");
348 if(m_hInst < (HINSTANCE) HINSTANCE_ERROR)
349 {
350 UT_DEBUGMSG(("UT_getEthernetAddress: could not load netapi32.dll\n"));
351 return false;
352 }
353
354 pNetbios m_Netbios = NULL;
355 m_Netbios = (pNetbios) GetProcAddress(m_hInst, "Netbios");
356
357 if(!m_Netbios)
358 {
359 UT_DEBUGMSG(("UT_getEthernetAddress: could load netbios functions\n"));
360 FreeLibrary(m_hInst);
361 return false;
362 }
363
364 NCB Ncb;
365 UT_uint32 iRet;
366 bool bRet = true;
367
368 memset(&Ncb, 0, sizeof(Ncb));
369 Ncb.ncb_command = NCBRESET;
370 Ncb.ncb_lana_num = 0;
371
372 iRet = m_Netbios(&Ncb);
373 bRet = (NRC_GOODRET == iRet);
374
375 if(!bRet)
376 {
377 UT_DEBUGMSG(("UT_getEthernetAddress: unable to use NETBIOS either\n"));
378 FreeLibrary(m_hInst);
379 return false;
380 }
381
382 memset(&Ncb, 0, sizeof (Ncb));
383 Ncb.ncb_command = NCBASTAT;
384 Ncb.ncb_lana_num = 0;
385
386 char namebuf[] = "* ";
387 strcpy((char *)Ncb.ncb_callname, namebuf);
388
389 ASTAT Adapter;
390 Ncb.ncb_buffer = (unsigned char *) &Adapter;
391 Ncb.ncb_length = sizeof(Adapter);
392
393 iRet = m_Netbios(&Ncb);
394 bRet &= (NRC_GOODRET == iRet);
395
396 if(!bRet)
397 {
398 UT_DEBUGMSG(("UT_getEthernetAddress: unable to use NETBIOS2 either\n"));
399 FreeLibrary(m_hInst);
400 return false;
401 }
402
403 for(UT_uint32 i = 0; i < 6; ++i)
404 A[i] = Adapter.adapt.adapter_address[i];
405
406 UT_DEBUGMSG(("MAC Address is %02x-%02x-%02x-%02x-%02x-%02x\n",
407 A[0],A[1],A[2],A[3],A[4],A[5]));
408
409 FreeLibrary(m_hInst);
410 return true;
411 }
412 #else
413 UT_return_val_if_fail(0, false);
414 #endif
415 }
416
417 /*!
418 Class that implements an assert dialogue; this is a private class, we only access it
419 through UT_Win32ThrowAssert() function
420 */
421 class ABI_EXPORT UT_Win32AssertDlg
422 {
423 friend int ABI_EXPORT UT_Win32ThrowAssert(const char *, const char *, int, int);
424
425 private:
UT_Win32AssertDlg(const char * pCond,const char * pFile,int iLine,int iCount)426 UT_Win32AssertDlg(const char * pCond, const char * pFile, int iLine, int iCount)
427 : m_iLine(iLine), m_pCond(pCond), m_pFile(pFile), m_iCount(iCount), m_myHWND(NULL), m_bTimersPaused(false){};
~UT_Win32AssertDlg()428 ~UT_Win32AssertDlg(){};
429
430 static BOOL CALLBACK s_dlgProc(HWND,UINT,WPARAM,LPARAM);
431
432 BOOL _onInitDialog(HWND hWnd, WPARAM wParam, LPARAM lParam);
433 BOOL _onCommand(HWND hWnd, WPARAM wParam, LPARAM lParam);
434
435 enum answer {AN_Debug, AN_Ignore, AN_IgnoreAll, AN_Abort};
436
437 answer runModal();
438
439 static BOOL CALLBACK s_DisableWindows(HWND hwnd, LPARAM lParam);
440 void enableWindows();
441
442 int m_iLine;
443 const char * m_pCond;
444 const char * m_pFile;
445 int m_iCount;
446
447 answer m_answer;
448
449 HWND m_myHWND;
450 UT_GenericVector<HWND> m_vecDisabledWnds;
451 bool m_bTimersPaused;
452 };
453
454 /*!
455 This function wraps arround the Assert dialog class and handles the returned
456 responses; it will abort application if the user chose Abort.
457
458 \return : 0 -- give control to debugger
459 > 0 ignore this time
460 < 0 ignore always
461 */
UT_Win32ThrowAssert(const char * pCond,const char * pFile,int iLine,int iCount)462 int ABI_EXPORT UT_Win32ThrowAssert(const char * pCond, const char * pFile, int iLine, int iCount)
463 {
464 UT_Win32AssertDlg dlg(pCond, pFile, iLine, iCount);
465 UT_Win32AssertDlg::answer a = dlg.runModal();
466
467 switch(a)
468 {
469 case UT_Win32AssertDlg::AN_Debug:
470 return 0;
471
472 case UT_Win32AssertDlg::AN_Ignore:
473 return 1;
474
475 case UT_Win32AssertDlg::AN_IgnoreAll:
476 return -1;
477
478 case UT_Win32AssertDlg::AN_Abort:
479 exit(0);
480 return 1;
481 }
482
483 return 1;
484 }
485
s_dlgProc(HWND hWnd,UINT msg,WPARAM wParam,LPARAM lParam)486 BOOL CALLBACK UT_Win32AssertDlg::s_dlgProc(HWND hWnd,UINT msg,WPARAM wParam,LPARAM lParam)
487 {
488 // This is a static function.
489
490 UT_Win32AssertDlg * pThis;
491
492
493 switch (msg){
494 case WM_INITDIALOG:
495 pThis = (UT_Win32AssertDlg *)lParam;
496 SetWindowLongPtrW(hWnd,DWLP_USER,lParam);
497 return pThis->_onInitDialog(hWnd,wParam,lParam);
498
499 case WM_COMMAND:
500 pThis = (UT_Win32AssertDlg *)GetWindowLongPtrW(hWnd,DWLP_USER);
501 return pThis->_onCommand(hWnd,wParam,lParam);
502
503 default:
504 return 0;
505 }
506 }
507
runModal()508 UT_Win32AssertDlg::answer UT_Win32AssertDlg::runModal()
509 {
510 LPSTR lpTemplate = MAKEINTRESOURCEA(UT_RID_DIALOG_ASSERT);
511
512 UT_DebugOnly<int> result = DialogBoxParamA(GetModuleHandleA(NULL),
513 lpTemplate,
514 NULL,
515 (DLGPROC)s_dlgProc,(LPARAM)this);
516
517 UT_ASSERT_HARMLESS((result != -1));
518
519 return m_answer;
520 }
521
_onCommand(HWND hWnd,WPARAM wParam,LPARAM)522 BOOL UT_Win32AssertDlg::_onCommand(HWND hWnd, WPARAM wParam, LPARAM /*lParam*/)
523 {
524 WORD wId = LOWORD(wParam);
525
526 switch (wId)
527 {
528 case UT_RID_DIALOG_ASSERT_DEBUG:
529 m_answer = AN_Debug;
530 break;
531
532 case UT_RID_DIALOG_ASSERT_ABORT:
533 m_answer = AN_Abort;
534 break;
535
536 case UT_RID_DIALOG_ASSERT_IGNORE:
537 m_answer = AN_Ignore;
538 break;
539
540 case UT_RID_DIALOG_ASSERT_IGNOREALLWAYS:
541 m_answer = AN_IgnoreAll;
542 break;
543
544 default:
545 return 0;
546 }
547
548 enableWindows();
549 UT_Win32Timer::pauseAllTimers(m_bTimersPaused);
550 EndDialog(hWnd,0);
551 return 1;
552
553 }
554
s_DisableWindows(HWND hwnd,LPARAM lParam)555 BOOL CALLBACK UT_Win32AssertDlg::s_DisableWindows(HWND hwnd, LPARAM lParam)
556 {
557 UT_Win32AssertDlg * pThis = (UT_Win32AssertDlg*)lParam;
558
559 if(pThis && (pThis->m_myHWND != hwnd) && IsWindowEnabled(hwnd))
560 {
561 pThis->m_vecDisabledWnds.addItem(hwnd);
562 EnableWindow(hwnd, FALSE);
563 }
564
565 // give me another one
566 return TRUE;
567 }
568
enableWindows()569 void UT_Win32AssertDlg::enableWindows()
570 {
571 for(UT_sint32 i = 0; i < m_vecDisabledWnds.getItemCount(); i++)
572 {
573 EnableWindow(m_vecDisabledWnds.getNthItem(i), TRUE);
574 }
575
576 m_vecDisabledWnds.clear();
577 }
578
_onInitDialog(HWND hWnd,WPARAM,LPARAM lParam)579 BOOL UT_Win32AssertDlg::_onInitDialog(HWND hWnd, WPARAM /*wParam*/, LPARAM lParam)
580 {
581 // pause all timers
582 m_bTimersPaused = UT_Win32Timer::timersPaused();
583 UT_Win32Timer::pauseAllTimers(true);
584
585 // We need to make this dlg 'Task Modal' (equivalent to MB_TASKMODAL of the
586 // MessageBox() function), otherwise mutliple assert boxes can potentially be created
587 // in response to things like mouse movement. The idea how to do this came from the
588 // WINE mailing list (http://www.winehq.com/hypermail/wine-devel/2004/11/0320.html)
589 // Basically, we disable all windows that belong to our thread, and remember which
590 // windows we did disable, so we can enable them before the dlg box closes.
591 m_myHWND = hWnd;
592 EnumThreadWindows(GetCurrentThreadId(), s_DisableWindows, lParam);
593
594 // set initial state
595 SetDlgItemTextA(hWnd,UT_RID_DIALOG_ASSERT_FILE,m_pFile);
596 SetDlgItemTextA(hWnd,UT_RID_DIALOG_ASSERT_CONDITION,m_pCond);
597
598 char buff[20];
599 _snprintf(buff, 19, "%d",m_iLine);
600 SetDlgItemTextA(hWnd,UT_RID_DIALOG_ASSERT_LINE,buff);
601
602 _snprintf(buff, 19, "%d",m_iCount);
603 SetDlgItemTextA(hWnd,UT_RID_DIALOG_ASSERT_COUNT,buff);
604
605 return 1; // 1 == we did not call SetFocus()
606 }
607