Delphi: Threads and Generic - multithreading

I am just trying to pass a generic type to the execute method of a TThread. I can't figure it out...
procedure MyThread.Execute;
begin
Obj := TDWResponseType.FromJsonString<T>(Response);
end;
Should I use a threadvar, How can I cast It ?

When you call TDWResponseType.FromJsonString<T> generic type T must be either explicitly defined or must be available as T from the outer context. There is not T available in the outer context and such code will not compile.
In your particular case, either MyThread class must be generic class or Execute method must be generic method.
Assuming that MyThread class is TThread descendant, then modifying Execute method is out of option.
You have two options:
declare MyThread<T> instead of MyThread
use specific class in TDWResponseType.FromJsonString<TSomeClass>

Thank you guys:
procedure TMyThread<T>.Execute; // with: TMyThread<T: class, constructor>=class(TThread)
begin
TJson.JsonToObject<T>(Response);
end;

Related

Using CoInitialize in a Delphi thread

I am using TIdHttp and TXMLDocument inside a thread in a Delphi program.
Now I want to know:
Do these classes use COM objects so I need to call CoInitialize and CoUninitialize in this thread?
If yes, do I have to use these functions at the body of execute method or at all methods that use TIdHttp or TXMLDocument classes?
TIdHTTP has no COM dependency.
TXMLDocument can have a dependency on COM. On Windows, out of the box it is a wrapper around Microsoft's MSXML ActiveX component, which uses COM. If you use another DOM vendor (for example, OmniXML, available from XE7) then there is no COM dependency. You can control this by setting the DefaultDOMVendor global variable.
CoInitialize and CoUninitialize must be called once from within the thread context. Typically in the Execute() method of TThread, as seen in this example flow:
procedure TMyThread.Execute;
begin
try
CoInitialize(nil);
try
while not Terminated do
begin
DoWorkThatMayUseCOM;
end;
finally
CoUninitialize();
end;
except
on E: Exception do
// log exception
Log(E);
end;
end;

Delphi pass string to procedure gets an access violation error

I am currently trying to create a test procedure and pass it a string. And the function checks the string for equivalence. The problem is that when the test runs, I get a access violation error of EAccessViolation on the string that I pass inside the Procedure. I understand that this is a memory declaration problem but I am not sure how to fix it.
Here is my code:
Declare
TestTForm1 = class(TTestCase)
strict private
FForm1: TForm1;
public
procedure SetUp; override;
procedure TearDown; override;
published
procedure TestCompareListBoxToFile(Method : String);
end;
Call
TestCompareListBoxToFile('Save');
Procedure
procedure TestTForm1.TestCompareListBoxToFile(Method : String);
begin
Check('Save' = Method,'they dont match');
end;
I am new to delphi so if there is anything I am missing, please let me know. please be specific in the response. Thank you.
That code is called by the unit test runner. It uses RTTI to find published methods whose names begin with 'Test'. On the face of it, this appears to be a DUnit test case.
The runner expects a procedure that accepts no parameters, and calls the method as such. You on the other hand, provide a method that does require a parameter. A parameter that is not provided. Hence the runtime error.
Now, somewhere in your code you say that you are calling the method like this:
TestCompareListBoxToFile('Save');
But that's just not how tests are invoked. Tests are invoked by the runner which uses RTTI to do so. If you attempt to call the function as well, that's just wrong.
You must declare your method like this:
procedure TestCompareListBoxToFile;
You probably need to go back to the documentation and examples for the unit test framework and learn how to design your test case to be able to accept parameters. In fact, before even doing that, I suggest you go right back to basics and make sure you fully understand how the runner discovers your tests and then runs them.

How to pass or make public variables or fields accessible to Tthread?

I am writing and building my software for Mono using Delphi Prism. So, I decided that my serial communication will be handled by a thread. Since global variables strictly are not allowed unless you enable the global variable option for the project, I decided to follow the Delphi Prism convention. So, then how do you pass or make the public variables or fields accessible to a thread?
Here is my test mainform code:
MainForm = partial class(System.Windows.Forms.Form)
private
method SignalBtn_Click(sender: System.Object; e: System.EventArgs);
method CommBtn_Click(sender: System.Object; e: System.EventArgs);
method button1_Click(sender: System.Object; e: System.EventArgs);
method button2_Click(sender: System.Object; e: System.EventArgs);
method button4_Click(sender: System.Object; e: System.EventArgs);
method button5_Click(sender: System.Object; e: System.EventArgs);
method MainForm_Load(sender: System.Object; e: System.EventArgs);
method ShutdownBtn_Click(sender: System.Object; e: System.EventArgs);
method MySerialData(sender: System.Object; e:SerialDataReceivedEventArgs);
method LoginBtn_Click(sender: System.Object; e: System.EventArgs);
protected
method Dispose(disposing: Boolean); override;
public
RX:Array[0..5] of byte;
TX:Array[0..6] of byte;
serialPort1:System.IO.Ports.SerialPort;
thr:Thread;
stoploop:Boolean;
mcommand:Byte;
thechannel:Integer;
constructor;
method FillTable;
end;
Here is the Thread for serial communication:
ThreadComm = class(MainForm)
public
class procedure mythread; static;
end;
Here is how ThreadComm runs:
class procedure ThreadComm.mythread;
begin
while true do
begin
TX[0]:=$FF;
TX[1]:=$01;
TX[2]:=$01;
TX[3]:=$04;
TX[4]:=$A2;
TX[5]:=(TX[2] xor TX[3] xor TX[4]);
SerialPort1.Write(TX,0,6);
while SerialPort1.BytesToWrite>0 do;
Thread.Sleep(100);
if (stoploop) then
break;
end;
end;
Every time I compile the code, it raises 30 or so similar error messages stating the following:
Cannot call instance member "SerialPort1" without an instance reference
I know what the error means, but the only way to solve it is by creating an instance of the mainform. If you do that, then it won't be the same instance as the main program's instance. If this is the case, then you will have to create new instance of mainform all the time when you need to access its fields or public variables. Right?
class method Program.Main(args: array of string);
begin
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Application.ThreadException += OnThreadException;
**using lMainForm := new MainForm do
Application.Run(lMainForm);**
end;
I want to use all them variables are in the thread and they happen to be within the mainform public area.
Thanks
Your thread procedure (class procedure mythread; static;) seems to be "static" or "class", while the the fields you want to access are regular fields of a MainForm instance. That can't work, because there's one and only one procedure mythread, but there may be multiple instances of MainForm. While in practice there will probably only be one instance of MainForm, the compiler doesn't know that.
You should start by removing both class prefix and static sufix from that procedure, to make it a regular instance procedure, that can read instance fields. Even that's not going to be enough, because you derived from your MainForm, and I assume you create a new instance of ThreadCom and start the thread: Once you make the method a regular instance method the code will compile, but the result isn't going to be the one you expect, because you'll be accessing members of a different instance of MainForm.
Solutions:
Make the mythread procedure a instance method of your MainForm. It'll gain access to all required fields and access them from the proper instance!
Follow Rob's suggestion: create a new class, not derived from MainForm, pass it an instance of MainForm in the constructor, you that from the thread procedure. You'll (again) need to avoid static/class fields and procedures, because those can't be linked to one instance of MainForm.
Pass the things your thread needs as parameters to its constructor. Store references to those objects as fields in your thread class. Use them while your thread runs.
And don't make your thread class a descendant of your main form. That makes no sense. If your thread needs access to the entire form, then pass a MainForm reference to the thread's constructor. But if all your thread needs is a serial-port object, then just pass that, not the entire form.
Do not create additional instances of MainForm. That will create more forms; it won't give you access to the field values of your real main form.

Inheritance problems in TThread, don't appear to work the same in D2010 as D7

In my Application I have two thread objects (Outer and Sash) that inherit from a base thread object (FrameObject) of type TThread. This all works fine in D7. The application needs to be extended and I am taking this opportunity to move it to D2010 - however when I try to compile Delphi complains that the FrameObject Create method declaration differs from the previous declaration.
The Class types and Constructors are shown below;
TFrameObject = class(TThread)
constructor TFrameObject.Create(BuildType: TBuildType; OnBatchStep: TBatchNotify; OnThreadComplete: TNotifyTermination);
begin
inherited Create(True);
...
end;
TOuter = class (TFrameObject)
constructor Create(BuildType: TBuildType; OnBatchStep: TBatchNotify; OnThreadComplete: TNotifyTermination; ExceptionHandler: TExceptionHandler);
begin
inherited create(BuildType, OnBatchStep, OnThreadComplete);
fExceptionHandler := ExceptionHandler;
...
end;
TSash = class (TFrameObject)
constructor Create(BuildType: TBuildType; OnBatchStep: TBatchNotify; OnThreadComplete: TNotifyTermination; ExceptionHandler: TExceptionHandler);
begin
inherited create(BuildType, OnBatchStep, OnThreadComplete);
fExceptionHandler := ExceptionHandler;
...
end;
The D2010 code is a direct copy of the D7 source files and as I say, this all works fine in D7 (perhaps it shouldn't!) - so where am I going wrong?
Check for types declared in multiple units where one of them is used in the interface and another in the implementation section, so for example TBuildType in your TFrameObject declaration (in the interface section) would resolve to UnitA.TBuildType and implementation to UnitB.TBuildType.
My guess at to what is happening here is that the uses clause in your implementation section is declaring a TBuildType, TBatchNotify or TNotifyTermination that is different from the one used in your interface section where you declare the constructor.
A quick way to check would be to fully qualify those types in the implementation of TFrameObject.Create.
As per other answers, a newly introduction type in a unit used in the implementation section hiding a type of the same name used or declared in the interface section is the most likely explanation.
However, unlike previous answers, since the problem only occurs in D2010 and not D7 then I would suspect the ExceptionHandler parameter of type TExceptionHandler, since D2010 includes a type with this name declared in ToolsAPI\IStreams.
You could qualify the name in the implementation section:
TFrameObject.Create(... ExceptionHandler: MyUnit.TExpectionHandler)
Where "MyUnit" is the name of the unit containing the "real" TExceptionHandler that you wish to use.
Or, you could alias the type in the interface section and change your parameter lists to consistently use the aliased type in both interface and implementation of this unit:
interface
type
TFrameExceptionHandler = TExceptionHandler;
TFrameObject = class...
...
constructor Create(... ExceptionHandler: TFrameExceptionHandler);
end;
implementation
constructor TFrameObject.Create(... ExceptionHandler: TFrameExceptionHandler);

How i can declare a global method in Oxygene

How i can declare a global method in delphi prism using the __Global class?
And It is recommended to use global methods?
unfortunately I have not found any example.
Yes, you can if you turn on the Allow Globals option in your project options. Then you can just do the following code:
interface
method GlobalMethod: Integer; public;
implementation
It is not recommended to use this construction. A more .Net way is to use a static/class method on a class.
type
TSomeClass = public class
public
class method GlobalMethod: Integer;
end;
// Call like this
I := TSomeClass.GlobalMethod;

Resources