Monday, June 13, 2011

WPF WebBrowser JS Errors capture

Definition of the problem: Catch all the JS errors occurred in WebBrowser used in WPF application.

Luckily DOM's window has so called "external" object and WebBrowser componenet has a "ObjectForScripting" property which is "equal" to "window.external".

There are some restrictions with permissions, f.e. you cannot set your window or application as a ObjectForScripting. Otherwise you receive Exception "The object type is not visible to COM. You need to set ComVisibleAttribute attribute to True."
So I propose to use another class to do that.

Here is the source:
1st Part of code is run only once, in the window/app constructor f.e.

ObjectForScriptingHelper ScriptHelper = new ObjectForScriptingHelper(this);
wb_browser.ObjectForScripting = ScriptHelper;


2nd Part is run on Browser's "Navigated" event

private void wb_browser_Navigated(object sender, System.Windows.Navigation.NavigationEventArgs e) {
string DisableScriptError = @"function externalError(errorMsg, document, lineNumber) {
window.external.onError(errorMsg, document, lineNumber);
return true;
}
window.onerror = externalError;";
HTMLDocument doc2 = wb_browser.Document as HTMLDocument;
IHTMLScriptElement scriptErrorSuppressed = (IHTMLScriptElement)doc2.createElement("SCRIPT");
scriptErrorSuppressed.type = "text/javascript";
scriptErrorSuppressed.text = DisableScriptError;
IHTMLElementCollection nodes = doc2.getElementsByTagName("head");
foreach (IHTMLElement elem in nodes)
{

HTMLHeadElement head = (HTMLHeadElement)elem;
head.appendChild((IHTMLDOMNode)scriptErrorSuppressed);
}
}


3rd Part of script is that special class:

[PermissionSet(SecurityAction.Demand, Name = "FullTrust")]
[ComVisible(true)]
public class ObjectForScriptingHelper
{
TestingWindow m_Window;

public ObjectForScriptingHelper(TestingWindow w)
{
m_Window = w;
}

public void onError(Object arg1, Object arg2, Object arg3)
{
m_Window.onError(arg1, arg2, arg3);
}
}


And 4th Part of code is point where you can write your code:

public void onError(Object arg1, Object arg2, Object arg3) {
//Put your handle here
}



GL & HF


SE keywords:
How to capture script error message in a WPF WebBrowser

WPF WebBrowser screenshot

Definition of the problem: Make a screenshot of WebBrowser component inside WPF application.

I've spent half of a day googling the problem.
Found some solutions
- Using "VisualBrush" - does not work for WPF, creates black image.
- Using "FromImage" - does not work either with WPF.

Finally found this one.
Much better! But there are still "BUTS"

1. "This API was accessed with arguments from the wrong context." Did not have time to figure out, why this exception is being thrown. Something with the access permission from window to window or whatever. With other words if you get exception like that, use "Dispatcher.Invoke" part of the source-code posted below.
2. Full-Page. Some pages are longer than just one screen, so you need to make more than one screenshot to map whole page into image. There are also some DOM problems related to this task - some of the properties, like body.scrollTop just ain't being updated after you call window.scrollTo, killed some time for debugging.

Here is the source code of my final solution


browser.Dispatcher.Invoke(
System.Windows.Threading.DispatcherPriority.Normal,
new Action(delegate(){
HTMLDocument htmlDoc = browser.Document as HTMLDocument;
HTMLBodyClass body = (HTMLBodyClass)htmlDoc.body;

var topLeftCorner = browser.PointToScreen(new System.Windows.Point(0, 0));

var topLeftGdiPoint = new System.Drawing.Point((int)topLeftCorner.X, (int)topLeftCorner.Y);
var size = new System.Drawing.Size(body.clientWidth, body.clientHeight);
var screenShot = new Bitmap(body.scrollWidth, body.scrollHeight);
var graphics = Graphics.FromImage(screenShot);
var cP = new System.Drawing.Point();
var i = 0;
var scrollTop = 0;
var cW = (int)browser.ActualWidth;
var cH = (int)browser.ActualHeight;
do
{
scrollTop = Math.Min(cH * i, body.scrollHeight - cH);
htmlDoc.parentWindow.scrollTo(0, scrollTop);
newWindow.UpdateLayout();
Thread.Sleep(1500);
cP.X = 0;
cP.Y = scrollTop;
graphics.CopyFromScreen(topLeftGdiPoint, cP, size, CopyPixelOperation.SourceCopy);
i++;
} while (scrollTop + cH < body.scrollHeight);

screenShot.Save(@"C:\temp\screens\page.png", ImageFormat.Png);
})
);


NB: This algo only scrolls vertically (downwards). But you can easily figure out the way of screening it horizontally too.

Gl & HF

Thursday, January 20, 2011

PHP improved gettype function

It's short and simple and more informative:

function get_type(&$v) {
return ($type = gettype($v))=="object" ? get_class($v) : $type;
}

Merge of PHP::gettype and PHP::get_class functions

Friday, September 24, 2010

Update multiple records with single query, or Bundle Record Update

Actually it's a lie, it's not a single query, it's 3 queries, but it's still much more sufficient if you deal with more than 3 records to be updated with different values.

So, the problem is to run lot of update queries like:
update table_name set value_column='value1' where key_column = key1;
update table_name set value_column='value2' where key_column = key2;
...
update table_name set value_column='valueN' where key_column = keyN;

If you have N more than 3, then you should test the performance of the following approach:
1. create temporary table temp_table(k int,v varchar);
2. insert int temp_tabl values
(key1,'value1'),
(key2,'value2'),
...,
(keyN,'valueN')

3. update table_name, temp_table set
table_name.value_column=temp_table.v
where table_name.key_column=temp_table.k

For ~50 records, it works ~2 times faster, so think about it.

Как-то-так

Friday, September 3, 2010

Eclipse PDT 64Bit Windows 7 problem

This was the first thing that I thought is happening - my PDT IDE is working too slow because of incompatibility with my 64bit OS. I was 100% sure... because PDT "ate" ~80-100% of one of my 4 CPUs. I hate these moments thinking that my QuatroCore SUCKS runing java app :( ... Thanks Lord I was wrong.

Just turn the Outline view OFF and CPU back to 10-20%... too many classes , methods, properties are killing Outline view in Eclipse PDT. Don't keep it constantly open.


Wednesday, September 1, 2010

SD Card size must be at least 9MB

If you receive this error when creating new Android Virtual Device (AVD) even though you've input much more than 9MiB into "SD Card size" field... Consider following advice:

Here is some additional detail on what sizes are considered valid or invalid when generating a new AVD (set to MiB): 0-8: invalid 9-2047: valid 2047-4104: invalid 4105-6143: valid 6144-8200: invalid 8201: valid

Taken from here

Installing Android SDK

What a wonderful day is when you start learning new things...

Today was one of those, cuz I've started with Android SDK. Downloaded (for Windows) it and run "SDK Setup.exe", then wanted to create new Virtual Device (AVD) and failed, cuz "Target dropdown" was disabled and New AVD Wizard complains that is should be selected.

I've started googling, got many advices, most of them too complicated to be true for my situation. From the bottom of my "IMHO" I felt that there should be easier way, and so it was.

I've noticed the error I ignored on the first run, something about "Setup was not able to fetch smth via https"...

So the first thing you should do after downloading and running Android SDK (Windows) , go to Settings and check "Force https:// ... sources to be fetched using http:// ...", then go to "Available packages" and Refresh. There should be more options available under main Repository URL... select them and Install.

Now you have some new Targets available on the "Create new Android Virtual Device (AVD)" Wizard window.

GL & HF