Reputation: 151
I have a JSONArray being passed as a parameter to my server. I have a method generated by the "generate datasnap Client Classes" procedure and when it gets to the first "SetJsonValue" line it gives me an access violation, this error comes and goes and has no aparent reason. I'm using a Rest Server in LAN (also use it with http but haven't tested it out with it yet). Sometimes it gets solved by restarting my computer/the compiler but other times it lasts longer.
the method looks as follows:
if FExecutaInsertCommand = nil then
begin
FExecutaInsertCommand := FConnection.CreateCommand;
FExecutaInsertCommand.RequestType := 'POST';
FExecutaInsertCommand.Text := 'TServerMethods."ExecutaInsert"';
FExecutaInsertCommand.Prepare(TServerMethods_ExecutaInsert);
end;
FExecutaInsertCommand.Parameters[0].Value.SetWideString(database);
FExecutaInsertCommand.Parameters[1].Value.SetWideString(Tabela);
FExecutaInsertCommand.Parameters[2].Value.SetJSONValue(aValores, FInstanceOwner);
FExecutaInsertCommand.Parameters[3].Value.SetJSONValue(aTipos, FInstanceOwner);
FExecutaInsertCommand.Parameters[4].Value.SetInt32(usuario);
FExecutaInsertCommand.Execute(ARequestFilter);
Result := FExecutaInsertCommand.Parameters[5].Value.GetInt32;
the method is Called from here:
result := DMClient.ServerMethodsClient.ExecutaInsert(banco, Tabela, GeraJSONArray(Registro), GeraJSONArray(GetStrings(Registro.DataType)), CodigoUsuarioLogado);
and the JSONArrays are generated like this:
function GeraJSONArray(Valores: TVariantArray) : TJSONArray;
var
i : Integer;
begin
result := TJSONArray.Create;
for i := 0 to Length(Valores.Values) - 1 do
result.Adiciona(Valores.Values[i], Valores.DataType[i]);
end;
function GeraJSONArray(Valores: TArray<String>) : TJSONArray;
var
i : Integer;
begin
result := TJSONArray.Create;
for i := 0 to Length(Valores) - 1 do
result.AddElement(TJSONString.Create(Valores[i]));
end;
JsonArray.Adiciona:
procedure TJSONArrayHelper.Adiciona(valor: variant; DataType: TFieldType);
var
JSONValue : TJSONValue;
begin
if not (VarIsEmpty(valor)) and not (VarIsNull(valor)) then
begin
case DataType of
TFieldType.ftString,
TFieldType.ftMemo,
TFieldType.ftFmtMemo,
TFieldType.ftFixedChar,
TFieldType.ftWideString : JSONValue := TJSONString.Create(valor);
//-----------------------------------------------//
TFieldType.ftSmallint,
TFieldType.ftInteger,
TFieldType.ftWord,
TFieldType.ftFloat,
TFieldType.ftCurrency,
TFieldType.ftAutoInc,
TFieldType.ftLargeint,
TFieldType.ftSingle,
TFieldType.ftBCD : JSONValue := TJSONNumber.Create(valor);
//-----------------------------------------------//
TFieldType.ftDateTime : JSONValue := TJSONString.Create(DateTimeParaString(valor));
end;
end
else
begin
JSONValue := TJSONNull.Create;
end;
if JSONValue <> nil then
AddElement(JSONValue);
end;
Parameter 2 generates the error but parameter 3 doesnt, I think there's aproblem with the Adiciona Procedure
Upvotes: 3
Views: 1073
Reputation: 163287
In your Adiciona
method, if DataType
is not any of the values listed in your case
statement, then your local JSONValue
variable remains uninitialized. In particular, note that the default value for the variable is not nil — there is no default value that a local object-reference variable will have — so your check at the end of the method for JSONValue <> nil
is a nonsense comparison.
The documentation lists 52 possible TFieldType
values; your code handles only 15. You can use an else
clause to account for what remains:
case DataType of
...
else raise Exception.CreateFmt('Unexpected field type (%d)', [Ord(DataType)]);
The compiler should have warned you that the variable might not be initialized, although it's impossible for the analysis required for that warning to be perfect, so it's possible the compiler was unable to detect that case. If the compiler warned you and you ignored the warning, then let this be a lesson for you.
The behavior we might expect from adding an uninitialized object reference to the array is consistent with the access-violation exception you've observed. The destructor will get the uninitialized value from the array and try to call GetOwned
on it. Calling methods on things that aren't object references can lead to any number of problems; the best thing you can hope for is what you're seeing now, which is an exception immediately telling you something is wrong. (Just think how bad this could have been if the program had silently continued running. Who knows what strange problems you would see if the destructor's Member.Free
call had run and actually destroyed something else in your program?)
Upvotes: 1