doug_stephens
asked on
Array parameters to SOAP Server
I am trying to write an invocable function in a Delphi 2005 SOAP Server which updates an array parameter. I can make it work if the array is the returned value but not if it is a parameter. The function is:
function TRFMain.TestArray(_test: array of string; var _message:String): Boolean;
begin
_test[0] := 'One';
_test[1] := 'And';
_test[2] := 'Tree';
_message := 'One And Tree';
result := True;
end;
I call it from a button in the client:
procedure TForm1.Button4Click(Sender : TObject);
var p : array of string;
w : IRFMainservice;
b : Boolean;
m : String;
begin
w := IRFMainservice.Create;
SetLength(p,3);
b := w.TestArray(p,m);
w.Dispose
end;
The array is never changed after the call to TestArray. The string parameter is passed OK.
function TRFMain.TestArray(_test: array of string; var _message:String): Boolean;
begin
_test[0] := 'One';
_test[1] := 'And';
_test[2] := 'Tree';
_message := 'One And Tree';
result := True;
end;
I call it from a button in the client:
procedure TForm1.Button4Click(Sender
var p : array of string;
w : IRFMainservice;
b : Boolean;
m : String;
begin
w := IRFMainservice.Create;
SetLength(p,3);
b := w.TestArray(p,m);
w.Dispose
end;
The array is never changed after the call to TestArray. The string parameter is passed OK.
ASKER
Wow, you're quick Btx. But I'm looking to pass an array of strings. Your type is just a 100 character string. Simple strings work fine defined as String, as I mentioned in my post. I also tried something like your idea by using
TMyString = array of string
or
TMyString = array[0..x] of string
instead of just declaring an open array parameter, but same result. My server function can see the values passed from the client. But can't send 'em back in a parameter, just as a result.
TMyString = array of string
or
TMyString = array[0..x] of string
instead of just declaring an open array parameter, but same result. My server function can see the values passed from the client. But can't send 'em back in a parameter, just as a result.
ASKER CERTIFIED SOLUTION
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
now, the function is declared as stdcall right?
ASKER
Yes it is stdcall.
I'll test TStringDynArray also.
Also tried putting the array into a remotable class, as:
TTestArray = class(TRemotable)
private
Fdata: TArray;
procedure Setdata(const Value: TArray);
published
property data : TArray read Fdata write Setdata;
end;
Same result.
I'll test TStringDynArray also.
Also tried putting the array into a remotable class, as:
TTestArray = class(TRemotable)
private
Fdata: TArray;
procedure Setdata(const Value: TArray);
published
property data : TArray read Fdata write Setdata;
end;
Same result.
and you have tried var...
function TRFMain.TestArray(var _test: array of string; var _message:String): Boolean;
function TRFMain.TestArray(var _test: array of string; var _message:String): Boolean;
ah... gets translated into WideString, I'll play some more and come back
ASKER
I think I've got it finally. On the server, you need to use:
function ServerFunc(var x: TStringDynArray)
On the client you use something like:
Function ClientFunc;
var a : TArrayOfString;
begin
....
ServerFunc(a);
If you declare as TStringDynArray in client it will not compile if you have var on the parameter in ServerFunc. If you leave out var, you don't get the modified array returned. It's a bit confusing and none of it is documented.
function ServerFunc(var x: TStringDynArray)
On the client you use something like:
Function ClientFunc;
var a : TArrayOfString;
begin
....
ServerFunc(a);
If you declare as TStringDynArray in client it will not compile if you have var on the parameter in ServerFunc. If you leave out var, you don't get the modified array returned. It's a bit confusing and none of it is documented.
i use dynamic arrays in SOAP in next way
types definition:
TItem = class(TRemotable)
.....
end;
TItemsArray = array of TItem;
TMainType = class(TRemotable)
private
....
FItems: TItemsArray;
published
...
property Items: TItemsArray read FItems write FItems;
end;
server side:
function ISomeInterfaceImpl.GetMain TypeData(o ut Data: TMainType ): Integer;
var
i, n: Integer;
begin
Data := nil;
result := 0;
begin
Data := TMainType.Create;
....
for i:=1 to 3 do begin
n := Length(Data.FItems);
SetLength(Data.FItems, n+1);
Data.FItems[n] := TItem.Create;
Data.FItems[n].field1 := ....;
.....
end;
end
end;
client side:
procedure getData(Sender: TObject);
var
intf: ISomeInterface;
Data: TMainType ;
i: Integer;
begin
intf:=nil; Data := nil;
try
intf := GetISomeInterface(false, '', nil);
intf.GetMainTypeData(Data) ;
.....
for i:=0 to Length(Data.Items) - 1 do begin
.....
end;
finally
intf:=nil;
if Assigned(Data) then begin
for I := 0 to Length(FItems)-1 do
if Assigned(FItems[I]) then
FItems[I].Free;
SetLength(FItems, 0);
Data.Free;
end;
end
end;
types definition:
TItem = class(TRemotable)
.....
end;
TItemsArray = array of TItem;
TMainType = class(TRemotable)
private
....
FItems: TItemsArray;
published
...
property Items: TItemsArray read FItems write FItems;
end;
server side:
function ISomeInterfaceImpl.GetMain
var
i, n: Integer;
begin
Data := nil;
result := 0;
begin
Data := TMainType.Create;
....
for i:=1 to 3 do begin
n := Length(Data.FItems);
SetLength(Data.FItems, n+1);
Data.FItems[n] := TItem.Create;
Data.FItems[n].field1 := ....;
.....
end;
end
end;
client side:
procedure getData(Sender: TObject);
var
intf: ISomeInterface;
Data: TMainType ;
i: Integer;
begin
intf:=nil; Data := nil;
try
intf := GetISomeInterface(false, '', nil);
intf.GetMainTypeData(Data)
.....
for i:=0 to Length(Data.Items) - 1 do begin
.....
end;
finally
intf:=nil;
if Assigned(Data) then begin
for I := 0 to Length(FItems)-1 do
if Assigned(FItems[I]) then
FItems[I].Free;
SetLength(FItems, 0);
Data.Free;
end;
end
end;
that would be the correct way of implementing them (like vadim_ti showed), I remember back in the days when SOAP came out it was recommended from Borland that all the complex types were descendant from TRemotable
ASKER
But with Delphi 2005 I believe that all TDynArrayxxxxxx types are pre-defined as TRemotable. My problem was that in the client you had to define them as TArrayOfxxxx type.
I don't think you can put non-scalar types into a Remotable class, unless they are also Remotable classes. I tried, for example:
TTestArray = class(TRemotable)
private
Fdata: array of string;
procedure Setdata(const Value: TArray);
published
property data : TArray read Fdata write Setdata;
end;
and it didn't work.
I'd like to exchange a variety of VCL classes such as TList or TStringList in this same way. BTW, is use of OUT parameters important here? I've never used them.
I don't think you can put non-scalar types into a Remotable class, unless they are also Remotable classes. I tried, for example:
TTestArray = class(TRemotable)
private
Fdata: array of string;
procedure Setdata(const Value: TArray);
published
property data : TArray read Fdata write Setdata;
end;
and it didn't work.
I'd like to exchange a variety of VCL classes such as TList or TStringList in this same way. BTW, is use of OUT parameters important here? I've never used them.
you have no problem to use non-scalar types in a remotable, you only need to register them and free them on client side after use. OUT is not so important, OUT only say function will ignore parameter value, without OUT you can "read" from parameter and modify it.
to put those types you would have to serialize them in a TByteDinArray (or whatever is called)
ASKER
So, suppose I wanted to pass an object containing a TStringList. I can't have something like this (or can I?):
MyClass = class(TRemotable)
private
Flist: TStringlist;
published
property List : TStringlist read Flist write Flist
end;
I think rather, as Btx says, you would have to use a TStringDynArray to serialize the list into an array and mimic all the Stringlist members, basically re-writing the class. Seems wrong.
Is there some easy way to register VCL classes? If not, then I don't quite see the value of remoting.
MyClass = class(TRemotable)
private
Flist: TStringlist;
published
property List : TStringlist read Flist write Flist
end;
I think rather, as Btx says, you would have to use a TStringDynArray to serialize the list into an array and mimic all the Stringlist members, basically re-writing the class. Seems wrong.
Is there some easy way to register VCL classes? If not, then I don't quite see the value of remoting.
from Delphi manual:
Remotable object example
This example shows how to create a remotable object for a parameter on an invokable
interface where you would otherwise use an existing class. In this example, the
existing class is a string list (TStringList). To keep the example small, it does not
reproduce the Objects property of the string list.
Because the new class is not scalar, it descends from TRemotable rather than
TRemotableXS. It includes a published property for every property of the string list
you want to communicate between the client and server. Each of these remotable
properties corresponds to a remotable type. In addition, the new remotable class
includes methods to convert to and from a string list.
TRemotableStringList = class(TRemotable)
private
FCaseSensitive: Boolean;
FSorted: Boolean;
FDuplicates: TDuplicates;
FStrings: TStringDynArray;
public
procedure Assign(SourceList: TStringList);
procedure AssignTo(DestList: TStringList);
published
property CaseSensitive: Boolean read FCaseSensitive write FCaseSensitive;
property Sorted: Boolean read FSorted write FSorted;
property Duplicates: TDuplicates read FDuplicates write FDuplicates;
property Strings: TStringDynArray read FStrings write FStrings;
end;
Note that TRemotableStringList exists only as a transport class. Thus, although it has a
Sorted property (to transport the value of a string list’s Sorted property), it does not
need to sort the strings it stores, it only needs to record whether the strings should be
sorted. This keeps the implementation very simple. You only need to implement the
Assign and AssignTo methods, which convert to and from a string list:
procedure TRemotableStringList.Assig n(SourceLi st: TStrings);
var I: Integer;
begin
SetLength(Strings, SourceList.Count);
for I := 0 to SourceList.Count - 1 do
Strings[I] := SourceList[I];
CaseSensitive := SourceList.CaseSensitive;
Sorted := SourceList.Sorted;
Duplicates := SourceList.Duplicates;
end;
procedure TRemotableStringList.Assig nTo(DestLi st: TStrings);
var I: Integer;
begin
DestList.Clear;
DestList.Capacity := Length(Strings);
DestList.CaseSensitive := CaseSensitive;
DestList.Sorted := Sorted;
DestList.Duplicates := Duplicates;
for I := 0 to Length(Strings) - 1 do
DestList.Add(Strings[I]);
end;
Optionally, you may want to register the new remotable class so that you can specify
its class name. If you do not register the class, it is registered automatically when you
register the interface that uses it. Similarly, if you register the class but not the
TDuplicates and TStringDynArray types that it uses, they are registered automatically.
This code shows how to register the TRemotableStringList class and the TDuplicates
type. TStringDynArray is registered automatically because it is one of the built-in
dynamic array types declared in the Types unit.
This registration code goes in the initialization section of the unit where you define
the remotable class:
RemClassRegistry.RegisterX SInfo(Type Info(TDupl icates), MyNameSpace, 'duplicateFlag');
RemClassRegistry.RegisterX SClass(TRe motableStr ingList, MyNameSpace, 'stringList', '',False);
Remotable object example
This example shows how to create a remotable object for a parameter on an invokable
interface where you would otherwise use an existing class. In this example, the
existing class is a string list (TStringList). To keep the example small, it does not
reproduce the Objects property of the string list.
Because the new class is not scalar, it descends from TRemotable rather than
TRemotableXS. It includes a published property for every property of the string list
you want to communicate between the client and server. Each of these remotable
properties corresponds to a remotable type. In addition, the new remotable class
includes methods to convert to and from a string list.
TRemotableStringList = class(TRemotable)
private
FCaseSensitive: Boolean;
FSorted: Boolean;
FDuplicates: TDuplicates;
FStrings: TStringDynArray;
public
procedure Assign(SourceList: TStringList);
procedure AssignTo(DestList: TStringList);
published
property CaseSensitive: Boolean read FCaseSensitive write FCaseSensitive;
property Sorted: Boolean read FSorted write FSorted;
property Duplicates: TDuplicates read FDuplicates write FDuplicates;
property Strings: TStringDynArray read FStrings write FStrings;
end;
Note that TRemotableStringList exists only as a transport class. Thus, although it has a
Sorted property (to transport the value of a string list’s Sorted property), it does not
need to sort the strings it stores, it only needs to record whether the strings should be
sorted. This keeps the implementation very simple. You only need to implement the
Assign and AssignTo methods, which convert to and from a string list:
procedure TRemotableStringList.Assig
var I: Integer;
begin
SetLength(Strings, SourceList.Count);
for I := 0 to SourceList.Count - 1 do
Strings[I] := SourceList[I];
CaseSensitive := SourceList.CaseSensitive;
Sorted := SourceList.Sorted;
Duplicates := SourceList.Duplicates;
end;
procedure TRemotableStringList.Assig
var I: Integer;
begin
DestList.Clear;
DestList.Capacity := Length(Strings);
DestList.CaseSensitive := CaseSensitive;
DestList.Sorted := Sorted;
DestList.Duplicates := Duplicates;
for I := 0 to Length(Strings) - 1 do
DestList.Add(Strings[I]);
end;
Optionally, you may want to register the new remotable class so that you can specify
its class name. If you do not register the class, it is registered automatically when you
register the interface that uses it. Similarly, if you register the class but not the
TDuplicates and TStringDynArray types that it uses, they are registered automatically.
This code shows how to register the TRemotableStringList class and the TDuplicates
type. TStringDynArray is registered automatically because it is one of the built-in
dynamic array types declared in the Types unit.
This registration code goes in the initialization section of the unit where you define
the remotable class:
RemClassRegistry.RegisterX
RemClassRegistry.RegisterX
ASKER
Yes, I've read that. A few times in fact! It seems to me that it says that you CANNOT include VCL classes (such as StringList) in a remotable object. Instead you have to reproduce all the members and use a TxxxDynArray. This makes it very inconvenient for the client app since it won't have access to the public functions to convert a real Stringlist object. Or you duplicate the Assign methods in both apps.
Also, why do the two assignment methods not match their interfaces (TStrings vs TStringList)?
Also, why do the two assignment methods not match their interfaces (TStrings vs TStringList)?
SOLUTION
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
ASKER
>> Btx: ah... gets translated into WideString, I'll play some more and come back
How did you determine that it got translated into a WideString? Did you use some kind of tool to examine the data type?
How did you determine that it got translated into a WideString? Did you use some kind of tool to examine the data type?
ASKER
Vadim_ti, that makes sense. I think this is coming together for me now.
One last supplemental question, really the heart of my post. I was originally having problems modifying an array parameter, although I could get an array returned. This is solved by using TDynStringArray as the parameter type. However, I still have the same problem with the remotable class. In the server I have:
TMyObject = class(TRemotable)
private
FMyData: String;
procedure SetMyData(const Value: String);
published
property MyData : String read FMyData write SetMyData;
end;
If my client does something like:
o := TMyObject.Create;
o.MyData := 'Set by client';
myServer.TestThis(o);
And my server does something like this with the parameter:
Function TMyServer.TestThis(o:TMyOb ject) : Boolean;
...
o.Mydata := 'Set by server';
return True;
o.MyData is unchanged in the client. If I make it a VAR parameter, I get this error: "referenced object with ID '1' was not found in the document". I have updated the web reference in the client. It is stdcall. I did not explicity register TMyObject (but I don't think you need to).
If I change the server so that it returns the object, like:
Function TMyServer.TestThis(o: TMyObject) : TMyObject;
...
o.Mydata := 'Set by Server';
result := o;
then I get the changed object. Can't I change remotable object properties and pass them back as parameters?
One last supplemental question, really the heart of my post. I was originally having problems modifying an array parameter, although I could get an array returned. This is solved by using TDynStringArray as the parameter type. However, I still have the same problem with the remotable class. In the server I have:
TMyObject = class(TRemotable)
private
FMyData: String;
procedure SetMyData(const Value: String);
published
property MyData : String read FMyData write SetMyData;
end;
If my client does something like:
o := TMyObject.Create;
o.MyData := 'Set by client';
myServer.TestThis(o);
And my server does something like this with the parameter:
Function TMyServer.TestThis(o:TMyOb
...
o.Mydata := 'Set by server';
return True;
o.MyData is unchanged in the client. If I make it a VAR parameter, I get this error: "referenced object with ID '1' was not found in the document". I have updated the web reference in the client. It is stdcall. I did not explicity register TMyObject (but I don't think you need to).
If I change the server so that it returns the object, like:
Function TMyServer.TestThis(o: TMyObject) : TMyObject;
...
o.Mydata := 'Set by Server';
result := o;
then I get the changed object. Can't I change remotable object properties and pass them back as parameters?
>>> I did not explicity register TMyObject (but I don't think you need to).
YOU NEED TO
YOU NEED TO
and you need to use
var or out to return value
var or out to return value
ASKER
>>> YOU NEED TO
Delphi manual says these are automatically registered when you register the interface which uses the remotable classes, unless you want to use a custom namespace. But anyway I tried adding this to my server interface unit:
RemClassRegistry.RegisterX SClass(TMy Object,'', 'TMyObject', '',False);
And added VAR to the parameter, as
Function TMyServer.TestThis(var o:TMyObject) : Boolean
AND IT WORKS!
Still, I dont' understand why Delphi manual says: "Optionally, you may want to register the new remotable class so that you can specify its class name. If you do not register the class, it is registered automatically when you register the interface that uses it."
Delphi manual says these are automatically registered when you register the interface which uses the remotable classes, unless you want to use a custom namespace. But anyway I tried adding this to my server interface unit:
RemClassRegistry.RegisterX
And added VAR to the parameter, as
Function TMyServer.TestThis(var o:TMyObject) : Boolean
AND IT WORKS!
Still, I dont' understand why Delphi manual says: "Optionally, you may want to register the new remotable class so that you can specify its class name. If you do not register the class, it is registered automatically when you register the interface that uses it."
ASKER
Sorry! I pushed to wrong button. Too eager for a solution maybe. It DOES NOT work with the manual registration and VAR parameter as above. Same error message "referenced object with ID '1' was not found in the document". That error message goes away if I remove VAR, but still don't get the changed object back to the client.
Is my registration correct? Still not sure why I would have to register it.
Is my registration correct? Still not sure why I would have to register it.
Optionally, you may want to register the new remotable class so that you can specify its class name
and you want to specify its name
registration is looking good
may be you register it on server side only, and client side does not see your registration?
(uses unit????)
add the same line
RemClassRegistry.RegisterX SClass(TMy Object,'', 'TMyObject', '',False);
in your client side unit
and you want to specify its name
registration is looking good
may be you register it on server side only, and client side does not see your registration?
(uses unit????)
add the same line
RemClassRegistry.RegisterX
in your client side unit
ASKER
Yes, I did only register in server. I guess I figuered that by adding the web reference all this registry stuff would become resolved automatically.
I tried putting the above into my client's main initialization section but doesn't compile. RegClassRegistry is undefined. I then tried adding InvokeRegistry to my uses clause but that cannot be resolved. (Windows VCL client).
I tried putting the above into my client's main initialization section but doesn't compile. RegClassRegistry is undefined. I then tried adding InvokeRegistry to my uses clause but that cannot be resolved. (Windows VCL client).
could you post your client side unit where TMyObject class is defined?
ASKER
No sure what you mean. I only have TMyObject defined in the server interface unit. Here is the code, with some parts removed for readability.
**********
CLIENT main unit looks like:
unit Main;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, Borland.Vcl.StdCtrls, Borland.Vcl.ComCtrls, System.ComponentModel,
localhost.IRFMainservice;
type
TForm1 = class(TForm)
... a bunch of form objects ...
procedure ChangeObjectClick(Sender: TObject);
private
public
end;
implementation
uses Types, Lib;
{$R *.nfm}
...
procedure TForm1.ChangeObjectClick(S ender: TObject);
var w : IRFMainservice;
mo : TMyObject;
m : String;
begin
w := IRFMainservice.Create;
mo := TMyObject.Create;
mo.MyData := 'MyData Set by Client';
m := 'Original MyData='+mo.MyData;
m := m + chr(13) + 'Return='+w.TestChangeObje ct(mo);
m := m + chr(13) +'After call MyData='+mo.MyData;
ShowMessage(m);
mo.Free;
w.Free;
end;
...
initialization
RemClassRegistry.RegisterX SClass(TMy Object,'', 'TMyObject', '',False);
end.
***************
SERVER interface unit looks like:
unit RFMainIntf;
interface
uses InvokeRegistry, Types, XSBuiltIns;
type
...
TMyObject = class(TRemotable)
private
FMyData: String;
procedure SetMyData(const Value: String);
published
property MyData : String read FMyData write SetMyData;
end;
....
IRFMain = interface(IInvokable)
['{E7FAFA7D-E245-4323-BBDD -DF785CE02 E52}']
... various other functions
function GetTestChangeObject(_o : TMyObject) : TMyObject; stdcall;
end;
implementation
....
procedure TMyObject.SetMyData(const Value: String);
begin
FMyData := Value;
end;
initialization
InvRegistry.RegisterInterf ace(TypeIn fo(IRFMain ));
RemClassRegistry.RegisterX SClass(TMy Object, '', 'TMyObject', '',False);
RemClassRegistry.RegisterX SClass(TRF Client, '', 'TRFClient', '',False);
end.
*************
SERVER implementation unit:
unit RFMainImpl;
interface
uses InvokeRegistry, Types, XSBuiltIns, RFMainIntf, ADODB;
type
TRFMain = class(TInvokableClass, IRFMain)
private
....
public
...
function GetTestChangeObject(_o : TMyObject) : TMyObject; stdcall;
end;
implementation
uses Main, Classes, Sysutils, Lib, SQLClasses;
....
function TRFMain.TestChangeObject(_ o: TMyObject): String;
begin
_o.MyData := 'Server set My Data to this';
result := 'Server MyData='+_o.MyData;
end;
initialization
InvRegistry.RegisterInvoka bleClass(T RFMain);
end.
**********
CLIENT main unit looks like:
unit Main;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, Borland.Vcl.StdCtrls, Borland.Vcl.ComCtrls, System.ComponentModel,
localhost.IRFMainservice;
type
TForm1 = class(TForm)
... a bunch of form objects ...
procedure ChangeObjectClick(Sender: TObject);
private
public
end;
implementation
uses Types, Lib;
{$R *.nfm}
...
procedure TForm1.ChangeObjectClick(S
var w : IRFMainservice;
mo : TMyObject;
m : String;
begin
w := IRFMainservice.Create;
mo := TMyObject.Create;
mo.MyData := 'MyData Set by Client';
m := 'Original MyData='+mo.MyData;
m := m + chr(13) + 'Return='+w.TestChangeObje
m := m + chr(13) +'After call MyData='+mo.MyData;
ShowMessage(m);
mo.Free;
w.Free;
end;
...
initialization
RemClassRegistry.RegisterX
end.
***************
SERVER interface unit looks like:
unit RFMainIntf;
interface
uses InvokeRegistry, Types, XSBuiltIns;
type
...
TMyObject = class(TRemotable)
private
FMyData: String;
procedure SetMyData(const Value: String);
published
property MyData : String read FMyData write SetMyData;
end;
....
IRFMain = interface(IInvokable)
['{E7FAFA7D-E245-4323-BBDD
... various other functions
function GetTestChangeObject(_o : TMyObject) : TMyObject; stdcall;
end;
implementation
....
procedure TMyObject.SetMyData(const Value: String);
begin
FMyData := Value;
end;
initialization
InvRegistry.RegisterInterf
RemClassRegistry.RegisterX
RemClassRegistry.RegisterX
end.
*************
SERVER implementation unit:
unit RFMainImpl;
interface
uses InvokeRegistry, Types, XSBuiltIns, RFMainIntf, ADODB;
type
TRFMain = class(TInvokableClass, IRFMain)
private
....
public
...
function GetTestChangeObject(_o : TMyObject) : TMyObject; stdcall;
end;
implementation
uses Main, Classes, Sysutils, Lib, SQLClasses;
....
function TRFMain.TestChangeObject(_
begin
_o.MyData := 'Server set My Data to this';
result := 'Server MyData='+_o.MyData;
end;
initialization
InvRegistry.RegisterInvoka
end.
ok
i do not know waht is localhost.IRFMainservice in uses clause
but if you will place instead it
RFMainIntf
i think all will work
i do not know waht is localhost.IRFMainservice in uses clause
but if you will place instead it
RFMainIntf
i think all will work
sure you will need to change
function GetTestChangeObject(_o : TMyObject) : TMyObject; stdcall;
to
function GetTestChangeObject(var _o : TMyObject) : TMyObject; stdcall;
function GetTestChangeObject(_o : TMyObject) : TMyObject; stdcall;
to
function GetTestChangeObject(var _o : TMyObject) : TMyObject; stdcall;
ASKER
In D2005 when you add a web reference, you get localhost.IRFMainservice added to your project. I just did File/Use Unit and it was there. That's how I am able to get the definition of TMyObject in my client app.
Are you suggesting that I add the RFMainIntf.pas to my client project? That won't work cause it uses InvokeRegistry to get TRemotable which I cannot compile in my client, as mentioned above.
Regarding var, I had it there during tests and forgot to take it out in my example. If I have var I get the error on my client call.
Also, I still can't do RemClassRegistry.RegisterX SClass on the client side. Do I really need that?
Are you suggesting that I add the RFMainIntf.pas to my client project? That won't work cause it uses InvokeRegistry to get TRemotable which I cannot compile in my client, as mentioned above.
Regarding var, I had it there during tests and forgot to take it out in my example. If I have var I get the error on my client call.
Also, I still can't do RemClassRegistry.RegisterX
i do not have 2005, so i do not know
ASKER
I think it would be the same thing - basically provides the client with the interface to the remotable classes.
what you mean in this message?
I tried putting the above into my client's main initialization section but doesn't compile. RegClassRegistry is undefined. I then tried adding InvokeRegistry to my uses clause but that cannot be resolved. (Windows VCL client).
what compilation error you get?
I tried putting the above into my client's main initialization section but doesn't compile. RegClassRegistry is undefined. I then tried adding InvokeRegistry to my uses clause but that cannot be resolved. (Windows VCL client).
what compilation error you get?
ASKER
As I said, the client is not able to resolve the names during compile.
I just cannot seem to pass back a changed object via a parameter from a SOAP server to a VCL client, except as a returned value, var parameters or not. Maybe this is not possible.
I think I can work with this, with some minor design changes. I'll just make sure I don't make any server functions that need to modify multiple objects. It's only a minor inconvenience.
Thanks very much to both of you for the assistance. I'm splitting the points.
I just cannot seem to pass back a changed object via a parameter from a SOAP server to a VCL client, except as a returned value, var parameters or not. Maybe this is not possible.
I think I can work with this, with some minor design changes. I'll just make sure I don't make any server functions that need to modify multiple objects. It's only a minor inconvenience.
Thanks very much to both of you for the assistance. I'm splitting the points.
ASKER
I FINALLY SOLVED MY PROBLEM!
I had to set 'soRootRefNodeToBody' to True in my server's pascal invoker, then everything above worked great. Found that in a 2002 post in Google Groups from Bruneau. From the sound of that thread, 'soRootRefNodeToBody' should be True by default, but is always False in my version (update2 applied). Why? Well, I do usually get a script error when D2005 loads. Hmmmmm.
Anyway, I'm happy as a pig in, well, jello. Think I'll give myself the rest of the day off!
I had to set 'soRootRefNodeToBody' to True in my server's pascal invoker, then everything above worked great. Found that in a 2002 post in Google Groups from Bruneau. From the sound of that thread, 'soRootRefNodeToBody' should be True by default, but is always False in my version (update2 applied). Why? Well, I do usually get a script error when D2005 loads. Hmmmmm.
Anyway, I'm happy as a pig in, well, jello. Think I'll give myself the rest of the day off!
TMyString = string[100];
function TRFMain.TestArray(_test: array of TMyString; var _message:TMyString): Boolean;
didn't test it, just suspect that's the problem