working with Freemaster 3.1 ActiveX on secondary thread

取消
显示结果 
显示  仅  | 搜索替代 
您的意思是: 
已解决

working with Freemaster 3.1 ActiveX on secondary thread

跳至解决方案
4,276 次查看
LorenzoB
Contributor II

I was able to communicate with Freemaster 3.1 within my application using the provided activeX object and everything works fine. But now I need to move all the communication procedure to a secondary thread to allow the program to have a higher reading frequency without making the main thread too heavy. Unfortunately it seems that on the secondary thread it is not in any way possible to instantiate the interface of the activeX object, during the creation phase the procedure goes into error. I also tried to create the interface on the main thread and then pass it to the secondary one, but again, as soon as the secondary thread references the interface pointer I have an exception.
Is it a solvable problem? Does it just depend on my development system (Delphi) or is it an intrinsic limitation of this object and cannot be solved?

thanks

Lorenzo

LorenzoB
标记 (2)
0 项奖励
回复
1 解答
4,212 次查看
MichalH
NXP Apps Support
NXP Apps Support

Dear Lorenzo,

I have made a test C++/MFC dialog-based UI application which starts several background threads. Each thread creates the FreeMASTER ActiveX object and invokes some of its methods (GetAppVersion, ReadVariable, StartStopComm). 

The test works as expected. No crash or bad behavior is observed. Each thread is able to access the object without problems. Also, no synchronization was needed. Actually the only synchronization I used was when printing to internal log buffer. Only the main UI thread is then able to update the log text on the screen (the EDIT dialog control).

I'm sorry I cannot help you with a Delphi issue, but this C++ test proves the multi-threaded access is possible.

Regards,
Michal

在原帖中查看解决方案

0 项奖励
回复
7 回复数
4,268 次查看
MichalH
NXP Apps Support
NXP Apps Support

Hello Lorenzo,

the FreeMASTER ActiveX server's proxy-stub DLL is registered to support "Both" threading models. So it should be possible to be created in both single-threaded and multi-threaded applications.

I don't have an experience with Delphi, so I can only give some general Win32 API advices. It is likely that Delphi wraps the functionality to some more "easy-to-use" API. Anyway:

  • Instead of CoInitialize(), you need to call CoInitializeEx with COINIT_MULTITHREADED in the application initialization. This must be called prior to creating any COM+ or ActiveX objects in your application which aims to access them from multiple threads.
  • The thread which creates the object should call CoMarshalInterThreadInterfaceInStream() before any other thread can use the same object instance. You need to call this and get a stream for each other thread you plan to use.
  • The other threads should call CoGetInterfaceAndReleaseStream() to get a valid thread-safe interface to the object. Each thread should use a different stream created by the main owning thread.

Unfortunately, thread-safe COM+ is not an easy topic, but there is a lot of documentation available.

Regards,
Michal

 

0 项奖励
回复
4,263 次查看
LorenzoB
Contributor II

I was already using the CoInitializeEx function like so:

CoInitializeEx(nil,COINIT_MULTITHREADED);

FM_DEV:=CoMcbPcm.Create;
IF FM_DEV.OpenProject(MyO) then 
   FM_DEV.StartStopComm(Olevariant(True)) 

CoUninitialize;

While I had not used CoMarshalInterThreadInterfaceInStream because I thought it was only needed if there were more threads that shared the COM resource, while in my case everything would be only on this secondary thread where the object is born, lives and then is destroyed.
Do I still have to use this feature?

LorenzoB
0 项奖励
回复
4,256 次查看
MichalH
NXP Apps Support
NXP Apps Support

Hello Lorenzo,

understood. So in this case, the marshaling should not be needed and the object access should work. Can you describe what action raises an exception? Is there any context information behind the exception like 32bit error code etc.?

Also, make sure the CoInitialize (or the Ex) is called from the main gui thread. 

FYI: Actually, the FreeMASTER code itself proves the multi-threaded access to its own ActiveX object is possible. Its multi-threded JSON-RPC interface uses the ActiveX interface internally to perform the desired operation. In this case, the ActiveX object is created by the TCP/IP listening thread and is marshalled to JSON-RPC worker thread where it is used.

Would it be possible for you to experiment a bit with splitting the object creation and object usage into two different threads and use the interface marshalling calls? As the marshalling calls are ready to handle multi-threaded operations, they could return some useful error codes for us to better understand what is the problem here (instead of when accessing the object just crashes). 

Thanks,
Michal

0 项奖励
回复
4,252 次查看
LorenzoB
Contributor II

I did not understand that CoInitialize should be used inside the main thread, I was calling it in the secondary one before the creation of the COM object!?
I'll try to make the change.
For the rest, unfortunately, at the moment I am unable to carry out realistic tests, as the system is already installed by the customer and I can simply try the connection to Freemaster but without HW. To understand if it works I rely on the answers that the functions give me, now for example I have managed, by putting the code in a critical section (?), To ensure that the program does not crash in the creation of the COM object and the functions project loading and start seem to work because they return TRUE value (and in fact FREEMASTER loads the project). To understand if it works I have to wait to be able to do some tests by the customer.

 

Thanks MichalH !

LorenzoB
0 项奖励
回复
4,213 次查看
MichalH
NXP Apps Support
NXP Apps Support

Dear Lorenzo,

I have made a test C++/MFC dialog-based UI application which starts several background threads. Each thread creates the FreeMASTER ActiveX object and invokes some of its methods (GetAppVersion, ReadVariable, StartStopComm). 

The test works as expected. No crash or bad behavior is observed. Each thread is able to access the object without problems. Also, no synchronization was needed. Actually the only synchronization I used was when printing to internal log buffer. Only the main UI thread is then able to update the log text on the screen (the EDIT dialog control).

I'm sorry I cannot help you with a Delphi issue, but this C++ test proves the multi-threaded access is possible.

Regards,
Michal

0 项奖励
回复
4,204 次查看
LorenzoB
Contributor II

Dear Michal,

first of all thank you for the time you have dedicated to me!
I just finished testing the changes made directly on my client's workstation and I can tell you that everything worked perfectly!
Unfortunately I have not been able to improve the data download rate much, but I think it depends on the fact that my client's PC is not fast enough. The two applications (Freemaster + mine) together bring the CPU usage over 80% and I was unable to exchange data with a frequency higher than 5Hz. We will do more tests as soon as my client has replaced the current system with a more performing one.
Anyway thanks for the help.

LorenzoB
0 项奖励
回复
4,250 次查看
MichalH
NXP Apps Support
NXP Apps Support

The CoInitialize should be called from each thread which makes any COM+/ActiveX activities. It should also be called from the main UI thread which processes the windows messages as WIndows use the messages internally to communicate between the client and server apps (as both are standalone executables).

Let me also do some tests (in C++ & plain Win32 API) to see if your scenario where the FreeMASTER object is created/used/deleted from a worker threads is viable.

I will reply as soon as I have any news.

Regards,
Michal

0 项奖励
回复