I am fairly new to Delphi & have to code a SOAP client. Importing the WSDL generates this code (which I obviously can't change as I obviously have to comply with the server side)
DataPart = class;
Message = class;
eMessage = class;
eventType = ( send, delete, etc );
DataPart = class(TRemotable)
private
FhasData: Boolean;
Fdata: TByteDynArray;
published
property hasData: Boolean read FhasData write FhasData;
property data: TByteDynArray read Fdata write Fdata;
end;
Message = class(TRemotable)
private
FMessageID: Int64;
Ftimestamp: TXSDateTime;
Fevent: eventType;
FmagicNumber: WideString;
FDataPart: DataPart;
published
property MessageID: Int64 read FMessageID write FMessageID;
property timestamp: TXSDateTime read Ftimestamp write Ftimestamp;
property event: eventType read Fevent write Fevent;
property magicNumber: WideString read FmagicNumber write FmagicNumber;
property DataPart: DataPart read FDataPart write FDataPart;
end;
eMessage = class(TRemotable)
private
FencryptedMessage: TByteDynArray;
Fdata: DataPart;
published
property encryptedMessage: TByteDynArray read FencryptedMessage write FencryptedMessage;
property data: DataPart read Fdata write Fdata;
end;
MyApplicationPortType = interface(IInvokable)
['{99767D33-6B4A-7547-4DAC-0608095CAC70}']
function sendMessage(const encryptedMessage: TByteDynArray; const data: DataPart): WideString; stdcall;
end;
Can anyone code me an example with dummy values that will call sendMessage() and not cause an access violation? I really don't know how to handle TByteDynArray
[Edit] as requested, here's my code, BUT - disclaimer - I had to hack it about a lot (reduce it) before posting, so it may not compile. Both parms to sendMessage() are non-null
var theMessageArray: TByteDynArray;
theResult : WideString;
messageData : TByteDynArray;
i : Integer;
begin
theMessage.messageID := theMessage.messageID + 1;
theMessage.timestamp := TXSDateTime.Create();
theMessage.timestamp.AsDateTime := Now();
theMessage.event := delete;
theMessage.magicNumber := 'magic # ' + IntToStr(theMessage.messageID);
SetLength(messageData, 1);
messageData[0] := 0;
theMessage.dataPart.hasData := True;
messageData := theMessage.dataPart.messageData;
SetLength(messageData, $1000 * dataSize);
for i := 0 to $1000 开发者_开发技巧* dataSize - 1 do
messageData[i] := i and $FF;
theMessage.DataPart.messageData := messageData;
theMessageArray := TByteDynArray(theMessage);
theResult := (HTTPRIO1 as MyApplicationPortType).sendMessage(theMessageArray, theMessage.dataPart);
New Idea: Do you have range checking on in this unit? Add {$R+}
If you want to use a dynamic array type, you must explicity set its length in the constructor before you access it, and when copying/assigning, you must be very careful as well.
Not only must you call SetLength on each TByteDynArray before accessing its elements:
SetLength(Fdata, MyDesiredLengthWhichIsGreaterThanZero):
You must also be careful here, I think this could get you in trouble:
property data: TByteDynArray read Fdata write Fdata;
Your auto-generator made that code for you, and if you really know you want a dynamic array, you apparently CAN make it published. (Updated: I was wrong about that initially).
TRemotable, as Rob points out, does not work with indexed properties but does work fine with "array of byte" (TByteDynArray) properties, so if you do everything right, you do not need to stop using TByteDynArray (I was wrong about that initially).
If it was me writing this from scratch, I would use a "string" type instead like TBytes. I am wondering why it didn't use TBytes, but I understand that you are implementing a SOAP client using some auto-generated WSDL-generator-code. So given that, it should be eminently possible to make your code not crash.
see also this Related question
I do not know how to write a SOAP client, but it looks like your code does some dodgy things. It looks like you need to fix your dynamic array handling, including the "uh-oh, why are you doing a Cast here" problem Rob pointed out to you. However, it does not look like you are free to just change types either, as it looks like you must use types that are known by and handled by your TRemotable mechanisms.
As for your request, this should work:
procedure TestMe( whatever:TWhatever );
var
FData:TByteDynArray;
begin
SetLength(FData,2);
FData[0] := 10;
FData[1] := 20;
sendMessage(FData, whatever);
end;
精彩评论