Calling procedure from dll in Delphi XE2 - windows

So, I'm trying to call a procedure from a DLL in Delphi XE2.
but the procedure just won't assign.
I have tried several examples found on the internet.
The DLL is being loaded as expected.
The exports are correctly written.
Everything seems fine but still no success.
What is up with that?
The code I have is the following
type
TStarter = procedure; stdcall;
...
fTheHookStart: TStarter;
...
procedure TForm1.LoadHookDLL;
begin
LogLn('Keyboard Hook: Loading...');
// Load the library
DLLHandle := LoadLibrary('thehookdll.DLL');
// If succesful ...
if Handle <> 0 then
begin
LogLn('Keyboard Hook: DLL load OK!');
LogLn('Keyboard Hook: assigning procedure ...');
fTheHookStart := TStarter(GetProcAddress(DLLHandle, 'StartTheHook'));
if #fTheHookStart <> nil then
begin
LogLn('Keyboard Hook: procedure assignment OK!');
LogLn('Keyboard Hook: Starting...');
fTheHookStart;
end
else
begin
LogLn('Keyboard Hook: procedure assignment FAIL!');
FreeLibrary(DLLHandle);
if Handle <> 0 then LogLn('Keyboard Hook: DLL free OK!') else LogLn('Keyboard Hook: DLL free FAIL!');
end;
end
else
begin
LogLn('Keyboard Hook: DLL load FAIL!');
end;
end;

One error is that you assign DllHandle when you load the dll, but then you check if Handle <> nil. Handle is actually your forms handle, which ofcourse is not nil. That will not matter if the loading succeeded, but if it failed, you will get wrong logging.
Since you also have some logging functions, what does the log show?

As I understand it, the DLL loads, but GetProcAddress returns nil. There is only one such failure mode. The DLL does not export a function with that name.
Watch out for name decoration and letter case. C and C++ DLLs may export decorated names. And exported names are sensitive to letter case.
Use dumpbin or Dependency Walker to check the exported function name.
For reference, when GetProcAddress fails, as the documentation explains, a call to GetLastError will yield an error code.
And it looks like the other answer is onto something. You believe that you have loaded the DLL correctly, but your code doesn't perform that check correctly.
If you'd called GetLastError then the system could have alerted you to this. If you'd inspected the variables under the debugger, the problem would have been obvious.

Related

How to automatically save the GUI to file? (Paradigm)

The good (and bad) old Delphi taught us the "classic" way of building application because of the way we write code "behind" the IDE.
Based on this paradigm, I built some time ago a library that allows me to save/load the GUI to INI file with a single line of code.
LoadForm(FormName)
BAM! That's it! No more INI files!
The library only saves "relevant" properties. For example, for a TCheckBox it saves only its Checked property not also its Color or Top/Left. But it is more than enough.
Saving the form has no issues. However, I have "problems" during app initialization. They are not quite problems, but the initialization code is not that nice/elegant.
For example when I assign Checkbox1.Checked := True it will trigger the OnClick event (supposing that the checkbox was unchecked at design time). But assigning a False value (naturally) will not trigger the OnClick event.
Therefore, I have to manually call CheckBox1Click(Sender) after SaveForm(FormName) to make sure that whatever code is in CheckBox1Click, it gets initialized. But this raises another problem. The CheckBox1Click(Sender) might be called twice during application start up (once by SaveForm and once my me, manually).
Of course, in theory the logic of the program should be put in individual objects and separated from the GUI. But even if we do this, the initialization problem remains. You load the properties of the object from disk and you assign them to the GUI. When you set whatever value you have in your object to Checkbox1, it will (or not) call CheckBox1Click(Sender) which will set the value back into the object.
On app startup:
procedure TForm.FormCreate (Sender: TObject);
begin
LogicObject.Load(File); // load app logic
Checkbox1.Checked := LogicObject.Checked; // assign object to GUI
end;
procedure TForm.CheckBox1Click(Sender: TObject);
begin
LogicObject.Checked := Checkbox1.Checked;
end;
Probably the solution involves writing stuff like this for EVERY control on the form:
OldEvent := CheckBox1.OnClick;
CheckBox1.OnClick := Nil;
CheckBox1.Checked := something;
CheckBox1.OnClick := OldEvent;
Not elegant.
Question:
How do you solve this specific problem OR what is your approach when saving/restoring your GUI to/from disk?
This is one of the things which botthered me in some components from the beginning. What I know the are 3 options, except separating GUI and the business logic as #David said, which is not always an option.
As you wrote above, always unassign the events so they don't get triggered
Use non-triggered events such as OnMouseDown or OnMouseUp
Or a similar solution that I use and I think is the most elegant
Create a global variable FormPreparing which is set during initialization and check its value at the beginning of the events like below.
procedure TForm.FormCreate (Sender: TObject);
begin
FormPreparing := True;
try
LogicObject.Load(File); // load app logic
Checkbox1.Checked := LogicObject.Checked; // assign object to GUI
finally
FormPreparing := False;
end;
end;
procedure TForm.CheckBox1Click(Sender: TObject);
begin
if FormPreparing then
Exit;
LogicObject.Checked := Checkbox1.Checked;
end;

PL/SQL Pragma incorrectly violated

My package header code looks like this:
CREATE OR REPLACE PACKAGE INST_PKG IS
...
FUNCTION Check_View (
view_name_ IN VARCHAR2 ) RETURN BOOLEAN;
PRAGMA restrict_references (Check_View, WNDS);
...
END INST_PKG;
And the body of the function is defined as follows:
CREATE OR REPLACE PACKAGE BODY INST_PKG IS
....
FUNCTION View_Exist (
view_name_ IN VARCHAR2 ) RETURN BOOLEAN
IS
ck_ NUMBER := 0;
CURSOR check_view IS
SELECT 1
FROM user_views uv
WHERE uv.view_name = upper(view_name_);
BEGIN
OPEN check_view;
FETCH check_view INTO ck_;
IF check_view%FOUND THEN
CLOSE check_view;
RETURN true;
ELSE
CLOSE check_view;
RETURN false;
END IF;
END View_Exist;
....
END INST_PKG;
I get an error message which reads as follows, when I try to compile the package body:
Compilation errors for PACKAGE BODY INST_PKG
Error: PLS-00452: Subprogram 'VIEW_EXIST' violates its associated pragma
Line: 684
Text: FUNCTION View_Exist (
Clearly, my pragma of "Write No Database State" is not violated, as there are no DML statements in the function. Has anyone seen such behaviour before?
Of course, I could drop the Pragma reference, but that would kind of defeat the purpose.
Worthy of note:
My database has been exported from an Oracle 10g instance, and has been re-imported to 12c. (This is an upgrade test, as you might imagine). Hence I get the above error on Oracle 12c.
I have tried to drop and re-create the package, but that doesn't seem to change things.
I have a feeling that there may be a library reference somewhere that has been imported in error, because when I drop the package, the same error comes up in another package, which contains a function of the same name. But when I re-create the INST_PKG, the second package compiles fine, almost as though the problem in the first package is masking it from being flagged in the second.
It emerges from the link you showed, that the issue is a result of a bug in USER_VIEWS (Oracle forgot to associate PRAGMA restrict_references with NO_ROOT_SW_FOR_LOCAL).
In this case you can be certain that your function doesn't violate WNDS assertion (doesn't write to the database), therefore just use TRUST option to disable assertions validation during compilation:
PRAGMA restrict_references (Check_View, WNDS, TRUST);
Thanks for pointing me in the right direction #MuhammadMuzzam. The problem is user_views has some references which apparently violate Pragma in Oracle 12.
Seems to be an known issue:
https://community.oracle.com/thread/3650610?start=0&tstart=0

Lazarus raising exception class after compiling program on Windows

I've written program in Lazarus Pascal. It was entirely written on Mac, then I've switched to windows, recompiled it (recompiled .dylib to .dll, recompiled and installed custom component), and it runs, but when I try to do anything, it throws error:
While on debug mode:
Project project1 raised exception class 'External:SIGSEGV'. At address 772CD4F1
Running .exe file :
Access violation.
// EDIT
I've noticed that it has a problem with that part of code, in particular at ListView.Clear command:
procedure AddressList.updateView(ListView : TListView);
var
element : ListElement;
newItem : TListItem;
begin
ListView.Clear;
element := first;
if element = nil then
exit;
while element <> nil do
begin
newItem := ListView.Items.Add;
newItem.Caption := element^.name;
newItem.SubItems.Add(element^.surname);
newItem.SubItems.Add(element^.address);
newItem.SubItems.Add(formatNumber(element^.phoneNumber));
element := element^.next;
end;
end;
How is that possible, what could I do wrong?
In Free Pascal Classe instances are always implicit pointers.
It seems that for some reason your ListView doesn't contain a properly created class instance. The "pointer" ListView points to wherever. When the class method Cleartries to access the data you get a segmentation fault.
A watch of ListView should show either garbage data or <invalid>.

ORA-06508: PL/SQL: could not find program unit being called

I am using oracle 10g and toad 11.5. I am trying to call an api from an anonymous block.
If I recompile the api after adding dbms_output.put_line and then try to execute the anonymous block, it shows error as:
"ORA-06508: PL/SQL: could not find program unit being called".
However if I end current session and open a new session, then the anonymous block will execute with out the error.
Due to this issue, i am made to reconnect the session everytime i make a change to API.
Can anyone help if this issue can be resolved by making any configurations in toad or database level.
I suspect you're only reporting the last error in a stack like this:
ORA-04068: existing state of packages has been discarded
ORA-04061: existing state of package body "schema.package" has been invalidated
ORA-04065: not executed, altered or dropped package body "schema.package"
ORA-06508: PL/SQL: could not find program unit being called: "schema.package"
If so, that's because your package is stateful:
The values of the variables, constants, and cursors that a package
declares (in either its specification or body) comprise its package
state. If a PL/SQL package declares at least one variable, constant,
or cursor, then the package is stateful; otherwise, it is stateless.
When you recompile the state is lost:
If the body of an instantiated, stateful package is recompiled (either
explicitly, with the "ALTER PACKAGE Statement", or implicitly), the
next invocation of a subprogram in the package causes Oracle Database
to discard the existing package state and raise the exception
ORA-04068.
After PL/SQL raises the exception, a reference to the package causes
Oracle Database to re-instantiate the package, which re-initializes
it...
You can't avoid this if your package has state. I think it's fairly rare to really need a package to be stateful though, so you should revisit anything you have declared in the package, but outside a function or procedure, to see if it's really needed at that level. Since you're on 10g though, that includes constants, not just variables and cursors.
But the last paragraph from the quoted documentation means that the next time you reference the package in the same session, you won't get the error and it will work as normal (until you recompile again).
seems like opening a new session is the key.
see this answer.
and here is an awesome explanation about this error
Based on previous answers. I resolved my issue by removing global variable at package level to procedure, since there was no impact in my case.
Original script was
create or replace PACKAGE BODY APPLICATION_VALIDATION AS
V_ERROR_NAME varchar2(200) := '';
PROCEDURE APP_ERROR_X47_VALIDATION ( PROCESS_ID IN VARCHAR2 ) AS BEGIN
------ rules for validation... END APP_ERROR_X47_VALIDATION ;
/* Some more code
*/
END APPLICATION_VALIDATION; /
Rewritten the same without global variable V_ERROR_NAME and moved to procedure under package level as
Modified Code
create or replace PACKAGE BODY APPLICATION_VALIDATION AS
PROCEDURE APP_ERROR_X47_VALIDATION ( PROCESS_ID IN VARCHAR2 ) AS
**V_ERROR_NAME varchar2(200) := '';**
BEGIN
------ rules for validation... END APP_ERROR_X47_VALIDATION ;
/* Some more code
*/
END APPLICATION_VALIDATION; /
I recompiled the package specification, even though the change was only in the package body. This resolved my issue

Problems creating a form in a jvPlugin dll and displaying it as a child of a control on the mail form

I'm trying to write a plugin system for my application based on jvPlugin. I create forms in the plugin dll, then reparent them into DevExpress docking controls. At first sight, it seems to work. The problem is that none of the controls on the dll forms ever receive focus. Also, when clicking on controls like TSplitter, a "Control 'xxx' has no parent window" exception is raised.
Here's how I'm doing it (condensed version).
The plugin host implements an IPluginHost interface
IPluginHost = interface
['{C0416F76-6824-45E7-8819-414AB8F39E19}']
function AddDockingForm(AForm: TForm): TObject;
function GetParentApplicationHandle: THandle;
end;
The plugin implments an IMyPlugin interface
IMyPlugin = interface
['{E5574F27-3130-4EB8-A8F4-F709422BB549}']
procedure AddUIComponents;
end;
The following event is called when the plugin is initialised:
procedure TMyPlugin.JvPlugInInitialize(Sender: TObject; var AllowLoad: Boolean);
var
RealApplicationHandle: THandle;
begin
if Supports(HostApplication.MainForm, IPluginHost, FPluginHost) then
begin
RealApplicationHandle := Application.Handle;
Application.Handle := FPluginHost.GetParentApplicationHandle; // Returns Application.Handle from the host application
try
FMyPluginForm:= TMyPluginForm.Create(Application); // Plugin host app owns the form
finally
Application.Handle := RealApplicationHandle;
end;
end;
end;
When the plugin host has loaded I call IMyPlugin.AddUIComponents in my plugin. It's implemented like this:
procedure TMyPlugin.AddUIComponents;
begin
// Add the docking form
FPluginHost.AddDockingForm(FMyPluginForm);
end;
AddDockingForm is implemented in the host like this:
function TfrmMyPluginHost.AddDockingForm(AForm: TForm): TObject;
var
DockPanel: TdxDockPanel;
begin
// Create a new dockpanel
DockPanel := TdxDockPanel.Create(Self);
DockPanel.Name := DPName;
DockPanel.Height := AForm.Height;
DockPanel.DockTo(dxDockSite1, dtBottom, 0);
DockPanel.AutoHide := TRUE;
// Rename the dock panel and parent the plugin
DockPanel.Caption := AForm.Caption;
DockPanel.Tag := Integer(AForm);
AForm.Parent := DockPanel;
AForm.BorderStyle := bsNone;
AForm.Align := alClient;
AForm.Show;
FDockedPluginFormList.Add(AForm);
Result := DockPanel;
end;
If I run the following function on any of the controls on the plugin form I see a list going all the way back to my host's main form. Yet TSplitters tell me they have no parent window. How can this be?
function TfrmMyPlugin.GetParents(WC: TWinControl): String;
begin
if (WC <> nil) and (WC is TWinControl) then
Result := WC.Name + ' [' + WC.ClassName + '] - ' + GetParents(WC.Parent);
end;
I must be missing something somewhere. Anybody got any good ideas?
Build both the plugin and the host application with runtime packages.
Without using runtime packages, the DLL uses a distinct copy of the RTL, VCL and any used units. For example, the DLL's TForm class is not the same as the host's TForm class (is operator fails across the host/DLL boundary and therefore a DLL control will not recognize host parent form as a valid instance of TForm), global variables like Application, Mouse, Screen are separate instances, you have two copies of RTTI, etc, etc.
The VCL was simply not designed to be used like this.
With runtime packages on, all the problems are solved and the plugin (either a runtime package itself or a DLL built with runtime packages) can integrate seamlessly with the host application using runtime packages.
We made it work using "runtime packages" adding "vcl,rtl,ourownpckg"
Where ourownpckg is a our-own-package created with all the DX dependencies and some other that we use across exe-plugins, including JVCL ones.
The three packages must be shiped along the exe
Hope it helps

Resources