How to write simple background thread in CWorkerThread

微笑、不失礼 提交于 2019-12-12 09:02:00

问题


I'm trying to asynchronously run function in my add-on for Internet Explorer (I'm writing BHO in VC++). As suggested here I'm trying to use CWorkerThread.

I've been trying to figure it out for hours but still have no idea how to do it. I don't have much experience in ATL. The lack of a good documentations or tutorials on Internet is killing me.

I'm creating class by Add->Class and choosing ATL Simple Object (that's how you add classed to ATL project right?). But how to implement this IWorkerThreadClient? I thought that choosing Add->Implement Interface in Class View would be good but there is no IWorkerThreadClient on the list.

I think I don't know ATL or COM enaugh but can't find good resource for learning this (esspessialy newest ATL7).

I even tried winapi CreateThread approach but it isn't working. I'm passing this class pointer to run static method but something is corrupting with memory later. Nevertheless if It had worked I still would rather use something else than CreateThread.

Right now I have something like this. In OnDocumentComplete there's RemoveImages(sptmlDoc) and I just want to run it asynchronously.

EDIT: What I did with CreateThread:

I tried running RemoveImages function (from here) asynchronously. I created static function in my class with signature like here. RemoveImages has parameter so I copied it to a member of a class:

if (htmlDoc2 != NULL)
{
    m_tmpHtmlDocument2 = htmlDoc2;
    m_hThread = CreateThread( NULL, 0, MyThreadFunction, this, 0, &m_threadId);
}

and MyThreadFunction:

static DWORD WINAPI MyThreadFunction( LPVOID lpParam ) 
{ 
    CHelloWorldBHO* myClass = (CHelloWorldBHO*)lpParam;
    myClass->RemoveImages(myClass->m_tmpHtmlDocument2);

    return 0; 
}

I get "Unhandled exception at 0x60c0da05 in iexplore.exe: 0xC0000005: Access violation reading location 0x000001b8." here in the bold line:

void CHelloWorldBHO::DontDisplayElement(CComPtr htmlElement)
{
    CComPtr style;
    HRESULT hr = htmlElement->get_style(&style);

    if (hr == S_OK && style != NULL)
    {       
        static const CComBSTR strNone(L"none");
        style->put_display(strNone);
    }
}

回答1:


Your performing a naughty by trying to use a COM handle allocated in 1 thread in another. BHO environment is STA (Single Threaded Apartment) so you should be marshalling the m_tmpHtmlDocument2 object for use in your thread.

Experiance has shown that in some cases IE may let you get away with passing the Browser com object from 1 thread to another and then getting the document and elements afterwards may work. This is entirely unreliable.

Depending on IE 6/7/8 you will have different target threads to execute your actions on, thinking at the levels of per security level/frame/tab/window. basically any time IE creates a new 'Site'

Also to prevent your app from holding the pages active even after navigation away from the page, in FireFox you would use an nsWeakPointer<> , I've never found the equivelant in IE.

Suggestion: Perhaps instead of marshalling com to another thread because your interaction with the page is slow, trying to improve the way you interact with the page and improve performance in process might be a better aim.

Here is an outline using the CThreadPool which will queue up requests, and then execute them when the pool has space.
I use pvWorkerParam to tie the threads back to the site.
I have different types of ActionRequests, you could of course simplify and just pass null for the request.
Note: This doesn't resolve marshalling issues you already have

class ActionRequest{ 
  DontDisplayElement();// define your do stuff in here 
};

class ScriptWorker
{
public:
ScriptWorker(void);
virtual ~ScriptWorker(void);

public:
BOOL Initialize(void* pvWorkerParam);
void Execute(ActionRequest *request, void* pvWorkerParam, OVERLAPPED* pOverlapped){
try{
       std::auto_ptr<ActionRequest> cleanupRequest(request);
       request.DontDisplayElement();
    } catch(...) {}
    }
void Terminate(void* pvWorkerParam);

private:
boolean m_bCoUninit;
};

Site{
  CThreadPool<ScriptWorker> m_scriptWorkerThread;
  Site() {
    void *pvWorkerParam = this;// or whatever you want to have passed to every script worker and execute in the pool.
    m_scriptWorkerThread.Initialize( pvWorkerParam, 1 );
  }
  OnDocumentComplete() {
    m_scriptWorkerThread.QueueRequest( new ActionRequest() );
  }
}



回答2:


and sptmlDoc - is it an IHTMLDocumet* ?

IWorkerThreadClient - never heard of it

"I even tried winapi CreateThread approach but it isn't working. I'm passing this class pointer to run static method but something is corrupting with memomory later"

Keeping it simple is the best design pattern of them all. So stick with CreateThread unless you have good reasons not to. Now, my guess is that the crash occurs because of sptmlDoc being passed to the thread for later processing. The thing is such pointers are only valid from the BeforeNavigate event until DocumentComplete event. Try to do that processing on the spot (inside your event handler) and see if it stil crashes. Some code posting would help too



来源:https://stackoverflow.com/questions/4294629/how-to-write-simple-background-thread-in-cworkerthread

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!