Delphi DLL Issues - wrong integer value recevied and access violation issue - windows

I'm having trouble getting to grips with DLLs in Delphi 7. I have two problems:
1) The procedure takes an integer parameter - but the dll receives a different value to the one I pass.
2) The application that called the dll crashes with an access violation after the function completes.
Here's my dll code:
library apmDLL;
uses
Classes, Messages, Windows, Dialogs, sysutils ;
const
WM_MY_MESSAGE = WM_USER + 1;
procedure sendtoACRPM (functionKey : integer); stdcall;
begin
showmessage('You sent - '+inttostr(functionKey));
showmessage('Finished Now');
end;
exports sendtoACRPM;
end.
So when I call this with the code below I get:
'Sending - 1'
'You Sent - 1636532'
'Finished Now'
Then the calling application crashes with an access violation.
The calling application looks like this:
unit Unit1;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, StdCtrls, shlobj, shellapi;
const
WM_MY_MESSAGE = WM_USER + 1;
type
TForm1 = class(TForm)
Edit1: TEdit;
Button2: TButton;
procedure Button2Click(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
end;
var
Form1: TForm1;
procedure sendtoACRPM (functionKey : integer) ; external 'apmDLL.dll';
implementation
{$R *.dfm}
procedure TForm1.Button2Click(Sender: TObject);
var
myInt: integer;
begin
myInt := strtoint(edit1.text);
showmessage('Sending - ' + inttostr(myInt));
sendtoACRPM(myInt);
end;
end.
Any ideas what I'm doing wrong here?

You need stdcall both in the DLL and in the calling code declaration. You only have it in the DLL.
Calling conventions need to match on both sides. :-)
procedure sendtoACRPM (functionKey : integer); stdcall; external 'apmDLL.dll';
You should use the standard Windows MessageBox instead of ShowMessage, so that the DLL can be used from non-Delphi applications as well.

Related

Unable to Register Class TFMXApplicationDelegate

I've read the Embarcadero and other docs, searched the net, and obviously something isn't sinking in. I have a significantly more complex Application and DLL/Dylib, but am now just using this simple example to try and get it to work. When I build the App and DLL for Win32 it works fine and it works fine if I do not make a function call to the DLL. As soon as I make a call to the DLL the error Unable to register Class TFMXApplicationDelegate is raised and the application terminates. This behaviour is the same whether the guest OS is on a VM (parallels) or a physical device (MacBook Pro 15 mid-2015).
QUESTION: How can I ensure that TFMXApplicationDelegate gets Registered, is there a setting or permission I need to set. It seems fairly basic since, according to the Apple documentation:
The app delegate is effectively the root object of your app.
The DPR:
Library pTestDLL;
uses
uTestDLL in 'uTestDLL.pas';
{$R *.res}
end.
And here's the simple PAS file (for the Dylib):
unit uTestDLL;
interface
uses
FMX.Dialogs;
// External functions and procedures
{$IFDEF MSWINDOWS}
function say_Hello(Hello: string): boolean; stdcall; forward;
{$ENDIF MSWINDOWS}
{$IFDEF MACOS}
function _say_Hello(Hello: string): boolean; cdecl; forward;
{$ENDIF MACOS}
exports
{$IFDEF MSWINDOWS}
say_Hello;
{$ENDIF MSWINDOWS}
{$IFDEF MACOS}
_say_Hello;
{$ENDIF MACOS}
Implementation
{$IFDEF MSWINDOWS}
function say_Hello(Hello: string): boolean; stdcall;
{$ENDIF MSWINDOWS}
{$IFDEF MACOS}
function _say_Hello(Hello: string): boolean; cdecl;
{$ENDIF MACOS}
begin
Result := True;
showmessage('In DLL: ' + Hello);
end;
end.
And lastly the simple test application:
unit uDylibTest1;
interface
uses
System.SysUtils, System.Types, System.UITypes, System.Classes, System.Variants,
FMX.Types, FMX.Controls, FMX.Forms, FMX.Graphics, FMX.Dialogs, FMX.StdCtrls,
FMX.Controls.Presentation;
const
// Windows DLL Names
{$IFDEF MSWINDOWS}
TestDLL = 'pTestDLL.dll';
{$ENDIF MSWINDOWS}
// macOS DYLIB Names
{$IFDEF MACOS}
TestDLL = 'libpTestDLL.dylib';
{$ENDIF MACOS}
type
TfDylibTest = class(TForm)
Button1: TButton;
procedure Button1Click(Sender: TObject);
procedure FormCreate(Sender: TObject);
procedure FormShow(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
end;
type
TuDylibTest = class(TForm)
private
{ Private declarations }
public
{ Public declarations }
end;
{$IFDEF MSWINDOWS}
function say_Hello(Hello: string): boolean; stdcall; external TestDLL Delayed;
{$ENDIF MSWINDOWS}
{$IFDEF MACOS}
function _say_Hello(Hello: string): boolean; cdecl; external TestDLL;
{$ENDIF MACOS}
var
fDylibTest: TfDylibTest;
implementation
{$R *.fmx}
procedure TfDylibTest.Button1Click(Sender: TObject);
var
b:boolean;
begin
showmessage('B4 function call);
b := False;
// Call the DLL Function
{$IFDEF MSWINDOWS}
b := say_Hello('The string passed to the DLL') then
{$ENDIF MSWINDOWS}
{$IFDEF MACOS}
b := _say_Hello('The string passed to the DLL');
{$ENDIF MACOS}
if b then
showmessage('Say Hello OK')
else
showmessage('Say Hello Failed');
end;
procedure TfDylibTest.FormCreate(Sender: TObject);
begin
showmessage('onCreate');
end;
procedure TfDylibTest.FormShow(Sender: TObject);
begin
showmessage('onShow');
end;
end.
I'm answering tis on behalf of Alexander Brazda López who posted this on the G+ Delphi forum (so I'm not taking credit for someone else's answer):
ShowMessage() use FMX.Dialogs inside a dll. I remember there were some
limitations about this and use bpl instead dll was recomended. I would
try to find some information about this. Just for check, try to use a
reversestring function for test open and calling dll functions its
working.
I cannot comment WRT the veracity of the next part of the response, but to be fair will include it. However the bottom line was that with FMX.Dialogs or FMX.Forms in the uses clause, I got the error. Remove those and the TFMXApplicationDelegate error did not occur.
In the non-trivial 'real' example, I was using the unit for showmessage and for application.processmessages which was in a cross platform function called WaitNoFreeze() which, as the name suggests, puts a wait in the main thread, but does not stop all other activity like sleep().
The problem is not that the dylib is not found, this can be checked by
calling a basic function in the dylib as function sum (a, b: integer):
integer
The problem is when we invoke the UI through some function or use
embedded resources such as forms. This is because if we do not use
bpl, local instances are created instead of shared global instances,
this prevents functions such as RegisterClass or GetMem from sharing
the data. In the case of FMX there are differences due to the api for
the example that Kevin has exposed, in Windows the Handle of the
window is not necessary, so 0 is a valid value, in MacOSX it is
necessary but the value in the dynlib is NULL since it has not been
started, although in the application, but because the FMX instances
are not shared, it is not possible to access this information. It is
the same thing that happens when we try to free a block of memory in a
dll in windows that has been allocated by the application, that is why
we must use a bpl so that the memory manager of the application and
the dll is the same and maintain the same instances regardless of
whether their methods are invoked from the dll or from the exe

True free heap not what it should be after ShellExecute

I have a simple Application that reads data from an .ini file into a record, and then does a ShellExecute. However, when I run it in debug mode,it tells me that the True free heap isn't what it should be:
"True heap size: 688128, True free heap: 688016, Should be: 688128"
This only happens if I actually run the ShellExecute (however even if I don't run the ShellExecute, I get a True free heap of 688016, but no complaint that it should be 688128), so I wonder if I either need to free the PChar() conversion (which shouldn't be the case from all that I've read), or the handle returned by ShellExecute (though CloseHandle(ShellHandle); doesn't change the message), or if it's intended behavior?
The IniSettings.SetupCommand in question is an .msi file that triggers a UAC prompt, but I really just want to make this a Fire & Forget thing since my Lazarus app doesn't care what it launches.
For reference, here's my whole Unit. I'm using Lazarus 1.6.2, FPC 3.0.0, i386-win32, and I'm targeting Windows only with this. My Debug Settings are at the very bottom, after the code.
unit MainUnit;
{$mode objfpc}{$H+}
interface
uses
Classes, SysUtils, FileUtil, Forms, Controls,
Graphics, Dialogs, StdCtrls, IniFiles, Windows;
type
{ IniSettings }
TAutorunIniSettings = record
AutorunTitle: AnsiString;
SetupCommand: AnsiString;
end;
{ TMainForm }
TMainForm = class(TForm)
btnSetup: TButton;
lblTitle: TLabel;
procedure btnSetupClick(Sender: TObject);
procedure FormCreate(Sender: TObject);
procedure FormDestroy(Sender: TObject);
private
{ private declarations }
IniSettings: TAutorunIniSettings;
public
{ public declarations }
end;
var
MainForm: TMainForm;
implementation
{$R *.lfm}
{ TMainForm }
procedure TMainForm.FormCreate(Sender: TObject);
var
AutorunIni: TIniFile;
begin
try
AutorunIni := TIniFile.Create('Autorun.ini');
IniSettings.AutorunTitle := AutorunIni.ReadString('Autorun', 'Title', 'Autorun');
IniSettings.SetupCommand := AutorunIni.ReadString('Autorun', 'Setup', '');
self.Caption := IniSettings.AutorunTitle;
finally
if(AutorunIni <> nil) then AutorunIni.Free;
end;
end;
procedure TMainForm.FormDestroy(Sender: TObject);
begin
end;
procedure TMainForm.btnSetupClick(Sender: TObject);
begin
ShellExecute(0, 'open', PChar(IniSettings.SetupCommand), nil, nil, SW_SHOWNORMAL);
end;
end.

Procedures on Free Pascal

Im relatively new to Pascal and, though i have a fair understanding of the language, there's still some stuff i cant figure out how to implement. I've ran into this problem and, after trying for like hours on my own and looking for similar cases on the internet, i have not found anything. I hope this question is a fair one because, honestly, i dont know how to figure this out.
Here's the thing.
I have an application which dynamically creates TextBoxes (TextEdits in this case) and adds them to a panel for displaying. Thing is, i need to execute some procedures on the newly created elements. I added a new procedure in my app (this is for explaining purposes only):
procedure Demo_Procedure(i: integer, a: String);
Then i proceeded to "develop" my procedure underneath the "implementation" part of the Form.
procedure Demo_Procedure(i: integer, a: String);
begin
ShowMessage(a, ' ' ,i);
end;
Now, for my dynamically created elements im trying to set the "OnKeyDow" event to run my new procedure (this is what i dont A- know if its possible to do or B- how to do it)
NewlyButton.OnClick:= Demo_Procedure(5, 'Hi');
Im getting different errors depending on how i call up my procedure. For example:
If i do it like this: Demo_Procedures(5, 'Hi'), it says:
Error: Incompatible types: got "untyped" expected "procedure variable type of procedure(TObject,var Word,TShiftState) of object;Register>"
Now, researching around i found out that some people that put an '#' before calling the method, the only difference is that this time instead of saying "untyped" it says that it got "procedure variable type of procedure(AnsiString,LongInt) of object" and that it was expecting the same as before (procedure(TObject,var> Word,Tshift...etc)
Can anyone help me out here? I really am lost so any help would be greatly appreciated. Thanks in advance :)
There are errors in your code:
procedure Demo_Procedure(i: integer, a: String); // Wrong
procedure Demo_Procedure(i: integer; a: String); // Right, use semicolon as parameters delimiter
ShowMessage(a, ' ' ,i); // Wrong, ShowMessage takes only one string parameter
ShowMessage(Format('%s %d', [a, i])); // Right, %s means string value and %d means decimal value, see help about Format function
Events is a procedural variables so they have its own types. For example, OnKeyDown event have a type
TKeyEvent = procedure(Sender: TObject; var Key: Word; Shift: TShiftState) of Object;
where of Object means that your event handler must be a class method.
So, you can not assign to the event any procedure but only class method with parameters provided in the type declaration.
Here is the simple code:
unit Unit1;
{$mode objfpc}{$H+}
interface
uses
Classes, SysUtils, Forms, Controls, Dialogs, StdCtrls;
type
{ TForm1 }
TForm1 = class(TForm)
Button1: TButton;
procedure Button1Click(Sender: TObject);
procedure EditKeyDown(Sender: TObject; var Key: Word; Shift: TShiftState);
private
public
{ public declarations }
end;
var
Form1: TForm1;
implementation
{$R *.lfm}
{ TForm1 }
procedure TForm1.Button1Click(Sender: TObject);
var
e: TEdit;
begin
e := TEdit.Create(Self); // Create new TEdit control
e.Parent := Self; // Place control onto the form
e.Left := 10; // Set control coordinates
e.Top := 10;
e.OnKeyDown := #EditKeyDown; // Assign event handler
end;
procedure TForm1.EditKeyDown(Sender: TObject; var Key: Word; Shift: TShiftState);
begin
ShowMessage(Format('Key code is %d', [Key]));
end;
end.

Lazarus - Mac OSX Debugging Error

I used to use Delphi 7 on my PC, but now that I bought a MacBook (Mid 2012 - OSX Mountain Lion), I wanted to continue programming in Pascal and have a similar Interface. Lazarus seems to deliver most of the things I wanted, but it seems to run into a lot of errors, when compiling even the simplest applications! To test it, I made a simple "Russian Roulette" application, just for fun, but the program just freezes, when launched or even when compiled inside Lazarus.
When launching it from command line, It shows me the following error:
TCarbonButton.SetFocus Error: SetKeyboardFocus failed with result -30585
I don't think my coding is the problem, but I guess I should include it:
unit RussianRouletteUnit;
{$mode objfpc}{$H+}
interface
uses
Classes, SysUtils, FileUtil, Forms, Controls, Graphics, Dialogs, StdCtrls,
Buttons, ExtCtrls;
type
{ TForm1 }
TForm1 = class(TForm)
Button1: TButton;
Kugeln: TLabeledEdit;
Memo1: TMemo;
procedure Button1Click(Sender: TObject);
private
{ private declarations }
public
{ public declarations }
end;
var
Form1: TForm1;
Number: Integer;
implementation
{$R *.lfm}
{ TForm1 }
procedure TForm1.Button1Click(Sender: TObject);
begin
if Random(StrToInt(Kugeln.Text))+1 = 1 then
begin
Button1.SetFocus;
Memo1.Color := clred;
Memo1.Text := 'BOOM';
Memo1.Lines.Add('HEADSHOT');
end;
end;
initialization
randomize;
end.
I hope you guys can help me out, any help is apprechiated :D

Why do I get multiple windows messages of same kind?

I'm trying to respond to some windows and application messages, but I get them multiple times.
For example, I write the following code to show a message box when the date of system is changed using WM_TIMECHANGE. WMTimeChange is executed more than once, and I see multiples (most times two or three) messageboxes one behind another. Maybe, I am missing something?
unit Unit1;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs;
type
TForm1 = class(TForm)
protected
procedure WMTimeChange(var Msg: TMessage) ; message WM_TIMECHANGE;
end;
var
Form1: TForm1;
implementation
{$R *.dfm}
{ TForm1 }
procedure TForm1.WMTimeChange(var Msg: TMessage);
begin
showmessage('Date/Time has changed!');
end;
end.
Testing in Windows XP.
EDIT: Just to clarify, my intention is understand WHY that happen and not how to get around the multiple calls. Anyway, if an answer to that is not possible, I will probably accept one answer to the later.
EDIT2: Removed Delphi Tag as it seems not a Delphi issue.
Your code is correct. It sounds like that Windows is sending the WM_TIMECHANGE message several times.
So you can just add a small time-hysteresis comparison to let your message be triggered only once per a 1% of day, i.e. more or less 15 minutes:
type
TForm1 = class(TForm)
protected
FWMTimeChangeTimeStamp: TDateTime;
procedure WMTimeChange(var Msg: TMessage) ; message WM_TIMECHANGE;
end;
var
Form1: TForm1;
implementation
{$R *.dfm}
{ TForm1 }
procedure TForm1.WMTimeChange(var Msg: TMessage);
begin
if Now-FWMTimeChangeTimeStamp>0.01 then
begin
showmessage('Date/Time has changed!');
FWMTimeChangeTimeStamp := Now;
end;
end;
This is kind of what I've used in my case to be resilient to this behavior. But as said in comments, will only work if the user take time to answer the application. So, Arnaud Bounchez is a better approach to general use. Only don't forget to initialize the FWMTimeChangeStamp to something that is different from current computer clock.
unit Unit1;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs;
type
TForm1 = class(TForm)
procedure FormCreate(Sender: TObject);
protected
procedure WMTimeChange(var Msg: TMessage) ; message WM_TIMECHANGE;
private
isTimeChangeEventShowing: Boolean;
end;
var
Form1: TForm1;
implementation
{$R *.dfm}
{ TForm1 }
procedure TForm1.FormCreate(Sender: TObject);
begin
isTimeChangeEventShowing := false
end;
procedure TForm1.WMTimeChange(var Msg: TMessage);
begin
if not isTimeChangeEventShowing then
begin
isTimeChangeEventShowing := true;
showmessage('Date/Time has changed!');
isTimeChangeEventShowing := false;
end;
end;
end.

Resources