ActiveX object custom event does not show results - javascript

tl/dr
The managed C# COM visible object that I created has an event. In the "OnCardRead" method (which fires the actual event) I check to see if the event is subscribed to and it is. The event callback code in javascript appears to never be called.
The story
The project is simple. Create a webpage that "opens" a rfid device and returns the serial number of the card. That portion of has its own set of unit tests and works just fine. To help my self debug if the Serial Ports address is set to 0 then it uses my MockRfidSDK. But for the sake of being thorough here is the code surrounding that (minus the actual RFID code).
IRfidDevice
[Guid("E86A9038-368D-4e8f-B389-FDEF38935B2F"), InterfaceType(ComInterfaceType.InterfaceIsDual), ComVisible(true)]
public interface IRfidDevice
{
[DispId(1)]
int Open();
[DispId(2)]
int Close();
[DispId(3)]
void SetComPort(int comport);
[DispId(4)]
void FlashLed(int led);
}
IRfidEvent
[ComVisible(true), GuidAttribute("0422D916-C11A-474e-947D-45A107038D12"), InterfaceTypeAttribute(ComInterfaceType.InterfaceIsIDispatch)]
public interface IRfidEvent
{
[DispIdAttribute(0x60020000)]
void CardUidReceived(byte sak, ushort atq, string uid);
}
RfidSdk/EmptyRfidSdk
internal interface RfidSdk
{
void Open();
int GetCard();
byte[] SelectCard();
int Close();
void FlashLed(int led);
}
class EmptyRfidSdk : RfidSdk
{
public void Open()
{
}
public int GetCard()
{
System.Threading.Thread.Sleep(10);
return 0x0344;
}
public byte[] SelectCard()
{
System.Threading.Thread.Sleep(2000);
return new byte[] { 0x20, 0x44, 0x03, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37 };
}
public int Close()
{
return 0;
}
public void FlashLed(int led)
{
}
}
RfidDevice
[Guid("873355E1-2D0D-476f-9BEF-C7E645024C32"), ProgId("MasterRDActiveX.RfidDevice"), ClassInterface(ClassInterfaceType.None), ComDefaultInterface(typeof(IRfidDevice)), ComVisible(true), ComSourceInterfaces(typeof(IRfidEvent))]
public class RfidDevice : InternetSafeObject, IRfidDevice
{
private RfidSdk sdk;
private bool _searchForCard = false;
[ComVisible(false)]
public delegate void RfidEventHandler(byte sak, ushort atq, string uid);
public event RfidEventHandler CardUidReceived;
public int Open()
{
try
{
sdk.Open();
_searchForCard = true;
Task.Factory.StartNew(SearchForCard);
return 0;
}
catch (Exception ex)
{
Trace.TraceError(ex.Message);
return -1;
}
}
public int Close()
{
try
{
_searchForCard = false;
sdk.Close();
sdk = null;
return 0;
}
catch (Exception ex)
{
Trace.TraceError(ex.Message);
return -1;
}
}
public void SetComPort(int comport)
{
if (sdk != null) Close();
if (comport < 1)
{
sdk = new EmptyRfidSdk();
}
else
{
sdk = new MasterRDWrapper.MasterRD(comport);
}
}
public void FlashLed(int led)
{
sdk.FlashLed(led);
}
private void SearchForCard()
{
while (_searchForCard)
{
int temp = 0;
while ((temp = sdk.GetCard()) == 0)
{
}
if (temp == sdk.GetCard())
{
var card = sdk.SelectCard();
FireCardUidReceived(card[0], BitConverter.ToUInt16(card, 1), card.Skip(3).ToArray());
}
}
}
private void FireCardUidReceived(byte sak, ushort atq, byte[] uid)
{
if (CardUidReceived != null)
{
Console.Beep(1000,100);
CardUidReceived(sak, atq, BitConverter.ToString(uid));
}
else
{
Console.Beep(2000, 2000);
}
}
So that code is working just fine. Remember the beeps. Short beep means the Event is subscribed to, long beep means it's not being subscribed too.
Now the webpage is where things get a little murky for me. I've looked at a lot of ActiveX tutorials here are some rules about the HTML code. It can't use deprecated HTML tags (IE... OBJECT). Here is my solution thus far
rfidScript.js
function onRfidCard(a, b, c) {
console.log('I am in your event');
document.getElementById('result').innerHTML = c;
console.log('all done in your event');
}
function tryRfid(rfidDevice) {
var uidTag = document.getElementById('result');
var comPort = parseInt(document.getElementById('comPortBox').value);
if (rfidDevice != null) {
rfidDevice.SetComPort(comPort);
if (rfidDevice.Open() === 0) {
uidTag.innerHTML = "Please Tap Card";
}
else {
document.getElementById('error').innerHTML = "Failed to Open";
}
}
}
function closeRfid(rfidDevice) {
rfidDevice.Close();
}
and last bit the html code
<!DOCTYPE html>
<html lang="en" xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta charset="utf-8" />
<title>RFID ActiveX Test</title>
<!--//C:\Users\snyder\Source\Repos\xPD\MasterRDActiveX\MasterRDActiveX\bin\Debug\testRfid.html-->
<script type="text/javascript">
var ctrl = new ActiveXObject('MasterRDActiveX.RfidDevice');
eval('function ctrl::CardUidReceived(a,b,c){ onRfidCard(a,b,c); }');
</script>
<script src="rfidScript.js"></script>
</head>
<body>
<h1>This is our Active X Test Page for RFID</h1>
<input type="text" id="comPortBox" /> <br />
<button type="button" onclick="tryRfid(ctrl);">Open</button>
<button type="button" onclick="closeRfid(ctrl);">Close</button>
<h2>
<output id="error" style="color: red;"></output>
<output id="result"></output>
</h2>
</body>
</html>
I fire up IE, put 0 in the textbox, click open and I hear 1 short beep(and only one.. not sure why on that one as I would expect a beep every 2 seconds see note1), but I don't see any text show up in the result output tag (and I've tried using alert to no avail.) So why?????? what am I missing?
Note 1 - Earlier when I was getting long beeps it would long beep over and over again as I would expect.

Related

Webview - How to trigger Java Function from Javascript Button

So I got a problem, where there is a button that will call a webview function
The button is the one with id play in the index.html that will call the javascript playVideo function in src.js, where the playVideo function will notify the webview that the button has been pressed to check the condition in the Java Function.
How do I achieve this?
The codes are below to help getting context
index.html
<!DOCTYPE html>
<html>
<head>
<title> Video</title>
<script type="text/javascript" src="src.js"></script>
</head>
<body bgcolor="#333">
<center>
<video preload="auto" width="480" height="270"
#Embed-video
</video>
<br>
<br>
<div>
<a class="first" href="#" id="play">Play</a>
<a class="second" href="#" id="pause">Pause</a>
</div>
</center>
Javascript code
window.onload = function(){
var video = document.getElementById('my-video');
var play = document.getElementById('play');
var pause = document.getElementById('pause');
// associate functions with the 'onclick' events
play.onclick = playVideo;
pause.onclick = pauseVideo;
function playVideo(e) {
//call java function
}
function pauseVideo(e) {
}
}
Java Function
webView.setWebViewClient(new WebViewClient() {
#Override
public boolean shouldOverrideUrlLoading(x,y) {
if (js.playVideo()) {
//do something
return true;
}else{
return false;
}
}
});
You should use annotation for Javascript like the following example
// Java File example
#JavascriptInterface
public void showToast(String toast) {
Toast.makeText(mContext, "show toast from web: " + toast, Toast.LENGTH_SHORT).show();
}
// Js file example
function showAndroidToast(msg) {
Android.showToast(msg);
}
Thus, you should try like the following code.
function playVideo(e) {
//call java function
Android.doSomething();
}
webView.setWebViewClient(new WebViewClient() {
#Override
public boolean shouldOverrideUrlLoading(x,y) {
// implment your code with doSomething func here
if (doSomething()) {
//do something
return true;
}else{
return false;
}
}
});
#JavascriptInterface
public boolean doSomething() {
// do something
if(isPlaying) return true;
return false;
}

Capture a custom Javascript event in C# WPF mshtml WebBrowser control

I'm unable to capture a custom Javascript event in a C# WPF mshtml WebBrowser control. I've created the example code below. A button click raises a custom event. I realise that there are easy ways to capture a button click event. I need to use a custom event. I've used a button just for this example and testing. What am I doing wrong?
XAML file:
<Window x:Class="WebEvent2.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="350" Width="525">
<Grid>
<WebBrowser Name="webBrowser1" Loaded="webBrowser1_Loaded" LoadCompleted="webBrowser1_LoadCompleted"></WebBrowser>
</Grid>
</Window>
C# file:
namespace WebEvent2{
public partial class MainWindow : Window{
public MainWindow(){
InitializeComponent();
}
private void webBrowser1_Loaded(object sender, RoutedEventArgs e{
webBrowser1.Navigate(#"file:///D:/test.html");
}
private void webBrowser1_LoadCompleted(object sender, NavigationEventArgs e){
try{
var evtListener = new EventListener();
var window = ((IHTMLDocument2)webBrowser1.Document).parentWindow as IHTMLWindow3;
window.attachEvent("MyCustomEvent", evtListener);
// Also not working:
// ((HTMLDocument)webBrowser1.Document).attachEvent("MyCustomEvent", evtListener);
}catch (UnauthorizedAccessException err){
Console.WriteLine("OOPS: " + err);
}
}
[ComVisible(true)]
[ClassInterface(ClassInterfaceType.AutoDispatch)]
public class EventListener{
[DispId(0)]
public void handler(IHTMLEventObj evt){
MessageBox.Show("message received");
}
}
}
}
HTML file:
<html>
<head>
<title>Test page</title>
<script>
function sendCustomEvent() {
var event;
if (typeof(Event) === 'function') {
event = new Event('MyCustomEvent');
} else {
event = document.createEvent('HTMLEvents');
event.initEvent('MyCustomEvent', true, false);
}
document.dispatchEvent(event);
}
</script>
</head>
<body>
<button onclick="sendCustomEvent()">Send custom event</button>
</body>
</html>
I was able to achieve the required functionality by using the WebBrowser controls 'ObjectForScripting' property. Nice and simple.
XAML remains the same as above.
WebInterface.cs:
namespace WebEvent2
{
[System.Runtime.InteropServices.ComVisibleAttribute(true)]
public class WebInterface
{
public void callMe()
{
MessageBox.Show("hello");
}
}
}
MainWindow.xaml.cs:
namespace WebEvent2
{
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
WebInterface wi = new WebInterface();
private void webBrowser1_Loaded(object sender, RoutedEventArgs e)
{
webBrowser1.ObjectForScripting = wi;
webBrowser1.Navigate(#"file:///D:/test.html");
}
}
}
test.html:
<html>
<head>
<title>Test page</title>
</head>
<body>
<button onclick="window.external.callMe()">Send custom event</button>
</body>
</html>

Call java applet method from JavaScript = is not a function

I have defined the following Java Applet:
import java.applet.Applet;
import java.awt.*;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
public class CombineData extends Applet{
private File destinationFile;
private FileOutputStream fileOutputStream;
public boolean setUpFile(String filePath) {
// This is just to check if it is a new file we write to or not
// We could return false at once saying file already exists
boolean result;
if ((destinationFile = new File(filePath)).exists()) {
result = false;
} else {
result = true;
}
try {
fileOutputStream = new FileOutputStream(destinationFile, true);
} catch (FileNotFoundException e) {
e.printStackTrace();
}
return result;
}
public boolean write_line(byte[] data) {
if (fileOutputStream == null) {
return false;
} else {
try {
fileOutputStream.write(data);
} catch (IOException e) {
e.printStackTrace();
}
return true;
}
}
public void finished() {
try {
fileOutputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
And the following JavaScript
function start_button() {
combineDataApplet = document.getElementById('combineDataApplet');
combineDataApplet.setUpFile("blabl");
var filename = "~/Downloads/" + prompt("Please enter file name (remember file extenstion)", "combined_data.txt");
console.log(filename);
}
And finally the HTML
<!DOCTYPE HTML>
<html lang="en">
<head>
<script src="js/page.js"></script>
</head>
<body>
<div id="content">
<div>
<applet id="combineDataApplet" code="CombineData.class" width="350" height="350">
APPLET
</applet>
<input id="start_button" type="button" onClick="start_button()" value="start"/>
</div>
</div>
</body>
</html>
And I get the following error: TypeError: combineDataApplet.setUpFile is not a function. (In 'combineDataApplet.setUpFile("blabl")', 'combineDataApplet.setUpFile' is undefined)
I have found a few post on stackoverflow which states that I need to put it into a div block without a display:none but there are no styles on any of my divs and by such there should be no display:none.
I hope someone can help me figure out what is wrong
Programming Applets is riding a dead horse.
untested: you should access the applet this way:
document.combineDataApplet.setUpFile("blabl");
and not by document.getElementById(). The latter only gets you the DOM element containing the applet.
Edit:
here is a link from January 2016 about Oracle deprecating Applets. This article gives the further link to Oracle's whitepaper for migrating applets. I haven't read it, but it might show you some alternatives.

Trying to create an event calendar using the datepicker javascript

I am attempting to use the javascript datepicker to help in showing a calendar and being able to select a date to add/view/edit an event on that date.
I currently have a view that shows the datepicker calender in a div
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Event Calendar</title>
<link rel="stylesheet" href="/Content/themes/base/jquery-ui.css">
<script src="/Scripts/jquery-1.10.2.js"></script>
<script src="/Scripts/jquery-ui-1.10.4.js"></script>
<link rel="stylesheet" href="/resources/demos/style.css">
<script>
$(function () {
$("#datepicker").datepicker();
});
</script>
</head>
<body>
Date: <div id="datepicker"></div>
</body>
</html>
I have a model Todays events that will show the events for the day
namespace TryEvents2.Models
{
public class TodaysEventsViewModel
{
public List<Event> events;
}
}
a model for the specific events
namespace TryEvents2.Models
{
[Bind(Exclude = "Id")]
public class Event
{
[ScaffoldColumn(false)]
public int Id { get; set; }
[DisplayName("Start Date")]
public DateTime Start { get; set; }
[DisplayName("End Date")]
public DateTime End { get; set; }
[DisplayName("Event Details")]
public string Message { get; set; }
[DisplayName("User")]
public string UserName { get; set; }
public void setEvent()
{
}
}
}
a model for the database calendar entities
namespace TryEvents2.Models
{
public class CalendarEntities : DbContext
{
public DbSet<Event> Events { get; set; }
}
}
I have a home controller of the following
namespace TryEvents2.Controllers
{
public class HomeController : Controller
{
CalendarEntities db = new CalendarEntities();
public ActionResult Index()
{
return View();
}
public ActionResult About()
{
return View();
}
public PartialViewResult GetWEntitiesByDate(DateTime date)
{
var entities = db.Events.Where(x => x.Start == date.Date);
var todaysEvents = new TodaysEventsViewModel {events = entities.ToList() };
return PartialView("_TodaysEvents", todaysEvents);
}
}
}
I am having difficulties going from here on creating the methods necessary to CRUD the calendar events and the view that would be displaying them. I am wanting to make the standard datepicker element bigger and have a popup window for the event detail display.
Can anyone help with this?
In my few experience doing something like that is quite difficult with jquery UI datepicker. If you want a calendar to works as a datepicker and able to edit events and shows depending on the day I think the best solution for you is Arshaw Fullcalendar
FullCalendar
This is a calendar (not a datepicker) but you can create custom events to make it works as a datepicker-calendar for your purposes. You can choose a date an render your events from a specific date. You are using MVC so for bringing the date you need JSON like this
public ActionResult GetEvents()
{
//code to return the events from DB
return Json(evevtsObject,JsonRequestBehavior.AllowGet);
}
And for the events to load on the calendar you need to call the "events" function from the calendar.
events: function (startdate, enddate, callback) {
var url = "Controller/GetEvents";
$.getJSON(url,
function (result) {
if (result != null) {
for (i in result) {
var calEvent = result[i];
calEvent.startdate = new Date(parseInt(calEvent.startdate.replace("/Date(", "").replace(")/", ""), 10));
calEvent.end = new Date(parseInt(calEvent.enddate.replace("/Date(", "").replace(")/", ""), 10));
}
}
var calevents = result;
callback(calevents);
});
}
Hope this helps.

JavascriptInterface in Android's WebView: multiple calls to JS cause deadlock

This is the entire Java code I've used. I will explain in more detail below...
public class Test7 extends Activity {
//debug
private final static String TAG = "JSInterface";
private WebView wv;
private class JSInterface {
private WebView wv;
// Variables to manage interfacing with JS
private String returnValue;
private boolean canReadReturnValue;
private Lock lockOnJS;
private Condition condVarOnJS;
public JSInterface (WebView wv) {
this.wv = wv;
this.canReadReturnValue = false;
this.lockOnJS = new ReentrantLock();
this.condVarOnJS = lockOnJS.newCondition();
}
public void setReturnValue(String ret) {
lockOnJS.lock();
returnValue = ret;
canReadReturnValue = true;
condVarOnJS.signal();
lockOnJS.unlock();
Log.d(TAG, "returnValue = " + returnValue);
}
public String getReturnValue() {
Log.d(TAG, "enter in getReturnValue");
lockOnJS.lock();
while (!canReadReturnValue) {
try {
Log.d(TAG, "get wait...");
condVarOnJS.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
lockOnJS.unlock();
Log.d(TAG, "returnValue: " + returnValue);
return returnValue;
}
public String getNewString() {
wv.loadUrl("javascript:JSInterface.setReturnValue(createNewString())");
return getReturnValue();
}
}
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
wv = (WebView) findViewById(R.id.webView1);
wv.getSettings().setJavaScriptEnabled(true);
wv.addJavascriptInterface(new JSInterface(wv), "JSInterface");
wv.loadUrl("file:///android_asset/prova7.html");
}
public void button1(View v) {
wv.loadUrl("javascript:func('1')");
}
}
And it seems work fine.
You can see that I've got a button (that we can call button1), and clicking on it, it tries to execute a JS method, called func().
public void button1(View v) {
wv.loadUrl("javascript:func('1')");
}
Inside this JS method, I have to call another Java method. This is the code:
function func(id) {
document.getElementById(id).innerHTML = JSInterface.getNewString();
}
I need to return the result of JSInterface.getNewString() to the innerHTML variable.
The code of JSInterface.getNewString() is this:
public String getNewString() {
wv.loadUrl("javascript:JSInterface.setReturnValue(createNewString())");
return getReturnValue();
}
You can see that I use the method setReturnValue and getReturnValue to return the value returned by another JS method. This is the code:
function createNewString() {
return "my New String";
}
The problem is that when I try to set the returnValue, the function createNewString is never executed! If I add a console.log() line, my logCat display nothing!
I cannot understand why this happens.
All the javascript and your JSInterface methods called from javascript are running on the single thread in Android WebView. So while you are waiting in condVarOnJS.await() no javascript can be executed, just because it is executed on the same thread.
Moreover, all the webview instances in your application share the same javascript thread.
In Internet Explorer I found the same problem. You can use setTimeout like this:
function func(id) {
setTimeout(
function(){
document.getElementById(id).innerHTML = JSInterface.getNewString();
},
500);
}
I did the functionality what you intend in that code,for me createNewString() is called,
I will show up the code i used,
In java
,
public String videoPlay(){
System.out.println("videoPlay");
mWebView.loadUrl("javascript:window.demo.setReturnValue(createNewString())");
return getReturnValue();}
public void setReturnValue(String test){
rotValue=test;
System.out.println(test);
}
public String getReturnValue(){
System.out.println("get"+rotValue);
return rotValue;
}
in HTML,
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<script>
function inform(){
alert('et');
document.getElementById('myText').value=window.demo.videoPlay();
alert('et');
}
function createNewString() {
return "my New String";
}
</script>
</head>
<body onload="init()">
<form>
<input type='text' id='myText' />
<input type="button" name="test" value="Click me" onclick="inform()">
</form>
</body>
</html>
The function getter and setter called and values also set, but i have the output log as..
11-08 19:18:17.155: INFO/System.out(847): videoPlay
11-08 19:18:17.165: INFO/System.out(847): getnull
11-08 19:18:17.875: INFO/System.out(847): my New String
videoPlay called from JS and createnewString() also called from java to JS , but it returns the value before it set , because i don`t what is the purpose to use lock , even i tried using lock as you did for that it will print
11-08 19:18:17.155: INFO/System.out(847): videoPlay
11-08 19:18:17.165: INFO/System.out(847): getnull
using lock also the function callback works in wrong manner, you need work on locks.

Categories