4

I have a piece of hardware which is handled using an ActiveX component in Delphi. If I drop that component on my form during design time, everything works fine. However, if I create it dynamically at run-time using Creat(Self), further execution of a method causes Access Violation in mfc100.dll. The code is pretty simple:

uses
  Windows, Messages, ...
  OleCtrls, MG17MotorLib_TLB; // <-- The latter is the hardware driver

type
  TForm1 = class(TForm)
    motorX: TMG17Motor;
    ...
  end;

...

procedure TForm1.FormCreate(Sender: TObject);
begin
  motorX := TMG17Motor.Create(Self);
  motorX.HWSerialNum := 94835472;
  motorX.StartCtrl; // <--- This causes AV in mfc100.dll
end;

The TMG17Motor class is a descendant of TOleControl and is supplied by a hardware producer.

Do I make a mistake in dynamically creating the ActiveX object, or does it rather look like a bug in TMG17Motor class? Perhaps, there is a way to cheat and create an object of TMG17Motor class in the same way as the application does it, if I avoid using Create(Self) by dropping the control on the form at design time?

P.S. The reason I want to be able to dynamically create a control is that I want to move the code for hardware handling to a worker thread.

Ilya
  • 43
  • 3
  • It's possible/likely that the control has some dependency (e.g. it expects to have a parent property set or something similar). If you have the source or the symbols for the target control, it should be simple to look in a debugger to see what's triggering the AV specifically. – EricLaw May 11 '13 at 15:35
  • I would talk to the vendor. http://www.thorlabs.com/newgrouppage9.cfm?objectgroup_id=2419 Also, try dumping out the DFM and setting ALL the properties to exactly what you see in the DFM. – Warren P May 11 '13 at 16:25
  • @EricLaw: Thank you for an idea! I didn't think about dependencies. Will try to look in that direction. – Ilya May 11 '13 at 16:34
  • @Warren P: Yes, I have notified Thorlabs about the problem. No response yet. – Ilya May 11 '13 at 16:36
  • Secondly, perhaps this ActiveX Control needs some time (even a few milliseconds) between when creation occurs, and when you call StartCtrl. Try decoupling them using a TTimer, or as a quick hack-test, add a sleep(100) and Application.ProcessMessages(); – Warren P May 11 '13 at 18:47
  • Thank you, Warren. That was the first thing I've tried. Unfortunately, it didn't help. – Ilya May 11 '13 at 18:57
  • 2
    One of the most notable differences between design time and run time instantiation of any control is that run time creation bypasses the streaming system, which means the `Loaded` method won't be called. If you have the source, check whether your ActiveX component has overridden the Loaded method. In case it has, it may help to call that after you create the component and before you set any other properties. – Marjan Venema May 11 '13 at 19:08
  • Hmmm... I'm not sure I understood correctly. I have the source for `MG17MotorLib_TLB` where `TMG17Motor` class is defined. `Loaded` method is not overriden there. Moreover, `motorX.Loaded` is not defined, so this method is somehow absent. – Ilya May 11 '13 at 20:03

1 Answers1

6

I took the effort to download the software and see what the problem is. It appears that TMG17Motor is a visual control. So your problem comes from the fact that your component has no parent window.

The fix is very easy:

procedure TForm1.FormCreate(Sender: TObject);
begin
  motorX := TMG17Motor.Create(Self);
  motorX.Parent := Self; // <--- you need this!
  // set other properties...
  motorX.HWSerialNum := 94835472;
  motorX.Align := alclient;
  motorX.StartCtrl;
end;

Sample screenshot: enter image description here You state in your question that you want to move your code to a worker thread, but this is a visual control so that won't be possible. Try to contact Thorlabs and see if they have other means to achieve what you want...

EDIT

As David suggested in the comments, you can try with a non VCL window and a worker thread. Since this is an ActiveX object, make sure you call CoInitialize(nil)/CoUninitialize in the Execute method of your worker thread.

whosrdaddy
  • 11,720
  • 4
  • 50
  • 99
  • +1 Good work. Should be fine to move to a worker thread. Parent it in a non-VCL window. – David Heffernan May 12 '13 at 09:20
  • @whosrdaddy: Thank you so much! An unexpected solution, since the line above also sends the parent. – Ilya May 12 '13 at 09:28
  • @David Heffernan: Thank you for the hint about a non-VCL window! I was sure there must be a work-around for this! – Ilya May 12 '13 at 09:29
  • Ok, I have created a WinAPI invisible window inside the worker thread using the [answer of Remy Lebeau](http://stackoverflow.com/questions/3638631/creating-a-window-inside-tthread) and created the ActiveX control inside that window. My worker thread was terminating without any visible reason until I saw the last comment about CoInitialize(nil)/CoUninitialize. Now everything works perfectly! Thank you! – Ilya May 12 '13 at 23:56