What the what?
What the what?
Why would a function retain the value of a previous call to the function?
I finally got some old D5 code that operates on Tstringlist members to work in Berlin/Unicode but...
It is a non-sorted list. In a for loop, I'm replacing the original string with the result of the function. In debugging when I step into my function the result displays the operated string correctly.
After the first element is passed into the function the resulting first stringlist element is an empty string. The next pass into the function shows (before any code execution) show a result already of the previous returned value.
After code execution on the second element when attempting to assign the new result I'm getting an AV (read of address)
If it makes any difference the function result is AnsiString.
Why would a function retain the value of a previous call to the function?
I finally got some old D5 code that operates on Tstringlist members to work in Berlin/Unicode but...
It is a non-sorted list. In a for loop, I'm replacing the original string with the result of the function. In debugging when I step into my function the result displays the operated string correctly.
After the first element is passed into the function the resulting first stringlist element is an empty string. The next pass into the function shows (before any code execution) show a result already of the previous returned value.
After code execution on the second element when attempting to assign the new result I'm getting an AV (read of address)
If it makes any difference the function result is AnsiString.
It sounds like that the function is returning a string. If that is the case, I believe (so I could be wrong) that functions returning strings (or any managed type) are changed (within the compiler) from:
ReplyDeletefunction myFunc() : string;
to:
procedure myFunc(; var Result : string);
Information from: http://stackoverflow.com/questions/3250827/initialise-string-function-result
Now, how does that help you, I am not sure but it might help you to understand what is happening behind the scene.
Is function parameter declared as AnsiString?
ReplyDeleteThis is my function declaration:
ReplyDeletefunction NewDecryptString(StrToDecrypt, Code: AnsiString): AnsiString;
Patrick Hughes
ReplyDeleteI suppose there are some unicode-related problems in a function.
For example using char or pchar instead of ansichar or pansichar.
For example, pchar variable which is set to ansistring and incremented length(s) times, so memory is corrupted.
That's the thing though, I just got the function "working" replacing variables into ansichar types.
ReplyDeleteOriginal D5 code used strings, and array of chars, I've changed them to ansistrings and array of ansichars
ReplyDeleteNicholas Ring is correct, and the reason why you see the same value is because the same temporary variable is passed to the function call.
ReplyDeleteIf it's any consolation this bit me as well at some point.
Now, this in itself should not lesd to an AV, so that must be something else.
I've just now modified to a procedure like Nicholas Ring suggested and it is not producing the AV on multiple calls. My code is generating the correct result within the procedure but the returned result is an empty string. WTF
ReplyDeletePatrick Hughes I didn't mean for you to change it - I was just saying what the compiler does behind the scenes, which would explain why the result of the function had the previous result value.
ReplyDeleteSorry for the confusion.
I'm just struggling with the transition from D5 to Berlin and not understanding why the result is not "cleared" after the first call, then causing the AV on subsequent calls. Makes no sense to me.
ReplyDeleteThe SO post you linked talks about (in that case) that the result needs to be initialize. If I do that then when I try to assign the calculated result I get an AV.
ReplyDeleteShow us code...
ReplyDeleteLet me see if I can get it back to a state that almost worked. I've got some rather profane variable names in it right now. And it broke differently now - it's pretty twisted around.
ReplyDeleteMy calling procedure:
ReplyDeleteprocedure LoadAppAnalytics(ThisApp: string);
var
I, J: integer;
Document: string;
ThisStr: string;
AppAnalyticsStrings: TStringList;
Stream: TFileStream;
begin
Document := SessionLogLocation + '\Analytics\AppAnalytics.dat';
AppAnalyticsStrings := TStringList.Create;
try
Stream := TFileStream.Create(Document, fmOpenRead or fmShareDenyNone);
try
AppAnalyticsStrings.LoadFromStream(Stream);
AppAnalyticsStrings.Sorted := false;
for I := 0 to AppAnalyticsStrings.Count - 1 do
begin
//// this after modifying to procedure
NewDecryptString(AppAnalyticsStrings[I], PrivacyCode, ThisStr);
AppAnalyticsStrings[I] := string(ThisStr);
WriteToErrorLog(ErrorLogFile, '->>>>>>>>>>>>> ' + AppAnalyticsStrings[I]);
end;
finally
Stream.Free;
end;
finally
AppAnalyticsStrings.Free;
end;
end;
My called procedure:
ReplyDeleteprocedure NewDecryptString(StrToDecrypt, Code: AnsiString; var result: string);
var
X, I: Integer;
MyPw: ansistring;
MyPwHash: ansiString;
HashArray: array of ansichar;
StringArray: array of ansichar;
ThisStr: ansistring;
begin
MyPwHash := Copy(StrToDecrypt, Length(StrToDecrypt) - 9, 10);
StrToDecrypt := Copy(StrToDecrypt, 0, Length(StrToDecrypt) - 10);
SetLength(HashArray, Length(MyPwHash));
for I := Low(HashArray) to High(HashArray) do
HashArray[I] := MyPwHash[I + 1];
X := 0; // initialize count
{$R-}
for I := Low(HashArray) to High(HashArray) do
begin
HashArray[I]:= AnsiChar(Ord(HashArray[I]) - Ord(Code[X]));
Inc(X);
if X = Length(Code) - 1 then
X := 0;
end;
{$R+}
MyPw := ansiString(HashArray); //'0507254092' '0507254092' <<-- 10 characters, unique each for string
SetLength(StringArray, Length(StrToDecrypt));
for I := Low(StringArray) to High(StringArray) do
StringArray[I] := StrToDecrypt[I + 1];
X := 0; // initialize count
{$R-}
for I := Low(StringArray) to High(StringArray) do
begin
StringArray[I]:= AnsiChar(Ord(StringArray[I]) - Ord(MyPW[X]));
Inc(X);
if X = Length(MyPW) - 1 then
X := 0;
end;
{$R+}
ThisStr := ansistring(StringArray);
Result := ThisStr;
//Result := ansistring(StringArray);
end;
when called ThisStr has a value but no matter what I try (even assigning it to a global variable the end result is an empty string.
So if you want to give it a go one encrypted string is:
ReplyDelete'8inbmifw„4'#$008F'ecv‚–¨¢i“ P'#$7F'§Å“Å“•sbeak_feme¨¥Å“¦¤Å¡¦^e¨Å¡Xnbff\1ameg[©•£k'#$009D'Å“¢e—'#$00AD'™X4df\hekkYC‘™„œŸ¥£^e¨Å¡Xjbl`d6`^s˜–‰™'#$009D'pŸ‹™Å“©c™¨eXhihjajf1`^™Å“ª¥ Ÿr•c•¯—]ec6eabpkh]£i”Å¡’˜¤c™¨eXgan^kgb)‘˜‘›`Å¡¬•(gihgbaec6gga`–¬¡^e¨Å¡Xkgaec0Y¨•¥–™¡ .•'#$00AD'•_kf`a0b^|„‡c™¨eXffj^hf`)q˜r©¡¬§•rx¤£«`Å¡¬•(ajco^hjf5Yy•£›«™¢y„§‘°`Å¡¬•(ef\jej]qc¢¤‚›egb•x•]agjf`a2`iY€ «™Å¾tŸ§† —¬b•x•]bihj`b3fiY{†x„•x•c•¯—]ee6\gho[¢¡“.•'#$00AD'•_dif`,iidn[~¢¦ež©Å¸©`Å¡¬•(fedp^mja0Yy§Å¾y§'#$009D'” XhYe—'#$00AD'™X3he\kkg]tw—|¢ –U\b)^Å¡¨Å“Zgkf,ceg`v¬›wr™™^œªš\a5baaog^ª a¤˜˜e—'#$00AD'™X6\gg`v—¦va“©Å¸©«Ë†™¤u c•¯—]f`,dkY{t‡‰ d‘©•e—'#$00AD'™X0\nYž¤Å¡¤‡ižc•¯—]fh7\hfh[0mipdk4hrk'
and the PrivacyCode is '8992671'
And with that I'm done for the evening.
ReplyDelete{$R-} simply asks for trouble. What possessed you. The issue is exactly as described in the very first comment. You have other huge problems caused by treating binary and text as interchangeable. When you encrypt you have binary data. Don't pretend that it is text.
ReplyDeletePatrick Hughes which encoding is that string? If you don't know, then you're in trouble. Deep trouble.
ReplyDeleteAt least a part
ReplyDeleteX := 0; // initialize count
{$R-}
for I := Low(HashArray) to High(HashArray) do
begin
HashArray[I]:= AnsiChar(Ord(HashArray[I]) - Ord(Code[X]));
looks strange,
On first pass x=0, so it will be Code[0]
Presumably there was a runtime bounds error which Patrick suppressed with {$R-} .......
ReplyDeleteIf your function returns a managed type (string, interface, record, etc.), your variable Result does not get initialized. Calling that method multiple times results in what you see: at the beginning Results points to the result of the last execution.
ReplyDeleteThat is the same reason why
function foo: string;
begin
end;
dont lead to "W1035 Return value of function 'foo' might be undefined", because the return value is defined. Changing the result type from string to Integer and the compiler shows you W1035.
When you are mixing binary operations like crypting with strings you get a lot of problems with encoding. In D5 this worked because the types are binary compatible, but since D2010 (i think) this wont work any longer. Maybe you have luck and you could try "RawByteString", but that is a dirty adhesive plaster and not a solution.
The range check suppression was added because at the time I began using the code I wasn't too well versed in debugging - I threw it in and it worked. Yeah, dumb, I know.
ReplyDeleteThe encryption is done by another function that is basically an inversion of the decryption function. Re the encoding: this is internet coding at its finest.
Lots for this feeble mind to digest. I'll keep at it and be back.
Thank you all.
Not sure how much this applies to this particular block of code, but change "Return value of function might be undefined" from True to Error in your project's Hints and Warning section. That way you are at least ensuring the Result var is being assigned something on all code paths.
ReplyDeleteRyan McGinty Not for managed types. They are deemed always to be initialised. And they are var parameters by implementation.
ReplyDeleteDavid Heffernan Ah yes, good point.
ReplyDeleteRyan McGinty as I said: managed types dont result in W1035, even if you treat this warning as error.
ReplyDeleteFabian S. Biehn and it's been on the bug list for more than a decade now.
ReplyDeleteLuckily FixInsight catches these.
I will not be defeated
ReplyDeleteIt might take me years to get my code working (and now this little bit does) but I will prevail.
Did you switch to working with binary yet. Or are you still making the mistake of treating the data as text.
ReplyDeleteFor the time being still following the dark path.
ReplyDeleteEvery day you continue down that path is a day wasted. Your data are byte arrays.
ReplyDeleteWell, I need to educate myself and get things fixed up. I'm transitioning from my current product written in D5 to a complete rewrite to Berlin so it's as good a time as any to follow your advice.
ReplyDeleteBut for all practical purpose isn't a byte array simply a representation of a "value" whether it be a string or a integer, float, etc..?
I mean when you boil it down every thing is binary.
(I'm probably really exposing my ignorance here, but that's ok, I have no shame)
Yes, everything is a byte array. But text is subject to encodings and liable to get mangled and transformed. Do some research. There are loads of articles on the subject.
ReplyDeletePatrick Hughes Nope. A string has an encoding. A byte array hasn't.
ReplyDeleteYeah, I'm seeing your contributions on SO. So if I'm understanding things correctly basically all you are saying is rather than passing the data into the function as a string I need to determine my encoding, pass it in as a byte array, operate on the byte array, and return the string.
ReplyDeleteI must have been typing my reply to David Heffernan while you replied Jeroen Wiert Pluimers
ReplyDeleteBut I'm also thinking that what the two of you are also saying is that it gets a little deeper that just the encryption/decryption routine. It also has to do with not loading the data into a stringlist. Is that correct?
Patrick Hughes I'd say first solve it in non-unicode Delphi ensuring you are encrypting/decrypting opaque bytes, then try again porting it.
ReplyDeleteAnd even there, you should be aware that strings have an encoding (hence all the code-page trouble back then).
From a string encode to byte array usually UTF8. Then encrypt. Then convert to string with base64. Reverse process in opposite direction.
ReplyDeleteMy head is beginning to hurt.
ReplyDeletePatrick Hughes and right so. You are in big trouble.
ReplyDeletePatrick Hughes Yes it's all bytes but encoding matters. You can write English using the Norwegian alphabet, the characters are the same, but the words are different.
ReplyDeleteSame with computers. It's all zeroes and ones in the end, but the "language" (encoding) used can make the zeroes and ones mean something else.
Jeroen Wiert Pluimers that's encouraging.
ReplyDeletePatrick Hughes I know. But it sets a baseline where you can start working from.
ReplyDeleteI meant the "you're in big trouble" comment :-)
ReplyDeletePatrick Hughes I meant as well (;
ReplyDeleteBTW, I'm getting there.
ReplyDelete