JavaScript Calls from C++



Click here for a larger image.

Environment: VC++ 6.0, NT 4.0, Win2000, Win95/98

Introduction

Sometimes, when we are using the IE Browser Control inside of a C++ application, we need to access the HTML elements. We can do it by using standard COM objects such as IWebBrowser2, IHTMLDocument2, and so forth. By doing this, we easily can implement features such as click button, click anchor, get input string, get HTML text, and so on. Unfortunately, Microsoft did not provide similar objects for JavaScript. In any case, it is possible to make a control for the JavaScript object inside an HTML page by using a traditional COM approach. This article describes the class CWebPage that allows you to do it and a technique to call a JavaScript function from C++ code.

How to Do This

As the result of using the presented class, it will be easy to call any JavaScript function from C++ code. To implement this feature, we should get a pointer to the IHTMLDocument2 interface. If we are using the CHtmlView class from MFC, we can get one by using member function CHtmlView::GetHtmlDocument(). In the case of using the IWebBrowser or IWebBrowser2 components, the function get_Document will bring us the desired interface. Here is an example:

CComPtr<IDispatch> spDisp = CHtmlView::GetHtmlDocument();
m_webPage.SetDocument(spDisp);

The rest of the things will be done by the CWebPage class. Here is an example of a JavaScript call without parameters.

m_webPage.CallJScript("Welcome");

The example of the JavaScript call with two parameters will look like this:

m_webPage.CallJScript("Miltiply","2.34","3.32");

The Class Implementation

class CWebPage
{
public:
  CWebPage();
  virtual ~CWebPage();

  bool SetDocument(IDispatch* pDisp);
  LPDISPATCH GetHtmlDocument() const;
  const CString GetLastError() const;
  bool GetJScript(CComPtr<IDispatch>& spDisp);
  bool GetJScripts(CComPtr<IHTMLElementCollection>& spColl);
  CString ScanJScript(CString& strAText,CStringArray& args);

  bool CallJScript(const CString strFunc);
  bool CallJScript(const CString strFunc,const CString strArg1);
  bool CallJScript(const CString strFunc,const CString strArg1,
                   const CString strArg2);
  bool CallJScript(const CString strFunc,const CString strArg1,
                   const CString strArg2,const CString strArg3);
  bool CallJScript(const CString strFunc,const 
                         CStringArray& paramArray);

protected

  CComPtr<IHTMLDocument2> m_spDoc;

};

Calling Technique

The previously mentioned technique splits the following steps:

  • Getting a pointer to the IHTMLDocument2 interface.
  • Getting IDispatch for a JavaScript object in an HTML document.
  • Getting DISPID for a given name of a JavaScript function.
  • Putting parameters to the DISPPARAM structure.
  • Calling a JavaScript function by using the Invoke method of the IDispatch interface.

Here is an example of getting a IDispatch pointer to the Java Scripts objects:

bool CWebPage::GetJScript(CComPtr<IDispatch>& spDisp)
{
  HRESULT hr = m_spDoc->get_Script(&spDisp);
  ATLASSERT(SUCCEEDED(hr));
  return SUCCEEDED(hr);
}

And here is the final function to call JavaScript:

CComVariant CWebPage::CallJScript(const CString strFunc,
                                  const CStringArray&
                                        paramArray)
{
  //Getting IDispatch for Java Script objects
  CComPtr<IDispatch> spScript;
  if(!GetJScript(spScript))
  {
    ShowError("Cannot GetScript");
    return false;
  }
  //Find dispid for given function in the object
  CComBSTR bstrMember(strFunc);
  DISPID dispid = NULL;
  HRESULT hr = spScript->GetIDsOfNames(IID_NULL,&bstrMember,1,
                         LOCALE_SYSTEM_DEFAULT,&dispid);
  if(FAILED(hr))
  {
    ShowError(GetSystemErrorMessage(hr));
    return false;
  }

  const int arraySize = paramArray.GetSize();
  //Putting parameters
  DISPPARAMS dispparams;
  memset(&dispparams, 0, sizeof dispparams);
  dispparams.cArgs      = arraySize;
  dispparams.rgvarg     = new VARIANT[dispparams.cArgs];
  dispparams.cNamedArgs = 0;
  
  for( int i = 0; i < arraySize; i++)
  {
    CComBSTR> bstr = paramArray.GetAt(arraySize - 1 - i);
              // back reading
    bstr.CopyTo(&dispparams.rgvarg[i].bstrVal);
    dispparams.rgvarg[i].vt = VT_BSTR;
  }
  EXCEPINFO excepInfo;
  memset(&excepInfo, 0, sizeof excepInfo);
  CComVariant vaResult;
  UINT nArgErr = (UINT)-1;      // initialize to invalid arg
  //Call JavaScript function
  hr = spScript->Invoke(dispid,IID_NULL,0,
                        DISPATCH_METHOD,&dispparams,
                        &vaResult,&excepInfo,&nArgErr);
  delete [] dispparams.rgvarg;
  if(FAILED(hr))
  {
    ShowError(GetSystemErrorMessage(hr));
    return false;
  }
  return vaResult;
}

Notes About the Demo

To call a JavaScript function from the demo, you should select the function in the tree of the left window. After this, press the ! button on the menu bar.

Downloads

Download demo project - 34 Kb
Download source - 3 Kb



Comments

  • Willing to hire someone to get SVG graphics working in Visual Studio CPP code

    Posted by Mark on 04/11/2014 03:23pm

    Hi, for my project I need to load, display, and move about SVG images. The 'JSCalls_demo' works well with the javascript elements of my sample pages but the graphics don't appear. An example, the following doesn't appear: Later I want to move around an image with the id tag from javascript. Works in IE11 and Chrome. I want to add this to a C++ windows program as with the 'JSCalls_demo' and 'mingw-webpage ' examples. If you can help with this, or point out a link that's good. I'm willing to pay someone to complete this. Sorry if I shouldn't ask to hire someone on this page, not sure of the rules here. I don't see a way to directly contact Eugene Khodakovsky. Thanks Mark

    Reply
  • Getting DISPID for a given name of a JavaScript function. FAILS

    Posted by bhargava.mohan on 04/18/2010 04:41pm

    I am using the above code in a ATL control. I can load the local HTML page and javascript and also see the functions when i call the scanjavascript function but my call to get Get DISPID fails and returns -1 as dispid. Any help is appreciated....

    Reply
  • More explanation

    Posted by inbugable on 10/21/2004 02:01am

    ROAR! Please explain some more. Plus, the code I downloaded is not the same as the code on your page. There are no CComVariant returning functions. Granted, the return has been put in as a parameter... but you know. I always likes me behind spotsless.

    Reply
  • Why doyou need to do back reading the parameter?

    Posted by Legacy on 02/04/2004 12:00am

    Originally posted by: Henry Saputra

    HI,

    I was wondering why do you nee to back reading for paramArray?

    Thanks,

    HS

    Reply
  • how to call a java classes (with RMI implementation) from c++ code

    Posted by Legacy on 03/31/2003 12:00am

    Originally posted by: ruilin yang

    I like to call java classes (implementation of RMI for multiple clients) from c++. I do not know how to call.

    Please help with code examples or guidelines.

    Thanks
    Ruilin

    Reply
  • how to automatically click a link

    Posted by Legacy on 02/20/2003 12:00am

    Originally posted by: Juan Romerio

    Hello,
    U mentioned that clicking a link/button can be easily implemented. How to do that?
    basically, I want to programatically open a web page, and click the link/button in IE.
    Thank you!
    Juan

    Reply
  • Caution: Memory leak in the CWebPage class

    Posted by Legacy on 02/14/2003 12:00am

    Originally posted by: Mike

    Hi,

    First of all thanks for the class, very innovative. I have used it with great success. However, I wanted to point out a memory leak in the following function:

    CComVariant CWebPage::CallJScript(const CString strFunc,
    const CStringArray&
    paramArray)

    The leak occurs here:

    for( int i = 0; i < arraySize; i++)
    {
    CComBSTR> bstr = paramArray.GetAt(arraySize - 1 - i);
    // back reading
    bstr.CopyTo(&dispparams.rgvarg[i].bstrVal);
    dispparams.rgvarg[i].vt = VT_BSTR;
    }

    Although the array is later deleted, each of the BSTRs in the array is lost, creating a nice memory leak. It is easily correctable, as follows:


    Add the following loop right BEFORE the call to delete [], so it would look like this:

    for(i = 0; i < arraySize; i++)
    {
    _bstr_t _bstrVal(dispparams.rgvarg[i].bstrVal,
    FALSE);
    }

    delete [] dispparams.rgvarg;

    You'll need to #include "comdef.h" if you are not already using it, this is for the _bstr_t

    Good Luck!

    • Very good!

      Posted by springbreak78 on 12/15/2004 01:28am

      Yes, it's sure, and it's very difficult to find the memory leak.

      Reply
    Reply
  • How can I check if Java Script is enabled on the computer?

    Posted by Legacy on 02/01/2003 12:00am

    Originally posted by: Paul Hurt

    How can I check, inside my program, if JS is enabled on the computer?

    Reply
  • How to do this in C# and .NET ?

    Posted by Legacy on 01/15/2003 12:00am

    Originally posted by: Chak

    How would i do this if i wanted a C# form, which has an embedded IE control, which renders HTML, which has Javascript in it, which calls a .NET method for the data updation ? (Is that a bit too long ?).

    Thanks.

    Reply
  • How to use IE's menu and toll bars in your exampe instead of your own?

    Posted by Legacy on 11/20/2002 12:00am

    Originally posted by: Christian Schildt

    Can the standard menu and currently active tool bars of the IE be used in your example instead of your own? Is it easy or does it require completely new design?

    Reply
  • Loading, Please Wait ...

Leave a Comment
  • Your email address will not be published. All fields are required.

Top White Papers and Webcasts

  • Learn How A Global Entertainment Company Saw a 448% ROI Every business today uses software to manage systems, deliver products, and empower employees to do their jobs. But software inevitably breaks, and when it does, businesses lose money -- in the form of dissatisfied customers, missed SLAs or lost productivity. PagerDuty, an operations performance platform, solves this problem by helping operations engineers and developers more effectively manage and resolve incidents across a company's global operations. …

  • Live Event Date: December 18, 2014 @ 2:00 p.m. ET / 11:00 a.m. PT The Internet of Things (IoT) incorporates physical devices into business processes using predictive analytics. While it relies heavily on existing Internet technologies, it differs by including physical devices, specialized protocols, physical analytics, and a unique partner network. To capture the real business value of IoT, the industry must move beyond customized projects to general patterns and platforms. Check out this upcoming webcast …

Most Popular Programming Stories

More for Developers

RSS Feeds