#undef DEBUG
using Microsoft.Win32;
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.Diagnostics;
using System.Diagnostics.Contracts;
using System.Drawing;
using System.IO;
using System.Linq;
using System.Net;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms; namespace AnfleCrawler.Common
{
/// <summary>
/// Chromium / CasperJS + PhantomJS
/// http://pinvoke.net/index.aspx
/// </summary>
public sealed partial class HttpBrowser : IHttpClient
{
#region NestedTypes
[Serializable]
public class AjaxBlockEntity
{
internal const string AjaxBlock = "_AjaxBlock";
public string ID { get; set; }
public string Text { get; set; }
public bool IsEvent { get; set; }
}
public class AjaxEventEntity : MarshalByRefObject
{
public string ListenerSelector { get; set; }
public bool EntryCall { get; set; }
public Action<string> FinalCallback { get; set; }
} [ComVisible(true)]
public sealed class STAContext : Disposable
{
#region Fields
public volatile bool IsRedirect;
//internal MessageLoopApartment _Apartment;
private SynchronizedCollection<Tuple<HtmlElement, EventHandler>> _releaseSet;
private AutoResetEvent _sendReceiveWaiter;
private CountdownEvent _ajaxWaiter;
private System.Threading.Timer _lazyTimer; internal volatile bool DoInvokeHtml;
private volatile string _outerHtml;
#endregion #region Properties
public Uri RequestUrl { get; private set; }
public HttpRequestContent RequestContent { get; private set; }
internal AutoResetEvent WaitHandle { get; set; } internal AutoResetEvent SendReceiveWaiter
{
get
{
if (_sendReceiveWaiter == null)
{
_sendReceiveWaiter = new AutoResetEvent(false);
}
return _sendReceiveWaiter;
}
}
internal AjaxBlockEntity[] AjaxBlocks { get; private set; }
internal CountdownEvent AjaxWaiter
{
get
{
if (_ajaxWaiter == null)
{
_ajaxWaiter = new CountdownEvent();
}
return _ajaxWaiter;
}
}
internal volatile bool IsProcessEvent;
internal AjaxEventEntity AjaxEvent { get; set; } internal string OuterHtml
{
get
{
DoInvokeHtml = true;
return _outerHtml;
}
set
{
_outerHtml = value;
}
}
#endregion #region Constructor
internal STAContext(Uri url, HttpRequestContent content)
{
this.RequestUrl = url;
this.RequestContent = content;
string ablock;
if (this.RequestContent != null && this.RequestContent.Form != null)
{
if (!string.IsNullOrEmpty(ablock = this.RequestContent.Form.Get(AjaxBlockEntity.AjaxBlock)))
{
this.AjaxBlocks = JsonConvert.DeserializeObject<AjaxBlockEntity[]>(ablock);
this.RequestContent.Form.Remove(AjaxBlockEntity.AjaxBlock);
}
}
DoInvokeHtml = true;
} protected override void DisposeInternal(bool disposing)
{
if (disposing)
{
//if (_Apartment != null)
//{
// _Apartment.Dispose();
// _Apartment = null;
//}
if (_lazyTimer != null)
{
_lazyTimer.Dispose();
_lazyTimer = null;
}
if (this.WaitHandle != null)
{
this.WaitHandle.Dispose();
this.WaitHandle = null;
} DisposeObject(_sendReceiveWaiter);
DisposeObject(_ajaxWaiter);
}
}
#endregion #region Methods
public void SetHtml(string html)
{
_outerHtml = html;
DoInvokeHtml = false;
} internal void RegisterLazyLoad(Action<object> func, object state)
{
if (_lazyTimer != null)
{
return;
}
_lazyTimer = new System.Threading.Timer(x => STA_Run(func, x, this), state, , Timeout.Infinite);
}
/// <summary>
/// 另种思路,在每次加载完毕后delay
/// </summary>
internal void DelayLazyLoad()
{
if (_lazyTimer == null)
{
return;
}
_lazyTimer.Change(, Timeout.Infinite);
} /// <summary>
/// STA
/// </summary>
/// <param name="node"></param>
/// <param name="e"></param>
internal void AjaxMark(HtmlElement node, EventHandler e)
{
if (_releaseSet == null)
{
_releaseSet = new SynchronizedCollection<Tuple<HtmlElement, EventHandler>>();
}
var q = from t in _releaseSet
where t.Item1 == node
select t;
if (q.Any())
{
return;
}
_releaseSet.Add(Tuple.Create(node, e));
node.AttachEventHandler("onpropertychange", e);
} /// <summary>
/// STA
/// </summary>
internal void AjaxUnmarks()
{
if (_releaseSet.IsNullOrEmpty())
{
return;
}
foreach (var item in _releaseSet)
{
var node = item.Item1;
node.DetachEventHandler("onpropertychange", item.Item2);
}
_releaseSet = null;
} internal void _ReleaseMemory()
{
return;
#if !DEBUG
var proc = Process.GetCurrentProcess();
//128M
if (proc.PrivateMemorySize64 <= 134217728L)
{
return;
}
base.ReleaseMemory();
#endif
}
#endregion
}
#endregion #region Static
public const string Callback_Snapshot = "_xSnapshot"; static HttpBrowser()
{
SetBrowserFeatureControl();
//NativeMethods.SetErrorMode(NativeMethods.ErrorModes.SYSTEM_DEFAULT);
NativeMethods.SetErrorMode(NativeMethods.ErrorModes.SEM_FAILCRITICALERRORS | NativeMethods.ErrorModes.SEM_NOGPFAULTERRORBOX | NativeMethods.ErrorModes.SEM_NOOPENFILEERRORBOX);
} /// <summary>
/// http://msdn.microsoft.com/en-us/library/ee330720(v=vs.85).aspx
/// </summary>
private static void SetBrowserFeatureControl()
{
// FeatureControl settings are per-process
string fileName = Path.GetFileName(Process.GetCurrentProcess().MainModule.FileName);
string[] skip = new string[] { "devenv.exe", "XDesProc.exe" };
if (skip.Any(p => p.Equals(fileName, StringComparison.OrdinalIgnoreCase)))
{
return;
} SetBrowserFeatureControlKey("FEATURE_BROWSER_EMULATION", fileName, GetBrowserEmulationMode());
SetBrowserFeatureControlKey("FEATURE_MANAGE_SCRIPT_CIRCULAR_REFS", fileName, );
//SetBrowserFeatureControlKey("FEATURE_GPU_RENDERING ", fileName, 1);
//SetBrowserFeatureControlKey("FEATURE_AJAX_CONNECTIONEVENTS", fileName, 1);
//SetBrowserFeatureControlKey("FEATURE_ENABLE_CLIPCHILDREN_OPTIMIZATION", fileName, 1);
//SetBrowserFeatureControlKey("FEATURE_DOMSTORAGE ", fileName, 1);
//SetBrowserFeatureControlKey("FEATURE_IVIEWOBJECTDRAW_DMLT9_WITH_GDI ", fileName, 0);
//SetBrowserFeatureControlKey("FEATURE_NINPUT_LEGACYMODE", fileName, 0);
//SetBrowserFeatureControlKey("FEATURE_DISABLE_LEGACY_COMPRESSION", fileName, 1);
//SetBrowserFeatureControlKey("FEATURE_LOCALMACHINE_LOCKDOWN", fileName, 0);
//SetBrowserFeatureControlKey("FEATURE_BLOCK_LMZ_OBJECT", fileName, 0);
//SetBrowserFeatureControlKey("FEATURE_BLOCK_LMZ_SCRIPT", fileName, 0);
//SetBrowserFeatureControlKey("FEATURE_DISABLE_NAVIGATION_SOUNDS", fileName, 1);
//SetBrowserFeatureControlKey("FEATURE_SCRIPTURL_MITIGATION", fileName, 1);
//SetBrowserFeatureControlKey("FEATURE_SPELLCHECKING", fileName, 0);
//SetBrowserFeatureControlKey("FEATURE_STATUS_BAR_THROTTLING", fileName, 1);
//SetBrowserFeatureControlKey("FEATURE_TABBED_BROWSING", fileName, 1);
//SetBrowserFeatureControlKey("FEATURE_VALIDATE_NAVIGATE_URL", fileName, 1);
//SetBrowserFeatureControlKey("FEATURE_WEBOC_DOCUMENT_ZOOM", fileName, 1);
//SetBrowserFeatureControlKey("FEATURE_WEBOC_POPUPMANAGEMENT", fileName, 0);
//SetBrowserFeatureControlKey("FEATURE_WEBOC_MOVESIZECHILD", fileName, 1);
//SetBrowserFeatureControlKey("FEATURE_ADDON_MANAGEMENT", fileName, 0);
//SetBrowserFeatureControlKey("FEATURE_WEBSOCKET", fileName, 1);
//SetBrowserFeatureControlKey("FEATURE_WINDOW_RESTRICTIONS ", fileName, 0);
//SetBrowserFeatureControlKey("FEATURE_XMLHTTP", fileName, 1);
}
/// <summary>
/// http://msdn.microsoft.com/en-us/library/ie/ee330730(v=vs.85).aspx
/// </summary>
/// <returns></returns>
private static uint GetBrowserEmulationMode()
{
int browserVersion;
using (var ieKey = Registry.LocalMachine.OpenSubKey(@"SOFTWARE\Microsoft\Internet Explorer",
RegistryKeyPermissionCheck.ReadSubTree, System.Security.AccessControl.RegistryRights.QueryValues))
{
var version = ieKey.GetValue("svcVersion") ?? ieKey.GetValue("Version");
if (version == null)
{
throw new ApplicationException("Microsoft Internet Explorer is required!");
}
int.TryParse(version.ToString().Split('.')[], out browserVersion);
}
if (browserVersion < )
{
throw new ApplicationException("Microsoft Internet Explorer 8 is required!");
}
switch (browserVersion)
{
case :
return ;
case :
return ;
case :
return ;
default:
return ;
}
}
private static void SetBrowserFeatureControlKey(string feature, string appName, uint value)
{
using (var key = Registry.CurrentUser.CreateSubKey(
String.Concat(@"Software\Microsoft\Internet Explorer\Main\FeatureControl\", feature),
RegistryKeyPermissionCheck.ReadWriteSubTree))
{
key.SetValue(appName, value, RegistryValueKind.DWord);
}
} private static void STA_Run(Action<object> func, object state, STAContext context)
{
var sta = new Thread(arg =>
{
var set = (object[])arg;
try
{
var func2 = (Action<object>)set[];
func2(set[]);
}
catch (Exception ex)
{
App.LogError(ex, "STA_Run");
}
}, * ); //1024 * 512, 默认1M
sta.IsBackground = true;
sta.SetApartmentState(ApartmentState.STA);
try
{
sta.Start(new object[] { func, state });
}
catch (OutOfMemoryException ex)
{
HandleException(ex);
} //context._Apartment.Invoke(func, state);
} public static void FillAjaxBlock(NameValueCollection form, AjaxBlockEntity[] set)
{
Contract.Requires(form != null); form[AjaxBlockEntity.AjaxBlock] = JsonConvert.SerializeObject(set, Formatting.None);
}
#endregion #region Fields
private EndPoint _proxyAddr;
private Lazy<IHttpClient> _lazyClient;
private CookieContainer _cookieContainer;
private Action<STAContext, HtmlDocument> _onLoad;
#endregion #region Properties
public int SendReceiveTimeout { get; set; }
public ushort? RetryCount { get; set; }
public TimeSpan? RetryWaitDuration { get; set; }
public bool UseCookies { get; set; }
public CookieContainer CookieContainer
{
get { return _cookieContainer; }
}
public string SaveFileDirectory { get; set; }
/// <summary>
/// 网页快照大小,Full Screenshot则设置Size.Empty
/// </summary>
public Size? Snapshot { get; set; }
/// <summary>
/// 供下载使用
/// </summary>
internal IHttpClient Client
{
get
{
var client = _lazyClient.Value;
client.SendReceiveTimeout = this.SendReceiveTimeout;
client.RetryCount = this.RetryCount;
client.RetryWaitDuration = this.RetryWaitDuration;
client.UseCookies = this.UseCookies;
client.SaveFileDirectory = this.SaveFileDirectory;
return client;
}
}
#endregion #region Constructors
public HttpBrowser()
{
this.SendReceiveTimeout = -;
_lazyClient = new Lazy<IHttpClient>(() => new HttpClient(), false);
_cookieContainer = new CookieContainer();
this.UseCookies = true;
}
/// <summary>
/// crossLoad中如有跨域交互,请继承扩展IsolateProxy
/// </summary>
/// <param name="crossLoad"></param>
public HttpBrowser(Action<STAContext, HtmlDocument> crossLoad)
: this()
{
_onLoad = crossLoad;
}
#endregion #region Methods
public void SetProxy(EndPoint address, NetworkCredential credential = null)
{
if (credential != null)
{
throw new NotSupportedException("credential");
} if (IsSpawned)
{
_proxyAddr = address;
}
else
{
#if DEBUG
App.LogInfo("SetProxy HttpBrowser {0}", address);
#endif
if (WinInetInterop.SetConnectionProxy(address.ToString()))
{
App.LogInfo("SetProxy HttpBrowser {0} succeed", address);
}
}
}
internal void RestoreSystemProxy()
{
if (IsSpawned)
{
_proxyAddr = null;
}
else
{
#if DEBUG
App.LogInfo("RestoreSystemProxy HttpBrowser");
#endif
if (WinInetInterop.RestoreSystemProxy())
{
App.LogInfo("RestoreSystemProxy HttpBrowser succeed");
}
}
} public string GetHtml(Uri requestUrl, HttpRequestContent content = null)
{
if (IsSpawned)
{
return SpawnedStart(_proxyAddr, requestUrl, content);
}
using (var arg = new STAContext(requestUrl, content))
{
arg.WaitHandle = new AutoResetEvent(false);
this.STA_Run(arg);
arg.WaitHandle.WaitOne();
return arg.OuterHtml;
}
} public string GetHtml(Uri requestUrl, AjaxEventEntity local, HttpRequestContent content = null)
{
Contract.Requires(requestUrl != null);
if (local == null)
{
return GetHtml(requestUrl, content);
} using (var arg = new STAContext(requestUrl, content))
{
arg.AjaxEvent = local;
arg.WaitHandle = new AutoResetEvent(false);
this.STA_Run(arg);
arg.WaitHandle.WaitOne();
return arg.OuterHtml;
}
} public Stream GetStream(Uri requestUrl, HttpRequestContent content = null)
{
return this.Client.GetStream(requestUrl, content);
} public void DownloadFile(Uri fileUrl, out string fileName)
{
this.Client.DownloadFile(fileUrl, out fileName);
}
#endregion #region Hepler
/// <summary>
/// 注入Script
/// </summary>
/// <param name="document"></param>
/// <param name="js"></param>
public void InjectScript(HtmlDocument document, string js)
{
Contract.Requires(document != null); if (!CheckDocument(document.Url))
{
App.LogInfo("HttpBrowser InjectScript Cancel");
return;
}
var head = document.GetElementsByTagName("head")[];
var script = document.CreateElement("script");
script.SetAttribute("type", "text/javascript");
script.SetAttribute("text", js);
head.AppendChild(script);
}
private bool CheckDocument(Uri documentUrl)
{
if (documentUrl != null && documentUrl.OriginalString.StartsWith("res://ieframe.dll", StringComparison.OrdinalIgnoreCase))
{
App.LogInfo("CheckDocument {0}", documentUrl);
return false;
}
return true;
} /// <summary>
/// 设置ajax参数
/// </summary>
/// <param name="browser"></param>
private void SetAjax(WebBrowser browser, bool isEvent)
{
var arg = (STAContext)browser.ObjectForScripting;
if (arg.AjaxBlocks.IsNullOrEmpty())
{
return;
}
foreach (var block in arg.AjaxBlocks.Where(p => p.IsEvent == isEvent))
{
var node = browser.Document.GetElementById(block.ID);
if (node == null)
{
continue;
}
arg.AjaxWaiter.AddCount();
arg.AjaxMark(node, (sender, e) =>
{
node = browser.Document.GetElementById(block.ID);
if (node == null || block.Text == null
|| (!block.Text.Equals(node.InnerText, StringComparison.OrdinalIgnoreCase)))
{
// bug 如果先Signal再AddCount就会出错
arg.AjaxWaiter.Signal();
}
});
}
arg.AjaxWaiter.Signal();
}
/// <summary>
/// 等待ajax执行
/// </summary>
/// <param name="arg"></param>
private bool WaitAjax(STAContext arg)
{
if (arg.AjaxBlocks.IsNullOrEmpty())
{
return false;
}
int aTimeout = this.SendReceiveTimeout;
if (aTimeout <= )
{
aTimeout = (int)TimeSpan.FromSeconds(60d).TotalMilliseconds;
}
if (!arg.AjaxWaiter.Wait(aTimeout))
{
App.LogInfo("HttpBrowser Ajax Timeout {0}", arg.RequestUrl);
return false;
}
return true;
} private void ProcessAjaxEvent(WebBrowser browser)
{
var arg = (STAContext)browser.ObjectForScripting;
if (arg.AjaxEvent == null || string.IsNullOrEmpty(arg.AjaxEvent.ListenerSelector))
{
return;
} arg.IsProcessEvent = true;
if (arg.AjaxEvent.EntryCall && arg.AjaxEvent.FinalCallback != null)
{
InvokeHtml(browser);
arg.AjaxEvent.FinalCallback(arg.OuterHtml);
}
object val = browser.Document.InvokeScript("Soubiscbot", new object[] { , arg.AjaxEvent.ListenerSelector });
var set = val.ToString().Split(',');
foreach (string id in set)
{
var node = browser.Document.GetElementById(id);
if (node == null)
{
continue;
}
arg.AjaxWaiter.Reset();
SetAjax(browser, true);
node.InvokeMember("click");
bool isSet = WaitAjax(arg);
Console.WriteLine("ProcessAjaxEvent isSet={0}", isSet);
if (arg.AjaxEvent.FinalCallback != null)
{
InvokeHtml(browser);
arg.AjaxEvent.FinalCallback(arg.OuterHtml);
}
}
arg.IsProcessEvent = false;
} /// <summary>
/// 读取页面OuterHtml
/// </summary>
/// <param name="browser"></param>
/// <returns></returns>
private void InvokeHtml(WebBrowser browser)
{
var scripting = (STAContext)browser.ObjectForScripting;
if (scripting == null)
{
throw new InvalidOperationException("InvokeHtml");
}
if (!scripting.DoInvokeHtml)
{
return;
}
scripting.OuterHtml = (string)browser.Document.InvokeScript("Soubiscbot");
}
#endregion #region STAThread
private void STA_Run(STAContext context)
{
context._ReleaseMemory();
//context._Apartment = new MessageLoopApartment();
STA_Run(state =>
{
var browser = new WebBrowser()
{
ScriptErrorsSuppressed = true,
IsWebBrowserContextMenuEnabled = false,
ObjectForScripting = state
};
browser.Navigating += browser_Navigating;
browser.DocumentCompleted += browser_DocumentCompleted;
browser.NewWindow += browser_NewWindow;
if (this.Snapshot.HasValue)
{
browser.ScrollBarsEnabled = false;
browser.Size = new Size(Screen.PrimaryScreen.WorkingArea.Width, );
browser.Show();
}
else
{
browser.Hide();
}
var arg = (STAContext)state;
byte[] postData = null;
string headers = null;
if (arg.RequestContent != null)
{
if (this.UseCookies)
{
if (arg.RequestContent.HasCookie)
{
_cookieContainer.Add(arg.RequestUrl, arg.RequestContent.Cookies);
}
string cookieHeader = arg.RequestContent.Headers[HttpRequestHeader.Cookie];
if (!string.IsNullOrEmpty(cookieHeader))
{
_cookieContainer.SetCookies(arg.RequestUrl, cookieHeader.Replace(';', ','));
arg.RequestContent.Headers.Remove(HttpRequestHeader.Cookie);
}
cookieHeader = _cookieContainer.GetCookieHeader(arg.RequestUrl);
if (cookieHeader.Length > )
{
arg.RequestContent.Headers[HttpRequestHeader.Cookie] = cookieHeader.Replace(',', ';');
}
//WinInetInterop.SaveCookies(_cookieContainer, absoluteUri);
}
else
{
arg.RequestContent.Headers[HttpRequestHeader.Cookie] = string.Empty;
//WinInetInterop.DeleteCache(WinInetInterop.CacheKind.Cookies);
}
if (arg.RequestContent.HasBody)
{
arg.RequestContent.Headers[HttpRequestHeader.ContentType] = "application/x-www-form-urlencoded";
postData = Encoding.UTF8.GetBytes(arg.RequestContent.GetFormString());
}
headers = arg.RequestContent.GetHeadersString();
}
browser.Navigate(arg.RequestUrl, "_self", postData, headers); STA_Run(STA_Wait, browser, arg);
//会阻塞当前线程
Application.Run();
}, context, context);
}
private void STA_Wait(object state)
{
var browser = (WebBrowser)state;
#if DEBUG
App.LogInfo("STA_Wait {0}", browser.Url);
#endif
var arg = (STAContext)browser.ObjectForScripting;
try
{
int srTimeout = this.SendReceiveTimeout;
if (srTimeout > - && !arg.SendReceiveWaiter.WaitOne(srTimeout))
{
//请求超时
browser.Invoke((Action)(() =>
{
if (browser.ReadyState != WebBrowserReadyState.Complete)
{
browser.Stop();
App.LogInfo("HttpBrowser SendReceive Timeout {0}", arg.RequestUrl);
}
}));
}
WaitAjax(arg);
}
catch (Exception ex)
{
App.LogError(ex, "HttpBrowser STA_Wait {0}", arg.RequestUrl);
HandleException(ex);
}
} private void browser_NewWindow(object sender, System.ComponentModel.CancelEventArgs e)
{
var browser = (WebBrowser)sender;
var node = browser.Document.ActiveElement;
string link;
if (node != null && !string.IsNullOrEmpty(link = node.GetAttribute("href")))
{
e.Cancel = true;
browser.Navigate(link);
}
}
private void browser_Navigating(object sender, WebBrowserNavigatingEventArgs e)
{
var browser = (WebBrowser)sender;
#if DEBUG
App.LogInfo("browser_Navigating {0}", browser.Url);
#endif
var arg = (STAContext)browser.ObjectForScripting;
arg.DelayLazyLoad();
}
private void browser_DocumentCompleted(object sender, WebBrowserDocumentCompletedEventArgs e)
{
var browser = (WebBrowser)sender;
#if DEBUG
App.LogInfo("browser_DocumentCompleted {0}", browser.Url);
#endif
var arg = (STAContext)browser.ObjectForScripting;
try
{
//e.Url不会变res://
if (!CheckDocument(browser.Url))
{
App.LogInfo("HttpBrowser DocumentCompleted Cancel {0}", browser.Url);
return;
}
if (browser.ReadyState != WebBrowserReadyState.Complete)
{
return;
} //发生redirect或iframe load
if (browser.Url != e.Url)
{
App.LogInfo("HttpBrowser Redirect {0} to {1}", arg.RequestUrl, e.Url);
}
if (this.UseCookies)
{
WinInetInterop.LoadCookies(_cookieContainer, browser.Document.Url);
}
InjectScript(browser.Document, @"if (typeof ($) == 'undefined') {
var script = document.createElement('script');
script.src = 'http://libs.baidu.com/jquery/1.9.0/jquery.js';
document.getElementsByTagName('head')[0].appendChild(script);
}
function Soubiscbot(kind) {
switch (kind) {
case 0:
var set = [];
$(arguments[1]).each(function (i, o) {
var me = $(o);
var id = me.attr('id');
if (!id) {
id = Math.random();
me.attr('id', id);
}
set[i] = id;
});
return set.toString();
break;
case 1:
try {
return arguments[1]();
}
catch (ex) {
return ex.toString();
}
break;
default:
return document.documentElement.outerHTML;
break;
}
}"); if (this.SendReceiveTimeout > -)
{
arg.SendReceiveWaiter.Set();
}
SetAjax(browser, false);
if (_onLoad != null)
{
_onLoad(arg, browser.Document);
}
if (arg.IsRedirect)
{
STA_Run(STA_Wait, browser, arg);
}
else
{
arg.RegisterLazyLoad(x =>
{
var b = (WebBrowser)x;
if (b.IsDisposed)
{
return;
}
b.Invoke((Action<WebBrowser>)ProcessAjaxEvent, b);
b.Invoke((Action<object>)Callback, b);
}, browser);
}
}
catch (Exception ex)
{
App.LogError(ex, "HttpBrowser DocumentCompleted RequestUrl={0} BrowserUrl={1}", arg.RequestUrl, browser.Url);
HandleException(ex);
}
} private static void HandleException(Exception ex)
{
if (ex is OutOfMemoryException || ex is AccessViolationException)
{
App.LogInfo("HttpBrowser auto exit {0}", ex.HResult);
Environment.Exit(ex.HResult);
}
}
#endregion #region Callback
private void Callback(object state)
{
var browser = (WebBrowser)state;
#if DEBUG
App.LogInfo("Callback {0}", browser.Url);
#endif
var arg = (STAContext)browser.ObjectForScripting;
if (!Monitor.TryEnter(arg))
{
return;
}
try
{
#warning HACK
if (this.Snapshot.HasValue)
{
Thread.Sleep();
}
browser.Invoke((Action)(() =>
{
if (this.Snapshot.HasValue)
{
//Guid fileID = CryptoManaged.MD5Hash(browser.Url.OriginalString);//browser.Url为ResponseUrl
Guid fileID = Guid.NewGuid();
var js = new StringBuilder();
js.AppendFormat("document.body.setAttribute('{0}', '{1}');", Callback_Snapshot, fileID);
js.Append(@" window.addEventListener('load', function () {
window.scrollTo(0, document.documentElement.offsetHeight);
});
");
browser.Document.InvokeScript("eval", new object[] { js.ToString() });
string savePath = Path.Combine(this.SaveFileDirectory, string.Format("{0}.png", fileID));
try
{
var shotSize = this.Snapshot.Value == Size.Empty ? browser.Document.Body.ScrollRectangle.Size : this.Snapshot.Value;
browser.Size = shotSize;
using (var img = new Bitmap(browser.Width, browser.Height))
{
//browser.DrawToBitmap(img, new Rectangle(Point.Empty, img.Size));
NativeMethods.DrawTo(browser.ActiveXInstance, img, Color.White);
img.Save(savePath, System.Drawing.Imaging.ImageFormat.Png);
App.LogInfo("xSnapshot {0} {1}", browser.Url, savePath);
}
}
catch (Exception ex)
{
App.LogError(ex, "xSnapshot {0} {1}", browser.Url, savePath);
}
}
InvokeHtml(browser);
}));
}
catch (Exception ex)
{
App.LogError(ex, "HttpBrowser Callback {0}", arg.RequestUrl);
HandleException(ex);
}
finally
{
Monitor.Exit(arg);
STA_Exit(browser);
}
} /// <summary>
/// !重要! 退出STAUI线程
/// </summary>
private void STA_Exit(WebBrowser browser)
{
#if DEBUG
App.LogInfo("STA_Exit {0}", browser.Url);
#endif
RestoreSystemProxy();
var arg = (STAContext)browser.ObjectForScripting;
if (arg.WaitHandle != null)
{
arg.WaitHandle.Set();
}
try
{
browser.Stop();
arg.AjaxUnmarks();
//arg._Apartment.Dispose();
browser.Invoke((Action)(() => Application.ExitThread()));
browser.Dispose();
}
catch (SystemException ex)
{
//AccessViolationException
//InvalidComObjectException
App.LogError(ex, "HttpBrowser STA_Exit {0}", arg.RequestUrl);
}
}
#endregion
}
}

HttpBrowser

#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 = 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[], arg)
{
RedirectStandardOutput = true,
UseShellExecute = false,
});
string html = proc.StandardOutput.ReadToEnd();
if (!proc.WaitForExit( * ))
{
proc.Kill();
}
proc.Close();
return html;
} public static bool SpawnedMain()
{
string[] args = Environment.GetCommandLineArgs();
if (!(args.Length > && args[].StartsWith("x#")))
{
return false;
}
var stream = new MemoryStream(Convert.FromBase64String(args[].Substring()));
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

C# HttpBrowser 跨进程访问,解决内存泄露问题的更多相关文章

  1. 在C#或者SWT上跨进程访问SWT控件的问题

    可能为了进程安全,无论是C#的Form还是Eclipse的SWT,都不允许跨进程访问控件. 通俗一点说就是: A进程创建了控件Widget,若想在B进程中访问控件Widget就会报错,必须在创建Wid ...

  2. 跨进程访问VCL的一个用例(Delphi6、TurboDelphi测试通过)

    Controls.pas单元中有一个FindControl函数,通过句柄获得对应的TWinControl对象. function FindControl(Handle: HWnd): TWinCont ...

  3. Ajax跨域访问解决办法

    方法1. jsonp实现ajax跨域访问示例 jsp代码: <body> <input type="button" onclick="testJsonp ...

  4. Delphi跨进程访问DBGRID

    要想跨进程访问DBGRID,貌似只能用HOOK,写一个DLL想办法注入到目标进程.注入成功后,使DLL与目标进程在同一进程空间中(其内有一些细节问题,请参见代码),这时可以访问目标进程的VCL组件.并 ...

  5. Android开发过程中使用弱引用解决内存泄露的习惯

    Java虽然有垃圾回收,但是仍然存在内存泄露,比如静态变量.缓存或其他长生命周期的对象引用了其他对象,这些被引用的对象就会长期不能被GC释放,导致内存泄露. 弱引用(WeakReference)是解决 ...

  6. As.net WebAPI CORS, 开启跨源访问,解决错误No 'Access-Control-Allow-Origin' header is present on the requested resource

    默认情况下ajax请求是有同源策略,限制了不同域请求的响应. 例子:http://localhost:23160/HtmlPage.html 请求不同源API http://localhost:228 ...

  7. [Unity WWW] 跨域访问解决方法

    什么是跨域访问 域(Domain)是Windows网络中独立运行的单位,域之间相互访问则需要建立信任关系(即Trust Relation).信任关系是连接在域与域之间的桥梁.当一个域与其他域建立了信任 ...

  8. Instruments检测解决内存泄露以及进行性能测试

    1.启动Xcode自带的Instruments.这里有两种方法启动. 方法一: 方法二: 2.选择Leaks选项.(该选项用来进行内存泄漏检测) 说明: Leaks:找到引发内存泄漏的起点. Time ...

  9. ASP.net Web API允许跨域访问解决办法

    来源 http://blog.csdn.net/wxg_kingwolfmsncn/article/details/48545099 遇到此跨域访问问题,解决办法如下:   方法一:   1. 在we ...

随机推荐

  1. 【BZOJ 3295】动态逆序对 - 分块+树状数组

    题目描述 给定一个1~n的序列,然后m次删除元素,每次删除之前询问逆序对的个数. 分析:分块+树状数组 (PS:本题的CDQ分治解法见下一篇) 首先将序列分成T块,每一块开一个树状数组,并且先把最初的 ...

  2. 扩展Date的format方法--格式化日期时间

    Date.prototype.format = function (format) { var o = { "M+": this.getMonth() + 1, "d+& ...

  3. JavaScript学习笔记(十二) 回调模式(Callback Pattern)

    函数就是对象,所以他们可以作为一个参数传递给其它函数: 当你将introduceBugs()作为一个参数传递给writeCode(),然后在某个时间点,writeCode()有可能执行(调用)intr ...

  4. 【转】HTML, CSS和Javascript调试入门

    转 http://www.cnblogs.com/PurpleTide/archive/2011/11/25/2262269.html HTML, CSS和Javascript调试入门 本文介绍一些入 ...

  5. socket头文件

    一. 三种类型的套接字:1.流式套接字(SOCKET_STREAM)    提供面向连接的可靠的数据传输服务.数据被看作是字节流,无长度限制.例如FTP协议就采用这种.2.数据报式套接字(SOCKET ...

  6. BluetoothAdapter.LeScanCallback 参考文档

      BluetoothAdapter.LeScanCallback 参考文档   [翻译自: android开发文档] Class Overview:回调接口被用于传输LE扫描后的结果; 详情参看: ...

  7. hdu 4405Aeroplane chess(概率DP)

    Aeroplane chess Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others) ...

  8. 获取txt文件指定行内容

    #!/usr/bin/python num=0; ni=open("C:\Python34\ceshi.txt") for line in ni: num=num+1;  #表示行 ...

  9. ADO.NET事务处理,初始回调函数,多张表的数据在同一个DataGridView中展示

    执行ADO.NET事务包含四个步骤,接下来以Transaction对象为例介绍. (1)调用SQLConnection对象的BeginTransaction()方法,创建一个SQLTransactio ...

  10. 减少HTTP请求之将图片转成二进制并生成Base64编码,可以在网页中通过url查看图片(大型网站优化技术)

    在网站开发过程中,对于页面的加载效率一般都想尽办法求快.那么,怎么让才能更快呢?减少页面请求 是一个优化页面加载速度很好的方法.上一篇博文我们讲解了 “利用将小图标合成一张背景图来减少HTTP请求”, ...