COMコンポーネント開発実践(七)---マルチスレッドActiveXコントロールと自動調整ActiveXコントロールサイズ(上)
転載先:http://www.cnblogs.com/phinecos/archive/2008/12/29/1364675.html
声明:本コードはCodeProjectの文章『A Complete ActiveX Web Control Tutorial』に基づいて修正されたので、同じくCode Project Open License(CPOL)に従う.
基于的热门,构筑项目.1)VS 2005将新编成MFC ActiveX控制项目.
2)MFC ActiveX Control WIzardダイアログボックスで
私たちはこの制御のみで制御からの出力をして入力を受け付けないので、STATICを選択します.Activates when visible and Flicker-free activationがチェックされていることを確認します.
Finishを選択すると、プロジェクトが作成されます.
3)静的リンクMFCライブラリの選択
この制御はMFC DLLがない場所で使用するため、エンジニアリングプロパティで静的リンクMFCライブラリを選択します.
このように作成されたプロジェクトには、次の3つのクラスがあります.
**CMyActiveXApp--COleControlModuleから継承されたActiveXアプリケーションクラスです.これはOLEコントロールモジュールオブジェクトから継承されたベースクラスです.メンバー関数:InitInstanceの初期化とExitInstanceの終了
**CMyActiveXCtrl--基本クラスのCOleControlから継承され、コントロールの大部分を実行します.
**CMyActiveXPropPage--コントロールのプロパティ・ページを管理するためにベースクラスのCOlePropertyPageから継承されます.Activeコントロールウィザードは、コントロールのプロパティページとしてデフォルトのダイアログボックスを作成しました.
4)アニメーションGIFのサポートを追加
ActiveXコントロールがアニメーションGIFを表示する機能をサポートするために、CPictureExクラスで表現します.
VS 2005のリソースにGIFピクチャを追加すると、「このファイルは使用可能なGIFファイルではありません」というエラーレポートが表示されます.次の手順で解決できます.
(4、1)GIFファイルをProcessingProgressBar.gifはプロジェクト作業ディレクトリにコピーし、ProcessingProgressBarと名前を変更します.gaf .
リソースビューでMyActiveXを右クリックします.rcファイル、リソースの追加を選択し、ProcessingProgressBarを選択します.gaf、リソースタイプ定義時にGIFを記入
最近2つのニーズに遭遇しました.
1)ActiveXコントロールで作業スレッドを使用して最下位のハードウェアデバイススキャンタスクを完了し、作業スレッドで操作結果に基づいて外部ウェブページのJavaScript関数をコールバックする.
2)コントロールタスクに応じてコントロールサイズを自動的に調整できます.
しかし、大量の資料を調べた後、ネット上でActiveXの中でマルチスレッド開発を議論する文章はほとんどなく、最後にcsdnフォーラムで達人に助けられた後、数日でこの2つの問題を解決することを模索した.本文の目的は私がこの2つの問題を解決する過程を記録し、後で同じ需要のある友达を助けることを望んでいる.
最初のタスクを簡単に抽象化するモデル:AcitveXコントロールでワークスレッドを開いてフィーチャータスクを実行した後、ワークスレッドの実行結果に基づいて外部のwebページのJavaScriptを通知します.マルチスレッドに入る前に、ActiveXで外部Webページを呼び出すJavaScript関数の2つの方法について説明します.
ActiveXでJavaScriptを呼び出す
1つ目の方法はイベントを使用することです.これは最も簡単な方法です.
[クラスビュー]で、CMyActiveXCtrlを右クリックし、[イベントを追加]を選択します.具体的な実装手順は次のとおりです.
1、イベント「ParameterLoaded」を追加する
2、関数でFireParameterLoadedを呼び出す
声明:本コードはCodeProjectの文章『A Complete ActiveX Web Control Tutorial』に基づいて修正されたので、同じくCode Project Open License(CPOL)に従う.
基于的热门,构筑项目.1)VS 2005将新编成MFC ActiveX控制项目.
2)MFC ActiveX Control WIzardダイアログボックスで
私たちはこの制御のみで制御からの出力をして入力を受け付けないので、STATICを選択します.Activates when visible and Flicker-free activationがチェックされていることを確認します.
Finishを選択すると、プロジェクトが作成されます.
3)静的リンクMFCライブラリの選択
この制御はMFC DLLがない場所で使用するため、エンジニアリングプロパティで静的リンクMFCライブラリを選択します.
このように作成されたプロジェクトには、次の3つのクラスがあります.
**CMyActiveXApp--COleControlModuleから継承されたActiveXアプリケーションクラスです.これはOLEコントロールモジュールオブジェクトから継承されたベースクラスです.メンバー関数:InitInstanceの初期化とExitInstanceの終了
**CMyActiveXCtrl--基本クラスのCOleControlから継承され、コントロールの大部分を実行します.
**CMyActiveXPropPage--コントロールのプロパティ・ページを管理するためにベースクラスのCOlePropertyPageから継承されます.Activeコントロールウィザードは、コントロールのプロパティページとしてデフォルトのダイアログボックスを作成しました.
4)アニメーションGIFのサポートを追加
ActiveXコントロールがアニメーションGIFを表示する機能をサポートするために、CPictureExクラスで表現します.
VS 2005のリソースにGIFピクチャを追加すると、「このファイルは使用可能なGIFファイルではありません」というエラーレポートが表示されます.次の手順で解決できます.
(4、1)GIFファイルをProcessingProgressBar.gifはプロジェクト作業ディレクトリにコピーし、ProcessingProgressBarと名前を変更します.gaf .
リソースビューでMyActiveXを右クリックします.rcファイル、リソースの追加を選択し、ProcessingProgressBarを選択します.gaf、リソースタイプ定義時にGIFを記入
最近2つのニーズに遭遇しました.
1)ActiveXコントロールで作業スレッドを使用して最下位のハードウェアデバイススキャンタスクを完了し、作業スレッドで操作結果に基づいて外部ウェブページのJavaScript関数をコールバックする.
2)コントロールタスクに応じてコントロールサイズを自動的に調整できます.
しかし、大量の資料を調べた後、ネット上でActiveXの中でマルチスレッド開発を議論する文章はほとんどなく、最後にcsdnフォーラムで達人に助けられた後、数日でこの2つの問題を解決することを模索した.本文の目的は私がこの2つの問題を解決する過程を記録し、後で同じ需要のある友达を助けることを望んでいる.
最初のタスクを簡単に抽象化するモデル:AcitveXコントロールでワークスレッドを開いてフィーチャータスクを実行した後、ワークスレッドの実行結果に基づいて外部のwebページのJavaScriptを通知します.マルチスレッドに入る前に、ActiveXで外部Webページを呼び出すJavaScript関数の2つの方法について説明します.
ActiveXでJavaScriptを呼び出す
1つ目の方法はイベントを使用することです.これは最も簡単な方法です.
[クラスビュー]で、CMyActiveXCtrlを右クリックし、[イベントを追加]を選択します.具体的な実装手順は次のとおりです.
1、イベント「ParameterLoaded」を追加する
BEGIN_EVENT_MAP(CMyActiveXCtrl, COleControl)
EVENT_CUSTOM_ID("ParameterLoaded", eventidParameterLoaded, FireParameterLoaded, VTS_NONE)
END_EVENT_MAP()
2、関数でFireParameterLoadedを呼び出す
void CMyActiveXCtrl::LoadParameter(void)
{
AFX_MANAGE_STATE(AfxGetStaticModuleState());
// TODO: Add your dispatch handler code here
<SCRIPT FOR=MyActiveX1 EVENT=ParameterLoaded()>
<!-- {
window.document.write("The parameter you entered is:<br> " + MyActiveX1.OutputParameter + " ")
-->
//Copy text from the input parameter to the output parameterm_OutputParameter = m_InputParameter;//Fire an event to notify web page//FireParameterLoaded();FireParameterLoaded();CString strOnLoaded("OnLoaded");this->CallJScript(strOnLoaded);}3、 javaScript LoadParameter.
function PassParameter()
{
if (StringInput.value != " ")
{
MyActiveX1.InputParameter = StringInput.value;
MyActiveX1.LoadParameter();
}
}
4、 javaScript "ParameterLoaded"
<SCRIPT FOR=MyActiveX1 EVENT=ParameterLoaded()>
<!-- {
window.document.write("The parameter you entered is:<br> " + MyActiveX1.OutputParameter + " ")
-->
</SCRIPT>
2つ の は、ActiveXコントロールを む Webページ のすべての にアクセスするために、IWebBrowser 2とIHTML Document 2の2つのCOMコンポーネントを することです.
な は のとおりです.
1.CMyActiveXCtrlクラスに2つの を します.
IWebBrowser2* pWebBrowser; //web
IHTMLDocument2* pHTMLDocument; // web
2、OnSetClientSite を ロードします.void CMyActiveXCtrl::OnSetClientSite()
{
HRESULT hr = S_OK;
IServiceProvider *isp, *isp2 = NULL;
if (!m_pClientSite)
{
COMRELEASE(pWebBrowser);
}
else
{
hr = m_pClientSite->QueryInterface(IID_IServiceProvider, reinterpret_cast<void **>(&isp));
if (FAILED(hr))
{
hr = S_OK;
goto cleanup;
}
hr = isp->QueryService(SID_STopLevelBrowser, IID_IServiceProvider, reinterpret_cast<void **>(&isp2));
if (FAILED(hr))
{
hr = S_OK;
goto cleanup;
}
hr = isp2->QueryService(SID_SWebBrowserApp, IID_IWebBrowser2, reinterpret_cast<void **>(&pWebBrowser));// IE
if (FAILED(hr))
{
hr = S_OK;
goto cleanup;
}
hr = pWebBrowser->get_Document((IDispatch**)&pHTMLDocument);
if(FAILED(hr))
{
hr = S_OK;
goto cleanup;
}
cleanup:
// Free resources.
COMRELEASE(isp);
COMRELEASE(isp2);
}
}
3,コントロールはロード にOnSetClientSite を び すので,コントロールを むWebページに して され,このページがあれば の を いてWebページのJavaScript を び すことができる.
のコードはCodeGuluの 『JavaScript Calls from C++』から ており、 があれば しく むことができます.bool CMyActiveXCtrl::GetJScript(CComPtr<IDispatch>& spDisp)
{
CHECK_POINTER(pHTMLDocument);
HRESULT hr = pHTMLDocument->get_Script(&spDisp);
ATLASSERT(SUCCEEDED(hr));
return SUCCEEDED(hr);
}
bool CMyActiveXCtrl::GetJScripts(CComPtr<IHTMLElementCollection>& spColl)
{
CHECK_POINTER(pHTMLDocument);
HRESULT hr = pHTMLDocument->get_scripts(&spColl);
ATLASSERT(SUCCEEDED(hr));
return SUCCEEDED(hr);
}
bool CMyActiveXCtrl::CallJScript(const CString strFunc,CComVariant* pVarResult)
{
CStringArray paramArray;
return CallJScript(strFunc,paramArray,pVarResult);
}
bool CMyActiveXCtrl::CallJScript(const CString strFunc,const CString strArg1,CComVariant* pVarResult)
{
CStringArray paramArray;
paramArray.Add(strArg1);
return CallJScript(strFunc,paramArray,pVarResult);
}
bool CMyActiveXCtrl::CallJScript(const CString strFunc,const CString strArg1,const CString strArg2,CComVariant* pVarResult)
{
CStringArray paramArray;
paramArray.Add(strArg1);
paramArray.Add(strArg2);
return CallJScript(strFunc,paramArray,pVarResult);
}
bool CMyActiveXCtrl::CallJScript(const CString strFunc,const CString strArg1,const CString strArg2,const CString strArg3,CComVariant* pVarResult)
{
CStringArray paramArray;
paramArray.Add(strArg1);
paramArray.Add(strArg2);
paramArray.Add(strArg3);
return CallJScript(strFunc,paramArray,pVarResult);
}
bool CMyActiveXCtrl::CallJScript(const CString strFunc, const CStringArray& paramArray,CComVariant* pVarResult)
{
CComPtr<IDispatch> spScript;
if(!GetJScript(spScript))
{
//ShowError("Cannot GetScript");
return false;
}
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();
DISPPARAMS dispparams;
memset(&dispparams, 0, sizeof dispparams);
dispparams.cArgs = arraySize;
dispparams.rgvarg = new VARIANT[dispparams.cArgs];
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;
}
dispparams.cNamedArgs = 0;
EXCEPINFO excepInfo;
memset(&excepInfo, 0, sizeof excepInfo);
CComVariant vaResult;
UINT nArgErr = (UINT)-1; // initialize to invalid arg
hr = spScript->Invoke(dispid,IID_NULL,0,
DISPATCH_METHOD,&dispparams,&vaResult,&excepInfo,&nArgErr);
delete [] dispparams.rgvarg;
if(FAILED(hr))
{
//ShowError(GetSystemErrorMessage(hr));
return false;
}
if(pVarResult)
{
*pVarResult = vaResult;
}
return true;
}
4, の2つのJavaScript を び す をテストすることができますが、 にするために、 コードに づいて しました.void CMyActiveXCtrl::LoadParameter(void)
{
AFX_MANAGE_STATE(AfxGetStaticModuleState());
// TODO: Add your dispatch handler code here
// Copy text from the input parameter to the output parameter
m_OutputParameter = m_InputParameter;
// Fire an event to notify web page
//FireParameterLoaded();
FireParameterLoaded();
CString strOnLoaded("OnLoaded");
this->CallJScript(strOnLoaded);
}
Webページにテスト のJavaScript を しましたfunction OnLoaded()
{
alert("phinecos");
}
マルチスレッドActiveXコントロール
JavaScript を び した で、コントロールに スレッドを し、タスクの に づいて Webページに するようにスレッドに します.
の の え は、メインスレッドにコールバック を し、サブスレッドを するときに、メインスレッドのポインタをサブスレッドに させ、スレッド に に づいてメインスレッドのコールバック をコールバックすることです.この え はよさそうだから、まずこのステップに ってください.
まず、プライマリ・スレッドにあるコールバック を すコールバック インタフェースを します.class ICallBack
{
public:
virtual void OnSuccesful() = 0;//
virtual void OnFailed() = 0;//
};
に、CMyActiveXCtrlコントロールクラスをこのダミーベースクラスから し、これらのコールバック インタフェースを します.class CMyActiveXCtrl : public COleControl,public ICallBack
スレッドベースクラス
スレッドの を にするために,CodeProject の「TrafficWatcher」という のCThreadクラスを い, し すると のCMyThreadクラスが られ,ICallBack*pCallBackというメインスレッドのコールバック インタフェースが わった.class CMyThread
{
public:
CMyThread()
{
m_pThreadFunction = CMyThread::EntryPoint;
m_runthread = FALSE;
}
virtual ~CMyThread()
{
if ( m_hThread )
Stop(true); //thread still running, so force the thread to stop!
}
DWORD Start(DWORD dwCreationFlags = 0)
{
m_runthread = true;
m_hThread = CreateThread(NULL, 0, m_pThreadFunction, this, dwCreationFlags,&m_dwTID);
m_dwExitCode = (DWORD)-1;
return GetLastError();
}
/**//**
* Stops the thread.
*
* @param bForceKill if true, the Thread is killed immediately
*/
DWORD Stop ( bool bForceKill = false )
{
if ( m_hThread )
{
// " "
if (m_runthread == TRUE)
m_runthread = FALSE; //first, try to stop the thread nice
GetExitCodeThread(m_hThread, &m_dwExitCode);
if ( m_dwExitCode == STILL_ACTIVE && bForceKill )
{//
TerminateThread(m_hThread, DWORD(-1));
m_hThread = NULL;
}
}
return m_dwExitCode;
}
/**//**
* Stops the thread. first tell the thread to stop itself and wait for the thread to stop itself.
* if timeout occurs and the thread hasn't stopped yet, then the thread is killed.
* @param timeout milliseconds to wait for the thread to stop itself
*/
DWORD Stop ( WORD timeout )
{
Stop(false);
WaitForSingleObject(m_hThread, timeout);//
return Stop(true);
}
/**//**
* suspends the thread. i.e. the thread is halted but not killed. To start a suspended thread call Resume().
*/
DWORD Suspend()
{//
return SuspendThread(m_hThread);
}
/**//**
* resumes the thread. this method starts a created and suspended thread again.
*/
DWORD Resume()
{//
return ResumeThread(m_hThread);
}
/**//**
* sets the priority of the thread.
* @param priority the priority. see SetThreadPriority() in windows sdk for possible values.
* @return true if successful
*/
BOOL SetPriority(int priority)
{//
return SetThreadPriority(m_hThread, priority);
}
/**//**
* gets the current priority value of the thread.
* @return the current priority value
*/
int GetPriority()
{//
return GetThreadPriority(m_hThread);
}
void SetICallBack(ICallBack* pCallBack)
{
this->pCallBack = pCallBack;
}
protected:
/**//**
* ,
*/
virtual DWORD ThreadMethod() = 0;
private:
/**//**
* DONT override this method.
*
* this method is the "function" used when creating the thread. it is static so that way
* a pointer to it is available inside the class. this method calls then the virtual
* method of the parent class.
*/
static DWORD WINAPI EntryPoint( LPVOID pArg)
{
CMyThread *pParent = reinterpret_cast<CMyThread*>(pArg);
pParent->ThreadMethod();// ,
return 0;
}
private:
HANDLE m_hThread; /**////<Thread Handle
DWORD m_dwTID; ///<Thread ID ID
LPVOID m_pParent; ///<this pointer of the parent CThread object
DWORD m_dwExitCode; ///<Exit Code of the thread
protected:
LPTHREAD_START_ROUTINE m_pThreadFunction; /**////<
BOOL m_runthread; ///<
ICallBack* pCallBack;
};
なワークラインサブクラス
なワークラインプログラムサブクラスはCMyThreadから し,ThreadMethodメソッドを ロードするだけでよいが, にするために, では が した のみをシミュレートし,もちろん の に づいて な コードを することができる.class CMyTaskThread :public CMyThread
{
public:
CMyTaskThread();
~CMyTaskThread(void);
private:
DWORD ThreadMethod();
};
DWORD CMyTaskThread::ThreadMethod()
{
while(m_runthread)
{
this->pCallBack->OnSuccesful();
Sleep(5000);
}
return 0;
}
コールバック
も らかな え に って、 1 の と びつけて、コールバック は のようにして、イベントを するか、 のJavaScript を び して のwebページの を すべきであることは らかです./////////////////////
//
/////////////////////////
void CMyActiveXCtrl::OnSuccesful()
{//
this->PostMessage(WM_OPTSUCCESS,(WPARAM)NULL,(LPARAM)NULL);
}
しかし なことに、このようにするのはまったく で、 のWebページは を け ることができなくて、この について も り して、 えの で て いがないようで、どうして することができませんか?.
では、 しいやり はどうでしょうか. の に られ, の では を え, なソースコードを する.