//比较好的教程
ocx 在 win7 系统会出现注册需要管理员权限的问题,这时候需要用管理员身份运行 cmd,然后运行 regsvr32注册。
很麻烦
尝试使用 nsis 做成安装包, 采用 regdll 注册 ocx, 成功。
ocx和外面的程序交互主要通过提供方法属性 + 事件
方法属性可以提供给js调用,
事件可以给js 通过下面的方式进行回调注入
<object id="xxx"></object>
<script language="JavaScript" for="xx" Event="eventFunction(x)">
alert(x);
</script>
或者
document.getElementByIdx_x(xx).attachEvent("eventFunction",function(x,y){
alert(x);
});
这两种功能都可以在类视图里面选择
XXXCtrl,右键选择 add ,会出现 方法属性事件
按照wizard进行添加就好。
主要记录一下如果ocx创建了线程,想通过事件回调js的话,会出现问题。
这时候解决方法就是通过 PostMessage(WM_THREADFIREEVENT,(WPARAM)NULL,(LPARAM)NULL); 下面的看看应该懂了
//-------------------------
SAMPLE: Firing Events From a Second Thread
---------------------------------------------------------------------
The information in this article applies to:
- Microsoft Visual C++, 32-bit Edition versions 4.0, 4.1, 4.2
---------------------------------------------------------------------
SUMMARY
=======
MFC based ActiveX controls typically fire their events from the same thread
that implements the sink interface of the container that the events are
being fired to.
Sometimes, it is desirable to start a second thread in an ActiveX control
which will fire events to the container. Since MFC ActiveX controls
use the Apartment threading model, special consideration must be taken
into account when firing events from a secondary thread.
MORE INFORMATION
================
An MFC based ActiveX control supports events by implementing the
IConnectionPointContainer and IConnectionPoint interfaces, as well as
supplying information about it's event interface in it's type information.
When an MFC based ActiveX control is embedded in a container that supports
events, that container will dynamically construct a sink interface that has
all of the methods specified in the control's type information for it's
event interface. Once the container constructs it's sink interface, it
will pass a pointer to that interface to the ActiveX control. The ActiveX
control will use it's implementation of IConnectionPoint to communicate
through the now hooked up sink interface that was constructed by the
container. This sample will demonstrate how to call methods of the
container's sink interface from a second thread.
The two most important things to consider when starting a new thread to
fire events from in an ActiveX control are:
1. MFC based ActiveX controls are in-process objects (implemented in a
DLL).
2. MFC based ActiveX controls use the Apartment threading model.
The Apartment threading model specifies that all threads that want to use
OLE services must initialize OLE in their thread prior to using OLE
services. Also, if a thread wants to use a pointer to an interface that is
either implemented by a different thread of the same process or has been
previously marshaled to a different thread of the same process, that
pointer must be marshaled to the requesting thread. In the Apartment
threading model, hidden windows are created to synchronize requests from
other threads to the thread being called. This means that all
communication between threads will be done by using hidden windows and
Windows messages in the Apartment model.
There are two possible ways to fire events from a second thread in an
ActiveX control (or any other in-proc server that implements connection
points) under the Apartment threading model. The first is to make the
interface call from the second thread by calling the event sink's method
from the second thread. The second is to have the second thread post a
message to the first thread when it is ready to fire the event, and have
the first thread fire the event.
The first method mentioned above is not the optimal way to fire an event
from a second thread. This is because for the second thread to fire the
event, it must make a call on an interface pointer that is held by the
thread that initialized the control. This means that the interface pointer
that will be used to fire the event must be marshaled to the second thread
which will cause OLE to set up hidden windows to communicate between the
threads. Windows messages will be used to communicate between the threads.
The MFC ActiveX control framework is not set up to easily fire events from
a second thread. It is possible to override the default MFC code to marshal
the sink interface pointers to the second thread, but this is not
recommended. The reason this is not recommended is that since Windows is
going to create hidden windows and use PostMessage to send messages between
threads anyway, it makes more sense for the second thread to post it's own
messages to the first thread and have that thread fire the event. This code
can be easily set up in an MFC ActiveX control. Take the following steps to
add a second thread which fires events to the container in an MFC ActiveX
control.
1. Create your control project.
2. Using ClassWizard, add a method that will start a second thread and
return. The code for a method that starts a second thread and returns
immediatly in an MFC ActiveX control is shown below. A global function
to serve as the second thread's work function is also declared:
LONG ThreadProc(LPVOID pParam);
void CFireeventCtrl::StartLengthyProcess()
{
DWORD dwID;
HANDLE threadHandle = CreateThread(NULL,NULL,
(LPTHREAD_START_ROUTINE)ThreadProc,
(LPVOID)this, NULL, &dwID);
TRACE("Started the thread %x/n",dwID);
}
3. Add any events you wish to fire from the second thread using
ClassWizard.
4. Define a custom message to be sent from the second thread. Also, add a
message map entry to the control's message map which will call the
message handling function when the custom message is received. This
message handler will fire the desired event. A sample of how to do this
in an MFC ActiveX control is shown below:
//define a custom message:
#define WM_THREADFIREEVENT WM_USER+101
//add an entry for the message to the message map of the control
BEGIN_MESSAGE_MAP(CFireeventCtrl, COleControl)
//{{AFX_MSG_MAP(CFireeventCtrl)
//}}AFX_MSG_MAP
ON_OLEVERB(AFX_IDS_VERB_PROPERTIES, OnProperties)
ON_MESSAGE(WM_THREADFIREEVENT,OnFireEventForThread) //custom handler
END_MESSAGE_MAP()
//add a handler for the custom message that will fire our event
LRESULT CFireeventCtrl::OnFireEventForThread(WPARAM wParam,
LPARAM lParam)
{
FireLengthyProcessDone();
return TRUE;
}
5. In the thread procedure for the second thread, when it's time for the
second thread to fire the event, post the custom message defined in step
3 back to the main thread. The event will be fired. The following code
demonstrates this:
LONG ThreadProc(LPVOID pParam)
{
Sleep(2000); //simulate lengthy processing
CFireeventCtrl *pCtrl = (CFireeventCtrl*)pParam;
PostMessage(pCtrl->m_hWnd,
WM_THREADFIREEVENT,
(WPARAM)NULL,
(LPARAM)NULL);
return TRUE;
}
Notice in the sample code above that the window handle of the ActiveX
control is used as the target to which the message from the second thread
will be posted. In most cases, an MFC based ActiveX control will be in-
place active when it's methods are called and will have a window handle.
It is possible, however for an ActiveX control to not have a window handle,
such as in the case of a windowless control. One way to work around this
is to create a hidden window that could be used to communicate between
threads. That window could then be destroyed when the thread terminated.
The FIREEV sample has code which is commented out in it's
StartLengthyProcess method and ThreadProc thread work function which
demonstrates creating a window which is wrapped by the CMyWindow class that
serves this purpose. Also notice that PostMessage is used instead of
PostThreadMessage. MFC's message maps are set up to intercept thread
messages in CWinThread derived classes only. Since MFC ActiveX controls
are derived from CWnd, they will not have messages sent with
PostThreadMessage routed to them. Messages sent with PostThreadMessage will
have a NULL hWnd.
----------------------------------//
里面提到的 hWnd,如果在IE使用空间,这个窗口句柄是空的,会导致回调照样失败,
这时候需要重载
OnSetClientSite
void CMyControl::OnSetClientSite()
{
// It doesn't matter who the parent window is or what the size of
// the window is because the control's window will be reparented
// and resized correctly later when it's in-place activated.
if (m_pClientSite)
VERIFY (CreateControlWindow (::GetDesktopWindow(), CRect(0,0,0,0), CRect(0,0,0,0)));
COleControl::OnSetClientSite();
}
这样,整个流程就走通了。
js调用 ocx, ocx回调js 全部走通。
在Windows XP中,可以在任务栏上看到控件的窗口
解决的方法是尽可能用GetForegroundWindow替代GetDesktopWindow(GetForegroundWindow有时返回NULL)
//参考