Here is my code in DLL:
procedure TTaskTest;
begin
TTask.Run(
procedure
begin
Sleep(300);
end);
end;
exports TTaskTest;
After calling this method in host app, then call FreeLibrary will freeze host app.
After debug , I found that the program freezes at if TMonitor.Wait(FLock, Timeout) then in TLightweightEvent.WaitFor , but the debugger cannot step into TMonitor.Wait.
How to solve?
This issue was reported (RSP-13742 Problem with ITask, IFuture inside DLL).
It was closed "Works as Expected" with a remark:
To prevent this failure using ITask or IFuture from a DLL, the DLL will need to be using its own instance of TThreadPool in place of the default instance of TThreadPool.
Here is an example from Embarcadero how to handle it:
library TestLib;
uses
System.SysUtils,
System.Classes,
System.Threading;
{$R *.res}
VAR
tpool: TThreadPool;
procedure TestDelay;
begin
tpool := TThreadPool.Create;
try
TTask.Run(
procedure begin
Sleep(300);
end,
tpool
);
finally
FreeAndNil(tpool);
end;
end;
exports
TestDelay;
begin
end.
Another way is to create the threadpool when the library is loaded, and add a release procedure, which you call before calling FreeLibrary.
// In dll
procedure TestDelay;
begin
TTask.Run(
procedure begin
Sleep(300);
end,
tpool
);
end;
procedure ReleaseThreadPool;
begin
FreeAndNil(tpool);
end;
exports
TestDelay,ReleaseThreadPool;
begin
tpool := TThreadPool.Create;
end.
Related
I have a dll that contains a class that implements a interface. The dll has an exported method that returns the interface.
I can explicit load the dll succefully, but when I try to use Free Library I get Access Violation. I did not tried use implicit link, because I need use the explicit mode.
If I just load the library and free right after, without geting the interface, everything works fine.
Dll
library Tef;
uses
uTTefFacade;
{$R *.res}
exports
CreateTef;
begin
end.
Interface in dll:
type
ITefFacade = interface
['{77691DD1-C6E9-4F75-951F-BFA1468DC36C}']
function IniciarTransacao(AParam: TTefIniciarTransacaoParamDTO): TTefIniciarTransacaoResultDTO;
end;
Class in dll:
type
TTefFacade = class (TInterfacedObject, ITefFacade)
private
function IniciarTransacao(AParam: TTefIniciarTransacaoParamDTO): TTefIniciarTransacaoResultDTO;
public
constructor Create;
destructor Free;
end;
function CreateTef: ITefFacade; export; stdcall;
function CreateTef: ITefFacade;
begin
Result := ITefFacade(TTefFacade.Create);
end;
Exe:
procedure TForm1.FormCreate(Sender: TObject);
var
CreateTef: function: ITefFacade; stdcall;
begin
try
FTef := nil;
FHTef := LoadLibrary('Tef.dll');
if (FHTef > 0) then
begin
#CreateTef := GetProcAddress(FHTef, 'CreateTef');
if (#CreateTef <> nil) then
FTef := CreateTef;
end;
if (FTef = nil) then
ShowMessage('Error.');
except
on E: Exception do
ShowMessage('Erro: ' + E.Message);
end;
end;
And here in the calling Free Library, access violation occurs.
procedure TForm1.FormDestroy(Sender: TObject);
begin
FreeLibrary(FHTef);
end;
You have to nil the FTef reference before releasing the DLL.
The object behind the interface lives in the DLL, you should respect this. If you try to unload the DLL without releasing the interface first, there will be problems when the object is accessed after the unload (such as when Delphi auto-nils the reference when it goes out of scope).
I put a dll file into a resources file (*.res)
ProjectTestLibrary.dll
library ProjectTestLibrary;
uses SysUtils, Classes, Dialogs;
{$R *.res}
procedure DllMessage; export;
begin
ShowMessage('Hello world from a Delphi DLL');
end;
exports DllMessage;
begin
end.
MyTestDLL.rc
TestDLL RCDATA ProjectTestLibrary.dll
Produce MyTestDLL.res by this command line
BRCC32 ProjectTestLibrary.rc
In main form
implementation
{$R *.dfm}
{$R MyTestDLL.RES}
procedure DllMessage; stdcall; external 'ProjectTestLibrary.dll';
procedure TForm1.Button1Click(Sender: TObject);
begin
DllMessage;
end;
Then error comes out
This application has failed to start because ProjectTestLibrary.dll was not found.
I understand that the problem because the dll file doesn't exist yet.
So I delete the 'DLLMessage;' code in onButton1Click.
Then onFormCreate, I added :
procedure TForm1.FormCreate(Sender: TObject);
var ms : TMemoryStream;
rs : TResourceStream;
begin
if 0 <> FindResource(hInstance, 'TestDLL', RT_RCDATA) then
begin
rs := TResourceStream.Create(hInstance, 'TestDLL', RT_RCDATA);
ms := TMemoryStream.Create;
ShowMessage('Found');
end else
begin
ShowMessage('Not Found');
end;
end;
I run it again then pop-up message said 'Found'
My question then :
1. How to save it in memory (not in PC hard drive), and finally
2. use its procedures/functions (procedure DLLMessage)
There is no official way to do it, although there exist techniques which emulate the Windows PE loader, and let you load DLL directly from a memory buffer, without having to store it on disk.
Here is a Delphi implementation, for example, of a DLL memory loader: https://github.com/DSPlayer/memorymodule
I'm doing a program to take a picture of the webcam using Delphi XE2 and VFrames to achieve this, the problem is that I have it all figured out, in a graphic application everything works fine, but when I use the unit in a console application, it returns me error saying
First chance exception at $76B6B727. Exception class EAccessViolation with message 'Access violation at address 004A271B in module 'console.exe'. Read of address 00000260'. Process console.exe (3676)
My Unit :
unit Webcam;
interface
uses SysUtils, Windows, Vcl.Imaging.Jpeg, Vcl.Graphics, VSample,
VFrames, Classes;
type
TWebcam = class
private
procedure NewVideoFrameEvent(Sender: TObject; Width, Height: integer;
DataPtr: pointer);
public
constructor Create;
destructor Destroy; override;
procedure capture_webcam(take_name: string);
end;
var
web_image: TVideoImage;
name_screen: string;
implementation
constructor TWebcam.Create;
begin
inherited Create;
end;
destructor TWebcam.Destroy;
begin
inherited Destroy;
end;
Procedure TWebcam.NewVideoFrameEvent(Sender: TObject; Width, Height: integer;
DataPtr: pointer);
var
bitmap: TBitmap;
name: string;
begin
name := name_screen;
if (FileExists(name)) then
begin
DeleteFile(Pchar(name));
end;
bitmap := TBitmap.Create;
bitmap.PixelFormat := pf24bit;
web_image.GetBitmap(bitmap);
bitmap.SaveToFile(name);
bitmap.Free;
web_image.VideoStop;
web_image.Free;
end;
procedure TWebcam.capture_webcam(take_name: string);
var
list_cams: TStringList;
begin
web_image := TVideoImage.Create();
list_cams := TStringList.Create;
web_image.GetListOfDevices(list_cams);
if not(list_cams.count = 0) then
begin
name_screen := take_name;
web_image.VideoStart(list_cams[0]);
end;
list_cams.Free;
web_image.OnNewVideoFrame := NewVideoFrameEvent;
end;
end.
Console :
program console;
{$APPTYPE CONSOLE}
{$R *.res}
uses
System.SysUtils,
Webcam;
var
webcamz: TWebcam;
begin
try
webcamz := TWebcam.Create();
webcamz.capture_webcam('test.jpg');
webcamz.Free();
except
on E: Exception do
Writeln(E.ClassName, ': ', E.Message);
end;
end.
What should I do ?
The relevent source code for the VFrames unit is available on Github:
https://github.com/heise/GRBLize/blob/edge/VFrames.pas
https://github.com/heise/GRBLize/blob/edge/VSample.pas
The TVideoImage.VideoStart() method has a dependancy on Application.MainForm.Handle. A console application does not have a MainForm by default, so that alone will crash the code in a console application unless you create a MainForm (which defeats the purpose of making a console app).
Aside from that, TVideoImage also has a dependancy on a message loop, as it creates a hidden window to receive video notifications used to fire the OnNewVideoFrame event. Your console application does not have a message loop. And even if it did, you are freeing the TVideoImage object before the event would fire anyway, as your capture_webcam() code is not waiting for the event to fire before exiting.
Also, TVideoSample class (which TVideoImage uses internally) uses the DirectShow API to capture images from the webcam's video stream. DirectShow is a COM-based API. Your console application is not initializing COM before using TVideoImage. That alone would cause GetListOfDevices() to fail and return a blank list. And if you attempted to ignore that and provide a device name anyway, VideoStart() would still crash when it tries to access a COM object that TVideoSample was not able to create during construction.
I found this source code from a Delphi sample codes, and
I am adding a control or component inside a Delphi dynamic DLL, I can't figure it out,
library DLLEntryLib;
uses
SysUtils,
Windows,
Dialogs,
Classes,
msHTML,
SHDocVw;
type
TMyWeb = class(TWebBrowser)
constructor create(Aowner: TComponent); override;
end;
var
web: TMyWeb;
// Initialize properties here
constructor TMyWeb.Create(AOwner: TComponent);
begin
inherited Create(Self);
end;
procedure getweb;
begin
web := TmyWeb.create(nil);
web.Navigate('http://mywebsite.com');
end;
procedure xDLLEntryPoint(dwReason: DWord);
begin
case dwReason of
DLL_PROCESS_ATTACH:
begin
getweb; //I THINK THE ERROR IS HERE, HOW TO WORK THIS OUT?
ShowMessage('Attaching to process');
end;
DLL_PROCESS_DETACH: ShowMessage('Detaching from process');
DLL_THREAD_ATTACH: MessageBeep(0);
DLL_THREAD_DETACH: MessageBeep(0);
end;
end;
begin
{ First, assign the procedure to the DLLProc variable }
DllProc := #xDLLEntryPoint;
{ Now invoke the procedure to reflect that the DLL is attaching to the
process }
xDLLEntryPoint(DLL_PROCESS_ATTACH);
end.
//IN MY APPLICATION FORM.
procedure TMainForm.btnLoadLibClick(Sender: TObject);
begin
if LibHandle = 0 then
begin
LibHandle := LoadLibrary('DLLENTRYLIB.DLL');
if LibHandle = 0 then
raise Exception.Create('Unable to Load DLL');
end
else
MessageDlg('Library already loaded', mtWarning, [mbok], 0);
end;
How do I get rid of the error?
raise to many consicutive exception
When you write:
inherited Create(Self);
you should write
inherited Create(AOwner);
You are asking the control to own itself. That just cannot work. That quite possibly leads to a non-terminated recursion if the constructor fails.
The other big problem is that you are creating a web browser control inside DllMain. That's a very big no-no. You'll want to stop doing that. Move that code into a separate exported function. Do nothing in DllMain.
Presumably the caller has already initialized COM. If not, you will need to ensure that the caller does so. If the caller is a VCL forms app then COM will be initialized automatically.
This question already has an answer here:
How can I make the second instance of my program pass control back to the first instance?
(1 answer)
Closed 8 years ago.
See also:
How can I tell if another instance of my program is already running?
i use the following code before starting my application, to check if another instance
of it is already started:
var _PreviousHandle : THandle;
begin
_PreviousHandle := FindWindow('TfrmMainForm',nil);
if _PreviousHandle <> 0 then
begin
ShowMessage('Application "" is already running!');
SetForegroundWindow(_PreviousHandle);
ShowWindow(_PreviousHandle, SW_SHOW);
Application.Terminate;
Exit;
end;
...
However, if it has started, i need to show that application. The problem is after it is shown in this way the minimize button no longer works, and when i click the icon in the taskbar, it "unminimizes" and the animation that is shown is as if it was minimized. Am i missing something? is there a proper way to activate and show external application while it's minimized?
Here is a complete project, which keeps running only one instance of the application, and which should bring already running instance window to front.
You can download a testing project or try the code, which follows:
Project1.dpr
program Project1;
uses
Forms,
Windows,
Unit1 in 'Unit1.pas' {Form1};
{$R *.res}
var
Mutex: THandle;
const
AppID = '{0AEEDBAF-2643-4576-83B1-8C9422726E98}';
begin
MessageID := RegisterWindowMessage(AppID);
Mutex := CreateMutex(nil, False, AppID);
if (Mutex <> 0) and (GetLastError = ERROR_ALREADY_EXISTS) then
begin
PostMessage(HWND_BROADCAST, MessageID, 0, 0);
Exit;
end;
Application.Initialize;
Application.MainFormOnTaskbar := True;
Application.CreateForm(TForm1, Form1);
Application.Run;
end.
Unit1.pas
unit Unit1;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, StrUtils, StdCtrls;
type
TForm1 = class(TForm)
private
function ForceForegroundWindow(WndHandle: HWND): Boolean;
function ForceRestoreWindow(WndHandle: HWND; Immediate: Boolean): Boolean;
protected
procedure WndProc(var AMessage: TMessage); override;
end;
var
Form1: TForm1;
MessageID: UINT;
implementation
{$R *.dfm}
{ TForm1 }
function TForm1.ForceForegroundWindow(WndHandle: HWND): Boolean;
var
CurrThreadID: DWORD;
ForeThreadID: DWORD;
begin
Result := True;
if (GetForegroundWindow <> WndHandle) then
begin
CurrThreadID := GetWindowThreadProcessId(WndHandle, nil);
ForeThreadID := GetWindowThreadProcessId(GetForegroundWindow, nil);
if (ForeThreadID <> CurrThreadID) then
begin
AttachThreadInput(ForeThreadID, CurrThreadID, True);
Result := SetForegroundWindow(WndHandle);
AttachThreadInput(ForeThreadID, CurrThreadID, False);
if Result then
Result := SetForegroundWindow(WndHandle);
end
else
Result := SetForegroundWindow(WndHandle);
end;
end;
function TForm1.ForceRestoreWindow(WndHandle: HWND;
Immediate: Boolean): Boolean;
var
WindowPlacement: TWindowPlacement;
begin
Result := False;
if Immediate then
begin
WindowPlacement.length := SizeOf(WindowPlacement);
if GetWindowPlacement(WndHandle, #WindowPlacement) then
begin
if (WindowPlacement.flags and WPF_RESTORETOMAXIMIZED) <> 0 then
WindowPlacement.showCmd := SW_MAXIMIZE
else
WindowPlacement.showCmd := SW_RESTORE;
Result := SetWindowPlacement(WndHandle, #WindowPlacement);
end;
end
else
Result := SendMessage(WndHandle, WM_SYSCOMMAND, SC_RESTORE, 0) = 0;
end;
procedure TForm1.WndProc(var AMessage: TMessage);
begin
inherited;
if AMessage.Msg = MessageID then
begin
if IsIconic(Handle) then
ForceRestoreWindow(Handle, True);
ForceForegroundWindow(Application.Handle);
end;
end;
end.
Tested on OS versions:
Windows 8.1 64-bit
Windows 7 SP1 64-bit Home Premium
Windows XP SP 3 32-bit Professional
Known issues and limitations:
The MainFormOnTaskbar is not taken into account at all; it must be set to True at this time
You're asking your Main form to show, but it may occur the application hidden window itself is minimized when you minimize the application to the task bar, in case of MainFormOnTaskBar being false.
Don't call the ShowWindow method from the oustide. IMHO it's better if you pass a message to the application and respond from inside, calling the Application.Restore` method, which performs the proper ShowWindow calls among other things.
This is a very common problem with VCL apps, and has been asked and answered many many times in the Borland/CodeGear/Embarcadero forums over the years. Using ShowWindow() in this manner does not work for VCL windows very well because of the way the MainForm interacts with the TApplication object at runtime, especially in different versions of Delphi. What you should do instead is have the second instance send a custom message to the first instance, and then let the first instance restore itself as needed when it receives the message, such as by setting its MainForm.WindowState property, or calling Application.Restore(), etc, and let the VCL work out the details for you, like #jachguate suggested.
The following works well for me. I'm not 100% certain I have fully understood the question though, so do let me know if I've got it wrong.
var
_PreviousHandle: HWND;
WindowPlacement: TWindowPlacement;
....
WindowPlacement.length := SizeOf(WindowPlacement);
GetWindowPlacement(_PreviousHandle, WindowPlacement);
if WindowPlacement.flags and WPF_RESTORETOMAXIMIZED<>0 then
WindowPlacement.showCmd := SW_MAXIMIZE
else
WindowPlacement.showCmd := SW_RESTORE;
SetWindowPlacement(_PreviousHandle, WindowPlacement);
SetForegroundWindow(_PreviousHandle);
Note that the correct type for _PreviousHandle is HWND and not THandle.