I have a Delphi Berlin Windows Service which will call a Delphi 11 DLL. This Delphi 11 DLL has REST API calls. I have used the following code to call the DLL method from my service:
function RESTAPICall(sURL, sDomain, sID, sJson: String;
var slLog: TStringList) : Boolean; stdcall; External Restcall.dll' name 'RESTAPICall' delayed;
Then I call this DLL method in the OnTimer() event in the Windows Service. But it throws an access violation error during runtime:
Access violation at address 633381F4 in module 'Restcall.dll'. Read of address FFFFFFFC.
Also sometimes I get this AV error:
Access violation at address 21285195 in module 'borlndmm.dll'. Write of address 6347C370.
Please note this error occurs only during Service call, it works fine if I call the DLL from EXE.
In simple words: you can't pass Strings or dynamic Arrays directly between application and DLL. You need to do one of the following:
Use the same memory manager for application and DLL by including it as first unit via uses.
Design you DLL functions to accept PChar, not String.
Note: I used FastMM in a lot of projects but I don't know if it is working with Delphi Alexandria.
Related
I know how to check the signing of an executable or dll by location from this question: Checking digital signature programmatically from Delphi
How can I know that an ActiveX library that I am using is signed with my certificate?
The executable can check the dll if it knows its location, but I want to be very sure that it is the one the executable is using at that moment. I know I can use the registry to find the library dll location (from the object IDs or library ID), but this seems like a weak spot vulnerable to spoofing.
Background:
I created an ActiveX library with an automation object. I sign the library dll and the consuming application with the same certificate. I can already check the consumer application from the library as follows
TSomeAutomationObj = class(TAutoObject, ISomeAutomationObj)
public
procedure Initialize; override;
end;
procedure TSomeAutomationObj.Initialize;
const
BufferSize = 2048;
var
LProcessPath: PChar;
begin
LProcessPath := StrAlloc(BufferSize);
try
GetModuleFileName(0, LProcessPath, BufferSize);
//Check signature of LProcessPath Executable as described here https://stackoverflow.com/questions/5993877/checking-digital-signature-programmatically-from-delphi
finally
StrDispose(LProcessPath);
end;
end;
initialization
TAutoObjectFactory.Create(ComServer, TSomeAutomationObj, Class_SomeAutomationObj,
ciMultiInstance, tmApartment);
What remains now is the check in the other direction (Executable to dll).
The automation objects will be registered and I will be using the Automation Object as follows
uses
LibraryThatHoldsAutomationObject_TLB;
TObjectWithApplicationLifetime = class
private
FSomeAutoObj : ISomeAutomationObj;
public
Constructor Create;
end;
Constructor TObjectWithApplicationLifetime.Create;
begin
FSomeAutoObj := CoSomeAutomationObj.Create;
// Check that the source library of this object is signed with my certificate
// If so, then use FSomeAutoObj else set it to nil, set a flag or prevent usage other ways
end;
A similar problem was covered in another question last year. The technique involves fetching the address of the interface's VMT (or vtable) and then asking the OS which module owns that memory.
An interface reference is a pointer to some data for the object. The first bytes of that data are in turn a pointer to the VMT for the interface.
var
VMT: Pointer;
Information: TMemoryBasicInformation;
DLL: HModule;
VMT := PPointer(FSomeAutoObj)^;
Win32Check(VirtualQueryEx(GetCurrentProcess, VMT, Information, SizeOf(Information)) = SizeOf(Information));
DLL := HModule(Information.AllocationBase);
When that code is finished, DLL should hold the handle of the DLL that holds the object that implements the interface. Call GetModuleFileName on it as in the question.
There are a few assumptions required for it to work:
It must be an in-process COM object; for an out-of-process object, the VMT will be that of the proxy, not the real object.
The object mustn't be behind any other sort of proxy, such as one inserted by the compiler. (I don't think Delphi does this, but I'm not sure. Just make sure the interface pointer you have was provided by the DLL and not by the RTL.)
The VMT of the interface needs to by static. This will be the case for most interfaces implemented in Delphi or C++, but interfaces implemented in other ways, such as with scripting languages, might have method tables allocated on the heap. In that case, the DLL variable above won't really hold a module handle.
Another assumption is that even if the DLL isn't signed by your required certificate, it's still trustworthy enough to load into memory in the first place. You're only testing the DLL after you've loaded it into your process space and started executing its code. (Loading the DLL calls its DllMain function. Instantiating the COM object involves calling the DLL's DllGetClassObject function plus whatever else the constructor of the COM object decides to do.) If you can't trust improperly signed DLLs, then you're already too late.
in mine project i have a Windows application and a dll. I have wrote dll so:
library MyDLL;
uses
System.SysUtils,
System.Classes;
{$R *.res}
function Prova: string; export;
begin
result := 'prova';
end;
exports Prova;
begin
end.
and in main program i have called routine so:
unit FrmMain;
interface
uses
// declaration uses //
function Prova: string; external 'MyDLL.dll';
type
// declaration type //
implementation
begin
...
TAdvEdit1.Text := Prova; // [1] //
...
end;
end.
When i compile all project not is reported error, and status report SUCCESS, but application not start.
If i remove the line [1] then it works correctly. In general, application not start when i call the function Prova.
What i can solve this problem? Thanks very much.
The behaviour you describe is what happens when your application fails to load. When run from the debugger you get a silent failure. When run without a debugger you will see an error message, "Application failed to initialize...". This will give details.
In your case it seems that the likely cause is that the dependency of the DLL cannot be resolved by the library loader. That's why the app runs when the call to the external function is removed. When you remove that call, you also remove the dependency on the external DLL. Solve the problem by making sure the DLL can be loaded. For example place it in the same directory as the executable.
The silent failure from the debugger is rather frustrating. Once you have experienced it a few times you'll know what to do - run without the debugger to find out what's really going wrong.
I also recommend that you don't pass managed Delphi strings across module boundaries. That will force you to use the same compiler for both executable and DLL. If you are going to accept that constraint then you may as well use packages. As your code stands, it would need to use ShareMem to work. But I don't recommend that.
Your program and your DLL have separate memory manager. As a general rule memory allocated from DLL should not be used inside your application (the opposite is also true).
Where the allocation comes from? In Delphi "string" is managed type i.e. when you assign some text to a string variable (in your case result := 'prova'), Delphi (behind the scene) allocates memory for that string using DLL's memory manager. Then, for example, if you assign other text value within your main application, the reallocation uses application's memory manager which is bad i.e. app's MM is touching memory that it hasn't allocated itself.
To solve this issue you have to include "SimpleShareMem" (Delphi >= 2010 IIRC?) unit as the first unit of your USES clause in the application (.dpr file) AND in the DLL:
library MyDLL;
uses
SimpleShareMem, // **MUST BE THE FIRST UNIT**
System.SysUtils,
System.Classes;
...
program YourApp;
uses
SimpleShareMem, // **MUST BE THE FIRST UNIT**
// declaration uses //
An example of this approach can be found in "Start > Programs > Embarcadero RAD Studio > Samples > Delphi > RTL > SimpleShareMem"
You can also use PCHAR to transfers strings between DLL and the APP.
In Delphi 2009 I'm finding that any time I do perform both a connection to Oracle (via OCI.dll) and a call to a web service method I get an exception in ntdll.dll when closing my application in the IDE.
For the connection to Oracle I tried using both DOA (Direct Oracle Access) 4.1.1.0 and ODAC components (latest trial version);
For the web service method call (just a simple "function HelloWorld: string") I am using Delphi stock capabilities, after importing the WSDL from the web service.
If I use ODAC components in "direct" mode, that is not using OCI.dll, no exception occurs on closing.
If I call a web service method only (without connecting to Oracle), no exception occurs on closing (even if I use either DOA or ODAC components).
If I connect to Oracle (via OCI.dll) only (without calling a web service method), everything goes fine too (no matter if I use either DOA or ODAC components).
The very same code runs perfect when executed both in Delphi 7 and Delphi XE2: no exception occurs on application closing.
Some information:
Delphi 2009 (stock and Update 3 version)
OS: Windows 7 32 bit
Oracle Instant Client 10.2.0.4 and Oracle Instant Client 10.2.0.5
I start suspecting that it might be an issue related to heap corruption in Delphi 2009 on application closing...
Any help please?
Steps to reproduce (from the comment):
Create a new VCL Forms Application
Place a TOracleSession DOA component (named OracleSession1) on the Form
Place a TButton on the Form (named Button1)
Place this event handler for the button click event:
Here is the code:
procedure TForm1.Button1Click(Sender: TObject);
var
MyWebService3Soap: WebService3Soap;
s: string;
begin
OracleSession1.LogonDatabase := 'SomeLogonDB';
OracleSession1.LogonUsername := 'SomeUsername';
OracleSession1.LogonPassword := 'SomePassword';
OracleSession1.Connected := True;
ShowMessage('Connected');
MyWebService3Soap := GetWebService3Soap();
s := MyWebService3Soap.HelloWorld(); // Just returns a string such as "Hello World"
ShowMessage(s);
end;
The "WebService3Soap" interface is the one automatically generated by Delphi 2009 WSDL Importer. Here is the meaningful part:
WebService3Soap = interface(IInvokable)
['{F6F12FA6-3881-8BB5-AD71-2408B47692CD}']
function HelloWorld: string; stdcall;
end;
function GetWebService3Soap(UseWSDL: Boolean=System.False; Addr: string=''; HTTPRIO: THTTPRIO = nil): WebService3Soap;
initialization
InvRegistry.RegisterInterface(TypeInfo(WebService3Soap), 'http://mytest.it/Test3', 'utf-8');
InvRegistry.RegisterDefaultSOAPAction(TypeInfo(WebService3Soap), 'http://mytest.it/Test3/HelloWorld');
InvRegistry.RegisterInvokeOptions(TypeInfo(WebService3Soap), ioDocument);
end.
Run the application inside the IDE, press the button (close the 2 following ShowMessages) and then close the form.
Given the hint that it might be a "DLL hell" issue, I was able to run a test both on Windows XP and on Vista: everything went fine. So I started thinking that this issue had somehow to be related to Delphi 2009 on Windows 7.
I was right and I found that there is an issue In Delphi 2009 with debugging on Windows 7.
Fortunately a patch is available:
ID: 27476, Hotfix 2 for Delphi 2009 and C++Builder 2009
Applying the patch solved!
I have embedded a form in a DLL and can call the DLL and show the form and return various functions from the DLL back to the main app, however I cannot figure out how to get the DLL to trigger events in the main applications form.
For example in the main app I have a dataset and I want to have a button on the form in the DLL to goto a certain record in the dataset but cannot see how this is done.
Can anybody could point me to an example or give me some pointers on how to to this?
If a DLL needs to invoke behavior in the host application, then the host should provide a callback function to the DLL that the DLL stores and calls when appropriate.
Your DLL exports a function that tells it to display the form, right? Add a couple of parameters to that function for the EXE to provide a pointer to a callback function. The callback function should accept at least one parameter, which should be of type Pointer. The caller (the EXE) will use that parameter as a context parameter, some way for it to be reminded why the DLL is calling the EXE's function. Your DLL will store the function pointer and the context pointer, and when it's time for the DLL to tell the EXE something, it will call that function and pass the context value back. The DLL won't do anything with the context value; it's just something to store and pass back to the EXE verbatim.
The DLL's interface will look like this:
type
TDllCallback = function(Context: Pointer): DWord; stdcall;
function DisplayForm(Parent: HWnd; Callback: TDllCallback; Context: Pointer): DWord; stdcall; external Dll;
The EXE will define a callback function like this:
function CallbackFunction(Context: Pointer): DWord; stdcall;
begin
TMainForm(Context).DoSomething;
Result := 0;
end;
It will call the DLL function like this:
procedure TMainForm.DoDllTaskClick(Sender: TObject);
begin
DisplayForm(Handle, CallbackFunction, Pointer(Self));
end;
Notice how the signature of CallbackFunction matches the TDllcallback type defined earlier. Tey both use the stdcall calling convention, and they're both standalone functions, not methods. Avoid methods since method pointers are particular to Delphi, and you shouldn't require your DLL to be used only by Delphi hosts, if possible.
Because DLL's code being executed in the same address space of main application there are a plenty ways of doing communications.
Define a callback function in the main app and give its address to the DLL.
Define a message handler in the main app and send a message from DLL in response to some event.
etc.
Im having problems using data from the host application.
I want to send a string through the dll into the host, then combine it with some data from the host app.
By just including the form in the uses clauses i can use methods to send data into the host, When the data is recived i try to add a lokal variable, this is when i get a access violation:
Host:
procedure TMainForm.DllLink(sMessage: String);
begin
try
//This is ok:
Showmessage(sMessage);
//This is causes Access error:
Showmessage(sMessage +sPid);
except
Showmessage('Access violation');
end;
end;
Dll:
procedure Transfer(sMessage: PChar); stdcall;
var
sMyPid : String;
begin
try
//Get error if i try to use this method to get sPid which is a string:
sMyPid := MainForm.GetPid;
//Or this:
MainForm.NextsysDllLink(sMessage);
except
showmessage('Error');
end;
end;
I dont think the dll is using the running applications forms that is what's causing the access violations (maybe im wrong ?)
How do I make the dll aware of a running application(that is its host app.) and use that instance to ether get or manipulate data from itself ?
Im using Delphi 5.
It is recommend to not pass native Delphi Objects between Application and DLL boundaries.
If you want to do that you should be using Packages instead of DLLs.
Global variables are not shared between application and Dll.
n your case, your referencing the global mainform variable in the DLL, if you debug that code you will find that mainform = nil or another address that is not the same as the mainform in your host application.
ShareMem should do the trick.