Главная страница | назад





Article #28514: Unofficial SOAP Bug Fixes

Unofficial SOAP Bug Fixes

By Bruneau Babet bbabet@nospam.borland.com
Remove the nospam. from email address when mailing me

In general, applying these fixes require you to rebuild some of the VCL. The easiest way to do this, is to go to Tools | Environment Options and from the Library tab add $(DELPHI)\Source\Internet;$(DELPHI)\Source\SOAP to your Library Path.

These fixes are unofficial, are not supported by Borland, and are to be used at your own risk.

Quick Jumps:

The case of serialized Boolean members of a complex type is incorrect (i.e. 'True'/'False' instead of 'true'/'false')
Description:
When serializing Boolean members of a TRemotable-descendant class, the value of the latter are sent as 'True' or 'False'. Some SOAP implementations, including Delphi's, will accept these values. However, many will complain, and for good reason, since the legal literal representations of a boolean are '1', '0', 'true' or 'false'.

Fix:
The fix for this problem is to modify the following function in OPToSOAPDomConv.pas as follows:

function TSOAPDomConv.GetObjectPropAsText(Instance: TObject;
PropInfo: PPropInfo): WideString;
var
I: LongInt;
E: Extended;
I64: Int64;
begin
case (PropInfo.PropType)^.Kind of
tkInteger:
begin
I := GetOrdProp(Instance, PropInfo);
Result := IntToStr(I);
end;
tkFloat:
begin
E := GetFloatProp(Instance, PropInfo);
Result := FloatToStrEx(E);
end;
tkWString:
Result := GetWideStrProp(Instance, PropInfo);
tkString,
tkLString:
Result := GetStrProp(Instance, PropInfo);
tkInt64:
begin
I64 := GetInt64Prop(Instance, PropInfo);
Result := IntToStr(I64);
end;
tkEnumeration:
Result := GetEnumProp(Instance, PropInfo);
begin Result := GetEnumProp(Instance, PropInfo); if PropInfo.PropType^ = TypeInfo(System.Boolean) then Result := Lowercase(Result); end;
tkChar: begin I := GetOrdProp(Instance, PropInfo); Result := InvString(Char(I)); end; tkWChar: begin I := GetOrdProp(Instance, PropInfo); Result := InvString(WideChar(I)); end; tkClass: ; tkSet, tkMethod, tkArray, tkRecord, tkInterface, tkDynArray, tkVariant: raise ESOAPDomConvertError.CreateFmt(SUnexpectedDataType, [KindNameArray[(PropInfo.PropType)^.Kind]]); end; end;

Memory leak in Servers that expose WideString parameters
Description:
Delphi SOAP fails to delete WideStrings allocated by the framework on behalf of Servers that expose WideString parameters.

Fix:
The fix for this problem is to modify InvokeRegistry.pas as follows:

type
{...}
TDataContext = class
protected
FObjsToDestroy: array of TObject;
DataOffset: Integer;
Data: array of Byte;
DataP: array of Pointer;
VarToClear: array of Pointer;
DynArrayToClear: array of TDynToClear;
StrToClear: array of Pointer;
WStrToClear: array of Pointer;
public constructor Create; destructor Destroy; override; function AllocData(Size: Integer): Pointer; procedure SetDataPointer(Index: Integer; P: Pointer); function GetDataPointer(Index: Integer): Pointer; procedure AddObjectToDestroy(Obj: TObject); procedure RemoveObjectToDestroy(Obj: TObject); procedure AddDynArrayToClear(P: Pointer; Info: PTypeInfo); procedure AddVariantToClear(P: PVarData); procedure AddStrToClear(P: Pointer);
procedure AddWStrToClear(P: Pointer);
end; implementation {...} procedure TDataContext.AddStrToClear(P: Pointer); var I: Integer; begin { If this string is in the list already, we're set } for I := 0 to Length(StrToClear) -1 do if StrToClear[I] = P then Exit; I := Length(StrToClear); SetLength(StrToClear, I + 1); StrToClear[I] := P; end;
procedure TDataContext.AddWStrToClear(P: Pointer); var I: Integer; begin { If this WideString is in the list already, we're set } for I := 0 to Length(WStrToClear) -1 do if WStrToClear[I] = P then Exit; I := Length(WStrToClear); SetLength(WStrToClear, I + 1); WStrToClear[I] := P; end;
constructor TDataContext.Create; begin inherited; end; destructor TDataContext.Destroy; var I: Integer; P: Pointer; begin { Clean up objects we've allocated } for I := 0 to Length(FObjsToDestroy) — 1 do begin if (FObjsToDestroy[I] <> nil) and (FObjsToDestroy[I].InheritsFrom(TRemotable)) then begin TRemotable(FObjsToDestroy[I]).Free; end; end; SetLength(FObjsToDestroy, 0); { Clean Variants we allocated } for I := 0 to Length(VarToClear) — 1 do begin if Assigned(VarToClear[I]) then Variant( PVarData(VarToClear[I])^) := NULL; end; SetLength(VarToClear, 0); { Clean up dynamic arrays we allocated } for I := 0 to Length(DynArrayToClear) — 1 do begin if Assigned(DynArrayToClear[I].P) then begin P := Pointer( PInteger(DynArrayToClear[I].P)^); DynArrayClear(P, DynArrayToClear[I].Info) end; end; SetLength(DynArrayToClear, 0); { Clean up strings we allocated } for I := 0 to Length(StrToClear) — 1 do begin if Assigned(StrToClear[I]) then PString(StrToClear[I])^ := ''; end; SetLength(StrToClear, 0);
{ Clean up WideStrings we allocated } for I := 0 to Length(WStrToClear) — 1 do begin if Assigned(WStrToClear[I]) then PWideString(WStrToClear[I])^ := ''; end; SetLength(WStrToClear, 0);
inherited; end; {...} procedure TInvContext.AllocServerData(const MD: TIntfMethEntry); var I: Integer; Info: PTypeInfo; P: Pointer; begin for I := 0 to MD.ParamCount — 1 do begin P := AllocData(GetTypeSize(MD.Params[I].Info)); SetParamPointer(I, P); if MD.Params[I].Info.Kind = tkVariant then begin Variant(PVarData(P)^) := NULL; AddVariantToClear(PVarData(P)); end else if MD.Params[I].Info.Kind = tkDynArray then begin AddDynArrayToClear(P, MD.Params[I].Info); end else if MD.Params[I].Info.Kind = tkLString then begin PString(P)^ := ''; AddStrToClear(P);
end else if MD.Params[I].Info.kind = tkWString then begin PWideString(P)^ := ''; AddWStrToClear(P);
end; end; if MD.ResultInfo <> nil then begin Info := MD.ResultInfo; case Info^.Kind of tkLString: begin P := AllocData(sizeof(PString)); PString(P)^ := ''; AddStrToClear(P); end;
tkWString: begin P := AllocData(sizeof(PWideString)); PWideString(P)^ := ''; AddWStrToClear(P); end;
tkInt64: P := AllocData(sizeof(Int64)); tkVariant: begin P := AllocData(sizeof(TVarData)); Variant( PVarData(P)^ ) := NULL; AddVariantToClear(PVarData(P)); end; tkDynArray: begin P := AllocData(GetTypeSize(Info)); AddDynArrayToClear(P, MD.ResultInfo); end; else P := AllocData(GetTypeSize(Info)); end; SetResultPointer(P); end; end;

Error publishing WebService's WSDL when MSXML4 is installed
Description:
As of SP#2, Delphi's msxmldom unit will attempt to use MSXMLDOM v4.0 if the latter is present. However, this may cause the creation and Publishing of a WSDL document by a Delphi WebService to fail. The typical symptom of this failure is that the client requesting the WSDL gets back an HTML document instead; and the document contains the following error message:

        Error: This name may not contain the ':' character

Fix:
The fix to this problem is to modify XMLDoc.pas as follows: (Note that this file is in the Source/Internet directory)

function TXMLNode.FindNamespaceDecl(const NamespaceURI: DOMString): IXMLNode;
var
I: Integer;
Attr: IXMLNode;
begin Result := nil; for I := 0 to AttributeNodes.Count — 1 do
if SameNamespace(VarToStr(AttributeNodes[I].NodeValue), NamespaceURI) and (AttributeNodes[I].Prefix = SXMLNS) then
begin Attr := AttributeNodes[I]; if SameNamespace(VarToStr(Attr.NodeValue), NamespaceURI) and ((Attr.Prefix = SXMLNS) or (Attr.NodeName = SXMLNS)) then
begin Result := AttributeNodes[I]; Break; end;
end;
if (Result = nil) and Assigned(FParentNode) then Result := FParentNode.FindNamespaceDecl(NamespaceURI); end; procedure TXMLNode.DeclareNamespace(const Prefix, URI: DOMString); begin if Prefix <> '' then SetAttributeNS(SXMLNS+NSDelim+Prefix, SXMLNamespaceURI, URI) else SetAttributeNS(SXMLNS, SXMLNamespaceURI, URI); end; function TXMLNode.GetPrefixedName(const Name, NamespaceURI: DOMString): DOMString; var NSDecl: IXMLNode; begin { The method adds a prefix to a localname based on the specified URI. If there is no corresponding namespace already declared or if the name is already prefixed, then nothing is done. } if (doAutoPrefix in OwnerDocument.Options) and not IsPrefixed(Name) then begin NSDecl := FindNamespaceDecl(NamespaceURI);
if Assigned(NSDecl) and (NSDecl.LocalName <> '') then
if Assigned(NSDecl) and (NSDecl.NodeName <> SXMLNS) then
Result := MakeNodeName(NSDecl.LocalName, Name) else Result := Name; end else Result := Name; end;

 

Links:

Last Modified: 15-MAR-02