Annotation of mstools/ole20/samples/ole2ui/busy.c, revision 1.1.1.1

1.1       root        1: /*
                      2:  * BUSY.C
                      3:  *
                      4:  * Implements the OleUIBusy function which invokes the "Server Busy"
                      5:  * dialog.
                      6:  *
                      7:  * Copyright (c)1992 Microsoft Corporation, All Right Reserved
                      8:  */
                      9:  
                     10: #define STRICT  1
                     11: #include "ole2ui.h"
                     12: #include "common.h"
                     13: #include "utility.h"
                     14: #include "busy.h" 
                     15: #include <ctype.h> // for tolower() and toupper()
                     16: 
                     17: #ifndef WIN32
                     18: #include <toolhelp.h>
                     19: #endif
                     20: 
                     21: 
                     22: /*
                     23:  * OleUIBusy
                     24:  *
                     25:  * Purpose:
                     26:  *  Invokes the standard OLE "Server Busy" dialog box which
                     27:  *  notifies the user that the server application is not receiving
                     28:  *  messages.  The dialog then asks the user to either cancel
                     29:  *  the operation, switch to the task which is blocked, or continue
                     30:  *  waiting.
                     31:  *
                     32:  * Parameters:
                     33:  *  lpBZ            LPOLEUIBUSY pointing to the in-out structure
                     34:  *                  for this dialog.
                     35:  *
                     36:  * Return Value:
                     37:  *              OLEUI_BZERR_HTASKINVALID  : Error
                     38:  *              OLEUI_BZ_SWITCHTOSELECTED : Success, user selected "switch to"
                     39:  *              OLEUI_BZ_RETRYSELECTED    : Success, user selected "retry"
                     40:  *              OLEUI_CANCEL              : Success, user selected "cancel"
                     41:  */
                     42:  
                     43: STDAPI_(UINT) OleUIBusy(LPOLEUIBUSY lpOBZ)
                     44:     {
                     45:     UINT        uRet = 0;
                     46:     HGLOBAL     hMemDlg=NULL;
                     47: 
                     48: #if defined( NT_BUG )
                     49: 
                     50:     uRet=UStandardValidation((LPOLEUISTANDARD)lpOBZ, sizeof(OLEUIBUSY)
                     51:                              , &hMemDlg);
                     52:                              
                     53:     // Error out if the standard validation failed
                     54:     if (OLEUI_SUCCESS!=uRet)
                     55:         return uRet;
                     56: 
                     57: #if !defined( WIN32 )
                     58:     // Validate HTASK
                     59:     if (!IsTask(lpOBZ->hTask))
                     60:         uRet = OLEUI_BZERR_HTASKINVALID;
                     61: #endif
                     62:         
                     63:     // Error out if our secondary validation failed        
                     64:     if (OLEUI_ERR_STANDARDMIN <= uRet)
                     65:         {
                     66:         if (NULL!=hMemDlg)
                     67:             FreeResource(hMemDlg);
                     68: 
                     69:         return uRet;
                     70:         }
                     71: 
                     72:     // Invoke the dialog.
                     73:     uRet=UStandardInvocation(BusyDialogProc, (LPOLEUISTANDARD)lpOBZ,
                     74:                              hMemDlg, MAKEINTRESOURCE(IDD_BUSY));
                     75: #endif
                     76: 
                     77:     return uRet;
                     78: }
                     79: 
                     80: 
                     81: /*
                     82:  * BusyDialogProc
                     83:  *
                     84:  * Purpose:
                     85:  *  Implements the OLE Busy dialog as invoked through the OleUIBusy function.
                     86:  *
                     87:  * Parameters:
                     88:  *  Standard
                     89:  *
                     90:  * Return Value:
                     91:  *  Standard
                     92:  *
                     93:  */
                     94: 
                     95: BOOL CALLBACK EXPORT BusyDialogProc(HWND hDlg, UINT iMsg, WPARAM wParam, LPARAM lParam)
                     96:     {
                     97:     LPBUSY         lpBZ;
                     98:     UINT           uRet = 0;   
                     99: 
                    100:     //Declare Win16/Win32 compatible WM_COMMAND parameters.
                    101:     COMMANDPARAMS(wID, wCode, hWndMsg);
                    102: 
                    103:     //This will fail under WM_INITDIALOG, where we allocate it.
                    104:     lpBZ=(LPBUSY)LpvStandardEntry(hDlg, iMsg, wParam, lParam, &uRet);
                    105: 
                    106:     //If the hook processed the message, we're done.
                    107:     if (0!=uRet)
                    108:         return (BOOL)uRet;
                    109: 
                    110:     //Process the temination message
                    111:     if (iMsg==uMsgEndDialog)
                    112:     {
                    113:         BusyCleanup(hDlg);
                    114:         StandardCleanup(lpBZ, hDlg);
                    115:         EndDialog(hDlg, wParam);
                    116:         return TRUE;
                    117:     }
                    118:     
                    119:     // Process our special "close" message.  If we get this message,
                    120:     // this means that the call got unblocked, so we need to
                    121:     // return OLEUI_BZ_CALLUNBLOCKED to our calling app.
                    122:     if (iMsg == uMsgCloseBusyDlg)
                    123:     {
                    124:         SendMessage(hDlg, uMsgEndDialog, OLEUI_BZ_CALLUNBLOCKED, 0L);
                    125:         return TRUE;
                    126:     }
                    127:         
                    128:     switch (iMsg)
                    129:         {
                    130:         case WM_INITDIALOG:
                    131:             FBusyInit(hDlg, wParam, lParam);
                    132:             return TRUE;
                    133: 
                    134:         case WM_COMMAND:
                    135:             switch (wID)
                    136:                 {
                    137:                 case IDBZ_SWITCHTO:
                    138:                 
                    139:                     // If user selects "Switch To...", switch activation 
                    140:                     // directly to the window which is causing the problem.  
                    141:                     if (IsWindow(lpBZ->hWndBlocked))
                    142:                         MakeWindowActive(lpBZ->hWndBlocked);
                    143:                     else                        
                    144:                         StartTaskManager(); // Fail safe: Start Task Manager
                    145: 
                    146:                     // If this is the app not responding case, then we want
                    147:                     // to bring down the dialog when "SwitchTo" is selected.
                    148:                     // If the app is busy (RetryRejectedCall situation) then
                    149:                     // we do NOT want to bring down the dialog. this is 
                    150:                     // the OLE2.0 user model design.
                    151:                     if (lpBZ->dwFlags & BZ_NOTRESPONDINGDIALOG)
                    152:                         SendMessage(hDlg, uMsgEndDialog, OLEUI_BZ_SWITCHTOSELECTED, 0L);
                    153:                     break;
                    154: 
                    155:                 case IDBZ_RETRY:
                    156:                     SendMessage(hDlg, uMsgEndDialog, OLEUI_BZ_RETRYSELECTED, 0L);
                    157:                     break;
                    158: 
                    159:                 case IDCANCEL:
                    160:                     SendMessage(hDlg, uMsgEndDialog, OLEUI_CANCEL, 0L);
                    161:                     break;
                    162:                 }
                    163:             break;
                    164:         }
                    165:     return FALSE;
                    166:     }
                    167: 
                    168: 
                    169: /*
                    170:  * FBusyInit
                    171:  *
                    172:  * Purpose:
                    173:  *  WM_INITIDIALOG handler for the Busy dialog box.
                    174:  *
                    175:  * Parameters:
                    176:  *  hDlg            HWND of the dialog
                    177:  *  wParam          WPARAM of the message
                    178:  *  lParam          LPARAM of the message
                    179:  *
                    180:  * Return Value:
                    181:  *  BOOL            Value to return for WM_INITDIALOG.
                    182:  */
                    183: 
                    184: BOOL FBusyInit(HWND hDlg, WPARAM wParam, LPARAM lParam)
                    185:     {
                    186:     LPBUSY           lpBZ;
                    187:     LPOLEUIBUSY      lpOBZ;
                    188:     HFONT            hFont;
                    189:     LPSTR            lpTaskName;
                    190:     LPSTR            lpWindowName;
                    191:     HICON            hIcon;  
                    192: 
                    193:     lpBZ=(LPBUSY)LpvStandardInit(hDlg, sizeof(OLEUIBUSY), TRUE, &hFont);
                    194: 
                    195:     // PvStandardInit sent a termination to us already.
                    196:     if (NULL==lpBZ)
                    197:         return FALSE;
                    198:     
                    199:     // Our original structure is in lParam    
                    200:     lpOBZ = (LPOLEUIBUSY)lParam;          
                    201:     
                    202:     // Copy it to our instance of the structure (in lpBZ)
                    203:     lpBZ->lpOBZ=lpOBZ;
                    204: 
                    205:     //Copy other information from lpOBZ that we might modify.
                    206:     lpBZ->dwFlags = lpOBZ->dwFlags;
                    207: 
                    208:     // Set default information    
                    209:     lpBZ->hWndBlocked = NULL;
                    210:     
                    211:     // Insert HWND of our dialog into the address pointed to by
                    212:     // lphWndDialog.  This can be used by the app who called
                    213:     // OleUIBusy to bring down the dialog with uMsgCloseBusyDialog
                    214:     if (lpOBZ->lphWndDialog && 
                    215:         !IsBadWritePtr((VOID FAR *)lpOBZ->lphWndDialog, sizeof(HWND)))
                    216:         {
                    217:         *lpOBZ->lphWndDialog = hDlg;
                    218:         }
                    219:     
                    220:     // Update text in text box -- 
                    221:     // GetTaskInfo will return two pointers, one to the task name
                    222:     // (file name) and one to the window name.  We need to call
                    223:     // IMallocMemFree on these when we're done with them.  We also
                    224:     // get the HWND which is blocked in this call
                    225:     //
                    226:     // In the case where this call fails, a default message should already
                    227:     // be present in the dialog template, so no action is needed
                    228:     
                    229:     if (GetTaskInfo(hDlg, lpOBZ->hTask, &lpTaskName, &lpWindowName, &lpBZ->hWndBlocked))
                    230:         {                   
                    231:         // Build string to present to user, place in IDBZ_MESSAGE1 control
                    232:         BuildBusyDialogString(hDlg, lpBZ->dwFlags, IDBZ_MESSAGE1, lpTaskName, lpWindowName);
                    233:         IMallocMemFree(lpTaskName);
                    234:         IMallocMemFree(lpWindowName);
                    235:         }
                    236:       
                    237:     // Update icon with the system "exclamation" icon
                    238:     hIcon = LoadIcon(NULL, IDI_EXCLAMATION);
                    239:     SendDlgItemMessage(hDlg, IDBZ_ICON, STM_SETICON, (WPARAM)hIcon, 0L);
                    240:     
                    241:     // Disable/Enable controls
                    242:     if ((lpBZ->dwFlags & BZ_DISABLECANCELBUTTON) ||
                    243:         (lpBZ->dwFlags & BZ_NOTRESPONDINGDIALOG))              // Disable cancel for "not responding" dialog
                    244:         EnableWindow(GetDlgItem(hDlg, IDCANCEL), FALSE);  
                    245: 
                    246:     if (lpBZ->dwFlags & BZ_DISABLESWITCHTOBUTTON)
                    247:         EnableWindow(GetDlgItem(hDlg, IDBZ_SWITCHTO), FALSE);
                    248:         
                    249:     if (lpBZ->dwFlags & BZ_DISABLERETRYBUTTON)
                    250:         EnableWindow(GetDlgItem(hDlg, IDBZ_RETRY), FALSE);
                    251: 
                    252:     // Call the hook with lCustData in lParam
                    253:     UStandardHook((LPVOID)lpBZ, hDlg, WM_INITDIALOG, wParam, lpOBZ->lCustData);
                    254: 
                    255:     // Update caption if lpszCaption was specified
                    256:     if (lpBZ->lpOBZ->lpszCaption && !IsBadReadPtr(lpBZ->lpOBZ->lpszCaption, 1)
                    257:           && lpBZ->lpOBZ->lpszCaption[0] != '\0')
                    258:         SetWindowText(hDlg, (LPSTR)lpBZ->lpOBZ->lpszCaption);
                    259: 
                    260:     return TRUE;
                    261:     }
                    262: 
                    263: 
                    264: /*
                    265:  * BuildBusyDialogString
                    266:  *
                    267:  * Purpose:
                    268:  *  Builds the string that will be displayed in the dialog from the
                    269:  *  task name and window name parameters.
                    270:  *
                    271:  * Parameters:
                    272:  *  hDlg            HWND of the dialog     
                    273:  *  dwFlags         DWORD containing flags passed into dialog
                    274:  *  iControl        Control ID to place the text string
                    275:  *  lpTaskName      LPSTR pointing to name of task (e.g. C:\TEST\TEST.EXE)
                    276:  *  lpWindowName    LPSTR for name of window
                    277:  *
                    278:  * Caveats:
                    279:  *  The caller of this function MUST de-allocate the lpTaskName and
                    280:  *  lpWindowName pointers itself with IMallocMemFree.
                    281:  *
                    282:  * Return Value:
                    283:  *  void
                    284:  */
                    285: 
                    286: void BuildBusyDialogString(HWND hDlg, DWORD dwFlags, int iControl, LPSTR lpTaskName, LPSTR lpWindowName)
                    287: {
                    288:     LPSTR       pszT, psz1, psz2, psz3;
                    289:     UINT        cch;
                    290:     LPSTR       pszDot, pszSlash;
                    291:     UINT        uiStringNum;
                    292:     
                    293:     /*
                    294:      * We need scratch memory for loading the stringtable string, 
                    295:      * the task name, and constructing the final string.  We therefore 
                    296:      * allocate three buffers as large as the maximum message 
                    297:      * length (512) plus the object type, guaranteeing that we have enough
                    298:      * in all cases.
                    299:      */
                    300:     cch=512;
                    301:     
                    302:     // Use OLE-supplied allocation
                    303:     if ((pszT = IMallocMemAlloc((DWORD)(3*cch))) == NULL)
                    304:         return;
                    305:              
                    306:     psz1=pszT;
                    307:     psz2=psz1+cch;
                    308:     psz3=psz2+cch;
                    309:     
                    310:     // Parse base name out of path name, use psz2 for the task
                    311:     // name to display
                    312:     
                    313:     _fstrcpy(psz2, lpTaskName);
                    314:     pszDot = _fstrrchr(psz2, '.');
                    315:     if (pszDot != NULL)
                    316:       *pszDot = '\0'; // Null terminate at the DOT
                    317: 
                    318:     pszSlash = _fstrrchr(psz2, '\\'); // Find last backslash in path
                    319:     if (pszSlash != NULL)
                    320:       psz2 = pszSlash + 1; // Nuke everything up to this point
                    321:     
                    322: #ifdef LOWERCASE_NAME      
                    323:     // Compile this with /DLOWERCASE_NAME if you want the lower-case
                    324:     // module name to be displayed in the dialog rather than the
                    325:     // all-caps name.
                    326:     {
                    327:     int i,l;
                    328:     
                    329:     // Now, lowercase all letters except first one
                    330:     l = _fstrlen(psz2);
                    331:     for(i=0;i<l;i++)
                    332:       psz2[i] = tolower(psz2[i]);
                    333:       
                    334:     psz2[0] = toupper(psz2[0]);  
                    335:     }
                    336: #endif
                    337:     
                    338:     // Check size of lpWindowName.  We can reasonably fit about 80 
                    339:     // characters into the text control, so truncate more than 80 chars
                    340:     if (_fstrlen(lpWindowName) > 80)
                    341:       lpWindowName[80] = '\0';
                    342:       
                    343:     // Load the format string out of stringtable, choose a different
                    344:     // string depending on what flags are passed in to the dialog
                    345:     if (dwFlags & BZ_NOTRESPONDINGDIALOG)
                    346:         uiStringNum = IDS_BZRESULTTEXTNOTRESPONDING;
                    347:     else
                    348:         uiStringNum = IDS_BZRESULTTEXTBUSY;
                    349: 
                    350:     if (LoadString(ghInst, uiStringNum, psz1, cch) == 0)
                    351:       return;
                    352: 
                    353:     // Build the string. The format string looks like this:
                    354:     // "This action cannot be completed because the '%s' application 
                    355:     // (%s) is [busy | not responding]. Choose \"Switch To\" to activate '%s' and 
                    356:     // correct the problem."
                    357:     
                    358:     wsprintf(psz3, psz1, (LPSTR)psz2, (LPSTR)lpWindowName, (LPSTR)psz2);
                    359:     SetDlgItemText(hDlg, iControl, (LPSTR)psz3);
                    360:     IMallocMemFree(pszT);
                    361:     
                    362:     return;
                    363: }
                    364: 
                    365: 
                    366: 
                    367: /*
                    368:  * BusyCleanup
                    369:  *
                    370:  * Purpose:
                    371:  *  Performs busy-specific cleanup before termination.
                    372:  *
                    373:  * Parameters:
                    374:  *  hDlg            HWND of the dialog box so we can access controls.
                    375:  *
                    376:  * Return Value:
                    377:  *  None
                    378:  */
                    379: void BusyCleanup(HWND hDlg)
                    380: {
                    381:    return;
                    382: }
                    383: 
                    384: 
                    385: 
                    386: /*
                    387:  * GetTaskInfo()
                    388:  *
                    389:  * Purpose:  Gets information about the specified task and places the
                    390:  * module name, window name and top-level HWND for the task in the specified 
                    391:  * pointers
                    392:  *
                    393:  * NOTE: The two string pointers allocated in this routine are
                    394:  * the responsibility of the CALLER to de-allocate.
                    395:  *
                    396:  * Parameters:
                    397:  *    hWnd             HWND who called this function
                    398:  *    htask            HTASK which we want to find out more info about
                    399:  *    lplpszTaskName   Location that the module name is returned
                    400:  *    lplpszWindowName Location where the window name is returned
                    401:  *
                    402:  */
                    403:     
                    404: BOOL GetTaskInfo(HWND hWnd, HTASK htask, LPSTR FAR* lplpszTaskName, LPSTR FAR*lplpszWindowName, HWND FAR*lphWnd)
                    405: {
                    406:     BOOL        fRet = FALSE;
                    407: #if !defined( WIN32 )
                    408:     TASKENTRY   te;
                    409: #endif
                    410:     HWND        hwndNext;
                    411:     LPSTR       lpszTN = NULL;
                    412:     LPSTR       lpszWN = NULL;
                    413:     HWND        hwndFind = NULL;
                    414:                    
                    415:     // Clear out return values in case of error                   
                    416:     *lplpszTaskName = NULL;
                    417:     *lplpszWindowName = NULL;
                    418:                    
                    419: #if !defined( WIN32 )
                    420:     te.dwSize = sizeof(TASKENTRY);                   
                    421:     if (TaskFindHandle(&te, htask))
                    422: #endif
                    423:         {
                    424:         // Now, enumerate top-level windows in system
                    425:         hwndNext = GetWindow(hWnd, GW_HWNDFIRST);
                    426:         while (hwndNext)
                    427:             {
                    428:             // See if we can find a non-owned top level window whose
                    429:             // hInstance matches the one we just got passed.  If we find one,
                    430:             // we can be fairly certain that this is the top-level window for
                    431:             // the task which is blocked.
                    432:             //
                    433:             // REVIEW:  Will this filter hold true for InProcServer DLL-created
                    434:             // windows?
                    435:             //
                    436:             if ((hwndNext != hWnd) &&
                    437: #if !defined( WIN32 )
                    438:                 (GetWindowWord(hwndNext, GWW_HINSTANCE) == (WORD)te.hInst) &&
                    439: #else                     
                    440:                 (GetWindowThreadProcessId(hwndNext,NULL) == htask) &&
                    441: #endif                     
                    442:                                (IsWindowVisible(hwndNext)) &&
                    443:                 !GetWindow(hwndNext, GW_OWNER))
                    444:                 {
                    445:                 // We found our window!  Alloc space for new strings
                    446:                 if ((lpszTN = IMallocMemAlloc(OLEUI_CCHPATHMAX)) == NULL)
                    447:                     return TRUE;  // continue task window enumeration
                    448:                    
                    449:                 if ((lpszWN = IMallocMemAlloc(OLEUI_CCHPATHMAX)) == NULL)
                    450:                     return TRUE;  // continue task window enumeration
                    451:                     
                    452:                 // We found the window we were looking for, copy info to
                    453:                 // local vars
                    454:                 GetWindowText(hwndNext, lpszWN, OLEUI_CCHPATHMAX);
                    455: #if !defined( WIN32 )
                    456:                  lstrcpyn(lpszTN, te.szModule, OLEUI_CCHPATHMAX);
                    457: #else                     
                    458:                 /* WIN32 NOTE: we are not able to get a module name
                    459:                 **    given a thread process id on WIN32. the best we
                    460:                 **    can do is use the window title as the module/app
                    461:                 **    name.
                    462:                 */
                    463:                  lstrcpyn(lpszTN, lpszWN, OLEUI_CCHPATHMAX);
                    464: #endif                     
                    465:                 hwndFind = hwndNext;
                    466:                 
                    467:                 fRet = TRUE;
                    468:                 goto OKDone;
                    469:                 }
                    470:                 
                    471:             hwndNext = GetWindow(hwndNext, GW_HWNDNEXT);
                    472:             }
                    473:         }
                    474: 
                    475: OKDone:
                    476: 
                    477:     // OK, everything was successful. Set string pointers to point to
                    478:     // our data.
                    479:     
                    480:     *lplpszTaskName = lpszTN;
                    481:     *lplpszWindowName = lpszWN;
                    482:     *lphWnd = hwndFind;
                    483:     
                    484:     return fRet;
                    485: }
                    486: 
                    487: 
                    488: /*
                    489:  * IMallocMemAlloc()
                    490:  *
                    491:  * Purpose: Allocates dwSize bytes of memory using the OLE-supplied
                    492:  * memory allocation.
                    493:  *
                    494:  * Parameters:
                    495:  *    dwSize           Size of memory requested
                    496:  *
                    497:  * Returns:
                    498:  *    void FAR *       Pointer to new memory, or NULL if failed
                    499:  *
                    500:  */
                    501:  
                    502: void FAR * IMallocMemAlloc(DWORD dwSize)
                    503: {
                    504:   LPMALLOC pIMalloc;
                    505:   LPSTR pszT;
                    506:   
                    507:   // Use OLE-supplied allocation
                    508:   if (CoGetMalloc(MEMCTX_TASK, &pIMalloc) != NOERROR)
                    509:      return NULL;
                    510: 
                    511:   pszT=(LPSTR)pIMalloc->lpVtbl->Alloc(pIMalloc, dwSize);
                    512:   pIMalloc->lpVtbl->Release(pIMalloc);
                    513:   return pszT;
                    514: }    
                    515: 
                    516: 
                    517: /*
                    518:  * IMallocMemFree()
                    519:  *
                    520:  * Purpose: Frees memory allocated with IMallocMemAlloc()
                    521:  *
                    522:  * Parameters:
                    523:  *    ptr              Pointer to memory to free
                    524:  *
                    525:  * Returns:
                    526:  *    SCODE            Return code from CoGetMalloc
                    527:  */
                    528:     
                    529: SCODE IMallocMemFree(void FAR *ptr)
                    530: {
                    531:   SCODE sc;
                    532:   LPMALLOC pIMalloc;
                    533:   
                    534:   sc = GetScode(CoGetMalloc(MEMCTX_TASK, &pIMalloc));
                    535:   if (SUCCEEDED(sc))
                    536:     {
                    537:     pIMalloc->lpVtbl->Free(pIMalloc, ptr);
                    538:     pIMalloc->lpVtbl->Release(pIMalloc);
                    539:     }
                    540:   return sc;
                    541:   
                    542: }
                    543: 
                    544: 
                    545: /*
                    546:  * StartTaskManager()
                    547:  *
                    548:  * Purpose: Starts Task Manager.  Used to bring up task manager to
                    549:  * assist in switching to a given blocked task.
                    550:  *
                    551:  */
                    552:  
                    553: StartTaskManager()
                    554: {
                    555:     WinExec("taskman.exe", SW_SHOW);
                    556:     return TRUE;
                    557: }    
                    558: 
                    559: 
                    560: 
                    561: /*
                    562:  * MakeWindowActive()
                    563:  *
                    564:  * Purpose: Makes specified window the active window.
                    565:  *
                    566:  */
                    567: 
                    568: void MakeWindowActive(HWND hWndSwitchTo)
                    569: {
                    570:     // Move the new window to the top of the Z-order
                    571:     SetWindowPos(hWndSwitchTo, HWND_TOP, 0, 0, 0, 0,
                    572:               SWP_NOSIZE | SWP_NOMOVE);
                    573: 
                    574:     // If it's iconic, we need to restore it.
                    575:     if (IsIconic(hWndSwitchTo))
                    576:         ShowWindow(hWndSwitchTo, SW_RESTORE);
                    577: }

unix.superglobalmegacorp.com

This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.