Solved

How do I use CoMarshalInterThreadInterfaceInStream to wrap an IHTMLElement?

Posted on 2009-04-01
7
1,616 Views
Last Modified: 2013-12-08
I need to access an IHTMLElement from another thread, in my Browser Helper Object.  Unfortunately, I've been unable to get it working without causing a segmentation fault when put_innerHTML is called.

Does anyone know how to (correctly) use CoMarshalInterThreadInterfaceInStream in a BHO?
// *********** This is the calls that are made in the main thread ************

       LPSTREAM st;

       CoMarshalInterThreadInterfaceInStream(IID_IHTMLElement, Status, &st);

       m_downloadParam.statusElement = reinterpret_cast<IHTMLElement*>(st);
 

       // Create a thread to download it

       m_pDownloadThread = CreateThread(NULL,

                                                                               0,

                                                                               StartYAIPPDownload,

                                                                               &m_downloadParam,

                                                                               CREATE_SUSPENDED,

                                                                               NULL);
 

// *********** This is the calls that are made in the second thread for downloading ************

DWORD WINAPI StartYAIPPDownload(PVOID pParam)

{

       CBhoApp::DOWNLOADPARAM* parameters =

reinterpret_cast<CBhoApp::DOWNLOADPARAM*>(pParam);

       IHTMLElement* Status = parameters->statusElement;

       BSTR URLToDownload = parameters->strURL;

       YAIPPDownloader* yaippdownloader = new YAIPPDownloader(Status);

       yaippdownloader->Status = Status; // pass the HTML element

       yaippdownloader->AddRef();

       HRESULT hr = URLDownloadToFile(NULL,"http://www.roket-games.com/games/19/gdownload"/*URLToDownload*/,"C:\\test.htm",0,yaippdownloader);
 

       if (hr)

       {

               YAIPPInProgress=false;

               YAIPPStatusText=L"Could Not Download File";

       }

       else

       {

               YAIPPInProgress=false;

               YAIPPStatusText=L"Downloaded File";

       }

       return S_OK;

}
 

// *********** This is the calls that are made in the downloader class's OnProgress function (note that the two Status variables ARE different) ************

if (Status != NULL)

	{

		char ulProgressBuffer [33];

		char ulProgressPercBuffer [33];

		counter++;

		if (ulProgressMax!=0)

			itoa((100/ulProgressMax*ulProgress),ulProgressPercBuffer,10);

		else

			itoa(counter,ulProgressPercBuffer,10);

		itoa(ulProgress,ulProgressBuffer,10);

		std::wstring text;

		text = L"Downloading... ";

		if (ulProgressMax!=0)

			text += ConvertToWideChar(ulProgressBuffer);

		else

			text += ConvertToWideChar(ulProgressPercBuffer);

		BSTR StatText;

		StatText = W2BSTR((text).c_str());
 

		YAIPPStatusText=StatText;

	

		// ************* SEGMENTATION FAULT ON NEXT LINE ****************

		Status->put_innerHTML(YAIPPStatusText);

	}

Open in new window

0
Comment
Question by:hach-que
  • 5
7 Comments
 
LVL 7

Accepted Solution

by:
HalfAsleep earned 500 total points
Comment Utility
Where do you do the
CoInitializeEx(NULL, COINIT_MULTITHREADED);

and the

CoGetInterfaceAndReleaseStream  ?

Without these in the other thread, the other thread still has not "received" the marshalled stream.

In one thread you need to to the

CoMarshalInterThreadInterfaceInStream ( longest method name ever)

In the other thread,  you need to use the
CoInitialize and the CoGetInterfaceAndReleaseStream, or the other one that does not release the stream, if you are doing it more than once.


thread 1:
 

_IHTMLElement* pIC;

HRESULT hr = QueryInterface(IID__IHTMLElement, (LPVOID*)&pIC);
 

hRunnerThread = CreateThread(NULL, 0, Runner, pIS, 0, &dwThreadId);
 

IStream* pIS;

hr = CoMarshalInterThreadInterfaceInStream(IID__IHTMLElement, pIC, &pIS);
 

Thread 2 (runner):

CoInitializeEx(NULL, COINIT_MULTITHREADED);

DWORD RetValue = S_OK;

_IHTMLElement* pIC;

HRESULT hr = CoGetInterfaceAndReleaseStream((IStream*) lParam, IID__IHTMLElement, (LPVOID*)&pIC);

_ASSERT(SUCCEEDED(hr));
 

// now you have a valid pointer to interface, the pIC.

pIC->put_innerHTML(YAIPPStatusText);

Open in new window

0
 
LVL 7

Assisted Solution

by:HalfAsleep
HalfAsleep earned 500 total points
Comment Utility
In my code, the CoMarshal should be above the CreateThread..sorry about that.
0
 
LVL 7

Assisted Solution

by:HalfAsleep
HalfAsleep earned 500 total points
Comment Utility
Also, remember to call pIC->Release() and
CoUninitialize(); before you exit your second thread, where you did the CoInitialize
0
Why You Should Analyze Threat Actor TTPs

After years of analyzing threat actor behavior, it’s become clear that at any given time there are specific tactics, techniques, and procedures (TTPs) that are particularly prevalent. By analyzing and understanding these TTPs, you can dramatically enhance your security program.

 
LVL 86

Expert Comment

by:jkr
Comment Utility
Take a look at http://svn.openqa.org/fisheye/browse/~raw,r=57/floyd/trunk/src/cpp/ie/IEBrowserObject.cpp - they do that like
void IEBrowserObject::click(IEHTMLElement *element, int button)

{

    COMAutoPtr<IHTMLDOMNode> comObj = element->getCOMObject();
 

	if (comObj != NULL)

	{

        COMAutoPtr<IHTMLElement2> elementObj = comObj.castTo<IHTMLElement2>(IID_IHTMLElement2);
 

	    if (elementObj != NULL)

	    {

            IStream * stream     = NULL;

            IUnknown *unknownPtr = NULL;
 

            elementObj->QueryInterface(IID_IUnknown, (void **)&unknownPtr);

            if (unknownPtr != NULL)

            {

                CoMarshalInterThreadInterfaceInStream(__uuidof(IHTMLElement2), unknownPtr, &stream);

                unknownPtr->Release();
 

                DWORD result = 0;
 

                sendMessageToThread(MSG_CLICK, (WPARAM)button, (LPARAM)stream, &result);

            }

            else

            {

                LOG4CXX_WARN(_logger, L"Cannot retrieve IDispatch pointer for the element object");

            }

        }

        else

        {

            throw IEBrowserException(L"Could not access the IHTMLElement2 interface for the element");

        }

	}

    else

    {

        throw IEBrowserException(L"Could not access the COM object of the element");

    }

}

Open in new window

0
 
LVL 7

Expert Comment

by:HalfAsleep
Comment Utility
Although you can use a windows handle and the windows message queue to communicate with the other thread, it is a slight cheat.

Don't get me wrong, it is a well known shortcut, and it is even documented on microsoft's own msdn webpages, but it is still not the "correct" way of doing it.

I had first hand experience of this, when I inherited the job of maintaining an old ActiveX automation server.  This old control relied indeed on a windows handle to communicate to the main thread from all its worker threads.  This made the code fairly straightforward, but it had some drawbacks.

First, the component needed to visually be added to a form (it needs a windows handle, remember?).

Second, and this is the reason we had to do it with "proper" marshaling, some environments will not let you cheat with a windows handle like that.  When we wanted to get our old control to work with WinXP/.NET, we discovered that .NET refused to give us such a windows handle (unmanaged code etc? I'm not sure).  We found that the only way to get around this, was to properly implement marshaling.  It was a bit tricky to get right at first, and the other method/cheat sure is easier to find on the net.  But once we had the proper marshaling code into place, we were not dependent on the windows handle and the windows message queue/pump any more.  The control now works in any environment we throw at it, be it .NET, delphi etc, and it does not even need to be tied to a form, it can just be referenced/created in code.
0
 
LVL 7

Expert Comment

by:HalfAsleep
Comment Utility
I used to have a good link to this stuff at work.  I will see if I can find it tomorrow.
0
 
LVL 2

Author Closing Comment

by:hach-que
Comment Utility
Thank you so much!  This stumped me for ages.

For anyone reading this answer, make sure you replace _IHTMLElement and IID__IHTMLElement with IHTMLElement and IID_IHTMLElement respectively.
0

Featured Post

Highfive + Dolby Voice = No More Audio Complaints!

Poor audio quality is one of the top reasons people don’t use video conferencing. Get the crispest, clearest audio powered by Dolby Voice in every meeting. Highfive and Dolby Voice deliver the best video conferencing and audio experience for every meeting and every room.

Join & Write a Comment

I annotated my article on ransomware somewhat extensively, but I keep adding new references and wanted to put a link to the reference library.  Despite all the reference tools I have on hand, it was not easy to find a way to do this easily. I finall…
Container Orchestration platforms empower organizations to scale their apps at an exceptional rate. This is the reason numerous innovation-driven companies are moving apps to an appropriated datacenter wide platform that empowers them to scale at a …
The viewer will learn how to pass data into a function in C++. This is one step further in using functions. Instead of only printing text onto the console, the function will be able to perform calculations with argumentents given by the user.
The viewer will learn how to use the return statement in functions in C++. The video will also teach the user how to pass data to a function and have the function return data back for further processing.

762 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question

Need Help in Real-Time?

Connect with top rated Experts

7 Experts available now in Live!

Get 1:1 Help Now