Recent

Author Topic: ThorLabs motor controller (ActiveX)  (Read 803 times)

Rik

  • New Member
  • *
  • Posts: 37
ThorLabs motor controller (ActiveX)
« on: July 04, 2022, 01:34:04 pm »
Hello,

I am trying to control a ThorLabs motor stage in Lazarus.
I could import a type library from their OCX file and it nicely shows up in the ActiveX container (see attached ActiveX.png).
But clicking anywhere inside the ActiveX container results in a "external: access violation" exception.
Also when adding any more commands that should control the motor stage it compiles OK but generates a "EOleSysError" with the message "catastrophic failure" when running.
The code of my main form is included below, the type library that was imported from the OCX file is attached.
Lazarus version = 2.2.2

I'm really stuck here, so any help is welcome,

Rik

Code: Pascal  [Select][+][-]
  1. unit MainUnit;
  2.  
  3. {$mode objfpc}{$H+}
  4.  
  5. interface
  6.  
  7. uses
  8.   Classes, SysUtils, Forms, Controls, Graphics, Dialogs, StdCtrls,
  9.   activexcontainer;
  10.  
  11. type
  12.  
  13.   { TMainForm }
  14.  
  15.   TMainForm = class(TForm)
  16.     ActiveXContainer: TActiveXContainer;
  17.     Button: TButton;
  18.     procedure ButtonClick(Sender: TObject);
  19.     procedure FormCreate(Sender: TObject);
  20.     procedure FormDestroy(Sender: TObject);
  21.     procedure FormResize(Sender: TObject);
  22.   private
  23.  
  24.   public
  25.  
  26.   end;
  27.  
  28. var
  29.   MainForm: TMainForm;
  30.  
  31. implementation
  32.  
  33. uses
  34.   APTPZMotorLib_1_0_TLB;
  35.  
  36. var
  37.   Motor: _DAPTPZMotor;
  38.  
  39. {$R *.lfm}
  40.  
  41. { TMainForm }
  42.  
  43. procedure TMainForm.FormCreate(Sender: TObject);
  44. begin
  45.  
  46.   Motor:= CoAPTPZMotor.Create;
  47.   ActiveXContainer.ComServer:= Motor;
  48.   ActiveXContainer.Active:= True;
  49.   Motor.HWSerialNum:= 27262824; // generates a "EOleSysError" with the message "catastrophic failure"
  50. end;
  51.  
  52. procedure TMainForm.FormDestroy(Sender: TObject);
  53. begin
  54.   ActiveXContainer.Destroy;
  55. end;
  56.  
  57. procedure TMainForm.FormResize(Sender: TObject);
  58. begin
  59.   Button.Top:= Height-Button.Height-8;
  60.   Button.Left:= (Width-Button.Width) div 2;
  61.   ActiveXContainer.Top:= 8;
  62.   ActiveXContainer.Left:= 8;
  63.   ActiveXContainer.Width:= Width-16;
  64.   ActiveXContainer.Height:= Button.Top-16;
  65. end;
  66.  
  67. procedure TMainForm.ButtonClick(Sender: TObject);
  68. begin
  69.   Motor.HWSerialNum:= 27262824; // generates a "EOleSysError" with the message "catastrophic failure"
  70. end;
  71.  
  72. end.
  73.  
« Last Edit: July 04, 2022, 01:36:39 pm by Rik »

jamie

  • Hero Member
  • *****
  • Posts: 4679
Re: ThorLabs motor controller (ActiveX)
« Reply #1 on: July 04, 2022, 10:56:21 pm »
you may need to call this first before doing any ole/COm creations.

activex.OleInitialize
The only true wisdom is knowing you know nothing

Rik

  • New Member
  • *
  • Posts: 37
Re: ThorLabs motor controller (ActiveX)
« Reply #2 on: July 05, 2022, 07:10:14 am »
I tried this, but the problem remains:

Code: Pascal  [Select][+][-]
  1. unit MainUnit;
  2.  
  3. {$mode objfpc}{$H+}
  4.  
  5. interface
  6.  
  7. uses
  8.   Classes, SysUtils, Forms, Controls, Graphics, Dialogs, StdCtrls,
  9.   activexcontainer, ActiveX;  // <--- added ActiveX
  10.  
...
Code: Pascal  [Select][+][-]
  1. procedure TMainForm.FormCreate(Sender: TObject);
  2. begin
  3.   OleInitialize(ActiveXContainer); // <--- added this line
  4.   Motor:= CoAPTPZMotor.Create;
  5.   OleInitialize(Motor); // <--- added this line
  6.   ActiveXContainer.ComServer:= Motor;
  7.   ActiveXContainer.Active:= True;
  8.   Motor.HWSerialNum:= 27262824; // still generates a "EOleSysError" with the message "catastrophic failure"
  9. end;
  10.  
...
Code: Pascal  [Select][+][-]
  1. procedure TMainForm.FormDestroy(Sender: TObject);
  2. begin
  3.   OleUninitialize; // <--- added this line
  4.   ActiveXContainer.Destroy;
  5. end;
  6.  

Is that what you meant?

sstvmaster

  • Sr. Member
  • ****
  • Posts: 287
Re: ThorLabs motor controller (ActiveX)
« Reply #3 on: July 05, 2022, 09:02:45 am »
Have you registered the ocx?
Quote
Regsvr32.exe c:\pathto\..\thorlabs.ocx

Is the bitness of the ocx the same as your compiled lazarus programm? Maybe the ocx is for 32bit and your lazarus programm is compiled for 64bit?
greetings Maik

Windows 10,
- Lazarus 2.2.2 (stable) + fpc 3.2.2 (stable)
- Lazarus 2.2.3 (fixes) + fpc 3.3.1 (main/trunk)

MarkMLl

  • Hero Member
  • *****
  • Posts: 4753
Re: ThorLabs motor controller (ActiveX)
« Reply #4 on: July 05, 2022, 09:27:54 am »
Also how is the motor controller physically accessed, and is that still usable with a modern OS?

MarkMLl
MT+86 & Turbo Pascal v1 on CCP/M-86, multitasking with LAN & graphics in 128Kb.
Pet hate: people who boast about the size and sophistication of their computer.
GitHub repositories: https://github.com/MarkMLl?tab=repositories

Rik

  • New Member
  • *
  • Posts: 37
Re: ThorLabs motor controller (ActiveX)
« Reply #5 on: July 05, 2022, 12:28:26 pm »
Quote
Have you registered the ocx?
I haven't, but I installed the Thorlabs software that drives the motor controller and I assume that includes the OCX registration.
Just checked it (regedit) and it seems to be registered, see attached Registry.png.

Quote
Is the bitness of the ocx the same as your compiled lazarus programm?
Yes, both are 64 bit.

Quote
Also how is the motor controller physically accessed, and is that still usable with a modern OS?
Via a USB port. In the device mgr it shows up as "ATP USB device".
With the Thorlabs software it works fine (on the same computer as used for Lazarus)
« Last Edit: July 05, 2022, 12:30:50 pm by Rik »

Rik

  • New Member
  • *
  • Posts: 37
Re: ThorLabs motor controller (ActiveX)
« Reply #6 on: July 08, 2022, 09:47:05 am »
I got it working after reading https://lazarus.lazarus.freepascal.narkive.com/FJzUzE7r/activex-tolecontrol and ended up with this code:

Code: Pascal  [Select][+][-]
  1. uses
  2.   MG17MotorLib_1_0_TLB,ComObj,ActiveX;
  3.  
  4. var
  5.   X: IUnknown; // windows basic COM interface
  6.   PSI: IPersistStreamInit;
  7.   BMC: _DMG17Motor;  
  8.  
  9. procedure TMainForm.FormCreate(Sender: TObject);
  10. begin
  11.   X:= CreateComObject(CLASS_MG17Motor);
  12.   OleCheck(X.QueryInterface(IPersistStreamInit,PSI));
  13.   OleCheck(PSI.InitNew);
  14.   BMC:= X as _DMG17Motor;
  15.   AXC.ComServer:= BMC;
  16.   AXC.Active:= True;
  17.   BMC.HWSerialNum:= 27262824;
  18.   BMC.StartCtrl;
  19. end;
  20.  

Accessing the ThorLabs motor controller from within Lazarus works OK now.

But closing the program generates a "external: ACCESS VIOLATION" exception, using:

Code: Pascal  [Select][+][-]
  1. procedure TMainForm.FormDestroy(Sender: TObject);
  2. begin
  3.   BMC.StopCtrl;
  4.   AXC.Active:= False;  // Tried all combinations of these,
  5.   AXC.Invalidate;        // it always results in a
  6.   AXC.Free;                // "external ACCESS VIOLATION"
  7.   AXC.Destroy;           // exception
  8. end;
  9.  

So the remaining question is how to terminate the program properly with an ActiveXContainer ?
Or should I start a new thread for that?

« Last Edit: July 08, 2022, 09:49:00 am by Rik »

marcov

  • Administrator
  • Hero Member
  • *
  • Posts: 10175
  • FPC developer.
Re: ThorLabs motor controller (ActiveX)
« Reply #7 on: July 08, 2022, 10:30:19 am »
Free includes destroy, so the second destroy is redundant

dseligo

  • Hero Member
  • *****
  • Posts: 651
Re: ThorLabs motor controller (ActiveX)
« Reply #8 on: July 08, 2022, 10:32:07 am »
But closing the program generates a "external: ACCESS VIOLATION" exception, using:

Code: Pascal  [Select][+][-]
  1. procedure TMainForm.FormDestroy(Sender: TObject);
  2. begin
  3.   BMC.StopCtrl;
  4.   AXC.Active:= False;  // Tried all combinations of these,
  5.   AXC.Invalidate;        // it always results in a
  6.   AXC.Free;                // "external ACCESS VIOLATION"
  7.   AXC.Destroy;           // exception
  8. end;
  9.  

In what line exactly you got exception?
If you call Free, don't call Destroy after that (AXC is destroyed already).

Rik

  • New Member
  • *
  • Posts: 37
Re: ThorLabs motor controller (ActiveX)
« Reply #9 on: July 08, 2022, 11:18:13 am »
Indeed, Free included Destroy. I should have known that.

The exception occurs with whatever combination of Active:= False / Invalidate / Free is used (including none).

After closing the error pop-up window another pop-up window appears showing the assembler line where the exception is raised.
But since with any combination the exception is raised it assume it is whatever line is first.
A screenshot of the error and assembler pop-up windows is attached.

I had a look on the web for code examples that use ActiveX, but nowhere they seem to care about freeing the ActiveXContainer.

marcov

  • Administrator
  • Hero Member
  • *
  • Posts: 10175
  • FPC developer.
Re: ThorLabs motor controller (ActiveX)
« Reply #10 on: July 08, 2022, 12:06:09 pm »
It is possible that e.g. stopctrl triggers events that crash when activex=false.

Try inserting sleeps (or a loop with a short sleep and application.processmessages) between some of the elements. between stopctrl and active=false, and same after active =false.

I don't see what invalidate is supposed to do. Usually it is a trigger to redraw, but you don't want that. If you don't know why it is there, remove it.

And then just the final free.

Does it matter for the crash what you do? If you shut down without doing (or programmatically starting something), is the error then gone?


Code: Pascal  [Select][+][-]
  1. procedure dosedsleep(sleeptimems:int64);
  2.  
  3. var currenttime,stoptime : int64;
  4.  
  5. begin
  6.       currenttime:=gettickcount64;
  7.       stoptime:=currenttime+sleeptimems;
  8.       Application.ProcessMessages; // always do minimally one processmsgs
  9.       currenttime:=gettickcount64; // get current time.
  10.       while currenttime<stoptijd do
  11.                 begin
  12.                   Application.ProcessMessages;
  13.                   sleep(10);                    // avoid busy waiting
  14.                   currenttime:=gettickcount64;
  15.                 end;
  16. end;
  17.  

dseligo

  • Hero Member
  • *****
  • Posts: 651
Re: ThorLabs motor controller (ActiveX)
« Reply #11 on: July 08, 2022, 12:38:35 pm »
What is AXC, how do you create it?
What happens if you put BMC.StopCtrl; after you free AXC, like that:
Code: Pascal  [Select][+][-]
  1. procedure TMainForm.FormDestroy(Sender: TObject);
  2. begin
  3.   AXC.Active:= False;
  4.   AXC.Free;
  5.   BMC.StopCtrl;
  6. end;

And one more thing: I see you are doing this in FormDestroy. Can you (just for test) put this code in some Button's OnClick event and leave FormDestroy empty, something like this:
Code: Pascal  [Select][+][-]
  1. procedure TMainForm.Button1Click(Sender: TObject);
  2. begin
  3.   AXC.Active:= False;
  4.   AXC.Free;
  5.   BMC.StopCtrl;
  6. end;

Does it change anything? When is exception shown now, when you click on Button or when exiting form?
« Last Edit: July 08, 2022, 12:40:08 pm by dseligo »

Rik

  • New Member
  • *
  • Posts: 37
Re: ThorLabs motor controller (ActiveX)
« Reply #12 on: July 08, 2022, 01:10:17 pm »
Quote
What is AXC, how do you create it?
It is an ActiveXContainer:
Code: Pascal  [Select][+][-]
  1.   TMainForm = class(TForm)
  2.     AXC: TActiveXContainer;
  3.  

Quote
Try inserting sleeps (or a loop with a short sleep and application.processmessages) between some of the elements. between stopctrl and active=false, and same after active =false.
Quote
What happens if you put BMC.StopCtrl; after you free AXC, like that:
Quote
Can you (just for test) put this code in some Button's OnClick event and leave FormDestroy empty
I added 3 separate buttons for BMC.StopCtrl; / AXC.Active:= False; / AXC.Free; so I can execute these in any order:
Code: Pascal  [Select][+][-]
  1. procedure TMainForm.Button1Click(Sender: TObject);
  2. begin
  3.   BMC.StopCtrl;
  4. end;
  5.  
  6. procedure TMainForm.Button2Click(Sender: TObject);
  7. begin
  8.   AXC.Active:= False;
  9. end;
  10.  
  11. procedure TMainForm.Button3Click(Sender: TObject);
  12. begin
  13.   AXC.Free;
  14. end;
  15.  
Clicking on Button1 generates no exception.
Clicking on Button2 or Button3 generate the exception, regardless wether Button1 is clicked before that or not.
The issue seems to be freeing the ActiveXContainer the right way.
Could it be related to the rather unconventional way _DMG17Motor is linked to ComServer?
Code: Pascal  [Select][+][-]
  1. procedure TMainForm.FormCreate(Sender: TObject);
  2. begin
  3.   X:= CreateComObject(CLASS_MG17Motor); //  X: IUnknown;
  4.   OleCheck(X.QueryInterface(IPersistStreamInit,PSI)); // PSI: IPersistStreamInit;
  5.   OleCheck(PSI.InitNew);
  6.   BMC:= X as _DMG17Motor;
  7.   AXC.ComServer:= BMC;
  8.   AXC.Active:= True;
« Last Edit: July 08, 2022, 01:14:08 pm by Rik »

marcov

  • Administrator
  • Hero Member
  • *
  • Posts: 10175
  • FPC developer.
Re: ThorLabs motor controller (ActiveX)
« Reply #13 on: July 08, 2022, 01:15:07 pm »
Note that if AXC is a design time component, you should free it. It will be automatically freed, just like the buttons on your form.

Thaddy

  • Hero Member
  • *****
  • Posts: 11774
Re: ThorLabs motor controller (ActiveX)
« Reply #14 on: July 08, 2022, 02:06:45 pm »
And since you are working with COM INTERFACES, not classes, the free is not necessary. The COM object will be free'd automatically when it goes out of scope.
I would not bother to use a component, I would instantiate it as an interface, so most of the latter code is correct.
« Last Edit: July 08, 2022, 02:12:19 pm by Thaddy »
Black themes should be banned.

 

TinyPortal © 2005-2018