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.
Related
I have the following function that raises an exception:
procedure Test();
var
WinHttpReq: Variant;
begin
WinHttpReq := CreateOleObject('WinHttp.WinHttpRequest.5.1');
try
WinHttpReq.Open('POST', 'https://tv.eurosport.com/', False);
WinHttpReq.SetRequestHeader('Content-Type', 'application/json');
WinHttpReq.Send('');
except
MsgBox(GetExceptionMessage, mbError, MB_OK);
end;
end;
Instead obtaining the error in a string GetExceptionMessage, I would like to obtain the error as an integer.
Is this possible?
Exceptions do not have any code in general. So there's no generic way to retrieve a code from an exception.
In this particular case the exception is based on an OLE automation error, which does have a code. Though, the code is not preserved, when the Inno Setup raises the exception for IDispatch interfaces (OLE).
What you can do, is to use the IUnknown interface. For that you need to:
define a Pascal-style interface for the IWinHttpRequest
define CLSID_WinHttpRequest
instantiate the object using the CreateComObject (instead of the CreateOleObject).
call the Send method defined as function Send(Body: Variant): HResult
check the returned HResult for 12038 (ERROR_INTERNET_SEC_CERT_CN_INVALID).
Particularly the first step is a huge and an error-prone task.
For examples and details, see:
Examples\CodeAutomation2.iss in your Inno Setup installation.
Pascal Scripting: Using COM Automation objects
If you decide to stick with error message comparison, you may try to use the FormatMessage function to retrieve the system/localized error message to compare with. See How i can retrieve the error description for a WinInet error code from delphi.
I'm building an Firemonkey app in Rad Studio XE7 where on single button click, I will need to do multiple( around 7) web service calls using TRestRequest. Every web service will return json object which will then populate dataset.
I'm looking for a way to these calls concurrently and not have UI of the app to lock.
Which way do you recommend to do this? I saw that Embarcadero introduced new Task and Feature functionality for threading, but I'm still not sure if we can use that and how. Also, I saw that there is a function to execute TRestRequest asynchronously, using this function:
function TCustomRESTRequest.ExecuteAsync(ACompletionHandler: TCompletionHandler = nil; ASynchronized: boolean = true; AFreeThread: boolean = true): TRESTExecutionThread;
but I can't find any documentation on how to use this method and what it does.
Any input will be greatly appreciated.
The ExecuteAsync(ACompletionHandler: TCompletionHandler = nil; ASynchronized: boolean = true; AFreeThread: boolean = true) method is indeed the way to go. As the name implies, it is asynchronous; meaning your program remains responsive after firing the request, or even after firing several requests.
However, it is important that you fire these different requests from different object instances; if you fire an ExecuteAsync from a TRESTRequest instance that is already performing an ExecuteAsync, the new request will get in the way of the existing request. You must create a separate TRESTRequest instance for each parallel call.
Note that its first parameter is a procedure; you pass a procedure of your choice as that parameter. The only requirement is that the procedure has the right signature; in this case, that it is a procedure that has no parameters.
The ExecuteAsync method is defined in REST.Client. (I've got Delphi XE-10.1. Berlin so it has one extra parameter, ACompletionHandlerWithError - which is called on error. The principle remains the same though).
Let's have a look:
function TCustomRESTRequest.ExecuteAsync(ACompletionHandler: TCompletionHandler = nil; ASynchronized: boolean = true;
AFreeThread: boolean = true; ACompletionHandlerWithError: TCompletionHandlerWithError = nil): TRESTExecutionThread;
begin
Result := TRESTExecutionThread.Create(Execute, self, ACompletionHandler, ASynchronized, AFreeThread, ACompletionHandlerWithError);
end;
What happens here is that a new thread is created in which the REST Request is executed. If the response comes in, it is handled by ACompletionHandler.
By default, ACompletionHandler runs on the new thread created by ExecuteAsync. If you want it to run on the main thread, you should set ASynchronized to true.
But how do you get access to that response, and make it accessible to the rest of your program?
FireMonkey's TRESTRequest class has a property Response, that refers to the TRESTResponse object that contains the server's response to our request.
Unfortunately, neither the TRESTRequest nor the TRESTResponse object are passed to our CompletionHandler!
So we need to somehow give the CompletionHandler this information. Fortunately, we can use a method as a CompletionHandler.
Let's assume that the class that is going to process the resulting data is called DataOwner. Our purpose is that DataOwner has access to the TRESTResponse instance associated with our TRESTRequest instance.
The easiest way to do this is to make TRESTRequest and/or TRESTResponse a member of DataOwner.
Assume your request was fired from a TRESTRequest instance named MyRESTRequest and processed by a function processResponse, you would use the following code:
type TDataOwner = class
MyData: TSomeDataType;
procedure GetData();
procedure FillDataSet();
end;
implementation
procedure DataOwner.GetData();
begin
// ... initialize MyRESTRequest here...
MyRESTRequest.ExecuteAsync(FillDataSet);
end;
procedure DataOwner.FillDataSet();
begin
MyData := processResponse(MyRESTRequest.Response);
end;
If you want to fire several requests in parallel, you need to use this pattern several times. It is unfortunate that we don't have direct access to the TRESTRequest instance or TRESTResponse instance in our Completion Handlers, because this means we have to do the bookkeeping ourselves. In other words, it is up to the programmer to make sure that the Completion Handler processes the right Response object.
I do some reporting on a form with reportbuilder.
On the main form I select some items on a grid and then a generate the reports of the items.
I want to do this in a Tthread but i get an error 'List index out of bounds'.
Here is the call stack:
Classes.TList.Get(1244868)
Classes.TList.Get(???)
Forms.TScreen.GetCustomForms(???)
Forms.TApplication.DoActionIdle
Forms.TApplication.Idle(???)
Forms.TApplication.HandleMessage
Forms.TApplication.Run
Seems some form is either not being added to the Screen.Forms
collection in a timely manner or one is being freed from it during the
loop in DoActionIdle.
Any ideas on how to circumvent this problem?
I work on windows XP and delphi 2010.
I Also I've the problem with a test procedure on my application
TForm3 is just a form with no code.
TDebugThread = class(TThread)
protected
procedure Execute; override;
public
constructor Create();
end;
constructor TDebugThread.Create;
begin
FreeOnTerminate := True;
inherited Create(False);
end;
procedure TDebugThread.Execute;
var
oReport: DeBugReport.TForm3;
begin
inherited;
oReport:= DeBugReport.TForm3.Create(Nil);
try
sleep(1000);
finally
oReport.Free;
end;
end;
....
procedure RunThread();
begin
TDebugThread.Create();
end;
Recapitulation:
I have a list of some Intervention on a form. Each detail and resumation of the intervention can I print on 2/5 reports. Therefore I use reports components (reportbuilder) on another form (not visible). The new feature was to multiselect some interventions on the list and set the reports in a folder in pdf format. That's was simple just on each intervention call the reportform and some parameters to change and save into pdf.
But this take to long. The user must wait until the procedure was ended. No problem I set the procedure in a thread. But there I get the error 'List index out of bounds'. ArgggArggg, I was suspected that the reportform (created, to his job and then destroyed) the problem was but hoped that there was another solution. I was thinking to change the TForm into TDataModule. Can I set all the components of the form into the datamodule. I use the TDbGrid to see some values in design. But in the Tdatamodule I can't set a TDBGrid. Ok, I can live without the TDbGrid. So I transformed the TForm into TDataModule.
But the TDataModule is not the answer. There I get the error 'Graphics.OutOfResource' from a TBitmap. I think that the TBitmap is calling from the TppReport. Now I'm done. I'm changing my code all more than 2 days with no result. I leave the TThread for this time.
Let's take a look at TApplication.DoActionIdle:
procedure TApplication.DoActionIdle;
var
I: Integer;
begin
for I := 0 to Screen.CustomFormCount - 1 do
with Screen.CustomForms[I] do
if HandleAllocated and IsWindowVisible(Handle) and
IsWindowEnabled(Handle) then
UpdateActions;
end;
Let's assume that Screen.CustomFormCount and is implemented correctly and always returns the number of items indexed by Screen.CustomForms. In which case the conclusion is that the body of the loop is deleting a form. That is Screen.CustomFormCount is changing during the execution of the loop.
The only way that can happen is if one of the form's action update handlers results in a form being deleted. So, I can't tell you any more than that, but this analysis should lead you to the root cause of the problem.
And the second part of your question is quite simple. You cannot use VCL components outside the main GUI thread.
In fact it is plausible that destroying the VCL form in your thread is what is leading to Screen.CustomFormCount changing during the execution in the GUI thread of TApplication.DoActionIdle.
i use this code in a thread (through Indy Onexecute event) . is there any problem ?
function TFrmMain.ShellExecute_AndWait(FileName, Params: string): bool;
var
exInfo: TShellExecuteInfo;
Ph: DWORD;
begin
FillChar(exInfo, SizeOf(exInfo), 0);
with exInfo do
begin
cbSize := SizeOf(exInfo);
fMask := SEE_MASK_NOCLOSEPROCESS or SEE_MASK_FLAG_DDEWAIT;
Wnd := GetActiveWindow();
exInfo.lpVerb := 'open';
exInfo.lpParameters := PChar(Params);
lpFile := PChar(FileName);
nShow := SW_NORMAL;
end;
if ShellExecuteEx(#exInfo) then
Ph := exInfo.hProcess
else
begin
Result := true;
exit;
end;
while WaitForSingleObject(exInfo.hProcess, 50) <> WAIT_OBJECT_0 do
begin
end;
CloseHandle(Ph);
Result := true;
end;
MSDN has this advice:
Because ShellExecuteEx can delegate execution to Shell extensions (data sources, context menu handlers, verb implementations) that are activated using Component Object Model (COM), COM should be initialized before ShellExecuteEx is called. Some Shell extensions require the COM single-threaded apartment (STA) type. In that case, COM should be initialized as shown here:
CoInitializeEx(NULL, COINIT_APARTMENTTHREADED | COINIT_DISABLE_OLE1DDE)
There are instances where ShellExecuteEx does not use one of these types of Shell extension and those instances would not require COM to be initialized at all. Nonetheless, it is good practice to always initalize COM before using this function.
(In Delphi, you'd of course replace the first parameter with nil and use or for the bitwise operation.)
Raymond Chen recently wrote about the consequences of getting this wrong. The specific example was that the function might fail with the Error_Access_Denied error code.
That's the only potential multithreading issue I see in your code. Below are further things that occurred to me when I read your code, although they have nothing to do with multithreading (and not even much to do with Indy).
You have a peculiar way of waiting for the program to stop running. You repeatedly wait for 50 milliseconds at a time, but if the process isn't finished yet, you do nothing but wait again. Describe your intention more accurately by specifying Infinite for the timeout.
The function always returns True. If there's no useful return value, then you should just make it a procedure so there's no return value at all. Don't confuse the caller with useless information. If you're going to keep it as a function, then use the Delphi native type Boolean instead of the Windows compatibility type Bool for the return type.
I'm a little wary about the idea of a server executing user-interactive programs upon receipt of network messages.
Notice when MSDN says you might not get a process handle. There are cases when ShellExecuteEx can service your request without creating a new process, so you'll have nothing to wait on.
The user might end up using the program awhile, and your server will be stuck waiting all that time. I wonder whether it really needs to wait at all. Is the client going to be waiting for a response from the server, too?
I have just written my own logging framework (very lightweight, no need for a big logging framework). It consists of an interface ILogger and a number of classes implementing that interface. The one I have a question about is TGUILogger which takes a TStrings as the logging target and synchronizes the logging with the main thread so that the Items member of a listbox can be used as the target.
type
ILogger = Interface (IInterface)
procedure Log (const LogString : String; LogLevel : TLogLevel);
procedure SetLoggingLevel (LogLevel : TLogLevel);
end;
type
TGUILogger = class (TInterfacedObject, ILogger)
public
constructor Create (Target : TStrings);
procedure Log (const LogString : String; LogLevel : TLogLevel);
procedure SetLoggingLevel (LogLevel : TLogLevel);
private
procedure PerformLogging;
end;
procedure TGUILogger.Log (const LogString : String; LogLevel : TLogLevel);
begin
TMonitor.Enter (Self);
try
FLogString := GetDateTimeString + ' ' + LogString;
TThread.Synchronize (TThread.CurrentThread, PerformLogging);
finally
TMonitor.Exit (Self);
end;
end;
procedure TGUILogger.PerformLogging;
begin
FTarget.Add (FLogString);
end;
The logging works, but the application does not close properly. It seems to hang in the Classes unit. The stack trace:
System.Halt0, System.FinalizeUnits, Classes.Finalization, Classes.FreeExternalThreads,
System.TObject.Free, Classes.TThread.Destroy, Classes.TThread.RemoveQueuedEvents
What am I doing wrong here?
EDIT: I just found the following hint in the Delphi help for TThread.StaticSynchronize
Warning: Do not call StaticSynchronize from within the main thread. This can cause
an infinite loop.
This could be exactly my problem since some logging request come from the main thread. How can I solve this?
If you compare the CurrentThreadID with MainThreadID then you can choose to synchronize or not.
Personally, I chose to have the GUI ask the log system for the latest info, rather than have threads pause. Otherwise your logging interferes with the fast operation of the thread which defeats the purpose of having it.
If you don't find any simpler way, you could try doing this:
At program initialization, (from the main thread,) have your logging subsystem call the Windows API function GetCurrentThreadID and store the result in a variable. (EDIT: the MainThreadID variable in the System unit, gets initialized this way automatically for you at startup. Thanks, mghie.) When a logging request comes in after that, call GetCurrentThreadID again, and only synchronize if it's coming from a different thread.
There are other tricks that don't involve the Windows API, but they end up being more complicated, especially if you have a bunch of different custom TThread descendants. The basic principle is the same, though: Verify whether or not you're in the main thread before you decide whether or not to call StaticSynchronize.