Delphi 7: function crashes upon call in dll, works in exe - delphi

I have a problem with a Delphi 7 string-decoder function ("Decrypt") I found online. It takes a string and four numeric values between 1 and 120 to decode it. Putting the function within an executable and running it works great. However, I want to run this within a DLL. I'm using an extra function to receive the necessary values from the calling task and call the decrypt function from there. For some reason I can't put my finger on, the host app crashes every time the dll calls the decrypt-function. For the last few hours I have been trying everything I could come up with to get this to work, but without success. I'm new to Delphi and Pascal in general, so may be I just overlooked something obvious. Either way, I'm lost. Any help would be greatly appreciated.
Edit: Here's the windows error that pops up upon crash:
Fault Module Name: StackHash_0a9e
Fault Module Version: 0.0.0.0
Fault Module Timestamp: 00000000
Exception Code: c0000005
Exception Offset: 00000000
OS Version: 6.1.7600.2.0.0.256.1
Locale ID: 1031
Additional Information 1: 0a9e
Additional Information 2: 0a9e372d3b4ad19135b953a78882e789
Additional Information 3: 0a9e
Additional Information 4: 0a9e372d3b4ad19135b953a78882e789
Here's the code that I use:
library decrypt_test_dll;
uses
SysUtils,
Classes,
Dialogs;
{$R *.res}
function callfunction(externalstring, value1, value2, value3, value4: PAnsiChar):integer; cdecl;
var
convkey1, convkey2, convkey3, convkey4 : string;
convstring, decodedstring : string;
Decrypt : function(Text : string; Key1, Key2, Key3, Key4 : Integer) : string;
begin
convkey1 := value1;
convkey2 := value2;
convkey3 := value3;
convkey4 := value4;
convstring := externalstring;
decodedstring := Decrypt(externalstring, strtoint(convkey1), strtoint(convkey2), strtoint(convkey3), strtoint(convkey4));
showmessage(decodedstring);
end;
function Decrypt(Text : string; Key1, Key2, Key3, Key4 : Integer) : string;
var
BufS, Hexa1, Hexa2 : string;
BufI, BufI2, Divzr, Sc, Sl, Num1, Num2, Num3, Num4, Res1, Res2, Res3, Res4 : Integer;
begin
showmessage('within decryption function');
Sl := Length(Text);
Sc := 0;
BufS := '';
if (Key1 in [1 .. 120]) and (Key2 in [1 .. 120]) and (Key3 in [1 .. 120]) and (Key4 in [1 .. 120]) then
begin
Divzr := Key1 * Key4;
BufI2 := Key3 * Key2;
Divzr := Divzr - BufI2;
if Divzr = 0 then
begin
Result := '';
Exit;
end;
end
else
begin
Result := '';
Exit;
end;
repeat
for BufI := 1 to 4 do
begin
Inc(Sc);
Hexa1 := IntToHex(Ord(Text[Sc]), 2);
Inc(Sc);
Hexa2 := IntToHex(Ord(Text[Sc]), 2);
if Hexa1 = 'FF' then
begin
Hexa1 := '00';
Hexa2 := '00';
end;
if Hexa1 = 'FE' then Hexa1 := '00';
if Hexa1 = 'FD' then
begin
Hexa1 := Hexa2;
Hexa2 := '00';
end;
case BufI of
1 : Res1 := StrToInt('$' + Hexa1 + Hexa2);
2 : Res2 := StrToInt('$' + Hexa1 + Hexa2);
3 : Res3 := StrToInt('$' + Hexa1 + Hexa2);
4 : Res4 := StrToInt('$' + Hexa1 + Hexa2);
end;
end;
BufI := Res1 * Key4;
BufI2 := Res2 * Key3;
Num1 := BufI - BufI2;
Num1 := Num1 div Divzr;
BufI := Res2 * Key1;
BufI2 := Res1 * Key2;
Num2 := BufI - BufI2;
Num2 := Num2 div Divzr;
BufI := Res3 * Key4;
BufI2 := Res4 * Key3;
Num3 := BufI - BufI2;
Num3 := Num3 div Divzr;
BufI := Res4 * Key1;
BufI2 := Res3 * Key2;
Num4 := BufI - BufI2;
Num4 := Num4 div Divzr;
BufS := BufS + Chr(Num1) + Chr(Num2) + Chr(Num3) + Chr(Num4);
until Sc >= Sl;
Result := BufS;
end;
exports
Decrypt index 1,
callfunction index 2;
begin
end.

This code is all wrong:
function callfunction(externalstring, value1, value2, value3, value4: PAnsiChar):integer; cdecl;
var
convkey1, convkey2, convkey3, convkey4 : string;
convstring, decodedstring : string;
Decrypt : function(Text : string; Key1, Key2, Key3, Key4 : Integer) : string;
begin
convkey1 := value1;
convkey2 := value2;
convkey3 := value3;
convkey4 := value4;
convstring := externalstring;
decodedstring := Decrypt(externalstring, strtoint(convkey1), strtoint(convkey2), strtoint(convkey3), strtoint(convkey4));
showmessage(decodedstring);
end;
The local variable Decrypt is never assigned to. So when you call Decrypt, anything can happen. You need to move callfunction to the bottom of the unit so that it appears after the real Decrypt. And you need to remove the function pointer variable.
function callfunction(externalstring, value1, value2, value3, value4: PAnsiChar):integer; cdecl;
var
convkey1, convkey2, convkey3, convkey4 : string;
convstring, decodedstring : string;
begin
convkey1 := value1;
convkey2 := value2;
convkey3 := value3;
convkey4 := value4;
convstring := externalstring;
decodedstring := Decrypt(externalstring, strtoint(convkey1), strtoint(convkey2), strtoint(convkey3), strtoint(convkey4));
showmessage(decodedstring);
end;
You also should not use string as a parameter (or indeed a return value) in an exported DLL function. It's not a valid type for interop since it relies on both sides of the interface using the same string implementation, and the same memory manager. You need to find a different way to define the Decrypt interface, if you are going to export it. Or possibly you should not be exporting that function at all. Impossible for me to know with any certainty. My guess is that you are actually calling callfunction rather than Decrypt from the calling code. Anyway, in its current state, you must not export Decrypt.
One other point to make. When you ask a question that involves a module interface, you should always show both sides of the interface. Here you have shown the DLL, but not the code that calls into the DLL. The error could be there. Indeed, there could be an error there in addition to the errors I have pointed out.

If the string manipulation code works as shown in an exe but not from a DLL check that the DLL has the delphi memory manager as the FIRST uses item.
uses
ShareMem
This is implicitt in a Dephi exe, but not in its DLLs.
This is always a favorite problem with Delphi 6 DLLs and strings, cant say if its still applicable in Delphi 7 however!

Related

How to get the string representation of a ShortCut Key including the SHIFTSTATE?

In a Delphi 10.4.2 Win32 VCL Application, and based on the question + solution here which provides a way to get the string representation of a Shortcut Key (but presumably with no possibility to also pass a SHIFTSTATE for the Shortcut Key) I wrote this code:
function MyGetSpecialShortcutName(ShortCut: TShortCut): string;
// gets shortcut name for e.g. VK_NUMPAD0 where TMenuItem.Shortcut gets the wrong shortcut name
var
ScanCode: Integer;
KeyName: array[0..255] of Char;
begin
Result := '';
FillChar(KeyName, SizeOf(KeyName), 0);
ScanCode := Winapi.Windows.MapVirtualKey(LoByte(Word(ShortCut)), 0) shl 16;
if ScanCode <> 0 then
begin
if Winapi.Windows.GetKeyNameText(ScanCode, KeyName, Length(KeyName)) <> 0 then
Result := KeyName;
end;
end;
function GetSpecialShortcutNameWithShiftState(const AScanCode: Word; const AShiftState: System.Classes.TShiftState = []): string;
begin
Result := MyGetSpecialShortcutName(Vcl.Menus.ShortCut(AScanCode, AShiftState));
end;
Usage:
Result := GetSpecialShortcutNameWithShiftState(VK_A, [ssCTRL]);
However, the Result is "A" where the expected Result should be "CTRL+A".
How to get the string representation of a ShortCut Key including the SHIFTSTATE?
The OP wants the key names fully localised, but for completeness I first show that the VCL already has a function to obtain a partly unlocalised string, namely, ShortCutToText in the Menus unit:
ShortCutToText(ShortCut(Ord('A'), [ssShift, ssAlt]))
This returns Shift+Alt+A on all systems.
Now, using the Win32 function GetKeyNameText already mentioned in the Q, it is easy to obtain a fully localised shortcut string:
function GetKeyName(AKey: Integer): string;
var
name: array[0..128] of Char;
begin
FillChar(name, SizeOf(name), 0);
GetKeyNameText(MapVirtualKey(AKey, 0) shl 16, #name[0], Length(name));
Result := name;
end;
function ModifierVirtualKey(AModifier: Integer): Integer;
begin
case AModifier of
Ord(ssShift):
Result := VK_SHIFT;
Ord(ssCtrl):
Result := VK_CONTROL;
Ord(ssAlt):
Result := VK_MENU;
else
Result := 0;
end;
end;
function ShortcutToString(AKey: Integer; AShiftState: TShiftState = []): string;
begin
Result := '';
for var Modifier in AShiftState do
begin
var ModifierKey := ModifierVirtualKey(Ord(Modifier));
if ModifierKey <> 0 then
Result := Result + IfThen(not Result.IsEmpty, '+') + GetKeyName(ModifierKey);
end;
Result := Result + IfThen(not Result.IsEmpty, '+') + GetKeyName(AKey);
end;
(Here I use a IfThen overload from StrUtils.)
Now,
ShortcutToString(Ord('A'), [ssShift, ssAlt])
returns SKIFT+ALT+A on my Swedish system. SKIFT is, as you might already have guessed, the Swedish name for the SHIFT key.

Delphi How To Convert String To Binary Using Only Pascal [duplicate]

This question already has answers here:
Converting decimal/integer to binary - how and why it works the way it does?
(6 answers)
Closed 4 years ago.
I have done some Example to convert a string to binary but i couldn't find a way to walk on each character in the string and complete the whole calculations process and then step to the next character in the string, Here is my code:
var i,j, rest, results :integer;
restResult : string;
begin
results := 1;
for i := 1 to length(stringValue) do
begin
while (results > 0) do
begin
results := ord(stringValue[i]) div 2;
rest := ord(stringValue[i]) mod 2;
restResult := restResult + inttostr(rest);
end;
end;
// Get The Rests Backwards
for i := length(restResult) downto 1 do
begin
result := result + restResult[i];
end;
The application always get into infinite loop, any suggestions?
Your results := ord(stringValue[i]) div 2; remains the same, because stringValue[i] does not change, so while loop is infinite.
To solve this mistake:
for i := 1 to length(stringValue) do
begin
t := ord(stringValue[i]);
repeat
restResult := restResult + inttostr(t mod 2);
t := t div 2;
until t = 0;
end;
But note that you cannot divide resulting string into pieces for distinct chars, because length of binary representation will vary depending on char itself.
This is example of code with fixed length for representation of char (here AnsiChar):
function AnsiStringToBinaryString(const s: AnsiString): String;
const
SBits: array[0..1] of string = ('0', '1');
var
i, k, t: Integer;
schar: string;
begin
Result := '';
for i := 1 to Length(s) do begin
t := Ord(s[i]);
schar := '';
for k := 1 to 8 * SizeOf(AnsiChar) do begin
schar := SBits[t mod 2] + schar;
t := t div 2
end;
Result := Result + schar;
end;
end;
'#A z': (division bars are mine)
01000000|01000001|00100000|01111010
# A space z

Delphi fast plus big integer?

function AddNumStrings (Str1, Str2 : string): string;
var
i : integer;
carryStr : string;
worker : integer;
workerStr,s : string;
begin
Result := inttostr (length(Str1));
Result := '';
carryStr := '0';
// make numbers the same length
s:=StringofChar('0',Length(Str1)-1);
Str2:=s+Str2;
i := 0;
while i < length(Str1) do
begin
worker := strtoint(copy(Str1, length(str1)-i, 1)) +
strtoint(copy(Str2, length(str2)-i, 1)) +
strtoint (carryStr);
if worker > 9 then
begin
workerStr := inttostr(worker);
carryStr := copy(workerStr, 1, 1);
result := copy(workerStr, 2, 1) + result;
end
else
begin
result := inttostr(worker) + result;
carryStr := '0';
end;
inc(i);
end; { while }
if carryStr <> '0' then
result := carryStr + result;
end;
procedure TForm1.Button1Click(Sender: TObject);
var
s,z:String;
begin
s:='1000';
repeat
s:=AddNumStrings(s,'1');
until
Length(s)=1000;
ShowMessage(s);
end;
end.
But this codes takes too time. Is there any options to fastest way for my codes?
I m working huge number so I have to write "Inc()" procedure manually for huge number billion digits. I know what you think about it bu I have to do it. Thank you..
INT128 lib for FPC
GNURZ lib (for FPC but should be compatible with Delphi)
GMP (FPC supports it, Delphi also)
BigInt and BigFloat
BigInt Delphi Library
Another BigInt
TPMath
DeHL for Delphi
BigNumbers BigInteger, BigDecimal and BigRational for Delphi
Hopefully one of those will be faster...

How to migrate from Delphi6 to Delphi2010 (Unicode Problem)

Hi I was using the Francois Piette's RasDial with Delphi 6, but it stopped working in Delphi 2010
How can I keep using these functions like before?
class function Encryption.DecriptPasswd(strPasswd: string): string;
type
PWORD = ^WORD;
var
Buffer : String;
PW : String[255];
P : PWORD;
I : Integer;
V : Integer;
begin
PW := ' ';
P := PWORD(#PW[0]);
I := 1;
while I <= Length(strPasswd) do
begin
Buffer := Copy(strPasswd, I, 5);
I := I + 5;
V := StrToInt(Buffer) - 34567;
P^ := V;
Inc(P);
end;
Result := PW;
end;
class function Encryption.EncriptPasswd(strPasswd: string): string;
type
PWORD = ^WORD;
var
Len : Integer;
I : Integer;
V : DWORD;
P : PChar;
Buffer : String[255];
begin
Buffer := strPasswd;
Len := Length(Buffer) + 1;
if (Len mod 2) <> 0 then
Inc(Len);
if Len < 10 then
Len := 10;
I := Length(Buffer);
if I = 0 then
Buffer := IntToStr(GetTickCount)
else
while Length(Buffer) < 10 do
Buffer := Buffer + Buffer;
SetLength(Buffer, I);
Result := '';
P := PChar(#Buffer[0]);
for I := 1 to Len div 2 do
begin
V := 34567 + PWORD(P)^;
P := P + 2;
Result := Result + Format('%5.5d', [V]);
end;
end;
You can start by changing all string declarations (except the string[255] ones, which already are) to AnsiString, all Char to AnsiChar, and all PChar to PAnsiChar.
Then go here for the first in a series of three articles on porting pre-Unicode versions of Delphi to Unicode. They're really well written by Nick Hodges, former Product Manager for Delphi when it was a CodeGear product. They cover all the details you need to make the changes to your other existing code.
String[255] is short string (one byte)
but when you add pchar, it grows two bytes by two bytes
try replace pchar by pansichar

How to expose a Delphi set type via Soap

I'm currently creating soap wrappers for some Delphi functions so that we can easily use them from PHP, C# and Delphi.
I wonder what's the best way to expose sets.
type
TCountry = (countryUnknown,countryNL,countryD,countryB,countryS,countryFIN,countryF,countryE,countryP,countryPl,countryL);
TCountrySet = set of TCountry;
function GetValidCountrySet(const LicensePlate:string; const PossibleCountriesSet:TCountrySet):TCountrySet;
I'm currently wrapping it like this for the soap server:
type
TCountryArray = array of TCountry;
function TVehicleInfo.GetValidCountrySet(const LicensePlate:string; const PossibleCountriesSet:TCountryArray):TCountryArray;
It works, but I need to write a lot of useless and ugly code to convert sets-->arrays and arrays-->sets.
Is there an easier, more elegant, or more generic way to do this?
You could use TypInfo and use a bit of clever casting.
uses TypInfo;
type
TCountry = (cnyNone, cnyNL, cnyD, cnyGB, cnyF, cnyI);
TCountrySet = set of TCountry;
TCountryArray = array of TCountry;
TEnumIntegerArray = array of Integer;
TEnumByteArray = array of Byte;
function GetEnumNamesInSet(const aTypeInfo: PTypeInfo; const aValue: Integer; const aSeparator: string = ','): string;
var
IntSet: TIntegerSet;
i: Integer;
begin
Result := '';
Integer( IntSet ) := aValue;
for i := 0 to SizeOf(Integer) * 8 - 1 do begin
if i in IntSet then begin
if Result <> '' then begin
Result := Result + ',';
end;
Result := Result + GetEnumName(aTypeInfo, i);
end;
end;
end;
function SetToIntegerArray(const aTypeInfo: PTypeInfo; const aValue: Integer): TEnumIntegerArray;
var
IntSet: TIntegerSet;
i: Integer;
begin
SetLength(Result, 0);
Integer( IntSet ) := aValue;
for i := 0 to SizeOf(Integer) * 8 - 1 do begin
if i in IntSet then begin
SetLength(Result, Length(Result) + 1);
Result[Length(Result) - 1] := i;
end;
end;
end;
function SetToByteArray(const aTypeInfo: PTypeInfo; const aValue: Byte): TEnumByteArray;
var
IntSet: TIntegerSet;
i: Integer;
begin
SetLength(Result, 0);
Integer( IntSet ) := aValue;
for i := 0 to SizeOf(Byte) * 8 - 1 do begin
if i in IntSet then begin
SetLength(Result, Length(Result) + 1);
Result[Length(Result) - 1] := i;
end;
end;
end;
Then use as:
procedure TEnumForm.FillMemo;
var
Countries: TCountrySet;
// EIA: TEnumIntegerArray;
EBA: TEnumByteArray;
CA: TCountryArray;
i: Integer;
cny: TCountry;
begin
Countries := [cnyNL, cnyD];
CountriesMemo.Text := GetEnumNamesInSet(TypeInfo(TCountry), Byte(Countries));
// if SizeOf(TCountry) > SizeOf(Byte) then begin
// EIA := SetToIntegerArray(TypeInfo(TCountry), Integer(Countries));
// end else begin
EBA := SetToByteArray(TypeInfo(TCountry), Byte(Countries));
// end;
CountriesMemo.Lines.Add('====');
CountriesMemo.Lines.Add('Values in Array: ');
// if SizeOf(TCountry) > SizeOf(Byte) then begin
// CA := TCountryArray(EIA);
// end else begin
CA := TCountryArray(EBA);
// end;
for i := 0 to Length(CA) - 1 do begin
CountriesMemo.Lines.Add(IntToStr(Ord(CA[i])));
end;
CountriesMemo.Lines.Add('====');
CountriesMemo.Lines.Add('Names in Array: ');
// if SizeOf(TCountry) > SizeOf(Byte) then begin
// CA := TCountryArray(EIA);
// end else begin
CA := TCountryArray(EBA);
// end;
for i := 0 to Length(CA) - 1 do begin
cny := CA[i];
CountriesMemo.Lines.Add(GetEnumName(TypeInfo(TCountry), Ord(cny)));
end;
end;
You will need to select the proper casting based on the size of the TCountry enum. If it has 8 members it will be a Byte, any bigger and it will be an Integer. Anyway, Delphi will complain on the cast of Byte(Countries) or Integer(Countries) when you get it wrong.
Please note:
The functions now take the TypeInfo of TCountry - the elements of the TCountrySet. They could be changed to take TypeInfo(TCountrySet). However that would mean having the functions work out what elements are in the set and I simply haven't had the time or inclination to do that yet.
Soap should be used in a platform and language agnostic way - I would design all data transfer objects (DTO) based on simple types e.g. array of string, without language specific features. Then map the DTO to the matching business objects. This also will give you an 'anticorruption layer'.

Resources