Delphi TDataSetProvider.ApplyUpdates how to use - delphi

I cannot figure out which parameter to pass to TDataSetProvider.ApplyUpdates
The definition says:
function ApplyUpdates(const Delta: OleVariant; MaxErrors: Integer; out ErrorCount: Integer): OleVariant;
What should I pass to the parameter Delta? I cannot find any example in the delphi documentation.
I am usign dbexpress and I want to call ApplyUpdates to apply the changes made to the records to the database.

If the provider and the clientdataset are connected (by setting the provider property of the CDS), changes are applied by
CDS.ApplyUpdates(0);

Related

Duplicate identifier of property and method parameter of a class

I transferred my project from Delphi to Lazarus. In a form I have a private method with parameter var Active: Boolean. In Delphi it was ok, but Lazarus give an error Error: Duplicate identifier "Active" and Hint: Identifier already defined in unit FORMS at line 641, on line 641 there is:
property Active: Boolean read FActive;
It is not difficult to change parameter name (with refactoring), but why can't I use the same name for property and parameter of method?
To make sure it is not an error of automatic conversion from Delphi, I created new project in Lazarus and added private method
procedure Test(var Active: Boolean);
The result was the same. Even if I use const or nothing instead of var.
I've looked into FPC docs and didn't find any such limitations. I'm just curious.
You should be able to use the same name for a property and a parameter. They have different scope, so the one nearest in scope (the parameter, which should be treated as being in the same scope as a local variable) should hide the one "further away" in scope (the property). In Delphi, you can still access the property, even inside that method, but then you should qualify it as Self.Active:
procedure TForm1.Test(var Active: Boolean);
var
ParamActive: Boolean;
FormActive: Boolean;
begin
ParamActive := Active; // gets the var parameter
FormActive := Self.Active; // gets the property
...
end;
I have no idea why FPC flags it as an error. It shouldn't.
Update
FWIW, if you change
{$mode objfpc}
to
{$mode delphi}
It does compile as expected, and you won't get an error. I just tried this.

Delphi OpenTools API get component property

I'm implementing a package to convert and auto-generate components in the delphi IDE. I'm aware that GExperts has a similar function but I need to customize some specific properties.
Right now I'm stuck on accessing the TADOQuery.SQL property, which is an instance of TStrings:
var
aVal : TValue;
aSqlS : TStrings;
begin
[...]
if (mycomp.GetComponentType = 'TADOQuery') then
if mycomp.GetPropValueByName('SQL', aVal) then
begin
aSqlS := TStrings(aVal.AsClass);
if Assigned(aSqlS) then <----- problem is here
ShowMessage(aSqlS.Text); <----- problem is here
end;
end;
I'm not really sure whether using TValue from RTTI is the correct way to go.
Thanks
Assuming GetPropValueByName() is returning a valid TValue (you did not show that code), then using aVal.AsClass is wrong since the SQL property getter does not return a metaclass type. It returns an object pointer, so use aVal.AsObject instead, or even aVal.AsType<TStrings>.
Update If comp is actually IOTAComponent than TValue is definitely wrong to use at all. The output of IOTAComponent.GetPropValueByName() is an untyped var that receives the raw data of the property value, or an IOTAComponent for TPersistent-derived objects:
var
aVal: IOTAComponent;
aSqlS : TStrings;
begin
[...]
if (mycomp.GetComponentType = 'TADOQuery') then
if mycomp.PropValueByName('SQL', aVal) then
ShowMessage(TStrings(aVal.GetComponentHandle).Text);
end;
However, a better option would be to access the actual TADOQuery object instead:
if (mycomp.GetComponentType = 'TADOQuery') then
ShowMessage(TADOQuery(comp.GetComponentHandle).SQL.Text);

What does [ref] do in a VCL application?

I'm working on a VCL application with Delphi 10 Seattle, and created a TDBGrid event handler via the IDE when I noticed that Delphi added a Ref custom attribute for the Rect argument:
procedure TfrmXxx.yyyDrawColumnCell(Sender: TObject;
const [Ref] Rect: TRect; DataCol: Integer; Column: TColumn;
State: TGridDrawState);
begin
//
end;
When or why does the IDE decide to insert this?
Does it have any effect in a VCL app?
update
Here's a video for those who cannot reproduce the behavior:
It is mentioned in the docs:
Constant parameters may be passed to the function by value or by reference, depending on the specific compiler used. To force the compiler to pass a constant parameter by reference, you can use the [Ref] decorator with the const keyword.
See Constant Parameters
When or why does the IDE decide to insert this?
The IDE never inserts this. It just copies the declaration of the event handler. Whoever wrote the event handler put the pass by[ref]erence marker in there.
Does it have any effect in a VCL app?
Yes.
If you mark a 8 byte parameter as const it will normally get passed by value in x64 and passed by reference in x86.
Declaring it as const [ref] will force it to be passed by reference in both cases.
It is very useful when doing inline assembly and in multi-threaded code.
Before const [ref] was introduced we were forced to use var instead of const to achieve the same effect.

Delphi: browsing components inside a property editor

When a property is a simple component of any class, the IDE's property editor is able to drop down a list of all compatible components in all the project's forms.
I want to do some equivalent task, but with some filtering based on acceptable component classes for the property; these classes common ancestor is only TComponent and they have custom interfaces.
Currently I have a working property editor that uses a paValueList attribute and some filtering in the GetValues procedure, based on checking the supported interfaces, but it is limited to the current form :-(.
How to browse all the forms like the IDE does?
I want to do some equivalent task, but with some filtering based on acceptable component classes for the property; these classes common ancestor is only TComponent and they have custom interfaces.
If you are filtering for only 1 interface, you should change the property in question to accept that interface type instead of a TComponent, and then the default property editor for interface properties (TInterfaceProperty) will filter the components automatically for you:
property MyProperty: IMyInterface read ... write ...;
Currently I have a working property editor that uses a paValueList attribute and some filtering in the GetValues procedure, based on checking the supported interfaces, but it is limited to the current form :-(.
How to browse all the forms like the IDE does?
To manually filter the components in a custom property editor, you need to do the same thing that the default component property editor (TComponentProperty) does to obtain the compatible components, and then you can filter them further as needed.
Internally, TComponentProperty.GetValues() simply calls Designer.GetComponentNames(), passing it the PTypeData of the property type that is being edited:
procedure TComponentProperty.GetValues(Proc: TGetStrProc);
begin
Designer.GetComponentNames(GetTypeData(GetPropType), Proc);
end;
So, if your property accepts a TComponent (since that is the only common ancestor of your intended components):
property MyProperty: TComponent read ... write ...;
Then GetPropType() in this case would return TypeInfo(TComponent).
GetComponentNames() (whose implementation is in the IDE and not available in the VCL source code) enumerates the components of the Root (Form, DataModule, or Frame) that owns the component being edited, as well as all linked Root objects that are accessible in other units specified in the edited Root's uses clause. This is documented behavior:
DesignIntf.IDesigner60.GetComponentNames
Executes a callback for every component that can be assigned a property of a specified type.
Use GetComponentNames to call the procedure specified by the Proc parameter for every component that can be assigned a property that matches the TypeData parameter. For each component, Proc is called with its S parameter set to the name of the component. This parameter can be used to obtain a reference to the component by calling the GetComponent method.
Note: GetComponentNames calls Proc for components in units that are in the uses clause of the current root object's unit (Delphi) or included by that unit (C++), as well as the entity that is the value of Root.
So, in your GetValues() implementation, call Designer.GetComponentNames() specifying the PTypeData for TComponent and let the IDE enumerate all available units and provide you with a list of each component's Name. Then you can loop through that list calling Designer.GetComponent() to get the actual TComponent objects and query them for your desired interface(s):
procedure TMyComponentProperty.GetValues(Proc: TGetStrProc);
var
Names: TStringList;
I: Integer;
begin
Names := TStringList.Create;
try
Designer.GetComponentNames(GetTypeData(TypInfo(TComponent)), Names.Append);
for I := 0 to Names.Count-1 do
begin
if Supports(Designer.GetComponent(Names[I]), IMyInterface) then
Proc(Names[I]);
end;
finally
Names.Free;
end;
end;
In fact, this is very similar to what the default TInterfaceProperty.GetValues() implementation does:
procedure TInterfaceProperty.ReceiveComponentNames(const S: string);
var
Temp: TComponent;
Intf: IInterface;
begin
Temp := Designer.GetComponent(S);
if Assigned(FGetValuesStrProc) and
Assigned(Temp) and
Supports(TObject(Temp), GetTypeData(GetPropType)^.Guid, Intf) then
FGetValuesStrProc(S);
end;
procedure TInterfaceProperty.GetValues(Proc: TGetStrProc);
begin
FGetValuesStrProc := Proc;
try
Designer.GetComponentNames(GetTypeData(TypeInfo(TComponent)), ReceiveComponentNames);
finally
FGetValuesStrProc := nil;
end;
end;
The only difference is that TInterfaceProperty does not waste memory collecting the names into a temp TStringList. It filters them in real-time as they are being enumerated.
Remy's solution works perfectly for my needs.
Nevertheless I've "simplified" a bit the filtering procedure:
procedure TMyComponentProperty.ReceiveComponentNames(const S: string);
var
Temp: TComponent;
Intf: IInterface;
begin
if Assigned(FGetValuesStrProc) then
begin
Temp := Designer.GetComponent(S);
if Assigned(Temp) then
if Temp.GetInterface(IMyInterface, IntF) then
FGetValuesStrProc(S);
// May add other interfaces checks here
end;
end;

Delphi Win32 Receiving a datatable from ASP.NET service

I'm building a Delphi Win32 app that should consume a Soap Service, which turns out to be a .NET based app. One function returns a DataTable. Of course, Delphi Win32 (not Delphi .NET) has no way of understanding this natively.
Any way I can make it work? I'll be happy to parse the XML manually too, but I've no idea how to get hold of the raw XML response.
The WSDL: https://stratus.voxamvia.co.za/api.asmx?WSDL
The function: GetNotifications which returns GetNotificationsResult, which builds as:
GetNotificationsResult = class(TRemotable)
private
Fnamespace: WideString;
Fnamespace_Specified: boolean;
FtableTypeName: WideString;
FtableTypeName_Specified: boolean;
procedure Setnamespace(Index: Integer; const AWideString: WideString);
function namespace_Specified(Index: Integer): boolean;
procedure SettableTypeName(Index: Integer; const AWideString: WideString);
function tableTypeName_Specified(Index: Integer): boolean;
published
property namespace: WideString Index (IS_ATTR or IS_OPTN) read Fnamespace write Setnamespace stored namespace_Specified;
property tableTypeName: WideString Index (IS_ATTR or IS_OPTN) read FtableTypeName write SettableTypeName stored tableTypeName_Specified;
end;
Any help appreciated!
Would it help if I implement RemObjects?
You can build your dataset from xml. This should give you a starting point: http://www.gekko-software.nl/DotNet/Art07.htm and http://www.gekko-software.nl/DotNet/Art08.htm.
I haven't used DataAbstract from RemObjects, so I can not give an advice on it.
LE: you can access and consume a web service written in .net by following this simple article well written by drbob - Consuming C# Web Services with Delphi 7 Professional
which contains also a small example on how to build dynamically and how to use the THttpRio (is the same as the Mikael Eriksson's answer)
Any way I can make it work? I'll be happy to parse the XML manually
too, but I've no idea how to get hold of the raw XML response.
You can get it in the OnAfterExecuteEvent on your THTTPRIO component. There you will have SOAPResponse: TStream as a parameter.
Update:
To get the event to fire you add a THTTPRIO component, create the event handler and use the RIO component as the third parameter to GetAPISoap.
This worked for me. HTTPRIO1 is a component on the form.
procedure TForm7.Button1Click(Sender: TObject);
var
A: APISoap;
begin
A := GetAPISoap(False, '', HTTPRIO1);
A.Validate_User('', '');
end;

Resources