2011-11-15 80 views
0

每個人。 我在ATL的ActiveX編程中遇到了一些麻煩。我嘗試製作一個可以從http服務器異步下載文件到本地文件夾的ActiveX,下載後它將調用JavaScript回調函數。 我的解決方案:運行一個線程M來監視下載線程D,當D完成工作時,M將自己終端並調用IDispatch接口來調用javascript函數。 * ** * ** * ** * ** * ** *有我的代碼:* ** * ** * ** * ** * ** *麻煩在ActiveX多線程調用javascript回調例程

/* javascript code */ 
funciton download() { 
    var xfm = new ActiveXObject("XFileMngr.FileManager.1"); 
    xfm.download(
    'http://somedomain/somefile','localdev:\\folder\localfile',function(msg){alert(msg);}); 
} 

/* C++ code */ 

// main routine 
STDMETHODIMP CFileManager::download(BSTR url, BSTR local, VARIANT scriptCallback) 
{ 
    CString csURL(url); 
    CString csLocal(local); 

    CAsyncDownload download; 
    download.Download(this, csURL, csLocal, scriptCallback); 

    return S_OK; 
} 

// parts of CAsyncDownload.h 
typedef struct tagThreadData { 
    CAsyncDownload* pThis; 
} THREAD_DATA, *LPTHREAD_DATA; 

class CAsyncDownload : 
    public IBindStatusCallback 
{ 
private: 
    LPUNKNOWN pcaller; 
    CString csRemoteFile; 
    CString csLocalFile; 
    CComPtr<IDispatch> spCallback; 
public: 
    void onDone(HRESULT hr); 

    HRESULT Download(LPUNKNOWN caller, CString& csRemote, CString& csLocal, VARIANT callback); 

    static DWORD __stdcall ThreadProc(void* param); 
}; 
// parts of CAsyncDownload.cpp 
void CAsyncDownload::onDone(HRESULT hr) { 
    if(spCallback) { 
     TRACE(TEXT("invoke callback function\n")); 
     CComVariant vParams[1]; 
     vParams[0] = "callback is working!"; 

     DISPPARAMS params = { vParams, NULL, 1, 0 }; 

     HRESULT hr = spCallback->Invoke(0, 
      IID_NULL, 
      LOCALE_USER_DEFAULT, 
      DISPATCH_METHOD, 
      &params, NULL, NULL, NULL); 

     if(FAILED(hr)) { 
      CString csBuffer; 
      csBuffer.Format(TEXT("invoke failed, result value: %d \n"),hr); 
      TRACE(csBuffer); 
     }else { 
      TRACE(TEXT("invoke was successful\n")); 
     } 
    } 
} 

HRESULT CAsyncDownload::Download(LPUNKNOWN caller, CString& csRemote, CString& csLocal, VARIANT callback) { 
    CoInitializeEx(NULL, COINIT_MULTITHREADED); 

    csRemoteFile = csRemote; 
    csLocalFile = csLocal; 
    pcaller = caller; 

    switch(callback.vt){ 
     case VT_DISPATCH: 
     case VT_VARIANT:{ 
      spCallback = callback.pdispVal; 
     } 
     break; 
     default:{ 
      spCallback = NULL; 
     } 
    } 

    LPTHREAD_DATA pData = new THREAD_DATA; 
    pData->pThis = this; 

    // create monitor thread M 
    HANDLE hThread = CreateThread(NULL, 0, ThreadProc, (void*)(pData), 0, NULL); 

    if(!hThread) { 
     delete pData; 
     return HRESULT_FROM_WIN32(GetLastError()); 
    } 

    WaitForSingleObject(hThread, INFINITE); 
    CloseHandle(hThread); 

    CoUninitialize(); 

    return S_OK; 
} 


DWORD __stdcall CAsyncDownload::ThreadProc(void* param) { 
    LPTHREAD_DATA pData = (LPTHREAD_DATA)param; 

    // here, we will create http download thread D 
    // when download job is finish, call onDone method; 

    pData->pThis->onDone(S_OK); 

    delete pData; 

    return 0; 
} 

** * ** * ** * ** * ** * *代號結束* ** * ** * ** * ** * ** * OK,上面是我的源代碼的一部分,如果我在子線程中調用onDone方法, 我會得到OLE ERROR(-2147418113(8000FFFF)災難性故障)。 我錯過了什麼嗎?請幫我弄明白。

回答

2

IE的JavaScript引擎是單線程的,ATL的事件提升代碼也是如此。讓子線程向創建ActiveX的線程發送消息(例如,如果有的話,則發送到ActiceX窗口的句柄),然後引發該事件。

+0

他擊敗了我... COM通常在一個線程上工作。還有其他一些方法來設置其他線程,以便您可以在它們上使用COM,然後將線程中的對象編組在一起,但這裏提到的方法要簡單得多(並且我實際上不知道如何去做另一個線程,只是知道它可以完成)。 – taxilian

+0

謝謝,江。它是不可見的ActiveX對象,它會靜靜地與硬件通信,所以我創建了一個線程來等待信號,如果有信號它會調用腳本回調函數,這是我想要的。如果ActiveX主線程等待讀線程完成,它會阻塞瀏覽器響應,我想也許IWebBrowser2(獲取腳本調用接口)或COM連接點(COM事件)將工作,我會嘗試它。 – code0tt

+0

我找到了一個話題:作者做了一個例子在同一個線程中調用javascript,他說:CoMarshalInterface和CoUnmarshalInterface可以找出多線程腳本回調,這是否與Taxilian的想法一樣? – code0tt