1using System; 2using System.Collections.Generic; 3using System.Diagnostics; 4using System.Drawing; 5using System.IO; 6using System.Net; 7using System.Runtime.InteropServices; 8using System.Text; 9using System.Text.RegularExpressions; 10using System.Windows.Forms; 11using WindowCapture.Native; 12 13namespace WindowCapture.Detection 14{ 15 /// <summary> 16 /// Media item found on a web page 17 /// </summary> 18 public class MediaItem 19 { 20 public string Url; 21 public string Type; // "image", "video", "audio" 22 public string FileName; 23 public string Description; 24 public string TagName; 25 public long Size; 26 27 public MediaItem(string url, string type) 28 { 29 Url = CleanUrl(url); 30 Type = type; 31 FileName = GetFileNameFromUrl(Url); 32 Size = -1; 33 } 34 35 private static string CleanUrl(string url) 36 { 37 if (string.IsNullOrEmpty(url)) return url; 38 url = url.Trim(); 39 if (url.StartsWith("//")) url = "https:" + url; 40 return url; 41 } 42 43 private string GetFileNameFromUrl(string url) 44 { 45 try 46 { 47 if (string.IsNullOrEmpty(url)) return "media_" + DateTime.Now.Ticks; 48 49 // Remove query and fragment 50 int q = url.IndexOf('?'); 51 if (q > 0) url = url.Substring(0, q); 52 int h = url.IndexOf('#'); 53 if (h > 0) url = url.Substring(0, h); 54 55 var uri = new Uri(url); 56 var name = Path.GetFileName(uri.LocalPath); 57 58 if (string.IsNullOrEmpty(name) || name.Length > 100) 59 name = "media_" + DateTime.Now.Ticks; 60 61 if (!Path.HasExtension(name)) 62 { 63 if (Type == "video") name += ".mp4"; 64 else if (Type == "audio") name += ".mp3"; 65 else name += ".jpg"; 66 } 67 68 return name; 69 } 70 catch 71 { 72 return "media_" + DateTime.Now.Ticks + ".bin"; 73 } 74 } 75 } 76 77 /// <summary> 78 /// Deep media parser - extracts media using multiple strategies without requiring Chrome debug flags 79 /// </summary> 80 public static class MediaParser 81 { 82 /// <summary> 83 /// WebClient with a connection/response timeout so a slow or unresponsive page never 84 /// hangs indefinitely (which would freeze the UI thread if the parse runs there). 85 /// </summary> 86 private class TimedWebClient : WebClient 87 { 88 public int TimeoutMs = 15000; 89 protected override WebRequest GetWebRequest(Uri address) 90 { 91 var r = base.GetWebRequest(address); 92 if (r != null) 93 { 94 r.Timeout = TimeoutMs; 95 var hr = r as HttpWebRequest; 96 if (hr != null) hr.ReadWriteTimeout = TimeoutMs * 2; 97 } 98 return r; 99 } 100 } 101 102 /// <summary> 103 /// Parse media from browser at screen point 104 /// </summary> 105 public static List<MediaItem> ParseMediaAt(Point screenPt, IntPtr excludeWindow) 106 { 107 var results = new List<MediaItem>(); 108 var seenUrls = new HashSet<string>(StringComparer.OrdinalIgnoreCase); 109 110 // Find the window under cursor 111 WinApi.POINT pt; 112 pt.X = screenPt.X; 113 pt.Y = screenPt.Y; 114 IntPtr hwnd = WinApi.FindAppWindowAtPointExcluding(pt, excludeWindow); 115 116 // Strategy 1: Get URL from clipboard (user might have right-clicked "Copy image address") 117 string clipUrl = GetClipboardUrl(); 118 if (!string.IsNullOrEmpty(clipUrl)) 119 { 120 AddUniqueMedia(results, seenUrls, clipUrl, "Clipboard", null); 121 } 122 123 // Strategy 2: Deep UI Automation scan of the element under cursor 124 var uiaMedia = GetMediaFromUIAutomation(screenPt); 125 foreach (var url in uiaMedia) 126 { 127 AddUniqueMedia(results, seenUrls, url, "UIAutomation", null); 128 } 129 130 // Strategy 3: Get browser URL from address bar and parse HTML 131 string browserUrl = GetBrowserUrlFromAddressBar(hwnd); 132 if (!string.IsNullOrEmpty(browserUrl)) 133 { 134 var pageMedia = ParsePageHtml(browserUrl); 135 foreach (var url in pageMedia) 136 { 137 AddUniqueMedia(results, seenUrls, url, "HTML", null); 138 } 139 } 140 141 // Strategy 4: Legacy IAccessible scan 142 if (hwnd != IntPtr.Zero) 143 { 144 var accMedia = GetMediaFromAccessibility(hwnd, screenPt); 145 foreach (var url in accMedia) 146 { 147 AddUniqueMedia(results, seenUrls, url, "Accessibility", null); 148 } 149 } 150 151 // Strategy 5: Scan visible text for URLs 152 var textUrls = ScanWindowTextForUrls(hwnd); 153 foreach (var url in textUrls) 154 { 155 AddUniqueMedia(results, seenUrls, url, "WindowText", null); 156 } 157 158 return results; 159 } 160 161 /// <summary> 162 /// Get media URLs using UI Automation (deeper access than IAccessible) 163 /// </summary> 164 private static List<string> GetMediaFromUIAutomation(Point screenPt) 165 { 166 var results = new List<string>(); 167 168 try 169 { 170 // Create UI Automation instance 171 IUIAutomation uiAutomation = (IUIAutomation)new CUIAutomation(); 172 if (uiAutomation == null) return results; 173 174 // Get element at point 175 IUIAutomationElement element = uiAutomation.ElementFromPoint(new tagPOINT { x = screenPt.X, y = screenPt.Y }); 176 if (element == null) return results; 177 178 // Extract URLs from this element and its ancestors/descendants 179 ExtractUrlsFromUIAElement(uiAutomation, element, results, 0, true); 180 181 // Also check parent elements (might contain image URL) 182 var walker = uiAutomation.CreateTreeWalker(uiAutomation.RawViewCondition); 183 IUIAutomationElement parent = element; 184 for (int i = 0; i < 5 && parent != null; i++) 185 { 186 try 187 { 188 parent = walker.GetParentElement(parent); 189 if (parent != null) 190 ExtractUrlsFromUIAElement(uiAutomation, parent, results, 0, false); 191 } 192 catch { break; } 193 } 194 } 195 catch { } 196 197 return results; 198 } 199 200 private static void ExtractUrlsFromUIAElement(IUIAutomation uiAutomation, IUIAutomationElement element, List<string> results, int depth, bool scanChildren) 201 { 202 if (depth > 10 || results.Count > 100 || element == null) return; 203 204 try 205 { 206 // Get various properties that might contain URLs 207 string[] propertiesToCheck = new string[10]; 208 209 try { propertiesToCheck[0] = element.CurrentName; } catch { } 210 try { propertiesToCheck[1] = element.CurrentHelpText; } catch { } 211 try { propertiesToCheck[2] = element.CurrentItemStatus; } catch { } 212 try { propertiesToCheck[3] = element.CurrentAutomationId; } catch { } 213 try { propertiesToCheck[4] = element.CurrentClassName; } catch { } 214 215 // Try to get Value pattern 216 try 217 { 218 object patternObj; 219 element.GetCurrentPattern(10002, out patternObj); // UIA_ValuePatternId 220 var valuePattern = patternObj as IUIAutomationValuePattern; 221 if (valuePattern != null) 222 { 223 propertiesToCheck[5] = valuePattern.CurrentValue; 224 } 225 } 226 catch { } 227 228 // Try to get LegacyIAccessible pattern for more data 229 try 230 { 231 object patternObj; 232 element.GetCurrentPattern(10018, out patternObj); // UIA_LegacyIAccessiblePatternId 233 var legacyPattern = patternObj as IUIAutomationLegacyIAccessiblePattern; 234 if (legacyPattern != null) 235 { 236 try { propertiesToCheck[6] = legacyPattern.CurrentValue; } catch { } 237 try { propertiesToCheck[7] = legacyPattern.CurrentDescription; } catch { } 238 try { propertiesToCheck[8] = legacyPattern.CurrentName; } catch { } 239 try { propertiesToCheck[9] = legacyPattern.CurrentDefaultAction; } catch { } 240 } 241 } 242 catch { } 243 244 // Extract URLs from all properties 245 foreach (var prop in propertiesToCheck) 246 { 247 if (string.IsNullOrEmpty(prop)) continue; 248 ExtractUrlsFromText(prop, results); 249 } 250 251 // Scan children if requested 252 if (scanChildren && depth < 5) 253 { 254 try 255 { 256 var children = element.FindAll(TreeScope.TreeScope_Children, uiAutomation.CreateTrueCondition()); 257 if (children != null) 258 { 259 int count = children.Length; 260 for (int i = 0; i < count && i < 50 && results.Count < 100; i++) 261 { 262 try 263 { 264 var child = children.GetElement(i); 265 ExtractUrlsFromUIAElement(uiAutomation, child, results, depth + 1, true); 266 } 267 catch { } 268 } 269 } 270 } 271 catch { } 272 } 273 } 274 catch { } 275 } 276 277 private static void ExtractUrlsFromText(string text, List<string> results) 278 { 279 if (string.IsNullOrEmpty(text)) return; 280 281 // Find HTTP/HTTPS URLs 282 var urlMatches = Regex.Matches(text, @"https?://[^\s\""'<>\]\)]+", RegexOptions.IgnoreCase); 283 foreach (Match m in urlMatches) 284 { 285 string url = m.Value.TrimEnd('.', ',', ';', ':', '!', '?'); 286 if (IsMediaUrl(url) && results.Count < 100) 287 results.Add(url); 288 } 289 290 // Find data: URLs for images (base64) 291 if (text.StartsWith("data:image/")) 292 { 293 // Skip data URLs for now - they're embedded 294 } 295 } 296 297 /// <summary> 298 /// Get browser URL from address bar using UI Automation 299 /// </summary> 300 private static string GetBrowserUrlFromAddressBar(IntPtr hwnd) 301 { 302 if (hwnd == IntPtr.Zero) return null; 303 304 try 305 { 306 IUIAutomation uiAutomation = (IUIAutomation)new CUIAutomation(); 307 var rootElement = uiAutomation.ElementFromHandle(hwnd); 308 if (rootElement == null) return null; 309 310 // Look for address bar - it's usually an Edit control with specific automation ID 311 // Chrome: "addressTextField" or class "OmniboxViewViews" 312 // Firefox: "urlbar-input" 313 // Edge: Similar to Chrome 314 315 string[] addressBarIds = { "addressTextField", "urlbar-input", "addressEditBox", "URL bar" }; 316 317 foreach (var id in addressBarIds) 318 { 319 try 320 { 321 var condition = uiAutomation.CreatePropertyCondition(30011, id); // AutomationIdProperty 322 var addressBar = rootElement.FindFirst(TreeScope.TreeScope_Descendants, condition); 323 if (addressBar != null) 324 { 325 object patternObj; 326 addressBar.GetCurrentPattern(10002, out patternObj); 327 var valuePattern = patternObj as IUIAutomationValuePattern; 328 if (valuePattern != null) 329 { 330 string url = valuePattern.CurrentValue; 331 if (!string.IsNullOrEmpty(url)) 332 { 333 if (!url.StartsWith("http")) url = "https://" + url; 334 return url; 335 } 336 } 337 } 338 } 339 catch { } 340 } 341 342 // Fallback: look for Edit controls with URL-like content 343 var editCondition = uiAutomation.CreatePropertyCondition(30003, 50004); // ControlTypeProperty = Edit 344 var edits = rootElement.FindAll(TreeScope.TreeScope_Descendants, editCondition); 345 if (edits != null) 346 { 347 int count = edits.Length; 348 for (int i = 0; i < count && i < 20; i++) 349 { 350 try 351 { 352 var edit = edits.GetElement(i); 353 object patternObj; 354 edit.GetCurrentPattern(10002, out patternObj); 355 var valuePattern = patternObj as IUIAutomationValuePattern; 356 if (valuePattern != null) 357 { 358 string value = valuePattern.CurrentValue; 359 if (!string.IsNullOrEmpty(value) && 360 (value.StartsWith("http://") || value.StartsWith("https://") || 361 value.Contains(".com") || value.Contains(".org") || value.Contains(".net"))) 362 { 363 if (!value.StartsWith("http")) value = "https://" + value; 364 return value; 365 } 366 } 367 } 368 catch { } 369 } 370 } 371 } 372 catch { } 373 374 return null; 375 } 376 377 /// <summary> 378 /// Parse HTML page for media URLs 379 /// </summary> 380 private static List<string> ParsePageHtml(string pageUrl) 381 { 382 var results = new List<string>(); 383 384 try 385 { 386 using (var client = new TimedWebClient()) 387 { 388 client.Headers.Add("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36"); 389 client.Headers.Add("Accept", "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8"); 390 client.Encoding = Encoding.UTF8; 391 392 string html = client.DownloadString(pageUrl); 393 Uri baseUri = new Uri(pageUrl); 394 395 // Find all src, href, data-src, poster, srcset attributes 396 var srcMatches = Regex.Matches(html, @"(?:src|href|data-src|data-original|poster|srcset)\s*=\s*[""']([^""']+)[""']", RegexOptions.IgnoreCase); 397 foreach (Match m in srcMatches) 398 { 399 string val = m.Groups[1].Value; 400 // Handle srcset (multiple URLs) 401 if (val.Contains(",")) 402 { 403 foreach (var part in val.Split(',')) 404 { 405 string url = part.Trim().Split(' ')[0]; 406 url = ResolveUrl(url, baseUri); 407 if (IsMediaUrl(url)) results.Add(url); 408 } 409 } 410 else 411 { 412 string url = ResolveUrl(val, baseUri); 413 if (IsMediaUrl(url)) results.Add(url); 414 } 415 } 416 417 // Find url() in CSS 418 var cssMatches = Regex.Matches(html, @"url\s*\(\s*[""']?([^""'\)]+)[""']?\s*\)", RegexOptions.IgnoreCase); 419 foreach (Match m in cssMatches) 420 { 421 string url = ResolveUrl(m.Groups[1].Value, baseUri); 422 if (IsMediaUrl(url)) results.Add(url); 423 } 424 425 // Find og:image, og:video, twitter:image meta tags 426 var metaMatches = Regex.Matches(html, @"<meta[^>]+(?:property|name)\s*=\s*[""'](?:og:image|og:video|og:video:url|twitter:image|twitter:player)[""'][^>]+content\s*=\s*[""']([^""']+)[""']", RegexOptions.IgnoreCase); 427 foreach (Match m in metaMatches) 428 { 429 string url = ResolveUrl(m.Groups[1].Value, baseUri); 430 if (!string.IsNullOrEmpty(url)) results.Add(url); 431 } 432 433 // Also check content before property (different order) 434 var metaMatches2 = Regex.Matches(html, @"<meta[^>]+content\s*=\s*[""']([^""']+)[""'][^>]+(?:property|name)\s*=\s*[""'](?:og:image|og:video|og:video:url|twitter:image)[""']", RegexOptions.IgnoreCase); 435 foreach (Match m in metaMatches2) 436 { 437 string url = ResolveUrl(m.Groups[1].Value, baseUri); 438 if (!string.IsNullOrEmpty(url)) results.Add(url); 439 } 440 441 // Find video sources in JavaScript (common patterns) 442 var jsVideoMatches = Regex.Matches(html, @"[""'](https?://[^""']+\.(?:mp4|webm|m3u8|mpd)(?:\?[^""']*)?)[""']", RegexOptions.IgnoreCase); 443 foreach (Match m in jsVideoMatches) 444 { 445 results.Add(m.Groups[1].Value); 446 } 447 448 // Find image URLs in JSON-LD 449 var jsonLdMatches = Regex.Matches(html, @"""(?:image|thumbnailUrl|contentUrl|url)""\s*:\s*""(https?://[^""]+)""", RegexOptions.IgnoreCase); 450 foreach (Match m in jsonLdMatches) 451 { 452 string url = m.Groups[1].Value; 453 if (IsMediaUrl(url)) results.Add(url); 454 } 455 } 456 } 457 catch { } 458 459 return results; 460 } 461 462 /// <summary> 463 /// Scan window text for URLs 464 /// </summary> 465 private static List<string> ScanWindowTextForUrls(IntPtr hwnd) 466 { 467 var results = new List<string>(); 468 if (hwnd == IntPtr.Zero) return results; 469 470 try 471 { 472 // Get window text 473 int length = GetWindowTextLength(hwnd); 474 if (length > 0 && length < 10000) 475 { 476 var sb = new StringBuilder(length + 1); 477 GetWindowText(hwnd, sb, sb.Capacity); 478 ExtractUrlsFromText(sb.ToString(), results); 479 } 480 481 // Enumerate child windows 482 EnumChildWindows(hwnd, (childHwnd, lParam) => 483 { 484 try 485 { 486 int len = GetWindowTextLength(childHwnd); 487 if (len > 0 && len < 5000) 488 { 489 var sb = new StringBuilder(len + 1); 490 GetWindowText(childHwnd, sb, sb.Capacity); 491 ExtractUrlsFromText(sb.ToString(), results); 492 } 493 } 494 catch { } 495 return results.Count < 100; 496 }, IntPtr.Zero); 497 } 498 catch { } 499 500 return results; 501 } 502 503 /// <summary> 504 /// Get media URLs from accessibility tree (legacy IAccessible) 505 /// </summary> 506 private static List<string> GetMediaFromAccessibility(IntPtr hwnd, Point screenPt) 507 { 508 var results = new List<string>(); 509 510 try 511 { 512 object accObj; 513 object childId; 514 int hr = AccessibleObjectFromPoint(screenPt, out accObj, out childId); 515 516 if (hr == 0 && accObj != null) 517 { 518 var acc = accObj as IAccessible; 519 if (acc != null) 520 { 521 ExtractUrlsFromAccessible(acc, childId, results, 0); 522 Marshal.ReleaseComObject(acc); 523 } 524 } 525 } 526 catch { } 527 528 return results; 529 } 530 531 private static void ExtractUrlsFromAccessible(IAccessible acc, object childId, List<string> results, int depth) 532 { 533 if (depth > 15 || results.Count > 100) return; 534 535 try 536 { 537 string[] props = new string[4]; 538 try { props[0] = acc.get_accName(childId); } catch { } 539 try { props[1] = acc.get_accValue(childId); } catch { } 540 try { props[2] = acc.get_accDescription(childId); } catch { } 541 try { props[3] = acc.get_accHelp(childId); } catch { } 542 543 foreach (var prop in props) 544 { 545 if (!string.IsNullOrEmpty(prop)) 546 ExtractUrlsFromText(prop, results); 547 } 548 549 // Check children 550 int childCount = 0; 551 try { childCount = acc.accChildCount; } catch { } 552 553 if (childCount > 0 && childCount < 100) 554 { 555 object[] children = new object[childCount]; 556 int obtained = 0; 557 AccessibleChildren(acc, 0, childCount, children, out obtained); 558 559 for (int i = 0; i < obtained && results.Count < 100; i++) 560 { 561 var childAcc = children[i] as IAccessible; 562 if (childAcc != null) 563 { 564 ExtractUrlsFromAccessible(childAcc, 0, results, depth + 1); 565 Marshal.ReleaseComObject(childAcc); 566 } 567 } 568 } 569 } 570 catch { } 571 } 572 573 private static string GetClipboardUrl() 574 { 575 try 576 { 577 if (Clipboard.ContainsText()) 578 { 579 string text = Clipboard.GetText().Trim(); 580 if ((text.StartsWith("http://") || text.StartsWith("https://")) && 581 !text.Contains(" ") && !text.Contains("\n")) 582 return text; 583 } 584 } 585 catch { } 586 return null; 587 } 588 589 private static void AddUniqueMedia(List<MediaItem> results, HashSet<string> seen, string url, string source, string tag) 590 { 591 if (string.IsNullOrEmpty(url)) return; 592 593 url = url.Trim(); 594 if (url.StartsWith("//")) url = "https:" + url; 595 if (!url.StartsWith("http")) return; 596 if (seen.Contains(url)) return; 597 598 // Skip tiny icons and tracking pixels 599 if (url.Contains("favicon") || url.Contains("1x1") || url.Contains("pixel") || 600 url.Contains("tracking") || url.Contains("beacon") || url.Contains("analytics")) 601 return; 602 603 // Skip common non-media URLs 604 if (url.Contains(".js") || url.Contains(".css") || url.Contains(".woff") || 605 url.Contains(".ttf") || url.Contains(".eot")) 606 return; 607 608 seen.Add(url); 609 610 string type = "image"; 611 string lower = url.ToLower(); 612 if (lower.Contains(".mp4") || lower.Contains(".webm") || lower.Contains(".avi") || 613 lower.Contains(".mkv") || lower.Contains(".mov") || lower.Contains(".m3u8") || 614 lower.Contains(".mpd") || lower.Contains("video") || 615 lower.Contains("youtube.com/embed") || lower.Contains("player.vimeo")) 616 type = "video"; 617 else if (lower.Contains(".mp3") || lower.Contains(".wav") || lower.Contains(".ogg") || 618 lower.Contains(".m4a") || lower.Contains(".flac") || lower.Contains(".aac") || 619 lower.Contains("audio")) 620 type = "audio"; 621 622 var item = new MediaItem(url, type); 623 item.Description = source + (tag != null ? ": <" + tag + ">" : ""); 624 item.TagName = tag; 625 results.Add(item); 626 } 627 628 private static string ResolveUrl(string url, Uri baseUri) 629 { 630 if (string.IsNullOrEmpty(url)) return null; 631 url = url.Trim(); 632 if (url.StartsWith("data:") || url.StartsWith("#") || url.StartsWith("javascript:") || url.StartsWith("blob:")) 633 return null; 634 635 try 636 { 637 if (url.StartsWith("//")) return baseUri.Scheme + ":" + url; 638 if (url.StartsWith("/")) return baseUri.Scheme + "://" + baseUri.Host + url; 639 if (!url.StartsWith("http")) return new Uri(baseUri, url).ToString(); 640 return url; 641 } 642 catch { return null; } 643 } 644 645 private static bool IsMediaUrl(string url) 646 { 647 if (string.IsNullOrEmpty(url)) return false; 648 string lower = url.ToLower(); 649 650 // Skip non-media 651 if (lower.EndsWith(".js") || lower.EndsWith(".css") || lower.EndsWith(".html") || lower.EndsWith(".htm")) 652 return false; 653 654 string[] exts = { ".jpg", ".jpeg", ".png", ".gif", ".webp", ".bmp", ".svg", ".ico", ".avif", ".tiff", 655 ".mp4", ".webm", ".avi", ".mkv", ".mov", ".flv", ".m4v", ".m3u8", ".mpd", ".ts", 656 ".mp3", ".wav", ".ogg", ".m4a", ".flac", ".aac", ".opus" }; 657 foreach (var ext in exts) 658 { 659 if (lower.Contains(ext + "?") || lower.EndsWith(ext)) 660 return true; 661 } 662 663 // URL path hints 664 if (lower.Contains("/image") || lower.Contains("/img/") || lower.Contains("/images/") || 665 lower.Contains("/video") || lower.Contains("/videos/") || lower.Contains("/audio") || 666 lower.Contains("/media/") || lower.Contains("/thumb") || lower.Contains("/photo") || 667 lower.Contains("/picture") || lower.Contains("/uploads/") || lower.Contains("/content/")) 668 return true; 669 670 // CDN hints 671 if (lower.Contains("cdn.") || lower.Contains("static.") || lower.Contains("img.") || 672 lower.Contains("images.") || lower.Contains("media.") || lower.Contains("i.imgur") || 673 lower.Contains("pbs.twimg") || lower.Contains("scontent")) 674 return true; 675 676 return false; 677 } 678 679 public static bool DownloadMedia(MediaItem item, string savePath) 680 { 681 try 682 { 683 using (var client = new TimedWebClient()) 684 { 685 client.Headers.Add("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36"); 686 client.Headers.Add("Accept", "*/*"); 687 client.Headers.Add("Referer", new Uri(item.Url).GetLeftPart(UriPartial.Authority) + "/"); 688 client.DownloadFile(item.Url, savePath); 689 return File.Exists(savePath) && new FileInfo(savePath).Length > 0; 690 } 691 } 692 catch { return false; } 693 } 694 695 // P/Invoke for IAccessible 696 [DllImport("oleacc.dll")] 697 private static extern int AccessibleObjectFromPoint(Point pt, 698 [MarshalAs(UnmanagedType.Interface)] out object ppacc, out object pvarChild); 699 700 [DllImport("oleacc.dll")] 701 private static extern int AccessibleChildren(IAccessible paccContainer, 702 int iChildStart, int cChildren, [Out] object[] rgvarChildren, out int pcObtained); 703 704 // P/Invoke for window text 705 [DllImport("user32.dll", CharSet = CharSet.Auto)] 706 private static extern int GetWindowText(IntPtr hWnd, StringBuilder lpString, int nMaxCount); 707 708 [DllImport("user32.dll")] 709 private static extern int GetWindowTextLength(IntPtr hWnd); 710 711 private delegate bool EnumWindowsProc(IntPtr hWnd, IntPtr lParam); 712 713 [DllImport("user32.dll")] 714 private static extern bool EnumChildWindows(IntPtr hwndParent, EnumWindowsProc lpEnumFunc, IntPtr lParam); 715 } 716 717 // IAccessible interface 718 [ComImport, Guid("618736E0-3C3D-11CF-810C-00AA00389B71"), InterfaceType(ComInterfaceType.InterfaceIsIDispatch)] 719 public interface IAccessible 720 { 721 [DispId(-5000)] object get_accParent(); 722 [DispId(-5001)] int accChildCount { get; } 723 [DispId(-5002)] object get_accChild(object varChild); 724 [DispId(-5003)] string get_accName(object varChild); 725 [DispId(-5004)] string get_accValue(object varChild); 726 [DispId(-5005)] string get_accDescription(object varChild); 727 [DispId(-5006)] object get_accRole(object varChild); 728 [DispId(-5007)] object get_accState(object varChild); 729 [DispId(-5008)] string get_accHelp(object varChild); 730 [DispId(-5009)] int get_accHelpTopic(out string pszHelpFile, object varChild); 731 [DispId(-5010)] string get_accKeyboardShortcut(object varChild); 732 [DispId(-5011)] object get_accFocus(); 733 [DispId(-5012)] object get_accSelection(); 734 [DispId(-5013)] string get_accDefaultAction(object varChild); 735 [DispId(-5014)] void accSelect(int flagsSelect, object varChild); 736 [DispId(-5015)] void accLocation(out int pxLeft, out int pyTop, out int pcxWidth, out int pcyHeight, object varChild); 737 [DispId(-5016)] object accNavigate(int navDir, object varStart); 738 [DispId(-5017)] object accHitTest(int xLeft, int yTop); 739 [DispId(-5018)] void accDoDefaultAction(object varChild); 740 [DispId(-5003)] void set_accName(object varChild, string szName); 741 [DispId(-5004)] void set_accValue(object varChild, string szValue); 742 } 743 744 // UI Automation COM interfaces 745 [ComImport, Guid("30CBE57D-D9D0-452A-AB13-7AC5AC4825EE"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] 746 public interface IUIAutomation 747 { 748 int CompareElements(IUIAutomationElement el1, IUIAutomationElement el2); 749 int CompareRuntimeIds(int[] runtimeId1, int[] runtimeId2); 750 IUIAutomationElement GetRootElement(); 751 IUIAutomationElement ElementFromHandle(IntPtr hwnd); 752 IUIAutomationElement ElementFromPoint(tagPOINT pt); 753 IUIAutomationElement GetFocusedElement(); 754 IUIAutomationTreeWalker CreateTreeWalker(IUIAutomationCondition pCondition); 755 IUIAutomationTreeWalker ControlViewWalker { get; } 756 IUIAutomationTreeWalker ContentViewWalker { get; } 757 IUIAutomationTreeWalker RawViewWalker { get; } 758 IUIAutomationCondition RawViewCondition { get; } 759 IUIAutomationCondition ControlViewCondition { get; } 760 IUIAutomationCondition ContentViewCondition { get; } 761 IUIAutomationCacheRequest CreateCacheRequest(); 762 IUIAutomationCondition CreateTrueCondition(); 763 IUIAutomationCondition CreateFalseCondition(); 764 IUIAutomationCondition CreatePropertyCondition(int propertyId, object value); 765 } 766 767 [ComImport, Guid("D22108AA-8AC5-49A5-837B-37BBB3D7591E"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] 768 public interface IUIAutomationElement 769 { 770 void SetFocus(); 771 int[] GetRuntimeId(); 772 IUIAutomationElement FindFirst(TreeScope scope, IUIAutomationCondition condition); 773 IUIAutomationElementArray FindAll(TreeScope scope, IUIAutomationCondition condition); 774 void FindFirstBuildCache(); 775 void FindAllBuildCache(); 776 void BuildUpdatedCache(); 777 void GetCurrentPropertyValue(int propertyId, out object retVal); 778 void MethodPlaceholder1(); 779 void MethodPlaceholder2(); 780 void MethodPlaceholder3(); 781 void MethodPlaceholder4(); 782 void GetCurrentPattern(int patternId, out object patternObject); 783 void MethodPlaceholder5(); 784 void MethodPlaceholder6(); 785 void MethodPlaceholder7(); 786 void MethodPlaceholder8(); 787 int CurrentProcessId { get; } 788 int CurrentControlType { get; } 789 string CurrentLocalizedControlType { get; } 790 string CurrentName { get; } 791 string CurrentAcceleratorKey { get; } 792 string CurrentAccessKey { get; } 793 int CurrentHasKeyboardFocus { get; } 794 int CurrentIsKeyboardFocusable { get; } 795 int CurrentIsEnabled { get; } 796 string CurrentAutomationId { get; } 797 string CurrentClassName { get; } 798 string CurrentHelpText { get; } 799 int CurrentCulture { get; } 800 int CurrentIsControlElement { get; } 801 int CurrentIsContentElement { get; } 802 int CurrentIsPassword { get; } 803 IntPtr CurrentNativeWindowHandle { get; } 804 string CurrentItemType { get; } 805 int CurrentIsOffscreen { get; } 806 int CurrentOrientation { get; } 807 string CurrentFrameworkId { get; } 808 int CurrentIsRequiredForForm { get; } 809 string CurrentItemStatus { get; } 810 } 811 812 [ComImport, Guid("14314595-B4BC-4055-95F2-58F2E42C9855"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] 813 public interface IUIAutomationElementArray 814 { 815 int Length { get; } 816 IUIAutomationElement GetElement(int index); 817 } 818 819 [ComImport, Guid("352FFBA8-0973-437C-A61F-F64CAFD81DF9"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] 820 public interface IUIAutomationCondition { } 821 822 [ComImport, Guid("4042C624-389C-4AFC-A630-9DF854A541FC"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] 823 public interface IUIAutomationTreeWalker 824 { 825 IUIAutomationElement GetParentElement(IUIAutomationElement element); 826 IUIAutomationElement GetFirstChildElement(IUIAutomationElement element); 827 IUIAutomationElement GetLastChildElement(IUIAutomationElement element); 828 IUIAutomationElement GetNextSiblingElement(IUIAutomationElement element); 829 IUIAutomationElement GetPreviousSiblingElement(IUIAutomationElement element); 830 } 831 832 [ComImport, Guid("B17D6187-0907-464B-A168-0EF17A1572B1"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] 833 public interface IUIAutomationCacheRequest { } 834 835 [ComImport, Guid("A94CD8B1-0844-4CD6-9D2D-640537AB39E9"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] 836 public interface IUIAutomationValuePattern 837 { 838 void SetValue(string val); 839 string CurrentValue { get; } 840 int CurrentIsReadOnly { get; } 841 } 842 843 [ComImport, Guid("828055AD-355B-4435-86D5-3B51C14A9B1B"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] 844 public interface IUIAutomationLegacyIAccessiblePattern 845 { 846 void Select(int flagsSelect); 847 void DoDefaultAction(); 848 void SetValue(string szValue); 849 int CurrentChildId { get; } 850 string CurrentName { get; } 851 string CurrentValue { get; } 852 string CurrentDescription { get; } 853 int CurrentRole { get; } 854 int CurrentState { get; } 855 string CurrentHelp { get; } 856 string CurrentKeyboardShortcut { get; } 857 IUIAutomationElementArray GetCurrentSelection(); 858 string CurrentDefaultAction { get; } 859 } 860 861 [StructLayout(LayoutKind.Sequential)] 862 public struct tagPOINT 863 { 864 public int x; 865 public int y; 866 } 867 868 public enum TreeScope 869 { 870 TreeScope_Element = 1, 871 TreeScope_Children = 2, 872 TreeScope_Descendants = 4, 873 TreeScope_Parent = 8, 874 TreeScope_Ancestors = 16, 875 TreeScope_Subtree = 7 876 } 877 878 [ComImport, Guid("FF48DBA4-60EF-4201-AA87-54103EEF594E"), ClassInterface(ClassInterfaceType.None)] 879 public class CUIAutomation { } 880}