I want to be able to launch multiple browser windows and get the html from them and interact with them like I can with the .net web browser control. The problem is the c# web browser control requires STA threading and that makes my program slower and error prone. Is their a way I can talk to the real IE browser or even Chrome, Firefox etc and do this?
I know there is paid third party solutions and stuff but I was wondering if anybody knew of any free libraries that are reliable and work. I can't use HttpWebRequest for what I am doing, so don't suggest that please.
I will be running on Windows and using C#.
I have used WebKit.NET. Its free and works great (supports flash, css, renders correctly, has a javascript engine). It may or may not be to extensive for what you are doing.
http://webkitdotnet.sourceforge.net/
There are plenty of guides, and the demo has a fully featured tab system.
EDIT: I was fooling around with it, and made an enhanced version of the demo
Awesomeium is a Webkit/Chromium (Chrome) based browser you can talk to via a .NET wrapper, much like the WebBrowser control. I'm not sure on the STA threading requirement though.
As for STA Threading making your application "slower and error prone" -- This sounds odd, perhaps you should investigate solving this.
For IE (only). Free:
Microsoft Internet Controls
using SHDocVw;
Microsoft HTML Object Library
using mshtml;
With these you can do things like
foreach (InternetExplorer brIns in _allWindows)
{
var htmlDoc = brIns.Document as HTMLDocument;
if (htmlDoc != null && htmlDoc.all.item(elementName) != null)
{
var elem = htmlDoc.all.item(elementName) as HTMLInputElement;
if (elem != null && attributeName == null)
{
_ieCurrentDoc = htmlDoc;
_currentHtml = htmlDoc.documentElement.outerHTML;
return true;
}
if (elem != null && elem.getAttribute(attributeName) != null)
{
_ieCurrentDoc = htmlDoc;
_currentHtml = htmlDoc.documentElement.outerHTML;
return true;
}
}
}
Or another webbrowser control: http://code.google.com/p/geckofx/
Embeding Firefox Brower In C# Using GeckoFX
Related
So the title says it all, I would like C# code (so please, PLEASE make sure it isn't Visual Basic code). And that is all I want to ask. I have tried the web browser built in to the .NET framework, but it looks like some old version of IE (if I am right or not). And if you answered, well thanks I guess! I need this for a small project where a bot would just log on to a website (its a base for future projects).
By default it's IE7. You can bang a registry entry in to make it later:
public static void EnsureBrowserEmulationEnabled(string exename = "YourAppName.exe", bool uninstall = false)
{
try
{
using (
var rk = Registry.CurrentUser.OpenSubKey(
#"SOFTWARE\Microsoft\Internet Explorer\Main\FeatureControl\FEATURE_BROWSER_EMULATION", true)
)
{
if (!uninstall)
{
dynamic value = rk.GetValue(exename);
if (value == null)
rk.SetValue(exename, (uint)11001, RegistryValueKind.DWord);
}
else
rk.DeleteValue(exename);
}
}
catch
{
}
}
Code courtesy of this blog
The values you can use in place of 11001 can be found in MSDN
Alternatively; can you do what you want by using WebClient/HttpWebRequest rather than poking at a web browser control to navigate around? Or can you find some web service/api version of the site that will respond with JSON rather than trying to manipulate html?
I was mildly curious why you'd care what a page looks like if it's a bot that is using it, but perhaps you're hitting a "your IE is too old" from the server..
I want to click on link after navigating to a website
webKitBrowser1.Navigate("http://www.somesite.com");
How to click on a link on this website assuming that the link's id is lnkId ?
Go to Google
In the default browser control that comes with Visual Studio, I can do that using the code below :
foreach (HtmlElement el in webBrowser1.Document.GetElementTagName("a")) {
if (el.GetAttribute("id") == "lnkId") {
el.InvokeMember("click");
}
}
What is the equivalent of the code above when I'm using WebkitDotNet control?
As the WebKit doesn't provide a Click() event (see here for details), you cannot do that in the above way. But a small trick may work as an equivalent of the original winforms way as below:
foreach (Node el in webKitBrowser1.Document.GetElementsByTagName("a"))
{
if (((Element) el).GetAttribute("id") == "lnkId")
{
string urlString = ((Element) el).Attributes["href"].NodeValue;
webKitBrowser1.Navigate(urlString);
}
}
Here what I am doing is casting the WebKit.DOM.Node object to its subclass WebKit.DOM.Element to get its Attributes. Then providing href to the NamedNodeMap, i.e. Attributes as the NodeName, you can easily extract the NodeValue, which is the target url in this case. You can then simply invoke the Navigate(urlString) method on the WebKitBrowser instance to replicate the click event.
I don't work with Windows and all my experience is on Webkit GTK. Following comments are based on that experience.
I am not sure which webkit .NET version you are using. Looks like there are multiple implementations. Assuming you are using the one mentioned by Wasif, you can evaluate javascript as mentioned in the example https://code.google.com/p/open-webkit-sharp/source/browse/JavaScriptExample/Form1.cs.
Actually if implementation is supporting javascript execution then you can do most, if not all the DOM operations. The API functions are usually same as javascript functions and most of the time call exact same functions internally despite of origination. Communication between your application and javascript can be little challenging, but if you can read alert messages, that also can be solved. It looks like this library does support alert handling mechanism. A tool I wrote at https://github.com/nhrdl/notesMD will show some examples of achieving this communication though it uses GTK version and is written in python.
Incidentally if you know the id of the element, then Document.GetElementById will save you the loop.
webKitBrowser1.StringByEvaluatingJavaScriptFromString("var inpt = document.createElement(\"input\"); inpt.setAttribute(\"type\", \"submit\"); inpt.setAttribute(\"id\", \"nut\"); inpt.setAttribute(\"type\", \"submit\"); inpt.setAttribute(\"name\", \"tmp\"); inpt.setAttribute(\"value\", \"tmp\"); var element = document.getElementById(\"lnk\"); element.appendChild(inpt);");
webKitBrowser1.StringByEvaluatingJavaScriptFromString("document.getElementById('nut').click();");
I'm trying to develop a BHO for the release preview of IE 10. The BHO needs to be able to allow javascript to call a C# function.
I've followed the instructions available here:
Calling C# BHO methods from Javascript
These instructions work great in IE 9.
Unfortunately, I've found that they only work for the first tab created in IE 10. The second and subsequent tabs do not expose the BHO interface to Javascript. On occasion, new tabs work, but most of the time they do not. Moreover, it fails silently. I'm guessing this has something to do with the process model -- but again, it works fine in IE 9 (which has the same process model).
Here is the specific snippet of code that I'm using:
private void InstallJSObject(IHTMLWindow2 window)
{
// Install our javascript object
IExpando windowEx = (IExpando)window;
PropertyInfo property = windowEx.GetProperty("myBHO", System.Reflection.BindingFlags.IgnoreCase);
if (property == null)
{
property = windowEx.AddProperty("myBHO");
}
property.SetValue(windowEx, this, null);
}
Before posting, I researched the following: http://bit.ly/R9qldf
var myATL = new ActiveXObject("MySampleATL.MyClass");
if (myATL.IsBHOInstalled)
alert (myATL. SayHelloFromBHO());
else
alert ("BHO isn't installed now !");
window.external.AddFavorite(<url>, "text");
Extracted from here.
The the blog is dated back to April'07, still, may be this is what you were looking for..
You can't have your cake and eat it too, apparently.
I'm currently using the System.Windows.Forms.WebBrowser in my application. The program currently depends on using the GetElementsByTagName function. I use it to gather up all the elements of a certain type (either "input"s or "textarea"s), so I can sort through them and return the value of a specific one. This is the code for that function (my WebBrowser is named web1):
// returns the value from a element.
public String FetchValue(String strTagType, String strName)
{
HtmlElementCollection elems;
HtmlDocument page = web1.Document.Window.Frames[1].Document;
elems = page.GetElementsByTagName(strTagType);
foreach (HtmlElement elem in elems)
{
if (elem.GetAttribute("name") == strName ||
elem.GetAttribute("ref") == strName)
{
if (elem.GetAttribute("value") != null)
{
return elem.GetAttribute("value");
}
}
}
return null;
}
(points to note: the webpage I need to pull from is in a frame, and depending on circumstances, the element's identifying name will be either in the name or the ref attribute)
All of that works like a dream with the System.Windows.Forms.WebBrowser.
But what it is unable to do, is redirect the opening of a new window to remain in the application. Anything that opens in a new window shoots to the user's default browser, thus losing the session. This functionality can be easily fixed with the NewWindow2 event, which System.Windows.Forms.WebBrowser doesn't have.
Now forgive me for being stunned at its absence. I have but recently ditched VB6 and moved on to C# (yes VB6, apparently I am employed under a rock), and in VB6, the WebBrowser possessed both the GetElementsByTagName function and the NewWindow2 event.
The AxSHDocVw.WebBrowser has a NewWindow2 event. It would be more than happy to help me route my new windows to where I need them. The code to do this in THAT WebBrowser is (frmNewWindow being a simple form containing only another WebBrowser called web2 (Dock set to Fill)):
private void web1_NewWindow2(
object sender,
AxSHDocVw.DWebBrowserEvents2_NewWindow2Event e)
{
frmNewWindow frmNW = new frmNewWindow();
e.ppDisp = frmNW.web2.Application;
frmNW.web2.RegisterAsBrowser = true;
frmNW.Visible = true;
}
I am unable to produce on my own a way to replicate that function with the underwhelming regular NewWindow event.
I am also unable to figure out how to replicate the FetchValue function I detailed above using the AxSHDocVw.WebBrowser. It appears to go about things in a totally different way and all my knowledge of how to do things is useless.
I know I'm a sick, twisted man for this bizarre fantasy of using these two things in a single application. But can you find it in your heart to help this foolish idealist?
I could no longer rely on the workaround, and had to abandon System.Windows.Forms.WebBrowser. I needed NewWindow2.
I eventually figured out how to accomplish what I needed with the AxWebBrowser. My original post was asking for either a solution for NewWindow2 on the System.Windows.Forms.WebBrowser, or an AxWebBrowser replacement for .GetElementsByTagName. The replacement requires about 4x as much code, but gets the job done. I thought it would be prudent to post my solution, for later Googlers with the same quandary. (also in case there's a better way to have done this)
IHTMLDocument2 webpage = (IHTMLDocument2)webbrowser.Document;
IHTMLFramesCollection2 allframes = webpage.frames;
IHTMLWindow2 targetframe = (IHTMLWindow2)allframes.item("name of target frame");
webpage = (IHTMLDocument2)targetframe.document;
IHTMLElementCollection elements = webpage.all.tags("target tagtype");
foreach (IHTMLElement element in elements)
{
if (elem.getAttribute("name") == strTargetElementName)
{
return element.getAttribute("value");
}
}
The webbrowser.Document is cast into an IHTMLDocument2, then the IHTMLDocument2's frames are put into a IHTMLFramesCollection2, then I cast the specific desired frame into an IHTMLWindow2 (you can choose frame by index # or name), then I cast the frame's .Document member into an IHTMLDocument2 (the originally used one, for convenience sake). From there, the IHTMLDocument2's .all.tags() method is functionally identical to the old WebBrowser.Document.GetElementsByTagName() method, except it requires an IHTMLElementCollection versus an HTMLElementCollection. Then, you can foreach the collection, the individual elements needing to be IHTMLElement, and use .getAttribute to retrieve the attributes. Note that the g is lowercase.
The WebBrowser control can handle the NewWindow event so that new popup windows will be opened in the WebBrowser.
private void webBrowser1_NewWindow(object sender, CancelEventArgs e)
{
// navigate current window to the url
webBrowser1.Navigate(webBrowser1.StatusText);
// cancel the new window opening
e.Cancel = true;
}
http://social.msdn.microsoft.com/Forums/en-US/csharpgeneral/thread/361b6655-3145-4371-b92c-051c223518f2/
The only solution to this I have seen was a good few years ago now, called csExWb2, now on Google code here.
It gives you an ExWebBrowser control, but with full-on access to all the interfaces and events offered by IE. I used it to get deep and dirty control of elements in a winforms-hosted html editor.
It may be a bit of a leap jumping straight into that, mind.
I am trying to develop a util (using system-hook) for that works like an expander (user selects some text and presses a hotkey and it is expands). It should work with Visual Studio.
I want to implement this using Windows API because I want to develop an app that works globally with any application (whether you're using VS, or wordpad, you should get the same functionality).
I've been able to do this successfully with notepad, wordpad, etc. using EM_ GETSEL and EM_REPLACESEL messages. But these APIs are not working with Visual Studio, or ms word.
What APIs should I use to be able to
1. Detect what text is selected.
2. Send input to the editor.
I am programming in C#. If you must know what I am trying to do... I am trying to make a universal port of ZenCoding that works on any editor. So all help will be appreciated.
For part 2 you could try using Windows Input Simulator which is an open source project I've just released to Codeplex to wrap the Win32 SendInput. Instead of SendKeys which just simulates text input, you can actually simulate real key strokes and complex chords to the active window.
In your case, if the user can perform the task with the Keyboard, this project will help you, otherwise you'd need to find another solution.
Hope this helps.
Why don't you use a System.Windows.Forms.SendKeys class for simulating keyboard input from user?
You can use:
SendKeys.SendWait("^C"); //CTRL+C
var selectedText = Clipboard.GetText();
var newText = Replace(selectedText);
SendKEys.SendWait("^V"); //CTRL+V
You can use WPF's Automation functionality, encapsulated in these two namespaces:
System.Windows.Automation
System.Windows.Automation.Provider
As an example, this is a method for finding an automation target element (e.g. a typical win control):
public static AutomationElement FindElement(AutomationElement context, PropertyCondition[] conditions)
{
// if no conditions, there's no search to do: just return the context, will be used as target
if (conditions == null)
{
return (context);
}
// create the condition to find
System.Windows.Automation.Condition condition = null;
if (conditions.Length <= 0)
{
throw (new ArgumentException("No conditions specified"));
}
else if (conditions.Length == 1)
{
condition = conditions[0];
}
else
{
AndCondition ac = new AndCondition(conditions);
condition = ac;
}
// find the element
CacheRequest creq = new CacheRequest();
creq.TreeFilter = Automation.ControlViewCondition;
using (creq.Activate())
{
AutomationElement e = AutomationContext(context);
AutomationElement target = e.FindFirst(TreeScope.Subtree, condition);
return (target);
}
}
Whatever you try, be absolutely sure to try it, ASAP, with Visual Studio 2010 beta 2. The editor has largely been rewritten, and hacks that work with an earlier version should be tested again.