My basic task is to work with a "visual" OCX which records live video and audio. It's an OCX containing a panel showing the live video from a local video cam and has all of the plumbing to handle the recording, set file name, save it, etc. And I feel absolutely foolish but I need some assistance to just get things rolling. I've read the programmer's Guide and it's just not clear to me. I "think" I can see all the pieces but don't understand how they fit together. To get my self started I have modified the ActiveXPanel sample and have generated code from the OCX. I have my OCX starting, but:
1) The JPanel will flash and repaints when clicked but I don't see any live video playing. Leading me to believe that the OCX isn't running.
2) I don't understand how to get a handle to the OCX to call it's methods (like getRecordingStatus(), setRecordingFileName())
Basically I'm just not getting the whole ComfyJ / JNIWrapper class and calling structure. I don't even understand how to get a handle to the OCX in the OleContainer so I can call it's methods and therefore can't get started.
Any pointers are appreciated and Thanks in advance.
- Kevin
Hi Kevin,
I don't understand how to get a handle to the OCX to call it's methods (like getRecordingStatus(), setRecordingFileName())
First, I suppose that you have created an OCX component in the OleContainer, like it's shown in ActiveXPanel Java example:
oleContainer.createObject(progID); // here progID is the String ID of the OCX component; for Windows Media Player it is "WMPlayer.OCX"
The following code demonstrates how to get an embedded ActiveX/OCX component from the Container:
IOleObjectImpl oleObject = oleContainer.getOleObject();
Then having this OleObject, which represents the emended OCX component in your case, you can cast it to the required type and call the methods of this object directly, or you can invoke the methods of this object indirectly, using Automation approach.
The following example demonstrates how to query the required object from the oleObject:
// query IMediaPlayer interface from oleObject
IMediaPlayerImpl mediaPlayer = new IMediaPlayerImpl(oleObject);
// invoke the method of IMediaPlayer object
mediaPlayer.setFileName(new BStr(path));
This example is taken from MediaPlayerIntegrationSample file which is available in ComfyJ distribution archive in the comfyj-2.4.zip\samples\MediaPlayer\src\ folder. And IMediaPlayerImpl here is the class which is generated using Codegen for ComfyJ application.
And as I mentioned above, the alternative way is Automation. This approach is quite similar to reflection technique in Java. The following code snippet demonstrates how to use Automation:
// Create Automation object for the oleObject
Automation automation = new Automation(oleObject);
// Invoke the property setter of the media player
String path = ...
automation.setProperty("FileName", path);
You can find these and many other integration/automation examples in comfyj-2.4.zip\samples\ folder.
As to another question:
The JPanel will flash and repaints when clicked but I don't see any live video playing. Leading me to believe that the OCX isn't running.
I think that the embedded component was not show. Did you call oleContainer.doVerb(OleVerbs.INPLACEACTIVATE); or oleContainer.doVerb(OleVerbs.SHOW); method? Or even better, can you please attach your example?
-Serge
Serge,
Thanks for the pointers. I've made tremendous strides and can now see and record live video and audio. I am now running into threading issues with the following error message: The application called an interface that was marshalled for a different thread. I'm accessing this with the following snippet which is triggered from the actionPerformed event from a JButton:
OleMessageLoop oleMessageLoop = oleContainer.getOleMessageLoop();
Runnable runnable = new Runnable()
{
public void run()
{
CtrlImpl ctrl = new CtrlImpl(oleContainer.getOleObject());
Recorder vRecorder = ctrl .getRecorder();
// first we get a handle to the userdef
API api = ctrl.getAPI();
Users users = api.getUsers();
UserDescription aUser = users.moveFirst();
String proposedName = "Kevin";
while (!aUser.getName().getValue().equalsIgnoreCase(proposedName)) {
users.moveNext(aUser);
}
// now pass the userdef to the recorder and verify the user.
vRecorder.verify(aUser);
}
};
try
{
oleMessageLoop.doInvokeAndWait(runnable);
}
catch (InterruptedException e)
{
e.printStackTrace();
}
catch (InvocationTargetException e)
{
e.printStackTrace();
}
}
The problem line is UserDescription aUser = users.moveFirst(); If I do not run this within an OleMessageLoop then it will error for not having ComFunctions.coInitialize() being called on this thread. If I add ComFunctions.coInitialize then it errors with the same error. If I keep it in the OleMessageLoop (with no ComFunctions.coInitialize()) then it has the same error plus the OCX errors with RPC_E_WRONG_THREAD.
Seems like I'm now trying to understand the threading model. But for what it's worth, I have the event model working.
Thanks again in advance.
- Kevin
Hi Kevin,
Such error message "The application called an interface that was marshalled for a different thread" indicates that you are trying to work with a COM object which was created in another thread. Actually such exception occurs when a COM object does not support multithreading model.
Taking into account that he problem line is "UserDescription aUser = users.moveFirst();" my first though was that 'users' COM object was just created in another thread. But then I have analyzed your example and it seems to be correct.
It's difficult to say why such problem occurs in this case without the ability to reproduce it. Can you please send us the complete test applicaiton wich reproduces the problem? We will investigate this issue and give you the solution.
-Serge
Things are finally working without fail. The OCX I'm working with has a main control (CTRL) object which retrieves other objects for you. It serves as a visual container (showing live images from the camera) and as an entry point for non-visual services like getting registered users. If I want a video recorder then I get it via the CTRL.getRecorder. Working visually the CTRL is part of the OCX and is created when the OleContainer is initialed for the OCX. So to get the CTRL I just did CTRL = oleContainer.getOleObject(). The interesting effect is that when in a different thread I could only get the objects held by the CTRL. So getting a valid (not null) Recorder worked fine. But if the Recorder made reference to another object then the marshalling problem occurs. For example Recoder.getStatus() which returned a Status object would cause a marshalling problem. The lesson learned for me is that when working in mulitple threads and needing to get to object deeper than those held by the top level object (in this case CTRL) it is necessary to create another instance of the CTRL. An instance that is used for non-visual interaction.
In this case it is necessary for me to create an instance of CTRL within a given OleMessageLoop and do all my work within that message loop. These two things allow me to traverse several layers of objects below the CTRL and ulitimately solved the threading problem.
So I end up with conceptually 2 instances of CTRL: 1) for visual use and is instantiated by the OleContainer, 2) for utilizing the services of the OCX and is instantiated by a create or queryInterface for the CTRL (as an INPROC_SERVER). Lastly is the usage of OleMessageLoop. This is a very important concept to get right if you intend to interact with the OCX.
Thanks for the help!
- Kevin
Hi Kevin,
Have You tried (in Your live stream ocx) to start up the activeX without registering it? (see http://support.teamdev.com/message/3393) Can You reffer to the issue of dislpaying unregistered activeX object on OleContainer? Right now I am trying to implement an applet that does not need to call regsrv32 and I am stuck because ocx loaded without registering won't show up ( although example that registers ocx does). If You have any experience with that, I would be graceful
Thanks, Tomasz