I’ve just discovered an issue when using the kbmMW smart client in a slightly more advanced way, on an Android device.
The kbmMW smart client use a special type of custom variant supported by Delphi, descending from TInvokeableVariantType, that allow writing calls to procedures/methods/functions that actually do not exist in the project.
It is a nice way to allow “embedding” script like functionality directly using an almost normal Delphi syntax, or in kbmMW’s case to allow calling server side methods without having to write stub/skeleton code.
The following code is a call from a kbmMW client to a service (someservice) in a kbmMW server. The call accepts 4 arguments, an ID (string), an integer (100) and two generic TObjectLists (x.List1 and x.List2).
One of the caveats of using the TInvokableVariantType, is that one can only use arguments that can be stored within a variant. So all the regular types, integer, int64, string, float etc. are perfectly fine to use, but objects can’t be passed on automatically, without doing some magic.
The magic in this case, is “casting” the object instance to a custom variant type which also supports controlling ownership of the object. Hence the Use.AsVariant(…) syntax which returns a special type of variant.
All this works perfectly fine on all platforms… as long as there are only one Use.AsVariant in the argument list.
On Android (and I assume NextGen in general), the variants are being deallocated before the call is actually being made, hence invalidating the contents of the record structures holding the relevant data, resulting in an “Invalid variant type” exception being thrown when attempting to run it on NextGen.
var cli:IkbmMWSmartClient; begin cli:=TkbmMWSmartRemoteClientFactory.GetClient(transport,'someservice'); cli.Service.SomeMethod(x.ID,100,Use.AsVariant(x.List1,false),Use.AsVariant(x.List2,false)); ... end;
Whats the solution? Well there are multiple… one of them would be to combine List1 and List2 in a 3rd object which is sent… but that would require the server also being updated to support receiving a combined object.
Fortunately there is an easy solution:
var cli:IkbmMWSmartClient; v1,v2:variant begin cli:=TkbmMWSmartRemoteClientFactory.GetClient(transport,'someservice'); v1:=Use.AsVariant(x.List1,false); v2:=Use.AsVariant(x.List2,false); cli.Service.SomeMethod(x.ID,100,v1,v2); ... end;
Define the variants as local variables, to prevent the compiler to prematurely deallocate the variant contents. Now everything works fine again, also on NextGen.