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

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.

Related

Delphi: Load bass.dll directly from memory via BTMemoryModule and play sound from resources

hello i try load dll into memory and play sound file from resources (Delphi2009). In this example i load dll from HDD to memory (i plan to load dll from resources to memory) but i got an error after Button1Click
First chance exception at $76E2C41F. Exception class EAccessViolation with message 'Access violation at address 00000000. Read of address 00000000'. Process DemoApp.exe (3020)
Sound doesn't play at all :/
some of code i used from here: http://www.cyberforum.ru/blogs/14360/blog1682.html#a_codemodez
but i couldn't compile it due to custom units strUtilz, MemModuleUnicode
BTMemoryModule v0.0.41 includes BTMemoryModule and also examples
http://code.google.com/p/memorymodule/downloads/list
BTMemoryModule v.1 (old probably) (with BTMemoryModule + BTMemoryModuleUnicode)
http://www.delphibasics.info/home/delphibasicssnippets/btmemorymodule
unit Main;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, BTMemoryModule, StdCtrls, xpman;
const // Constants :::::::::::::::::::::::::::::::::::::::::::::: ::::::::::::::::::
_s = '';
_n=#13#10; // line break
ver = '1.0 '; // Articles
tit = 'Bass Memory App' + ver; // title - the name of the application
msgYN=$04; msgERR=$10; msgINF=$40; // <-type codes posts
res1='dll'; // resource name with dllkoy
res2='snd'; // name of the resource with sound
type // TYPES :::::::::::::::::::::::::::::::::::::::::::::: ::::::::::::::::::::::::
MemRes = record // structure for the projection of the resource in memory
p: pointer; // pointer to the memory
sz: int64; // size (length)
rd: cardinal; // hResData
ri: cardinal; // hResInfo
end;
type
TBASS_ChannelPlay = function (handle: cardinal; restart: bool): bool; stdcall;
TBASS_StreamCreateFile = function (mem: bool; f: Pointer; offset, length: int64; flags: cardinal): cardinal; stdcall;
TBASS_StreamFree = function (handle: cardinal): bool; stdcall;
TBASS_Init = function (device: integer; freq, flags: cardinal; win: cardinal; clsid: pGUID): bool; stdcall;
TBASS_Free = function: bool; stdcall;
TForm1 = class(TForm)
BtnFileCAll: TButton;
BtnMemCall: TButton;
Button1: TButton;
procedure FormCreate(Sender: TObject);
procedure FormDestroy(Sender: TObject);
procedure Button1Click(Sender: TObject);
private
mp_DllData : Pointer;
m_DllDataSize : Integer;
mp_MemoryModule: PBTMemoryModule;
//m_DllHandle: Cardinal;
m_BASS_ChannelPlay: TBASS_ChannelPlay;
m_BASS_StreamCreateFile: TBASS_StreamCreateFile;
//m_BASS_StreamFree: TBASS_StreamFree;
m_BASS_Init: TBASS_Init;
//m_BASS_Free: TBASS_Free;
public
{ Public declarations }
end;
var
Form1: TForm1;
wnd: cardinal; // window handle
ss: cardinal; // handle audio stream
snd: MemRes; // pointer to the audio file in memory
dll: MemRes; // pointer to memory dllku
bass: Pointer; // structure projection dll in memory
stp: word; // execution step (for debug)
st: boolean; // status of the audio stream
th: cardinal; // handle the flow of replacement buttons
ti: cardinal; // id flow
ms : TMemoryStream;
rs : TResourceStream;
implementation
{$R *.dfm}
{$R BassMem.RES} // snd CookieJarLoop.ogg RCData
function Res2Mem(hInst:cardinal;res:string;rtype:pChar):MemRes;
begin
result.p:=nil;
result.ri:=FindResource(hInst,pchar(res),rtype);
if result.ri=0 then exit;
result.sz:=SizeOfResource(hInst,result.ri);
if result.sz=0 then exit;
result.rd:=LoadResource(hInst,result.ri);
if result.rd=0 then exit;
result.p:=LockResource(result.rd);
end;
procedure TForm1.FormCreate(Sender: TObject);
var
MemoryStream: TMemoryStream;
begin
Position := poScreenCenter;
MemoryStream := TMemoryStream.Create;
MemoryStream.LoadFromFile('bass.dll');
MemoryStream.Position := 0;
m_DllDataSize := MemoryStream.Size;
mp_DllData := GetMemory(m_DllDataSize);
MemoryStream.Read(mp_DllData^, m_DllDataSize);
MemoryStream.Free;
end;
procedure TForm1.FormDestroy(Sender: TObject);
begin
FreeMemory(mp_DllData);
end;
procedure TForm1.Button1Click(Sender: TObject);
begin
mp_MemoryModule := BTMemoryLoadLibary(mp_DllData, m_DllDataSize);
try
if mp_MemoryModule = nil then showmessage('err1');
#m_BASS_Init := BTMemoryGetProcAddress(mp_MemoryModule, 'BASS_Init');
if #m_BASS_Init = nil then showmessage('err2');
#m_BASS_ChannelPlay := BTMemoryGetProcAddress(mp_MemoryModule, 'BASS_ChannelPlay');
if #m_BASS_ChannelPlay = nil then showmessage('err3');
m_BASS_Init(-1, 44100, 0, Handle, nil);
snd:=Res2Mem(hInstance, res2 ,RT_RCDATA);
ss:=m_BASS_StreamCreateFile(true,snd.p,0,snd.sz,4{=BASS_SAMPLE_LOOP});
if ss=0 then showmessage('err ss=0');
m_BASS_ChannelPlay(ss, false);
except
Showmessage('An error occoured while loading the dll: ' + BTMemoryGetLastError);
end;
if mp_MemoryModule <> nil then BTMemoryFreeLibrary(mp_MemoryModule);
end;
end.
You do not initialise m_BASS_StreamCreateFile. Consequently it has the value nil when you call it. Which explains the error message.
You need to add a call to BTMemoryGetProcAddress to initialise m_BASS_StreamCreateFile.
#m_BASS_StreamCreateFile := BTMemoryGetProcAddress(mp_MemoryModule,
'BASS_StreamCreateFile');
if #m_BASS_StreamCreateFile = nil then ....
This would have been simple enough to discover had you run the code under the debugger. The exception would have been trapped by the debugger and the call stack will have led to the call of m_BASS_StreamCreateFile. You could then have inspected its value to discover that it was nil.
Well, first of all: Nafalem, your code is terrible =\
As David Heffernan said before - there are missing initialization for m_BASS_StreamCreateFile; but also: what did you expected to do with if mp_MemoryModule <> nil then MemFreeLibrary(mp_MemoryModule); just right after m_BASS_ChannelPlay???
Just think a bit - you starting to play sound and then removing library from memory...
You need to move MemFreeLibrary into FormDestory before FreeMemory(mp_DllData);.
But sadly, even fixing your code isn't enough to make it work.
As i said in my article (http://www.cyberforum.ru/blogs/14360/blog1682.html (sry, i'm too lazy to translate it to English)):
there are a bug inside the BTMemoryModule:
if (l_section.Characteristics and IMAGE_SCN_MEM_DISCARDABLE) <> 0 then begin
// section is not needed any more and can safely be freed
VirtualFree(Pointer(l_section.Misc.PhysicalAddress), l_section.SizeOfRawData, MEM_DECOMMIT);
inc(longword(l_section), sizeof(TImageSectionHeader));
continue;
end;
this part of code frees sections with enabled Discardable-flag, but it shouldn't be done in our case!
As you can find in msdn, discardable-flag means that section can be discarded as needed, as you see it CAN be discarded, not MUST be... and it's for physical PE-files, which could be readed from file on demand!
If speak more bout this, Discardable-flag was used in 16-bit Windows and it was saying that its contents may be not uploaded into the swap, and readed from a file, if necessary. In the 32-bit Win OS and higher versions system looks priorly at IMAGE_SCN_MEM_NOT_PAGED, so just leave discardable-flag to low-core driver development, and never use it in user-mode PE-modules.
P.S. >
i couldn't compile it due to custom units strUtilz, MemModuleUnicode
just download archive from my article, it contains MemModule with my fixes, as to strUtilz - just replace it with SysUtils, i wrote in comments that it's simply a module with str/int conversion-routines coded in asm.
//Sorry for my dirty English =P

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.

Problems with closing main application window when having only the ID of its process

I have ID of the process. This process is an application which have a main window.
I am trying to close this application by sending WM_CLOSE to its main window.
I am searching its main window by using EnumWindows.
The problem is, that this application which I try to close, does not close always.
It is multithreaded application. Notepad and Calc are always closing when I use the same method which is presented below. But I am not sure if it is working properly cause it returns me many handles to the same window, even for Calc.exe.
Is it possible that thread is taking a handle to window and then this handle somehow become corrupted? Or maybe I should not use GetWindowThreadProcessId(hHwnd,pPid) but some other function in the callback?
I am out of ideas, would be grateful for any help. Thanks.
Code snippet:
unit Unit22;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, StdCtrls;
type
TForm22 = class(TForm)
edtprocID: TEdit;
lblEnterProcessID: TLabel;
btnCloseProcessWindow: TButton;
lblStatus: TLabel;
procedure btnCloseProcessWindowClick(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
end;
type
THandleAndHWND = record
ProcID: THandle;
WindowHandle: HWND;
end;
var
Form22: TForm22;
var
HandleAndHWNDArray: array of THandleAndHWND;
HandeIndex, lp: Integer;
implementation
{$R *.dfm}
function EnumProcess(hHwnd: HWND; lParam : integer): boolean; stdcall;
var
pPid : DWORD;
begin
//if the returned value in null the
//callback has failed, so set to false and exit.
if (hHwnd=0) then
begin
result := false;
end else
begin
GetWindowThreadProcessId(hHwnd,pPid);
Inc(HandeIndex);
HandleAndHWNDArray[HandeIndex].ProcID := pPid;
HandleAndHWNDArray[HandeIndex].WindowHandle := hHwnd;
Result := true;
end;
end;
procedure TForm22.btnCloseProcessWindowClick(Sender: TObject);
var
ProcID: Cardinal;
i, LastError: Integer;
begin
HandeIndex := -1;
ProcID := StrToInt(edtprocID.Text);
SetLength(HandleAndHWNDArray, 3000);
EnumWindows(#EnumProcess,lp);
for i := 0 to HandeIndex do //After EnumWindows HandleIndex is above 500 despite the fact that I have like 10 openned windows max
begin //That means that EnumWindows was called 500 times?
if HandleAndHWNDArray[i].ProcID = ProcID then //search for process equal to procces ID given by the user
begin
//if we have a processID then we have a handle to its main window
if PostMessage(HandleAndHWNDArray[i].WindowHandle, WM_CLOSE, 0, 0) then
begin
lblStatus.Caption := 'message posted!';
end else
begin
LastError := GetLastError;
lblStatus.Caption := Format('Error: [%d] ' + SysErrorMessage(LastError), [LastError]);
end;
Exit;
end;
end;
end;
end.
Have a look in this Knowledge Base Article here on how to close another application as cleanly as possible. You are doing it right so far. The Article suggests that you
first post WM_CLOSE to all windows of the application (since you cannot know for sure which one is the main).
wait with a timeout and if the timeout elapses
kill the application using TerminateProcess
I agree.

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