Tuesday, November 17, 2009

VB.NET/C#.NET Communication with JavaScript

This article will help you to learn how to do communication between javascript and (vb.net/c#.net)

To start off, we'll start with a very simple example; all this will do is call a JavaScript function from VB.NET to display an alert with message 'Hello world'. Similarly, from the HTML page using JavaScript, we'll call a VB.NET function which will again display a messagebox with the message 'Hello world'. These are the steps you need to do to make it happen:

Calling JavaScript from VB.NET

In Visual Studio, create a new WinForms application, name it anything you like.
Add an import statement like Imports System.Security.Permissions in your form1 (main form).
Add a couple of attributes to form1, like:

<PermissionSet(SecurityAction.Demand, Name:="FullTrust")> _
<System.Runtime.InteropServices.ComVisibleAttribute(True)> _
Public Class Form1
End Class
All they do is tell the .NET Framework that we want fulltrust and make the class visible to COM so this class is visible to JavaScript.

Add a WebBrowser control to the form and set its url property to c:\temp\mypage.html (just an example path, you should set it to the HTML page you'll be using).
Add a Button control on the form, and on its click handler, write this code:

Private Sub Button1_Click(ByVal sender As System.Object, _
ByVal e As System.EventArgs) Handles Button1.Click
Me.WebBrowser1.Document.InvokeScript("showJavascriptHelloWorld")
End Sub
In your HTML page, add this code:

<script type="text/javascript">
function showJavascriptHelloWorld() {
alert("Hello world");
}
</script>
Calling VB.NET from JavaScript

Continuing with the last project, add the following lines of code in your form1:

Public Sub showVbHelloWorld()
MsgBox("Hello world")
End Sub
In your form1 load event, add this line of code:

Me.WebBrowser1.ObjectForScripting = Me
What this line does is, it exposes your form1 class to the JavaScript on the HTML page. You could expose any class as long as you make it visible to COM and set its permission to fulltrust.

In your HTML page, add a Button, and on its click event, call this function:

<script type="text/javascript">
function showVbHelloWorld() {
window.external.showVbHelloWorld();
}
</script>
That is it. We've accomplished two way communication. But, most developers must have seen similar examples on MSDN or in different forums. This is just the starting point, our next step is to pass arguments to the functions.
Passing arguments to functions

Passing arguments to JavaScript functions

To pass arguments to a JavaScript function, you need to simply declare functions in your JavaScript code like you normally do. In your VB.NET code, you can pass a single argument like:


Me.WebBrowser1.Document.InvokeScript("javascriptFunction", _
New String() {"Message from vb.net to javascript"})
If you want to pass more than one variable, you can simply add it like:


Me.WebBrowser1.Document.InvokeScript("javascriptFunction", _
New String() {"Message from vb.net to javascript", _
"Argument 1","Argument 2" , "Argument n"})
So far, I am only passing string type arguments, but you are not restricted to simple strings. You can easily pass string, integers, booleans without any special additions. E.g.:


Me.WebBrowser1.Document.InvokeScript("javascriptFunction", _
New Object() {"Message from vb.net to javascript",200,true})
Arguments are not restricted to simple types, you can pass any type you want. The only restriction is you'll have to make it COM visible. In this example, I am going to pass a Person object to JavaScript. To make it work, I'll follow these steps:

Create a class called Person, like:

<PermissionSet(SecurityAction.Demand, Name:="FullTrust")> _
<System.Runtime.InteropServices.ComVisibleAttribute(True)> _
Public Class Person
Public Sub New(ByVal firstName As String, _
ByVal lastName As String, ByVal age As Integer)
Me._firstName = firstName
Me._lastName = lastName
Me._age = age
End Sub

Private _firstName As String
Private _lastName As String
Private _age As Integer

Public Function getFirstName() As String
Return Me._firstName
End Function

Public Function getLastName() As String
Return Me._lastName
End Function

Public Function getAge() As Integer
Return Me._age
End Function

End Class
In your form1, call it like this:

Me.WebBrowser1.Document.InvokeScript("sendPerson", _
New Person() {New Person("John", "Smith", 37)})
In your JavaScript, you can use the Person object like:

<script type="text/javascript">
function sendPerson(person) {
alert("First Name:" + person.getFirstName() +
"Last Name:" + person.getLastName() +
"Age:" + person.getAge());
}
</script>
Most developers should be aware of this, but I must point out that when you pass your object to JavaScript, it is fully exposed (all public methods/properties), and JavaScript can modify your object. As an example, if I add this function in my Person class:

Public Sub setFirstName(ByVal firstName as String)
Me._firstName=firstName
End Sub
Now, in your JavaScript, modify the function sendPerson like:


function sendPerson(person) {
person.setFirstName('Roger');
}
After this call, your person will have first name as 'Roger' instead of 'John'. To avoid this situation, there are two options. Make setFristName private/protected, but that means you won't be able to access this method from VB.NET. The better solution is to add an attribute to setFirstName, like:


<System.Runtime.InteropServices.ComVisibleAttribute(False)> _
Public Sub setFirstName(ByVal firstName as String)
Me._firstName=firstName
End Sub
Now, your function is visible in your VB.NET code, but JavaScript won't be able to call it, and as a result modify it in case you don't want to expose a particular method to JavaScript for any reason.

Passing arrays to JavaScript functions (unsuccessful attempt)

OK, passing arrays to JavaScript. Some developers might think why is it a separate section. The reason is it is not straightforward to pass arrays as most people would think. If you try this code:


Dim firstNames() As String = {"John", "Richard", "Micheal"}
Dim lastNames() As String = {"Smith", "Stone", "Day"}
Me.WebBrowser1.Document.InvokeScript("passNameArrays", _
New Object() {firstNames, lastNames})
In your JavaScript, if you try doing this:


<script type="text/javascript">
function passNameArrays(firstNames, lastNames) {
alert(firstNames[0]);
}
</script>
you'll get a JavaScript error (if error messages are enabled).

So, why didn't that work, you must be asking. The reason is pretty simple. You can only pass simple types, and arrays are not simple types. You'll have to put a workaround to pass arrays.

Passing arrays to JavaScript functions (successful attempt)

OK, it is understood that we can't pass arrays to JavaScript (at least, I am not aware of a way for passing them; if anyone knows a method, they are welcome to correct me). The simplest workaround is to create a type which wraps your array and passes that type instead of a simple array. E.g., you could wrap your array of strings like:


<PermissionSet(SecurityAction.Demand, Name:="FullTrust")> _
<System.Runtime.InteropServices.ComVisibleAttribute(True)> _
Public Class myArr
Implements IList(Of String)

Private _list As New List(Of String)

Public Sub New()

End Sub
Public Sub New(ByVal arr() As String)
For Each Str As String In arr
Me._list.Add(Str)
Next
End Sub

Public Sub Add(ByVal item As String) _
Implements System.Collections.Generic.ICollection(Of String).Add
Me._list.Add(item)
End Sub

Public Sub Clear() _
Implements System.Collections.Generic.ICollection(Of String).Clear
Me._list.Clear()
End Sub

Public Function Contains(ByVal item As String) As Boolean _
Implements System.Collections.Generic.ICollection(Of String).Contains
Return _list.Contains(item)
End Function

Public Sub CopyTo(ByVal array() As String, ByVal arrayIndex As Integer) _
Implements System.Collections.Generic.ICollection(Of String).CopyTo

End Sub

Public ReadOnly Property Count() As Integer _
Implements System.Collections.Generic.ICollection(Of String).Count
Get
Return Me._list.Count
End Get
End Property

'Rest of the class method needs to be implemented here
End Class
And now, if we take on the example from the previous section where we unsuccessfully tried to pass an array of first and last names, let's give it another try:


Dim firstNames As New myArr(New String() {"John", "Richard", "Micheal"})
Dim lastNames As New myArr(New String() {"Smith", "Stone", "Day"})
Me.WebBrowser1.Document.InvokeScript("passNameArrays", _
New Object() {firstNames, lastNames})
In your JavaScript, you could use it like:


<script type="text/javascript">

function passNameArrays(firstNames, lastNames) {
alert(firstNames.item(0));
}
</script>
And now, you should see an alert with the text "John". All the rest of the functions like count, indexOf etc. are available from JavaScript.

I have implemented the class as IList(Of String), but you could implement it as IList(Of Object) and pass any type of array (as long as they are COM visible).

An important fact to remember

One important fact which most developers should be aware of is that you are not bound to pass a set number of arguments to the JavaScript function. You could pass any number of arguments to the JavaScript function, e.g., our last JavaScript function could be written and used like this:


<script type="text/javascript">
function passNameArrays() {
alert(arguments[0].item(0));
}
</script>
Passing arguments from JavaScript to VB.NET functions

We have spend enough time passing arguments to JavaScript. Let's move on to the next step, passing arguments to VB.NET functions. You could simply do it like this in JavaScript:


<script type="text/javascript">
function sendPerson() {
window.external.sendPerson("John", "Smith", 26);
}
</script>
and on your VB.NET side, you could define the function to receive these arguments, like:


Public Sub sendPerson(ByVal firstName As String, _
ByVal lastName As String, ByVal age As Integer)
MsgBox(String.Format("First name: {0} Last Name: {1} Age: {2}", _
firstName, lastName, age))
End Sub
and you are done.

Again, you are not bound to pass simple types as arguments. You could pass JavaScript objects as arguments, if required. Take a look at the last example in a different way. The JavaScript will be:


<script type="text/javascript">
function sendPerson() {
var person = new Array();
person["firstName"] = "John";
person["lastName"] = "Smith";
person["age"] = 26;
window.external.sendPerson(person );
}
</script>
and you can use the Person object on the VB.NET side, like:


Public Sub sendPerson(ByVal person As Object)
MsgBox(String.Format("First name: {0} Last Name: {1} Age: {2}", _
person.firstName, person.lastName, person.age))
End Sub
similar example but different method to achieve the same result.

Returning values from functions

Returning a value from a JavaScript function to VB.NET

What about returning a value from functions? Simple, you could easily get the value from a JavaScript function; e.g., if we call this line in VB.NET:


Dim str As String = _
Me.WebBrowser1.Document.InvokeScript("getJavascriptString")
MsgBox(str)
and in JavaScript, we could write the function to return a string like:


<script type="text/javascript">
function getJavascriptString() {
return "String returned from javascript";
}
</script>
Again, you are not bound to simple types. You can return your custom JavaScript objects very easily. E.g., let's create a Person class in JavaScript this time, and write a function which will return the Person object back to VB.NET.


<script type="text/javascript">
function person(name,age) {
this.name = name;
this.age = age;
this.getName = function() { return this.name; }
this.getAge = function() { return this.age; }
}

function getPersonObject() {

myPerson = new person("Chris McCreadie", 48);
return myPerson;
}
</script>
On your VB.NET side, you can do something like this (must warn you the code below is not going to work):


Dim jsPerson As Object = Me.WebBrowser1.Document.InvokeScript("getPersonObject")
MsgBox(String.Format("Name: {0} Age:{1}", jsPerson.getName(), jsPerson.getAge()))
If you try the above code, the first line will run fine, but at runtime (as it is late binding), you'll receive an exception on the second line. If you are asking why it doesn't work, frankly speaking, I don't know, but if you know the answer, you are welcome to explain it.

So, what is the solution? The solution is pretty simple. You take help from System.Reflection, and modify the above code to something like this:


Dim jsPerson As Object = Me.WebBrowser1.Document.InvokeScript("getPersonObject")
Dim jsPersonType As Type = jsPerson.GetType()
Dim name As String = jsPersonType.InvokeMember("getName", _
Reflection.BindingFlags.InvokeMethod, Nothing, jsPerson, Nothing)
Dim age As Integer = jsPersonType.InvokeMember("getAge", _
Reflection.BindingFlags.InvokeMethod, Nothing, jsPerson, Nothing)
MsgBox(String.Format("Name: {0} Age: {1}", name, age))
The above code should work perfectly and give you the desired results. I won't go in to the details of explaining the InvokeMember function, there's plenty of help on MSDN and Google for that.

What's the next step now? We can access simple types and complex (custom) types. Can we access the objects (HTML buttons, text fields, div) on the page? Yes, we can. And surprisingly, they are easier to access. Let's pull our sleeves and write a simple function in JavaScript which will return a Button object which happens to be on the page.


<script type="text/javascript">
function getHtmlButton() {
//assuming there's a button with id myBtn on the page
return document.getElementById('myBtn');
}
</script>
Now, on the VB.NET side, we could do something like this:


Dim htmlButton As Object = Me.WebBrowser1.Document.InvokeScript("getHtmlButton")
htmlButton.value = "Set from vb.net"
The above code will change the button text to "Set from vb.net". We could slightly improve the above code by doing a couple of things:

Add a reference in your project for "Microsoft HTML Object Library", can be found under the COM section.
And now, change the first line of the above code to:

Dim htmlButton As mshtml.HTMLInputElement = _
Me.WebBrowser1.Document.InvokeScript("getHtmlButton")
What these changes will do is, firstly, it will make our htmlButton object a strongly type one instead of the Object type. Secondly, you'll get intellisence from Visual Studio, and it will make your job easier in trying to figure out what method/properties to call/set on the htmlButton.

I would give another example just to show the possibilities.


'assuming JavaScript got a function which will return a div on the page
Dim htmlDiv As mshtml.HTMLDivElement = _
Me.WebBrowser1.Document.InvokeScript("getHtmlDiv")
htmlDiv.style.background = "Red"
Most of the types in MSHTML are self explanatory, and you can guess what will be converted from the HTML to the MSHTML type. But if you can't tell, here's a small tip: Set your variable to type Object initially, and once you have received an HTML object from JavaScript (assuming you've setup a break point), hover your mouse over it and Visual studio will let you know the exact type, or alternatively, you can look in the Autos or Local window in Visual Studio.

A tip: All (most) developers know this, but if anyone doesn't, here is a little tip if you want more than one value back from JavaScript. If you are passing objects, JavaScript can modify those objects. If we take the example where we created a list(of String) in VB.NET and if we need to get the list of first names and last names from JavaScript, we could do something like this:


Dim firstNames As New myArr()
Dim lastNames As New myArr()
Me.WebBrowser1.Document.InvokeScript("getNames", _
New Object() {firstNames, lastNames})
and on the JavaScript side, we could do something like:


function getNames(firstNames, lastNames) {
firstNames.add("John");
firstNames.add("Chris");
lastNames.add("Martin");
lastNames.add("Churn");
}
and as a result, we could easily get multiple values back from JavaScript. I agree this is not the best example, but I hope you get the concept I am trying to explain.

Returning a value from a VB.NET function to JavaScript

I am sure I don't need this section, but just for completeness, I'll give an example here. On the VB.NET side, we could write a simple function to return the current date and time like:


Public Function getCurrentTime() As DateTime
Return DateTime.Now
End Function
and on the JavaScript side:


function getCurrentTime() {
alert(window.external.getCurrentTime());
}
Ask me a question :-) I know you must be asking a question right now. Have I done some mistake or submitted an incomplete code? How can I return a DateTime from VB.NET when it is not a simple type and I haven't added any wrapper class around it? The answer is it's a fully working code, and even though DateTime is not a simple type, it's one of the special types which automatically get converted to the JavaScript Date type. Every other concept in returning values from VB.NET is explained above, and I don't think any further explanation is needed. You can return string, integer, boolean, datetime, without any special arrangements, but for other types, you'll have to expose the type/class to COM for it to be visible.

Exception handling

So far, all the examples I have done (for simplicity), I haven't done any exception handling, but that doesn't mean in a production code you'll be doing this. Every call from one platform to the other must be done with an assumption that it wont' work and something at some point will go wrong. As JavaScript is an interpreted language, even if there's some mistake in the script, it might not be visible until runtime when you call JavaScript from your VB.NET code. Similarly, when you call VB.NET from JavaScript, if an exception is thrown from the VB.NET function, it won't crash your program/exe but will be thrown back to the JavaScript and you'll get a JavaScript error dialog from Internet explorer.

Share This!


46 comments:

rahul said...

I found good resources of c#. Check this out

http://csharptalk.com

Anonymous said...

Having read this I believed it was extremely informative.
I appreciate you taking the time and energy to put this content together.

I once again find myself personally spending a lot of time both reading and
posting comments. But so what, it was still worth it!

Anonymous said...

I do not know if it's just me or if everybody else
experiencing problems with your site. It appears like some of the text within your content are running off the screen. Can somebody else please comment and let me
know if this is happening to them as well? This might be a problem with my browser because I've had this
happen before. Many thanks

Anonymous said...

I quite like reading through a post that will make people think.
Also, many thanks for allowing for me to comment!

Anonymous said...

Fantastic goods from you, man. I have understand your
stuff previous to and you're just extremely great.
I really like what you have acquired here, certainly like what you are saying
and the way in which you say it. You make it entertaining and you still care for to keep it
wise. I can not wait to read far more from you. This is actually a tremendous website.

Anonymous said...

A motivating discussion is worth comment. I do believe that you should publish more about
this topic, it might not be a taboo matter but typically
folks don't talk about these subjects. To the next!
Best wishes!!

Anonymous said...

Hmm it looks like your website ate my first comment (it was super long) so I guess I'll just sum it up what I
wrote and say, I'm thoroughly enjoying your blog.
I as well am an aspiring blog blogger but I'm still new to everything.
Do you have any helpful hints for first-time blog writers?
I'd really appreciate it.

Anonymous said...

It is perfect time to make some plans for the future and it's time to be happy.
I have read this post and if I could I desire to suggest you few interesting
things or tips. Maybe you could write next articles referring to
this article. I wish to read even more things about it!

Anonymous said...

hi!,I love your writing so so much! percentage we be in contact more approximately your post on AOL?
I require a specialist on this space to resolve my problem.
Maybe that's you! Taking a look ahead to peer you.

Anonymous said...

Wow, marvelous blog layout! How long have you been blogging for?
you make blogging look easy. The overall look of your web site is excellent, let alone
the content!

Anonymous said...

Heya! I'm at work browsing your blog from my new iphone
4! Just wanted to say I love reading through your blog and look forward to
all your posts! Carry on the fantastic work!

Anonymous said...

You really make it seem so easy with your presentation but I
find this matter to be really something which I think
I would never understand. It seems too complex and extremely broad
for me. I'm looking forward for your next post, I will
try to get the hang of it!

Anonymous said...

This text is worth everyone's attention. When can I find out more?

Anonymous said...

Have you ever thought about including a little bit more than just your articles?
I mean, what you say is valuable and everything.
However imagine if you added some great visuals or videos to give your posts more,
"pop"! Your content is excellent but with pics and clips, this site could certainly be one of the best in its niche.
Amazing blog!

Anonymous said...

Nice answers in return of this question with firm arguments and telling everything concerning that.

Anonymous said...

Yesterday, while I was at work, my cousin stole my iphone and tested to see if
it can survive a thirty foot drop, just so she
can be a youtube sensation. My apple ipad is now destroyed and she
has 83 views. I know this is totally off topic but I had to
share it with someone!

Anonymous said...

Hi there everyone, it's my first visit at
this site, and piece of writing is really fruitful in support of
me, keep up posting these types of posts.

Anonymous said...

At this time it seems like BlogEngine is the preferred blogging platform out there right
now. (from what I've read) Is that what you are
using on your blog?

Anonymous said...

Very nice post. I just stumbled upon your blog and wanted to say that I've truly
enjoyed surfing around your blog posts. After all I
will be subscribing to your feed and I hope you write again soon!

Anonymous said...

Wow, this piece of writing is good, my younger sister is analyzing these kinds of
things, so I am going to inform her.

Anonymous said...

Excellent post. I was checking continuously this blog and I am
impressed! Very useful info particularly the last part :) I care for such information much.

I was seeking this certain information for a long
time. Thank you and best of luck.

Anonymous said...

Thanks for sharing your thoughts about Golden Goose Records Hrvatska.
Regards

Anonymous said...

You're so cool! I don't think I have read through a single thing like this before.
So great to discover somebody with some original thoughts on this subject.
Really.. many thanks for starting this up. This website
is one thing that's needed on the internet, someone with a little originality!

Anonymous said...

Hey I know this is off topic but I was wondering if you knew of any widgets I
could add to my blog that automatically tweet my newest twitter updates.
I've been looking for a plug-in like this for quite some time
and was hoping maybe you would have some experience with something like this.
Please let me know if you run into anything. I truly
enjoy reading your blog and I look forward to your new updates.

Anonymous said...

A person essentially lend a hand to make significantly posts
I would state. That is the very first time I frequented your website page
and so far? I amazed with the analysis you made to create this actual put up amazing.
Excellent task!

Anonymous said...

Hi colleagues, how is the whole thing, and what you wish for to say about this article, in my view its in fact remarkable in support of me.

Anonymous said...

Attractive section of content. I just stumbled upon your site and in accession capital to assert that I acquire actually enjoyed
account your blog posts. Any way I will be subscribing to your feeds and even I achievement
you access consistently quickly.

Anonymous said...

My coder is trying to persuade me to move to .net
from PHP. I have always disliked the idea because of the costs.
But he's tryiong none the less. I've been using Movable-type on various
websites for about a year and am concerned about switching to another
platform. I have heard excellent things about blogengine.net.
Is there a way I can transfer all my wordpress posts
into it? Any help would be really appreciated!

Anonymous said...

It is the best time to make some plans for the long run and
it's time to be happy. I have learn this post and if I may
just I want to recommend you some fascinating issues or advice.
Perhaps you can write subsequent articles referring to this article.
I want to learn even more things about it!

Anonymous said...

Wow that was odd. I just wrote an incredibly long comment but after I clicked
submit my comment didn't show up. Grrrr... well I'm not writing
all that over again. Anyways, just wanted to say great blog!

Anonymous said...

I like the valuable info you provide in your articles.
I will bookmark your weblog and check again here frequently.
I'm quite sure I will learn many new stuff right here!
Good luck for the next!

Anonymous said...

Great post.

Anonymous said...

Good day! This is kind of off topic but I need some advice from an established blog.
Is it tough to set up your own blog? I'm not very techincal but I can figure
things out pretty fast. I'm thinking about creating my own but I'm not sure
where to start. Do you have any tips or suggestions?
Cheers

Anonymous said...

Having read this I thought it was extremely enlightening.
I appreciate you finding the time and energy to
put this article together. I once again find myself personally spending
a lot of time both reading and leaving comments. But so what, it was still
worthwhile!

Anonymous said...

My family members always say that I am killing my time here at net, except I know
I am getting experience daily by reading such pleasant articles.

Anonymous said...

I have been browsing online more than 3 hours today, yet I never found any interesting
article like yours. It is pretty worth enough for me.
Personally, if all site owners and bloggers made good content as you
did, the web will be much more useful than ever before.

Anonymous said...

Hi just wanted to give you a brief heads up and let you know a few of the images aren't loading properly.
I'm not sure why but I think its a linking issue. I've tried it in two different browsers and both show the
same results.

Anonymous said...

Hi, this weekend is pleasant for me, as this occasion i am reading this fantastic informative paragraph
here at my home.

Anonymous said...

If some one wants expert view concerning blogging afterward i recommend him/her to go
to see this web site, Keep up the fastidious job.

Anonymous said...

If some one wishes to be updated with hottest technologies therefore he must be pay a
visit this web site and be up to date all the time.

Anonymous said...

Ahaa, its good discussion regarding this article at this place at this website, I have read all
that, so now me also commenting at this place.

Anonymous said...

I am really happy to read this website posts which contains tons of helpful data, thanks for
providing such data.

Anonymous said...

This article provides clear idea in support of the new users of
blogging, that truly how to do running a blog.

Anonymous said...

Your style is so unique in comparison to
other folks I have read stuff from. Thanks for posting when you have the
opportunity, Guess I will just bookmark this web site.

Anonymous said...

I am genuinely thankful to the holder of this website who has shared this great piece of
writing at here.

Anonymous said...

You actually make it appear really easy together with
your presentation however I in finding this topic to be really one thing that I feel I'd by no
means understand. It seems too complicated and very
broad for me. I am taking a look ahead to your next put up, I will try to get the
hold of it!

Powered By Blogger · Designed By Seo Blogger Templates