Delphi 2010 : How to emulate the Delphi XE TStrings.Encoding property? - delphi

Delphi XE added an Encoding property to the TStrings class, which stores the encoding read from the BOM when LoadFromFile() is called.
Delphi 2010 does not have this property.
I would like to emulate it.
I created the below class helper for TStrings.
The helper works, but to get the file's BOM, the only solution I have found is to reload the same file in a FileStream. I would like to avoid this since TStrings.LoadFromFile() already got the BOM.
How can I tell the helper to re-use the BOM that was already found?
unit TestEncodingName_00;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, StdCtrls, ExtDlgs;
type
TForm1 = class(TForm)
Memo1: TMemo;
procedure FormCreate(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
end;
TMyStrings = class helper for TStrings // emulate TStrings.Encoding
private
function GetEncodingName(fPath:string):string;
public
property EncodingName[fPath:string]:string read GetEncodingName;
end;
var
Form1: TForm1;
implementation
{$R *.dfm}
function TMyStrings.GetEncodingName(fPath:string):string;
var
fLen : integer;
fBuffer : TBytes;
fEncoding : TEncoding;
fName : string;
fFs : TFileStream;
begin
fFs := TFileStream.Create(fPath, fmOpenRead);
try
SetLength(fBuffer, 4);
flen := fFs.Read(fBuffer[0], 4);
if flen < 4 then
SetLength(fBuffer, flen);
fEncoding := nil;
TEncoding.GetBufferEncoding(fBuffer, fEncoding);
if fEncoding = TEncoding.Unicode then
fName := 'Unicode'
else if fEncoding = TEncoding.UTF8 then
fName := 'UTF8'
else fName := 'Default';
finally
fFs.Free;
end;
result := fName;
end;
procedure TForm1.FormCreate(Sender: TObject);
var
EncName : string;
begin
(* sample UTF8.txt
Ā ā Ă ă
Ρ Σ Τ Υ
ぁ あ ぃ
*)
Memo1.Lines.LoadFromFile('Sample UTF8.txt');
//from here TStrings knows the BOM but I don't know
// how to refer to it...
// so I have to create again a stream of the same file to
// get the BOM. I don't like that.
EncName := Memo1.Lines.EncodingName['Sample UTF8.txt'];
Memo1.Lines.Add(#13#10'Encoding : ' + EncName);
end;
end.

First off, it is LoadFromStream() that discovers the BOM encoding, not LoadFromFile(). LoadFromFile() simply opens the file into a TFileStream and then calls LoadFromStream().
In Delphi (2009 and) 2010, the discovered BOM encoding is not stored anywhere that you can access. That is the very problem that XE solved by adding the new Encoding property. The encoding is only used as a local variable inside of LoadFromStream() when decoding the file data to a UnicodeString prior to parsing, and then it gets discarded when LoadFromStream() exits. There is nothing you can do to change that behavior.
So, the only solution is to load the file manually so you can discover its BOM. Ideally, you would override LoadFromStream() in a descendant class, but you can't make TMemo.Lines use a custom class. And a class helper cannot override virtual methods, either.
However, you can derive a custom class from TStringList to override LoadFromStream(), load the file yourself, and then Assign() the TStringList to TMemo.Lines. For example:
unit TestEncodingName_00;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, StdCtrls, ExtDlgs;
type
TForm1 = class(TForm)
Memo1: TMemo;
procedure FormCreate(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
end;
TMyStringList = class(TStringList)
private
fEncoding: TEncoding;
public
{ The single-parameter LoadFromStream(Stream: TStream) simply
calls LoadFromStream(Stream: TStream; Encoding: TEncoding) with
the Encoding parameter set to nil, so you only have to override
that version of LoadFromStream()... }
procedure LoadFromStream(Stream: TStream; Encoding: TEncoding); override;
property Encoding: TEncoding read fEncoding;
end;
var
Form1: TForm1;
implementation
{$R *.dfm}
procedure TMyStringList.LoadFromStream(Stream: TStream; Encoding: TEncoding);
var
Size: Integer;
Buffer: TBytes;
begin
{ this is similar to the implementation that LoadFromStream()
uses in XE+, but with some differences:
1. the Encoding property is assigned a bit differently, as XE+
utilizes a TEncoding.Clone() method when the specified Encoding
is not a standard RTL encoding (ie, is a custom user class), but
Clone() does not exist in D2009/2010.
2. XE+ has a TStrings.DefaultEncoding property, which is passed
to TEncoding.GetBufferEncoding() as the default to return if no
BOM is detected, but that property and parameter do not exist in
D2009/2010.
3. TStrings.LoadFromStream() does not check if Size is 0 (file is empty)
before dereferencing the Buffer that is passed to Stream.Read().
That is a runtime crash waiting to happen! }
BeginUpdate;
try
Size := Stream.Size - Stream.Position;
SetLength(Buffer, Size);
if Size > 0 then
Stream.Read(Buffer[0], Size);
Size := TEncoding.GetBufferEncoding(Buffer, Encoding);
fEncoding := Encoding;
SetTextStr(Encoding.GetString(Buffer, Size, Length(Buffer) - Size));
finally
EndUpdate;
end;
end;
procedure TForm1.FormCreate(Sender: TObject);
var
EncName : string;
List: TMyStringList;
begin
List := TMyStringList.Create;
try
List.LoadFromFile('Sample UTF8.txt');
if List.Encoding = TEncoding.Unicode then
EncName := 'Unicode'
else if List.Encoding = TEncoding.UTF8 then
EncName := 'UTF8'
else
EncName := 'Default';
Memo1.Lines.Assign(List);
Memo1.Lines.Add;
Memo1.Lines.Add('Encoding : ' + EncName);
finally
List.Free;
end;
end;
end.

Related

How to change my code to get the correct enumerate name value?

I аm trying to get the enumeration name value using RTTI.
My objective is to get the corresponding enumerate name value in Enum1(Tsex) from the selected enumerate name value in Enum2(iterator) using a string value.
Here is the code that I have implemented. I am using Delphi 7.
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,Dialogs,typinfo;
type
Tsex = (homme,femme);
iterator = (H,F);
TForm1 = class(TForm)
procedure FormShow(Sender: TObject);
private
{ Déclarations privées }
public
{ Déclarations publiques }
end;
var
Form1: TForm1;
implementation
{$R *.dfm}
procedure TForm1.FormShow(Sender: TObject);
var
i : integer;
OT: Tsex;
FT: iterator;
begin
i:=0;
OT := Low(Tsex);
for FT := Low(iterator) to High(iterator) do
if GetEnumName(TypeInfo(iterator), Ord(FT)) = 'F' then
begin
showmessage(GetEnumName(TypeInfo(Tsex), Ord(OT)));
end;
i:=i+1;
OT:=Succ(OT);
end;
When I use H as a string I get homme, but when I use F I also get homme. But it needs to be femme.
Problem:
The problem in your code is that you are missing a begin after for, and this causes increment of i and assignment of OT to happen after the iteration is complete.
What you need to change is:
var
i : integer;
OT: Tsex;
FT: iterator;
begin
i:=0;
OT := Low(Tsex);
for FT := Low(iterator) to High(iterator) do
begin // <- Add begin here
if GetEnumName(TypeInfo(iterator), Ord(FT)) = 'F' then
begin
showmessage(GetEnumName(TypeInfo(Tsex), Ord(OT)));
end;
i:=i+1;
OT:=Succ(OT);
end;
end; // <- Add end; here
Alternative solutions:
As David has pointed out, it is better to use an array to map another set of values to your enum. Like this:
type
TSex = (homme, femme);
const
SexDBValues: array [TSex] of string =
('H', 'F');
type
TForm1 = class(TForm)
procedure FormShow(Sender: TObject);
private
public
end;
var
Form1: TForm1;
implementation
{$R *.dfm}
function GetMyEnumValue(const aDBValue: string): TSex;
var
value: TSex;
begin
for value := Low(TSex) to High(TSex) do
begin
if SameText(SexDBValues[value], aDBValue) then
begin
Result := value;
Exit;
end;
end;
end;
procedure TForm1.FormShow(Sender: TObject);
var
value: TSex;
begin
value := GetMyEnumValue('H');
ShowMessage(GetEnumName(TypeInfo(TSex), Ord(value)));
end;
And when your enum type contains only two values, and is unlikely to have additional values in future, you can just use good old if-else operator:
function GetMyEnumValue(const aDBValue: string): TSex;
begin
if SameText(aDBValue, 'F') then
begin
Result := femme;
end else
begin
Result := homme;
end;
end;
In few words, avoid overengineering problems.
Note: We are using string to store the character value and SameText to compare it, as it compares text case-insensitively. Plus, it allows you to compare text of multiple characters, if in future you change your mind on how values are stored in DB.
Advice:
I would also recommend you to consult with Delphi Coding Style Guide.
It might seem unrelated to problem, but following good practice on indentation helps to avoid such problems.
Guidelines on naming types and variables are also important. They will similarly save you in other situations.

Active Directory in Delphi

*i used this code :
but ther is an errore on ADS_SEARCH_HANDLE
can any body help me?*
Code :
unit Unapp;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, StdCtrls;
type
TFormApp = class(TForm)
Button1: TButton;
private
function GetADDisplayName(const Username: String): String;
{ Private declarations }
public
{ Public declarations }
end;
var
FormApp: TFormApp;
implementation
uses
ActiveX,
JwaAdsTlb, JwaActiveDS; // From Jedi ApiLib
{$R *.dfm}
function TFormApp.GetADDisplayName(const Username: String): String;
var
hr: HRESULT;
DirSearch: IDirectorySearch;
SearchInfo: ADS_SEARCHPREF_INFO;
hSearch: ADS_SEARCH_HANDLE; // ************this has error**************
col: ADS_SEARCH_COLUMN;
Filter: String;
Attributes: array of PChar;
begin
Result := 'Undefined Result';
// Initialize COM
CoInitialize(nil);
try
// Change line below with your domain name
hr := ADsGetObject('LDAP://dc=Tbco,dc=com',
IID_IDirectorySearch, Pointer(DirSearch));
Win32Check(Succeeded(hr));
SearchInfo.dwSearchPref := ADS_SEARCHPREF_SEARCH_SCOPE;
SearchInfo.vValue.dwType := ADSTYPE_INTEGER;
SearchInfo.vValue.dwType := ADS_SCOPE_SUBTREE;
hr := DirSearch.SetSearchPreference(SearchInfo,1);
Win32Check(Succeeded(hr));
Filter := Format('(&(objectClass=user)(sAMAccountName=%s))',
[Username]);
SetLength(Attributes, 1);
Attributes[0] := 'displayName';
// When using Dynamic Array with WinApi ALWAYS use pointer to first element!
hr := DirSearch.ExecuteSearch(PWideChar(Filter),PWideChar(Attributes[0]),Length(Attributes), Pointer(hSearch));
Win32Check(Succeeded(hr));
try
hr := DirSearch.GetFirstRow(Pointer(hSearch));
Win32Check(Succeeded(hr));
hr := DirSearch.GetColumn(hSearch, Attributes[0], col);
if Succeeded(hr) then
begin
Result := col.pADsValues^.CaseIgnoreString;
DirSearch.FreeColumn(#col);
end;
finally
DirSearch.CloseSearchHandle(hSearch);
end;
finally
// UnInitialize COM
CoUninitialize;
end;
end;
end.
Delphi Error;
[Error] Unapp.pas(33): Undeclared identifier: 'ADS_SEARCH_HANDLE'
[Error] Unapp.pas(70): Types of actual and formal var parameters must be identical
[Error] Unapp.pas(70): Incompatible types: 'Char' and 'WideChar'
[Error] Unapp.pas(73): Undeclared identifier: 'CaseIgnoreString'
[Error] Unapp.pas(74): Types of actual and formal var parameters must be identical
[Error] Unapp.pas(77): Types of actual and formal var parameters must be identical
[Fatal Error] ProjApp.dpr(5): Could not compile used unit 'Unapp.pas'
Looks like DirSearch wasn't initialized
Use this code in order to initialize it
AdsGetObject(PWideChar('LDAP://YourDomain'), IDirectorySearch, DirSearch);
Don't forget to replace YourDomain with your actual domain

How should I call this particular dll function in Delphi 6

I am absolutely new at calling functions from DLLs (call it bad programming habits, but I never needed to).
I have this C++ dll (CidGen32.dll at https://skydrive.live.com/redir?resid=4FA1892BF2106B62!1066) that is supposed to export a function with the following signature:
extern "C" __declspec(dllexport) int GetCid(const char* pid, char* cid);
What it should do is to get a 13 char string such as '1111111111118' and return a 20 char hash.
I have tried for the last couple of days to call this function in Delphi 6 but to no avail. I have desperately tried I guess 50+ combinations and I got quite close on one occasion but my computer froze and I lost all my effort. Since it was based on luck, I could not redo it anymore.
I am also aiming not to register the DLL, but rather place it in the same folder.
Anyway, the plan was to have something like this:
unit Unit1;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs;
type
TForm1 = class(TForm)
procedure FormCreate(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
end;
var
Form1: TForm1;
implementation
{$R *.dfm}
procedure TForm1.FormCreate(Sender: TObject);
function GenerateCID(Prm: string): string;
var
aCID: PAnsiChar;
uCID: AnsiString;
i: integer;
Hbar: Thandle;
GetCID: function (X: PAnsiChar; Y: PAnsiChar): integer; {$IFDEF WIN32} stdcall; {$ENDIF}
begin
ucid := '';
hbar := LoadLibrary('CidGen32.dll');
if Hbar >= 32 then
begin
#GetCID := GetProcAddress(HBar, 'GetCID');
if Assigned(GetCID) then
begin
i := GetCID(pAnsiChar(prm), aCID);
uCID := aCID;
end;
FreeLibrary(HBar);
end
else
begin
//ShowMessage('Error: could not find dll');
end;
result := uCID;
end;
begin
ShowMessage(GenerateCID('1111111111118'));
end;
end.
But it seems I am dead wrong.
You are using the wrong name to import the function. Its name is GetCid but you are trying to import GetCID. Letter case matters when you call GetProcAddress. If that still doesn't result in the GetProcAddress call succeeding, double check the name with which the function is exported using a tool like Dependency Walker.
The function is cdecl so you should declare it like this:
GetCID: function(pid, cid: PAnsiChar): Integer; cdecl;
And the other problem is that you are responsible for allocating the buffer behind cid. You did not do that. Do it like this:
SetLength(uCID, 20);
i := GetCID(pAnsiChar(prm), pAnsiChar(uCID));
And delete the aCID variable. And that >32 error check is wrong, compare against 0.

AccessViolationException in Delphi - impossible (check it, unbelievable...)

Delphi XE. Windows 7.
There is a function (please see a code below) or I:=0 that causes an AV error in a big project. There is no the error with the same function in a new project!!! I deleted everything from the big project, and I left only a button and that function. It still causes the error...
A line with the error:
if ISAeroEnabled then // this line is a cause
i:=0; // or this line
I set breakpoints everywhere (I checked the whole function, I set breakpoints on EACH LINE -> no errors in the function), a debugger shows me that the error is in i:=0;
If to delete a function (and leave i:=0;) -> all is ok!
The error message: First chance exception at $747FB727. Exception class EAccessViolation with message 'Access violation at address 004AE5AF in module 'MngProject.exe'. Write of address 0017FFF8'. Process MngProject.exe (4980)
Why does it work in a new project but not in mine?
Here's the whole project: http://www.2shared.com/file/UP22Om4j/Bug.html
The code:
unit MainFormModule;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
StdCtrls;
type
TMainForm = class(TForm)
Button1: TButton;
procedure Button1Click(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
end;
var
mainform:tmainform;
implementation
{$R *.dfm}
function ISAeroEnabled: Boolean;
type
_DwmIsCompositionEnabledFunc = function(IsEnabled: PBoolean): HRESULT; stdcall;
var
Flag : Boolean;
DllHandle : THandle;
OsVersion : TOSVersionInfo;
DwmIsCompositionEnabledFunc: _DwmIsCompositionEnabledFunc;
begin
Result:=False;
ZeroMemory(#OsVersion, SizeOf(OsVersion));
OsVersion.dwOSVersionInfoSize := SizeOf(TOSVERSIONINFO);
if ((GetVersionEx(OsVersion)) and (OsVersion.dwPlatformId = VER_PLATFORM_WIN32_NT) and (OsVersion.dwMajorVersion >= 6)) then //is Vista or Win7?
begin
DllHandle := LoadLibrary('dwmapi.dll');
if DllHandle <> 0 then
begin
#DwmIsCompositionEnabledFunc := GetProcAddress(DllHandle, 'DwmIsCompositionEnabled');
if (#DwmIsCompositionEnabledFunc <> nil) then
begin
DwmIsCompositionEnabledFunc(#Flag);
Result:=Flag;
end;
end;
FreeLibrary(DllHandle);
end;
end;
procedure Tmainform.Button1Click(Sender: TObject);
var i:integer;
begin
if ISAeroEnabled then // AV is here
i:=0; // Or here
end;
end.
Try changing PBoolean to PBOOL
function(IsEnabled: PBOOL): HRESULT; stdcall;
var
Flag: BOOL;
PBoolean is a pointer to a Pascal Boolean which is 1 byte in size. PBOOL is a pointer to a Windows (C based) BOOL, which is 4 bytes in size. You need to match the size expected by windows.
In general, when translating Windows API calls to Delphi, use the same named data type as the API. Windows.pas has type definitions mapping these to Delphi types, e.g. type BOOL = LongBool;
Also it is usual (but not required) in Delphi to change pointer parameters to var. A var parameter is Pascal syntactic sugar for pass-by-reference which isn't available in C.
function(var IsEnabled: BOOL): HRESULT; stdcall;
....
DwmIsCompositionEnabledFunc(Flag); // no # operator
NOTE: I can't test this, as I only have XP available.

Please help how to Convert C code to Delphi code (qsBarcode)

I need to use a DLL file from qsBarcode http://www.qsbarcode.de/en/index.htm (here is the download link http://www.qsbarcode.de/en/download/qsbar39.zip). The dll will decode a bitmap image that contain barcode code39 into a string.
In their example there are only VB and C example, but I need to use it in Delphi.
here is the official example code in C:
#include <windows.h>
#include <stdio.h>
typedef int (WINAPI * CODE39_PROC)(char *, char *);
int main(int argc, char* argv[])
{
HINSTANCE hinstLib;
CODE39_PROC ProcAdd;
BOOL fFreeResult;
char cFileName[512] = "\0";
char cResult[512] = "\0";
int iReturn = 0;
if(argc < 2) return 0; //no bitmap filename in argv[1]
strcpy(cFileName,argv[1]);
hinstLib = LoadLibrary("qsBar39");
if (hinstLib == NULL) return -1; //can't load lib
ProcAdd = (CODE39_PROC) GetProcAddress(hinstLib, "ReadCode39");
if (NULL == ProcAdd) return -1; //can't access Proc
//dll Proc call
iReturn = (ProcAdd) (cFileName, cResult);
printf("%s", cResult);
fFreeResult = FreeLibrary(hinstLib);
return iReturn;
}
and this is what I try to code in Delphi
unit uRead;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, StdCtrls, Mask, JvExMask, JvToolEdit;
type
TDLLFunc = function(namafile: PChar; hasil:PChar):integer;
TForm2 = class(TForm)
JvFilenameEdit1: TJvFilenameEdit;
Edit1: TEdit;
Button1: TButton;
procedure Button1Click(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
end;
const
DLLFunc: TDLLFunc = nil;
var
Form2: TForm2;
DLLHandle: THandle;
implementation
{$R *.dfm}
procedure TForm2.Button1Click(Sender: TObject);
var
feedback : integer;
hasil:PChar;
begin
DLLHandle := LoadLibrary('qsBar39.dll');
if (DLLHandle < HINSTANCE_ERROR) then
raise Exception.Create('library can not be loaded or not found. ' + SysErrorMessage(GetLastError));
try
{ load an address of required procedure}
#DLLFunc := GetProcAddress(DLLHandle, 'ReadCode39');
{if procedure is found in the dll}
if Assigned(DLLFunc) then
feedback := DLLFunc(PChar(JvFilenameEdit1.Text), PChar(hasil));
showmessage(hasil);
finally
{unload a library}
FreeLibrary(DLLHandle);
end;
end;
end.
When I execute this code and debug, hasil only contains #$11'½
while it should return some character in the barcode image (you can get the file image in the zip file).
Please help me, thank you.
latest update:
#500, thanks, I have put stdcall
#dthorpe, thanks, done
Actually the advice is great, my code is supposed to running well, but I've mistakenly put JvFilenameEdit1.text instead of JvFilenameEdit1.FileName, my bad :)
Thank you again for the advice, so here is the working code:
unit uRead;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, StdCtrls, Mask, JvExMask, JvToolEdit;
type
TDLLFunc = function(namafile: PAnsiChar; hasil:PAnsiChar):integer; stdcall;
TForm2 = class(TForm)
JvFilenameEdit1: TJvFilenameEdit;
Edit1: TEdit;
Button1: TButton;
procedure Button1Click(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
end;
const
DLLFunc: TDLLFunc = nil;
var
Form2: TForm2;
DLLHandle: THandle;
implementation
{$R *.dfm}
procedure TForm2.Button1Click(Sender: TObject);
var
feedback : integer;
hasil: array [0..512] of char;
begin
DLLHandle := LoadLibrary('qsBar39.dll');
if (DLLHandle < HINSTANCE_ERROR) then
raise Exception.Create('library can not be loaded or not found. ' + SysErrorMessage(GetLastError));
try
{ load an address of required procedure}
#DLLFunc := GetProcAddress(DLLHandle, 'ReadCode39');
{if procedure is found in the dll}
if Assigned(DLLFunc) then
feedback := DLLFunc(PAnsiChar(JvFilenameEdit1.FileName), #hasil);
edit1.Text := StrPas(#hasil);
finally
{unload a library}
FreeLibrary(DLLHandle);
end;
end;
end.
If I were you I would take the opportunity to wrap this function call up in a more Delphi like wrapper.
function ReadCode39(FileName, Result: PAnsiChar): LongBool; stdcall;
external 'qsBar39';
function ReadCode(const FileName: string): string;
var
cResult: array [0..512-1] of AnsiChar;
begin
if not ReadCode39(PAnsiChar(AnsiString(FileName)), #cResult[0]) then
raise Exception.Create('ReadCode39 failed');
Result := string(cResult);
end;
Notes:
I'm using an implicit DLL import (using external) rather than an explicit GetProcAddress. This reduces the amount of boilerplate code considerably.
I'm converting the C style integer code error handling into a Delphi exception. Based on your comment I'm guessing that a non-zero return value means success. Older versions of C do not have a boolean type and use 0 to mean false and every non-zero value evaluates as true. The natural way to map this to a Delphi boolean type is with LongBool. This means that your calling code doesn't need to worry about error codes.
All the conversion to and from null-terminated strings is handled in one routine and your calling code again need not concern itself over such trivia.
I've written the code so that it is portable between both ANSI and Unicode versions of Delphi.
This allows your calling code to read much more clearly:
procedure TForm2.Button1Click(Sender: TObject);
var
hasil: string;
begin
hasil := ReadCode(JvFilenameEdit1.Text);
ShowMessage(hasil);
end;
Stick a stdcall; directive at the end of your TDLLFunc declaration to tell the compiler that it's using the WINAPI calling convention, and, as Dorin points out, if you're using a unicode-based version of Delphi you probably want to use PAnsiChar.
In addition to the stdcall mentioned in another answer, you also need to allocate space for the pchar pointers you're passing into the DLLFunc. Notice that in the C code the cResult var is defined as char cResult[512]; That means the caller is reserving space for a char buffer of 512 chars and passing the address of that buffer to the DLL func.
You're not doing the equivalent in your Delphi code.
Change your Delphi code to define hasil as an array of char:
var hasil: array [0..512] of char;
then pass the address of hasil to the DLLFunc call:
DLLFunc(PChar(JvFilenameEdit1.Text), #hasil);

Resources