首页 > 代码库 > C# HttpBrowser 跨进程访问,解决内存泄露问题
C# HttpBrowser 跨进程访问,解决内存泄露问题
1 #undef DEBUG 2 using Microsoft.Win32; 3 using Newtonsoft.Json; 4 using System; 5 using System.Collections.Generic; 6 using System.Collections.Specialized; 7 using System.Diagnostics; 8 using System.Diagnostics.Contracts; 9 using System.Drawing; 10 using System.IO; 11 using System.Linq; 12 using System.Net; 13 using System.Runtime.InteropServices; 14 using System.Text; 15 using System.Threading; 16 using System.Threading.Tasks; 17 using System.Windows.Forms; 18 19 namespace AnfleCrawler.Common 20 { 21 /// <summary> 22 /// Chromium / CasperJS + PhantomJS 23 /// http://pinvoke.net/index.aspx 24 /// </summary> 25 public sealed partial class HttpBrowser : IHttpClient 26 { 27 #region NestedTypes 28 [Serializable] 29 public class AjaxBlockEntity 30 { 31 internal const string AjaxBlock = "_AjaxBlock"; 32 public string ID { get; set; } 33 public string Text { get; set; } 34 public bool IsEvent { get; set; } 35 } 36 public class AjaxEventEntity : MarshalByRefObject 37 { 38 public string ListenerSelector { get; set; } 39 public bool EntryCall { get; set; } 40 public Action<string> FinalCallback { get; set; } 41 } 42 43 [ComVisible(true)] 44 public sealed class STAContext : Disposable 45 { 46 #region Fields 47 public volatile bool IsRedirect; 48 //internal MessageLoopApartment _Apartment; 49 private SynchronizedCollection<Tuple<HtmlElement, EventHandler>> _releaseSet; 50 private AutoResetEvent _sendReceiveWaiter; 51 private CountdownEvent _ajaxWaiter; 52 private System.Threading.Timer _lazyTimer; 53 54 internal volatile bool DoInvokeHtml; 55 private volatile string _outerHtml; 56 #endregion 57 58 #region Properties 59 public Uri RequestUrl { get; private set; } 60 public HttpRequestContent RequestContent { get; private set; } 61 internal AutoResetEvent WaitHandle { get; set; } 62 63 internal AutoResetEvent SendReceiveWaiter 64 { 65 get 66 { 67 if (_sendReceiveWaiter == null) 68 { 69 _sendReceiveWaiter = new AutoResetEvent(false); 70 } 71 return _sendReceiveWaiter; 72 } 73 } 74 internal AjaxBlockEntity[] AjaxBlocks { get; private set; } 75 internal CountdownEvent AjaxWaiter 76 { 77 get 78 { 79 if (_ajaxWaiter == null) 80 { 81 _ajaxWaiter = new CountdownEvent(1); 82 } 83 return _ajaxWaiter; 84 } 85 } 86 internal volatile bool IsProcessEvent; 87 internal AjaxEventEntity AjaxEvent { get; set; } 88 89 internal string OuterHtml 90 { 91 get 92 { 93 DoInvokeHtml = true; 94 return _outerHtml; 95 } 96 set 97 { 98 _outerHtml = value; 99 }100 }101 #endregion102 103 #region Constructor104 internal STAContext(Uri url, HttpRequestContent content)105 {106 this.RequestUrl = url;107 this.RequestContent = content;108 string ablock;109 if (this.RequestContent != null && this.RequestContent.Form != null)110 {111 if (!string.IsNullOrEmpty(ablock = this.RequestContent.Form.Get(AjaxBlockEntity.AjaxBlock)))112 {113 this.AjaxBlocks = JsonConvert.DeserializeObject<AjaxBlockEntity[]>(ablock);114 this.RequestContent.Form.Remove(AjaxBlockEntity.AjaxBlock);115 }116 }117 DoInvokeHtml = true;118 }119 120 protected override void DisposeInternal(bool disposing)121 {122 if (disposing)123 {124 //if (_Apartment != null)125 //{126 // _Apartment.Dispose();127 // _Apartment = null;128 //}129 if (_lazyTimer != null)130 {131 _lazyTimer.Dispose();132 _lazyTimer = null;133 }134 if (this.WaitHandle != null)135 {136 this.WaitHandle.Dispose();137 this.WaitHandle = null;138 }139 140 DisposeObject(_sendReceiveWaiter);141 DisposeObject(_ajaxWaiter);142 }143 }144 #endregion145 146 #region Methods147 public void SetHtml(string html)148 {149 _outerHtml = html;150 DoInvokeHtml = false;151 }152 153 internal void RegisterLazyLoad(Action<object> func, object state)154 {155 if (_lazyTimer != null)156 {157 return;158 }159 _lazyTimer = new System.Threading.Timer(x => STA_Run(func, x, this), state, 2000, Timeout.Infinite);160 }161 /// <summary>162 /// 另种思路,在每次加载完毕后delay163 /// </summary>164 internal void DelayLazyLoad()165 {166 if (_lazyTimer == null)167 {168 return;169 }170 _lazyTimer.Change(2000, Timeout.Infinite);171 }172 173 /// <summary>174 /// STA175 /// </summary>176 /// <param name="node"></param>177 /// <param name="e"></param>178 internal void AjaxMark(HtmlElement node, EventHandler e)179 {180 if (_releaseSet == null)181 {182 _releaseSet = new SynchronizedCollection<Tuple<HtmlElement, EventHandler>>();183 }184 var q = from t in _releaseSet185 where t.Item1 == node186 select t;187 if (q.Any())188 {189 return;190 }191 _releaseSet.Add(Tuple.Create(node, e));192 node.AttachEventHandler("onpropertychange", e);193 }194 195 /// <summary>196 /// STA197 /// </summary>198 internal void AjaxUnmarks()199 {200 if (_releaseSet.IsNullOrEmpty())201 {202 return;203 }204 foreach (var item in _releaseSet)205 {206 var node = item.Item1;207 node.DetachEventHandler("onpropertychange", item.Item2);208 }209 _releaseSet = null;210 }211 212 internal void _ReleaseMemory()213 {214 return;215 #if !DEBUG216 var proc = Process.GetCurrentProcess();217 //128M218 if (proc.PrivateMemorySize64 <= 134217728L)219 {220 return;221 }222 base.ReleaseMemory();223 #endif224 }225 #endregion226 }227 #endregion228 229 #region Static230 public const string Callback_Snapshot = "_xSnapshot";231 232 static HttpBrowser()233 {234 SetBrowserFeatureControl();235 //NativeMethods.SetErrorMode(NativeMethods.ErrorModes.SYSTEM_DEFAULT);236 NativeMethods.SetErrorMode(NativeMethods.ErrorModes.SEM_FAILCRITICALERRORS | NativeMethods.ErrorModes.SEM_NOGPFAULTERRORBOX | NativeMethods.ErrorModes.SEM_NOOPENFILEERRORBOX);237 }238 239 /// <summary>240 /// http://msdn.microsoft.com/en-us/library/ee330720(v=vs.85).aspx241 /// </summary>242 private static void SetBrowserFeatureControl()243 {244 // FeatureControl settings are per-process245 string fileName = Path.GetFileName(Process.GetCurrentProcess().MainModule.FileName);246 string[] skip = new string[] { "devenv.exe", "XDesProc.exe" };247 if (skip.Any(p => p.Equals(fileName, StringComparison.OrdinalIgnoreCase)))248 {249 return;250 }251 252 SetBrowserFeatureControlKey("FEATURE_BROWSER_EMULATION", fileName, GetBrowserEmulationMode());253 SetBrowserFeatureControlKey("FEATURE_MANAGE_SCRIPT_CIRCULAR_REFS", fileName, 1);254 //SetBrowserFeatureControlKey("FEATURE_GPU_RENDERING ", fileName, 1);255 //SetBrowserFeatureControlKey("FEATURE_AJAX_CONNECTIONEVENTS", fileName, 1);256 //SetBrowserFeatureControlKey("FEATURE_ENABLE_CLIPCHILDREN_OPTIMIZATION", fileName, 1);257 //SetBrowserFeatureControlKey("FEATURE_DOMSTORAGE ", fileName, 1);258 //SetBrowserFeatureControlKey("FEATURE_IVIEWOBJECTDRAW_DMLT9_WITH_GDI ", fileName, 0);259 //SetBrowserFeatureControlKey("FEATURE_NINPUT_LEGACYMODE", fileName, 0);260 //SetBrowserFeatureControlKey("FEATURE_DISABLE_LEGACY_COMPRESSION", fileName, 1);261 //SetBrowserFeatureControlKey("FEATURE_LOCALMACHINE_LOCKDOWN", fileName, 0);262 //SetBrowserFeatureControlKey("FEATURE_BLOCK_LMZ_OBJECT", fileName, 0);263 //SetBrowserFeatureControlKey("FEATURE_BLOCK_LMZ_SCRIPT", fileName, 0);264 //SetBrowserFeatureControlKey("FEATURE_DISABLE_NAVIGATION_SOUNDS", fileName, 1);265 //SetBrowserFeatureControlKey("FEATURE_SCRIPTURL_MITIGATION", fileName, 1);266 //SetBrowserFeatureControlKey("FEATURE_SPELLCHECKING", fileName, 0);267 //SetBrowserFeatureControlKey("FEATURE_STATUS_BAR_THROTTLING", fileName, 1);268 //SetBrowserFeatureControlKey("FEATURE_TABBED_BROWSING", fileName, 1);269 //SetBrowserFeatureControlKey("FEATURE_VALIDATE_NAVIGATE_URL", fileName, 1);270 //SetBrowserFeatureControlKey("FEATURE_WEBOC_DOCUMENT_ZOOM", fileName, 1);271 //SetBrowserFeatureControlKey("FEATURE_WEBOC_POPUPMANAGEMENT", fileName, 0);272 //SetBrowserFeatureControlKey("FEATURE_WEBOC_MOVESIZECHILD", fileName, 1);273 //SetBrowserFeatureControlKey("FEATURE_ADDON_MANAGEMENT", fileName, 0);274 //SetBrowserFeatureControlKey("FEATURE_WEBSOCKET", fileName, 1);275 //SetBrowserFeatureControlKey("FEATURE_WINDOW_RESTRICTIONS ", fileName, 0);276 //SetBrowserFeatureControlKey("FEATURE_XMLHTTP", fileName, 1);277 }278 /// <summary>279 /// http://msdn.microsoft.com/en-us/library/ie/ee330730(v=vs.85).aspx280 /// </summary>281 /// <returns></returns>282 private static uint GetBrowserEmulationMode()283 {284 int browserVersion;285 using (var ieKey = Registry.LocalMachine.OpenSubKey(@"SOFTWARE\Microsoft\Internet Explorer",286 RegistryKeyPermissionCheck.ReadSubTree, System.Security.AccessControl.RegistryRights.QueryValues))287 {288 var version = ieKey.GetValue("svcVersion") ?? ieKey.GetValue("Version");289 if (version == null)290 {291 throw new ApplicationException("Microsoft Internet Explorer is required!");292 }293 int.TryParse(version.ToString().Split(‘.‘)[0], out browserVersion);294 }295 if (browserVersion < 8)296 {297 throw new ApplicationException("Microsoft Internet Explorer 8 is required!");298 }299 switch (browserVersion)300 {301 case 9:302 return 9000;303 case 10:304 return 10000;305 case 11:306 return 11000;307 default:308 return 8000;309 }310 }311 private static void SetBrowserFeatureControlKey(string feature, string appName, uint value)312 {313 using (var key = Registry.CurrentUser.CreateSubKey(314 String.Concat(@"Software\Microsoft\Internet Explorer\Main\FeatureControl\", feature),315 RegistryKeyPermissionCheck.ReadWriteSubTree))316 {317 key.SetValue(appName, value, RegistryValueKind.DWord);318 }319 }320 321 private static void STA_Run(Action<object> func, object state, STAContext context)322 {323 var sta = new Thread(arg =>324 {325 var set = (object[])arg;326 try327 {328 var func2 = (Action<object>)set[0];329 func2(set[1]);330 }331 catch (Exception ex)332 {333 App.LogError(ex, "STA_Run");334 }335 }, 1024 * 512); //1024 * 512, 默认1M336 sta.IsBackground = true;337 sta.SetApartmentState(ApartmentState.STA);338 try339 {340 sta.Start(new object[2] { func, state });341 }342 catch (OutOfMemoryException ex)343 {344 HandleException(ex);345 }346 347 //context._Apartment.Invoke(func, state);348 }349 350 public static void FillAjaxBlock(NameValueCollection form, AjaxBlockEntity[] set)351 {352 Contract.Requires(form != null);353 354 form[AjaxBlockEntity.AjaxBlock] = JsonConvert.SerializeObject(set, Formatting.None);355 }356 #endregion357 358 #region Fields359 private EndPoint _proxyAddr;360 private Lazy<IHttpClient> _lazyClient;361 private CookieContainer _cookieContainer;362 private Action<STAContext, HtmlDocument> _onLoad;363 #endregion364 365 #region Properties366 public int SendReceiveTimeout { get; set; }367 public ushort? RetryCount { get; set; }368 public TimeSpan? RetryWaitDuration { get; set; }369 public bool UseCookies { get; set; }370 public CookieContainer CookieContainer371 {372 get { return _cookieContainer; }373 }374 public string SaveFileDirectory { get; set; }375 /// <summary>376 /// 网页快照大小,Full Screenshot则设置Size.Empty377 /// </summary>378 public Size? Snapshot { get; set; }379 /// <summary>380 /// 供下载使用381 /// </summary>382 internal IHttpClient Client383 {384 get385 {386 var client = _lazyClient.Value;387 client.SendReceiveTimeout = this.SendReceiveTimeout;388 client.RetryCount = this.RetryCount;389 client.RetryWaitDuration = this.RetryWaitDuration;390 client.UseCookies = this.UseCookies;391 client.SaveFileDirectory = this.SaveFileDirectory;392 return client;393 }394 }395 #endregion396 397 #region Constructors398 public HttpBrowser()399 {400 this.SendReceiveTimeout = -1;401 _lazyClient = new Lazy<IHttpClient>(() => new HttpClient(), false);402 _cookieContainer = new CookieContainer();403 this.UseCookies = true;404 }405 /// <summary>406 /// crossLoad中如有跨域交互,请继承扩展IsolateProxy407 /// </summary>408 /// <param name="crossLoad"></param>409 public HttpBrowser(Action<STAContext, HtmlDocument> crossLoad)410 : this()411 {412 _onLoad = crossLoad;413 }414 #endregion415 416 #region Methods417 public void SetProxy(EndPoint address, NetworkCredential credential = null)418 {419 if (credential != null)420 {421 throw new NotSupportedException("credential");422 }423 424 if (IsSpawned)425 {426 _proxyAddr = address;427 }428 else429 {430 #if DEBUG431 App.LogInfo("SetProxy HttpBrowser {0}", address);432 #endif433 if (WinInetInterop.SetConnectionProxy(address.ToString()))434 {435 App.LogInfo("SetProxy HttpBrowser {0} succeed", address);436 }437 }438 }439 internal void RestoreSystemProxy()440 {441 if (IsSpawned)442 {443 _proxyAddr = null;444 }445 else446 {447 #if DEBUG448 App.LogInfo("RestoreSystemProxy HttpBrowser");449 #endif450 if (WinInetInterop.RestoreSystemProxy())451 {452 App.LogInfo("RestoreSystemProxy HttpBrowser succeed");453 }454 }455 }456 457 public string GetHtml(Uri requestUrl, HttpRequestContent content = null)458 {459 if (IsSpawned)460 {461 return SpawnedStart(_proxyAddr, requestUrl, content);462 }463 using (var arg = new STAContext(requestUrl, content))464 {465 arg.WaitHandle = new AutoResetEvent(false);466 this.STA_Run(arg);467 arg.WaitHandle.WaitOne();468 return arg.OuterHtml;469 }470 }471 472 public string GetHtml(Uri requestUrl, AjaxEventEntity local, HttpRequestContent content = null)473 {474 Contract.Requires(requestUrl != null);475 if (local == null)476 {477 return GetHtml(requestUrl, content);478 }479 480 using (var arg = new STAContext(requestUrl, content))481 {482 arg.AjaxEvent = local;483 arg.WaitHandle = new AutoResetEvent(false);484 this.STA_Run(arg);485 arg.WaitHandle.WaitOne();486 return arg.OuterHtml;487 }488 }489 490 public Stream GetStream(Uri requestUrl, HttpRequestContent content = null)491 {492 return this.Client.GetStream(requestUrl, content);493 }494 495 public void DownloadFile(Uri fileUrl, out string fileName)496 {497 this.Client.DownloadFile(fileUrl, out fileName);498 }499 #endregion500 501 #region Hepler502 /// <summary>503 /// 注入Script504 /// </summary>505 /// <param name="document"></param>506 /// <param name="js"></param>507 public void InjectScript(HtmlDocument document, string js)508 {509 Contract.Requires(document != null);510 511 if (!CheckDocument(document.Url))512 {513 App.LogInfo("HttpBrowser InjectScript Cancel");514 return;515 }516 var head = document.GetElementsByTagName("head")[0];517 var script = document.CreateElement("script");518 script.SetAttribute("type", "text/javascript");519 script.SetAttribute("text", js);520 head.AppendChild(script);521 }522 private bool CheckDocument(Uri documentUrl)523 {524 if (documentUrl != null && documentUrl.OriginalString.StartsWith("res://ieframe.dll", StringComparison.OrdinalIgnoreCase))525 {526 App.LogInfo("CheckDocument {0}", documentUrl);527 return false;528 }529 return true;530 }531 532 /// <summary>533 /// 设置ajax参数534 /// </summary>535 /// <param name="browser"></param>536 private void SetAjax(WebBrowser browser, bool isEvent)537 {538 var arg = (STAContext)browser.ObjectForScripting;539 if (arg.AjaxBlocks.IsNullOrEmpty())540 {541 return;542 }543 foreach (var block in arg.AjaxBlocks.Where(p => p.IsEvent == isEvent))544 {545 var node = browser.Document.GetElementById(block.ID);546 if (node == null)547 {548 continue;549 }550 arg.AjaxWaiter.AddCount();551 arg.AjaxMark(node, (sender, e) =>552 {553 node = browser.Document.GetElementById(block.ID);554 if (node == null || block.Text == null555 || (!block.Text.Equals(node.InnerText, StringComparison.OrdinalIgnoreCase)))556 {557 // bug 如果先Signal再AddCount就会出错558 arg.AjaxWaiter.Signal();559 }560 });561 }562 arg.AjaxWaiter.Signal();563 }564 /// <summary>565 /// 等待ajax执行566 /// </summary>567 /// <param name="arg"></param>568 private bool WaitAjax(STAContext arg)569 {570 if (arg.AjaxBlocks.IsNullOrEmpty())571 {572 return false;573 }574 int aTimeout = this.SendReceiveTimeout;575 if (aTimeout <= 0)576 {577 aTimeout = (int)TimeSpan.FromSeconds(60d).TotalMilliseconds;578 }579 if (!arg.AjaxWaiter.Wait(aTimeout))580 {581 App.LogInfo("HttpBrowser Ajax Timeout {0}", arg.RequestUrl);582 return false;583 }584 return true;585 }586 587 private void ProcessAjaxEvent(WebBrowser browser)588 {589 var arg = (STAContext)browser.ObjectForScripting;590 if (arg.AjaxEvent == null || string.IsNullOrEmpty(arg.AjaxEvent.ListenerSelector))591 {592 return;593 }594 595 arg.IsProcessEvent = true;596 if (arg.AjaxEvent.EntryCall && arg.AjaxEvent.FinalCallback != null)597 {598 InvokeHtml(browser);599 arg.AjaxEvent.FinalCallback(arg.OuterHtml);600 }601 object val = browser.Document.InvokeScript("Soubiscbot", new object[] { 0, arg.AjaxEvent.ListenerSelector });602 var set = val.ToString().Split(‘,‘);603 foreach (string id in set)604 {605 var node = browser.Document.GetElementById(id);606 if (node == null)607 {608 continue;609 }610 arg.AjaxWaiter.Reset();611 SetAjax(browser, true);612 node.InvokeMember("click");613 bool isSet = WaitAjax(arg);614 Console.WriteLine("ProcessAjaxEvent isSet={0}", isSet);615 if (arg.AjaxEvent.FinalCallback != null)616 {617 InvokeHtml(browser);618 arg.AjaxEvent.FinalCallback(arg.OuterHtml);619 }620 }621 arg.IsProcessEvent = false;622 }623 624 /// <summary>625 /// 读取页面OuterHtml626 /// </summary>627 /// <param name="browser"></param>628 /// <returns></returns>629 private void InvokeHtml(WebBrowser browser)630 {631 var scripting = (STAContext)browser.ObjectForScripting;632 if (scripting == null)633 {634 throw new InvalidOperationException("InvokeHtml");635 }636 if (!scripting.DoInvokeHtml)637 {638 return;639 }640 scripting.OuterHtml = (string)browser.Document.InvokeScript("Soubiscbot");641 }642 #endregion643 644 #region STAThread645 private void STA_Run(STAContext context)646 {647 context._ReleaseMemory();648 //context._Apartment = new MessageLoopApartment();649 STA_Run(state =>650 {651 var browser = new WebBrowser()652 {653 ScriptErrorsSuppressed = true,654 IsWebBrowserContextMenuEnabled = false,655 ObjectForScripting = state656 };657 browser.Navigating += browser_Navigating;658 browser.DocumentCompleted += browser_DocumentCompleted;659 browser.NewWindow += browser_NewWindow;660 if (this.Snapshot.HasValue)661 {662 browser.ScrollBarsEnabled = false;663 browser.Size = new Size(Screen.PrimaryScreen.WorkingArea.Width, 10240);664 browser.Show();665 }666 else667 {668 browser.Hide();669 }670 var arg = (STAContext)state;671 byte[] postData = http://www.mamicode.com/null;672 string headers = null;673 if (arg.RequestContent != null)674 {675 if (this.UseCookies)676 {677 if (arg.RequestContent.HasCookie)678 {679 _cookieContainer.Add(arg.RequestUrl, arg.RequestContent.Cookies);680 }681 string cookieHeader = arg.RequestContent.Headers[HttpRequestHeader.Cookie];682 if (!string.IsNullOrEmpty(cookieHeader))683 {684 _cookieContainer.SetCookies(arg.RequestUrl, cookieHeader.Replace(‘;‘, ‘,‘));685 arg.RequestContent.Headers.Remove(HttpRequestHeader.Cookie);686 }687 cookieHeader = _cookieContainer.GetCookieHeader(arg.RequestUrl);688 if (cookieHeader.Length > 0)689 {690 arg.RequestContent.Headers[HttpRequestHeader.Cookie] = cookieHeader.Replace(‘,‘, ‘;‘);691 }692 //WinInetInterop.SaveCookies(_cookieContainer, absoluteUri);693 }694 else695 {696 arg.RequestContent.Headers[HttpRequestHeader.Cookie] = string.Empty;697 //WinInetInterop.DeleteCache(WinInetInterop.CacheKind.Cookies);698 }699 if (arg.RequestContent.HasBody)700 {701 arg.RequestContent.Headers[HttpRequestHeader.ContentType] = "application/x-www-form-urlencoded";702 postData =http://www.mamicode.com/ Encoding.UTF8.GetBytes(arg.RequestContent.GetFormString());703 }704 headers = arg.RequestContent.GetHeadersString();705 }706 browser.Navigate(arg.RequestUrl, "_self", postData, headers);707 708 STA_Run(STA_Wait, browser, arg);709 //会阻塞当前线程710 Application.Run();711 }, context, context);712 }713 private void STA_Wait(object state)714 {715 var browser = (WebBrowser)state;716 #if DEBUG717 App.LogInfo("STA_Wait {0}", browser.Url);718 #endif719 var arg = (STAContext)browser.ObjectForScripting;720 try721 {722 int srTimeout = this.SendReceiveTimeout;723 if (srTimeout > -1 && !arg.SendReceiveWaiter.WaitOne(srTimeout))724 {725 //请求超时726 browser.Invoke((Action)(() =>727 {728 if (browser.ReadyState != WebBrowserReadyState.Complete)729 {730 browser.Stop();731 App.LogInfo("HttpBrowser SendReceive Timeout {0}", arg.RequestUrl);732 }733 }));734 }735 WaitAjax(arg);736 }737 catch (Exception ex)738 {739 App.LogError(ex, "HttpBrowser STA_Wait {0}", arg.RequestUrl);740 HandleException(ex);741 }742 }743 744 private void browser_NewWindow(object sender, System.ComponentModel.CancelEventArgs e)745 {746 var browser = (WebBrowser)sender;747 var node = browser.Document.ActiveElement;748 string link;749 if (node != null && !string.IsNullOrEmpty(link = node.GetAttribute("href")))750 {751 e.Cancel = true;752 browser.Navigate(link);753 }754 }755 private void browser_Navigating(object sender, WebBrowserNavigatingEventArgs e)756 {757 var browser = (WebBrowser)sender;758 #if DEBUG759 App.LogInfo("browser_Navigating {0}", browser.Url);760 #endif761 var arg = (STAContext)browser.ObjectForScripting;762 arg.DelayLazyLoad();763 }764 private void browser_DocumentCompleted(object sender, WebBrowserDocumentCompletedEventArgs e)765 {766 var browser = (WebBrowser)sender;767 #if DEBUG768 App.LogInfo("browser_DocumentCompleted {0}", browser.Url);769 #endif770 var arg = (STAContext)browser.ObjectForScripting;771 try772 {773 //e.Url不会变res:// 774 if (!CheckDocument(browser.Url))775 {776 App.LogInfo("HttpBrowser DocumentCompleted Cancel {0}", browser.Url);777 return;778 }779 if (browser.ReadyState != WebBrowserReadyState.Complete)780 {781 return;782 }783 784 //发生redirect或iframe load785 if (browser.Url != e.Url)786 {787 App.LogInfo("HttpBrowser Redirect {0} to {1}", arg.RequestUrl, e.Url);788 }789 if (this.UseCookies)790 {791 WinInetInterop.LoadCookies(_cookieContainer, browser.Document.Url);792 }793 InjectScript(browser.Document, @"if (typeof ($) == ‘undefined‘) {794 var script = document.createElement(‘script‘);795 script.src = http://www.mamicode.com/‘http://libs.baidu.com/jquery/1.9.0/jquery.js‘;>796 document.getElementsByTagName(‘head‘)[0].appendChild(script);797 }798 function Soubiscbot(kind) {799 switch (kind) {800 case 0:801 var set = [];802 $(arguments[1]).each(function (i, o) {803 var me = $(o);804 var id = me.attr(‘id‘);805 if (!id) {806 id = Math.random();807 me.attr(‘id‘, id);808 }809 set[i] = id;810 });811 return set.toString();812 break;813 case 1:814 try {815 return arguments[1]();816 }817 catch (ex) {818 return ex.toString();819 }820 break;821 default:822 return document.documentElement.outerHTML;823 break;824 }825 }");826 827 if (this.SendReceiveTimeout > -1)828 {829 arg.SendReceiveWaiter.Set();830 }831 SetAjax(browser, false);832 if (_onLoad != null)833 {834 _onLoad(arg, browser.Document);835 }836 if (arg.IsRedirect)837 {838 STA_Run(STA_Wait, browser, arg);839 }840 else841 {842 arg.RegisterLazyLoad(x =>843 {844 var b = (WebBrowser)x;845 if (b.IsDisposed)846 {847 return;848 }849 b.Invoke((Action<WebBrowser>)ProcessAjaxEvent, b);850 b.Invoke((Action<object>)Callback, b);851 }, browser);852 }853 }854 catch (Exception ex)855 {856 App.LogError(ex, "HttpBrowser DocumentCompleted RequestUrl={0} BrowserUrl={1}", arg.RequestUrl, browser.Url);857 HandleException(ex);858 }859 }860 861 private static void HandleException(Exception ex)862 {863 if (ex is OutOfMemoryException || ex is AccessViolationException)864 {865 App.LogInfo("HttpBrowser auto exit {0}", ex.HResult);866 Environment.Exit(ex.HResult);867 }868 }869 #endregion870 871 #region Callback872 private void Callback(object state)873 {874 var browser = (WebBrowser)state;875 #if DEBUG876 App.LogInfo("Callback {0}", browser.Url);877 #endif878 var arg = (STAContext)browser.ObjectForScripting;879 if (!Monitor.TryEnter(arg))880 {881 return;882 }883 try884 {885 #warning HACK886 if (this.Snapshot.HasValue)887 {888 Thread.Sleep(4000);889 }890 browser.Invoke((Action)(() =>891 {892 if (this.Snapshot.HasValue)893 {894 //Guid fileID = CryptoManaged.MD5Hash(browser.Url.OriginalString);//browser.Url为ResponseUrl895 Guid fileID = Guid.NewGuid();896 var js = new StringBuilder();897 js.AppendFormat("document.body.setAttribute(‘{0}‘, ‘{1}‘);", Callback_Snapshot, fileID);898 js.Append(@" window.addEventListener(‘load‘, function () {899 window.scrollTo(0, document.documentElement.offsetHeight);900 });901 ");902 browser.Document.InvokeScript("eval", new object[] { js.ToString() });903 string savePath = Path.Combine(this.SaveFileDirectory, string.Format("{0}.png", fileID));904 try905 {906 var shotSize = this.Snapshot.Value =http://www.mamicode.com/= Size.Empty ? browser.Document.Body.ScrollRectangle.Size : this.Snapshot.Value;907 browser.Size = shotSize;908 using (var img = new Bitmap(browser.Width, browser.Height))909 {910 //browser.DrawToBitmap(img, new Rectangle(Point.Empty, img.Size));911 NativeMethods.DrawTo(browser.ActiveXInstance, img, Color.White);912 img.Save(savePath, System.Drawing.Imaging.ImageFormat.Png);913 App.LogInfo("xSnapshot {0} {1}", browser.Url, savePath);914 }915 }916 catch (Exception ex)917 {918 App.LogError(ex, "xSnapshot {0} {1}", browser.Url, savePath);919 }920 }921 InvokeHtml(browser);922 }));923 }924 catch (Exception ex)925 {926 App.LogError(ex, "HttpBrowser Callback {0}", arg.RequestUrl);927 HandleException(ex);928 }929 finally930 {931 Monitor.Exit(arg);932 STA_Exit(browser);933 }934 }935 936 /// <summary>937 /// !重要! 退出STAUI线程938 /// </summary>939 private void STA_Exit(WebBrowser browser)940 {941 #if DEBUG942 App.LogInfo("STA_Exit {0}", browser.Url);943 #endif944 RestoreSystemProxy();945 var arg = (STAContext)browser.ObjectForScripting;946 if (arg.WaitHandle != null)947 {948 arg.WaitHandle.Set();949 }950 try951 {952 browser.Stop();953 arg.AjaxUnmarks();954 //arg._Apartment.Dispose();955 browser.Invoke((Action)(() => Application.ExitThread()));956 browser.Dispose();957 }958 catch (SystemException ex)959 {960 //AccessViolationException961 //InvalidComObjectException962 App.LogError(ex, "HttpBrowser STA_Exit {0}", arg.RequestUrl);963 }964 }965 #endregion966 }967 }
#region Spawned Process public bool IsSpawned { get; set; } internal string SpawnedStart(EndPoint proxy, Uri requestUrl, HttpRequestContent content) {#if DEBUG App.LogInfo("SpawnedStart: Proxy={0}\tUrl={1}", proxy, requestUrl);#endif bool hasValue = http://www.mamicode.com/content != null; var stream = Serializer.Serialize(Tuple.Create(proxy, requestUrl, hasValue ? content.Headers : null, hasValue ? content.Form : null)); RestoreSystemProxy(); string[] args = Environment.GetCommandLineArgs(); string arg = string.Format("x#{0}", Convert.ToBase64String(stream.ToArray())); var proc = Process.Start(new ProcessStartInfo(args[0], arg) { RedirectStandardOutput = true, UseShellExecute = false, }); string html = proc.StandardOutput.ReadToEnd(); if (!proc.WaitForExit(120 * 1000)) { proc.Kill(); } proc.Close(); return html; } public static bool SpawnedMain() { string[] args = Environment.GetCommandLineArgs(); if (!(args.Length > 1 && args[1].StartsWith("x#"))) { return false; } var stream = new MemoryStream(Convert.FromBase64String(args[1].Substring(2))); var arg = (Tuple<EndPoint, Uri, WebHeaderCollection, NameValueCollection>)Serializer.Deserialize(stream); var client = (IHttpClient)new HttpBrowser(); if (arg.Item1 != null) { client.SetProxy(arg.Item1); } string html = client.GetHtml(arg.Item2, new HttpRequestContent() { Headers = arg.Item3, Form = arg.Item4 }); Console.WriteLine(html); return true; } #endregion
声明:以上内容来自用户投稿及互联网公开渠道收集整理发布,本网站不拥有所有权,未作人工编辑处理,也不承担相关法律责任,若内容有误或涉及侵权可进行投诉: 投诉/举报 工作人员会在5个工作日内联系你,一经查实,本站将立刻删除涉嫌侵权内容。