#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. windows下重新安装TCP/IP协议栈

    一.windows重装TCP/IP协议     前两天在windows下安装开发环境的时候,把系统的TCP/IP协议栈给搞跪了,导致系统无法ping localhost.无法在程序中创建socket等 ...

  2. vs调试 LINK : fatal error LNK1104 ...exe

    出现错误 LINK : fatal error LNK1104  ...exe (1)任务管理器中杀死...exe (2)此工程是复制过来的,但之前的已经装入内存,所以不能打开.重启VS即可.

  3. Element can be click when out of view

    WebDriver can't action the element when out of view Webdriver can't action the element when the elem ...

  4. Linux命令行下编译Android NDK的示例代码

    这几天琢磨写一个Android的Runtime用来加速HTML5 Canvas,让GameBuilder+CanTK 不但开发速度快,运行速度也能接近原生应用.所以花了点时间研究 Android ND ...

  5. java 多线程8(join)

    join():  加入.一个线程如果执行了join语句,那么就有新的线程加入,执行该语ij 句的线程必须要让步给新加入的线程先完成任务,然后才能继续执行. import java.util.Scann ...

  6. 例题:计算运费。c#语言基础,比较简单。看代码输入格式和方法。同样方法可以做一个 出租车打车的程序

    while (true) { Console.WriteLine("请输入行李重量"); double k = Convert.ToDouble(Console .ReadLine ...

  7. mysql通过binlog日志来恢复数据

    简介 在生产的过程中有这么一个业务场景:比如我在2016-11-19 09:30:00 通过mysqldump的方式备份了数据库,但是在2016-11-19 10:30:00的时候数据库崩溃了,如果通 ...

  8. 《Java程序设计》第三周学习总结

    20145224-陈颢文 <Java程序设计>第三周学习总结 教材学习内容总结 一.定义类: ·类定义时使用class关键字,要对类中变量(值域成员/对象数据成员)行类型声明. class ...

  9. css -- 题目汇总

    1.描述下浮动和它的工作原理.模块浮动,使其脱离文档流,并且生成一个块级框.(所以父级撑不开就得到了很好的解释) 2.清除浮动的方法有那些,分别适用于什么情形.clear  ,  父级元素overfl ...

  10. WindowsService(Windows服务)开发步骤附Demo 【转】

    转http://www.cnblogs.com/moretry/p/4149489.html 1.打开VS,新建项目,选择Windows服务,然后设置目录及项目名称后点击确定. 2.展开Service ...