I have the following DLL procedure:
procedure OPENDB(IBTrans: TIBTransaction; IBQuery: TIBQuery;
SQL: String); stdcall;
begin
if IBQuery.Active = True then IBQuery.Active := False;
if IBTrans.Active = True then IBTrans.Active := False;
IBQuery.SQL.Clear;
IBQuery.SQL.Add(SQL);
IBTrans.Active := true;
IBQuery.Active := True;
end;
In my program I call the ddl that is in the \'winnt\\system32\' folder as follows:
procedure OPENDB(IBTrans: TIBTransaction; IBQuery: TIBQuery;
SQL: String); stdcall; external \'saudedll.dll\';
when i'm going to use the procedure to open the database i see an error. I only happens with procedures that use interbase components. If I declare the procedure in my own executable, it doesn't make any kind of mistake, it works perfectly. Can someone give me a hint? Thank you in advance!!
UPDATE -----------------
library Biblio;
{ Important note about DLL memory management: ShareMem must be the
first unit in your library's USES Project-View Source) USES clause if your DLL exports procedures or functions that pass strings as parameters or function results. This to all strings passed to and from your DLL—even those are nested in records and classes. ShareMem is the interface unit to the BORLNDMM.DLL shared memory manager, which must be deployed
with your DLL. To avoid using BORLNDMM.DLL, pass string
using PChar or ShortString parameters. }
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, DB, Grids, DBGrids, Buttons, StdCtrls, Mask, DBCtrls, ExtCtrls,
ActnList, ComCtrls, FMTBcd, Provider, DBClient;
{$R *.res}
procedure LimpaCampos(vForm: TForm; vFormHandle: THandle);
var i : Integer;
begin
for i := 0 to (vForm.ControlCount -1) do
if vForm.Controls[i].ClassName = ´TEdit´ then
begin
TEdit(vForm.Controls[i]).Text:=´´;
end;
for i := 0 to (vForm.ControlCount -1) do
if vForm.Controls[i].ClassName = ´TDBEdit´ then
begin
TDBEdit(vForm.Controls[i]).Text:=´´;
end;
for i := 0 to (vForm.ControlCount -1) do
if vForm.Controls[i].ClassName = ´TCheckBox´ then
begin
TCheckBox(vForm.Controls[i]).Checked:= False;
end;
for i := 0 to (vForm.ControlCount -1) do
if vForm.Controls[i].ClassName = ´TDBCheckBox´ then
begin
TDBCheckBox(vForm.Controls[i]).Checked:= False;
end;
Exports
LimpaCampos;
begin
end.
And in the Form frmCadKits I declare the procedure and the dll.
...
public
{ Public declarations }
end;
procedure LimpaCampos(vForm: TForm; vFormHandle: THandle); external ´biblio.dll´;
var
...
And I make a call in the Form show
procedure TfrmCadKits.FormShow(Sender: TObject);
begin
LimpaCampos(frmCadKits,frmCadKits.Handle);
end;
And I get the error:
Project Application.exe raised class EAccessViolation with message 'Access violation
Related
Delphi 7 / QuickReport 5.02.2
We've used similar code for several years but have run into an issue recently now that we're migrating workstations to Windows 10. Previously, we were using Windows 7 and all was fine. Maybe there's something I'm missing or doing wrong?
Here's a simple test project I put together to test this. When the report is within a DLL every call to Printer.GetPrinter fails in Windows 10. Though, if the report is on a form within the main application it works fine.
Below is the code, and a zipped up folder for anyone that's interested. There is the dependency on QuickReport though, which can't be helped. Thanks for looking.
https://1drv.ms/u/s!AsbtokV75aocsXM6MQZcrvwpHKcg
DLL Project.
library test_dll;
uses
SysUtils,
Classes,
Forms,
report in 'report.pas' {report_test};
{$R *.res}
function Report_Print(PrinterName: Widestring): Integer; export;
var
Receipt: Treport_test;
begin
try
Receipt := Treport_test.Create(nil);
try
Receipt.Print(PrinterName);
Receipt.Close;
finally
Receipt.Free;
end;
except
Application.HandleException(Application.Mainform);
end;
Result := 1;
end;
exports
Report_Print;
begin
end.
Report Unit
unit report;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, QRCtrls, QuickRpt, ExtCtrls, Printers, QRpCtrls, QRPrntr;
type
Treport_test = class(TForm)
QuickRep1: TQuickRep;
DetailBand1: TQRBand;
TitleBand1: TQRBand;
QRLabel1: TQRLabel;
SummaryBand1: TQRBand;
QRLabel2: TQRLabel;
QRLabel3: TQRLabel;
private
{ Private declarations }
public
{ Public declarations }
procedure Print(const PrinterName: string);
end;
var
report_test: Treport_test;
procedure SetupPrinter(QuickRep: TQuickRep; const PrinterName: string);
function SelectPrinter(QuickRep: TQuickRep; const PrinterName: string): boolean;
implementation
var
DLL_QRPrinter: TQRPrinter;
{$R *.dfm}
function SelectPrinter(QuickRep: TQuickRep; const PrinterName: string): boolean;
var
i: integer;
compareLength: integer;
windowsPrinterName: string;
selectedPrinter: Integer;
defaultPrinterAvailable: Boolean;
begin
defaultPrinterAvailable := True;
try // an exception will occur if there is no default printer
i := Printer.printerIndex;
if i > 0 then ; // this line is here so Delphi does not generate a hint
except
defaultPrinterAvailable := False;
end;
compareLength := Length(PrinterName);
if (not Assigned(QuickRep.QRPrinter)) then
begin
QuickRep.QRPrinter := DLL_QRPrinter;
end;
// Look for the printer.
selectedPrinter := -1;
// Attempt #1: first try to find an exact match
for i := 0 to QuickRep.QRPrinter.Printers.Count - 1 do
begin
windowsPrinterName := Copy(QuickRep.QRPrinter.Printers.Strings[i], 1, compareLength);
if (UpperCase(windowsPrinterName) = UpperCase(PrinterName)) then
begin
selectedPrinter := i;
Break;
end;
end;
// Attempt #2: if no exact matches, look for the closest
if (selectedPrinter < 0) then
for i := 0 to QuickRep.QRPrinter.Printers.Count - 1 do
begin
windowsPrinterName := Copy(QuickRep.QRPrinter.Printers.Strings[i], 1, compareLength);
if (Pos(UpperCase(PrinterName), UpperCase(QuickRep.QRPrinter.Printers.Strings[i])) > 0) then
begin
selectedPrinter := i;
Break;
end;
end;
// Attempt #3: if no exact matches, and nothing close, use default printer
if (selectedPrinter < 0) and (defaultPrinterAvailable) then
selectedPrinter := QuickRep.Printer.printerIndex;
Result := False;
if (selectedPrinter > -1) then
begin
QuickRep.PrinterSettings.PrinterIndex := selectedPrinter;
Result := True;
end;
end;
procedure SetupPrinter(QuickRep: TQuickRep; const PrinterName: string);
begin
//check if we have the default printer instead of the selected printer
SelectPrinter(QuickRep, PrinterName);
QuickRep.Page.Units := Inches;
QuickRep.Page.Length := 11;
end;
procedure Treport_test.Print(const PrinterName: string);
begin
SetupPrinter(QuickRep1, PrinterName);
QuickRep1.Print;
end;
initialization
DLL_QRPrinter := TQRPrinter.Create(nil);
finalization
DLL_QRPrinter.Free;
DLL_QRPrinter := nil;
end.
Test Application
program Project1;
uses
Forms,
Unit1 in 'Unit1.pas' {Form1};
{$R *.res}
begin
Application.Initialize;
Application.CreateForm(TForm1, Form1);
Application.Run;
end.
Main Form
unit Unit1;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms, QRPrntr,
Dialogs, StdCtrls, QuickRpt, QRCtrls, ExtCtrls, Printers, QRPCtrls;
type
TForm1 = class(TForm)
Button1: TButton;
procedure Button1Click(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
end;
type
TPrintReport = function(PrinterName: Widestring): Integer;
var
Form1: TForm1;
procedure PrintReport(const PrinterName: string);
implementation
var
DLLHandle: THandle = 0;
POS: TPrintReport = nil;
{$R *.dfm}
procedure PrintReport(const PrinterName: string);
begin
try
POS(PrinterName);
except on e: Exception do
ShowMessage(e.Message);
end;
end;
procedure LoadDLL;
var
DLLName: string;
DLLRoutine: PChar;
begin
DLLName := 'test_dll.dll';
DLLRoutine := 'Report_Print';
if not (FileExists(DLLName)) then
raise Exception.CreateFmt('The DLL "%s" is missing. Build the DLL project and try again.', [DLLName]);
Application.ProcessMessages;
DLLHandle := LoadLibrary(PChar(DLLName));
Application.ProcessMessages;
if (DLLHandle = 0) then
raise Exception.CreateFmt('Error: %s, while attempting to load DLL %s.', [IntToStr(GetLastError), DLLName]);
POS := GetProcAddress(DLLHandle, DLLRoutine);
if (#POS = nil) then
raise Exception.CreateFmt('Error: %s, while attempting get address to %s in DLL %s.', [IntToStr(GetLastError), DLLRoutine, DLLName]);
end;
procedure TForm1.Button1Click(Sender: TObject);
begin
LoadDLL;
ShowMessage('dll loaded');
PrintReport('MyPrinter');
FreeLibrary(DLLHandle);
end;
end.
Snippet from QuickReport
procedure TPrinterSettings.ApplySettings;
var
Cancel : boolean;
begin
FPrinter.GetPrinter(FDevice, FDriver, FPort, DeviceMode);
DevMode := GlobalLock(DeviceMode);
begin
SetField(dm_paperlength);
...
DeviceMode is 0, so SetField throws an access violation. See below.
Access violation at address 036BFBA7 in module 'test_dll.dll'. Write of address 00000028.
Try comment out those 2 lines for GetPrinter and for DevMode
procedure TPrinterSettings.ApplySettings;
var
Cancel : boolean;
begin
// FPrinter.GetPrinter(FDevice, FDriver, FPort, DeviceMode);
// DevMode := GlobalLock(DeviceMode);
begin
SetField(dm_paperlength);
...
end
uses ComObj, ActiveX, StdVcl;
if Printer.Printers.Count>0 then
begin
FSWbemLocator := CreateOleObject('WbemScripting.SWbemLocator');
FWMIService := FSWbemLocator.ConnectServer('localhost', 'root\CIMV2', '', '');
FWbemObject := FWMIService.Get(Format('Win32_Printer.DeviceID="%s"',[Printer.Printers.Strings[0]]));
if not VarIsClear(FWbemObject) then
FWbemObject.SetDefaultPrinter();
end;
new solution
Windows 10 have not default printer with this code u can set the default printer
I started learning classes and objects programming today. There is code in the handbook that I must copy to run and save. I need to create a class(TLine) and use that class for instantiating an object.
Problem : No output is displayed in my RichEdit component. I copied the code exactly from the book to delphi, but no output is displayed.
How the output should look: "**********"
My class:
unit Lines_U;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs;
Type
TLine = Class
Public
fSize : integer;
fPattern : char;
public
Constructor Create;
Procedure Draw(Var line: string);
end;
implementation
{ TLine }
Constructor TLine.Create;
begin
fSize := 10;
fPattern := '*';
end;
Procedure TLine.Draw(Var line: string);
Var
loop : integer;
begin
for loop := 1 to fSize do
begin
line := line + fPattern;
end;
end;
end.
Code for instantiating the Object of the TLine Class:
unit UseLine_U;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, Lines_U, StdCtrls, ComCtrls;
type
TForm1 = class(TForm)
redOut: TRichEdit;
Procedure FormCreate(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
end;
var
Form1: TForm1;
line : TLine;
implementation
{$R *.dfm}
Procedure TForm1.FormCreate(Sender: TObject);
Var tempLine : string;
begin
line := TLine.Create;
line.Draw(tempLine);
redOut.Lines.Add(tempLine);
end;
end.
The reason your code is not running is that your event handler Form1.FormCreate is not linked to the OnCreate event. Restore the link in the object inspector.
About event handlers
Never write event handlers (all those procedures starting with On...) manually. Always use the Object inspector to create them.
If you double click on an event, Delphi will create a code template for you that you can fill with data.
Make sure your event handlers are filled in the object inspector. If not they will not work (as you've seen).
If you want to remove an event handler do not remove it in the object inspector, but reduce the code inside the event handling procedure back to the empty template.
Delphi will see that it is empty and remove it on the next compile.
About your code
Other than the missing link there is nothing wrong with your code. It runs just fine.
There are a few style issues though, these have no bearing on the operation, but are important none the less.
Here's how I would rewrite your code.
unit Lines_U;
interface
//only import units that you actually use.
type //please type reserved words in all lowercase, this is Pascal not VB.
TLine = class
private //make data members private.
fSize : integer;
fPattern : char;
public
constructor Create;
procedure Draw(var line: string);
property Size: integer read fSize write fSize; //Use properties to expose data members.
property Pattern: char read fPattern write fPattern;
end;
implementation
{ TLine }
constructor TLine.Create;
begin
inherited; //make the inherited call in your constructor explicit.
fSize := 10;
fPattern := '*';
end;
procedure TLine.Draw(var line: string);
//var
//loop : integer; //use consistent indentation
begin
//Changing a string ten times in a row is inefficient.
//try to do your changes all at once.
//for loop := 1 to fSize do begin
// line := line + fPattern;
//end;
Line:= Line + StringOfChar(fPattern, fSize);
end;
end.
Your form:
unit UseLine_U;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, StdCtrls, ComCtrls, Lines_U;
//put your own unit last, to prevent name clashes with built in classes and functions.
type
TForm1 = class(TForm)
//note that the {nothing} line is really **published**.
//And data members should be private
//Line : TLine; //Line should be private.
RedOut: TRichEdit;
procedure FormCreate(Sender: TObject);
private
//Prefix all private data with `F` for Field.
FLine: TLine; //Line should be a item in the form, not a global var.
public
property Line: TLine read FLine; //read only access to line.
end;
var
Form1: TForm1;
implementation
{$R *.dfm}
procedure TForm1.FormCreate(Sender: TObject);
var
tempLine : string;
i: integer;
begin
//tempLine:= ''; //local variables should be initialized.
//However strings are always initialized to '', because they are managed types.
//everything else will contain random data unless you fill it!
FLine := TLine.Create;
Line.Draw(tempLine);
i:= 0; //init i, otherwise it will be random!
while i < 5 do begin //always use `begin-end` in loops, never a naked `do`
RedOut.Lines.Add(tempLine);
i:= i + 1;
end; {while} //I like to annotate my loop `end`s, but that's just me.
FreeAndNil(FLine); //Dispose of TLine when you're done with it.
end;
end.
I can think of other things, but I don't want to overload you.
Using delphi 7 TRichEdit component, RTF data is being imported from a msword document through copy and paste, but if data is contained in a box, it is not displaying correctly i.e.
Please assist
Try to use the following, it should subclass the TRichEdit class to version 4.1. However I don't know if Delphi 7 supports interposed classes, so just try to paste the following code and try to build the project.If it compiles then if you put a TRichEdit component and run the project you should get RichEdit 4.1.
unit Unit1;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, StdCtrls, ComCtrls, RichEdit;
type
TRichEdit = class(ComCtrls.TRichEdit)
protected
procedure CreateParams(var Params: TCreateParams); override;
end;
type
TForm1 = class(TForm)
private
{ Private declarations }
public
{ Public declarations }
end;
var
Form1: TForm1;
FRichEditModule: THandle;
implementation
{$R *.dfm}
{ TRichEdit }
procedure TRichEdit.CreateParams(var Params: TCreateParams);
const
RichEditClassName = 'RICHEDIT50A';
RichEditModuleName = 'MSFTEDIT.DLL';
HideScrollBarsStyle: array[Boolean] of DWORD = (ES_DISABLENOSCROLL, 0);
HideSelectionsStyle: array[Boolean] of DWORD = (ES_NOHIDESEL, 0);
begin
if FRichEditModule = 0 then
begin
FRichEditModule := LoadLibrary(RichEditModuleName);
if FRichEditModule <= HINSTANCE_ERROR then
FRichEditModule := 0;
end;
inherited CreateParams(Params);
CreateSubClass(Params, RichEditClassName);
Params.Style := Params.Style or HideScrollBarsStyle[HideScrollBars] or
HideSelectionsStyle[HideSelection];
Params.WindowClass.style := Params.WindowClass.style and
not (CS_HREDRAW or CS_VREDRAW);
end;
initialization
finalization
if FRichEditModule <> 0 then
FreeLibrary(FRichEditModule);
end.
Finally got it to work,
It was as simple as adding the Riched20.dll (Latest version) to the application folder
This is my code:
unit Unit1;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, StdCtrls, ComCtrls;
...
implementation
{$R *.dfm}
uses Unit2;
...
procedure TForm1.Button4Click(Sender: TObject);
begin
Frame2.Show;
end;
I got this compiler error:
Undeclared identifier: 'Frame2'
Then I tried to declare it:
Frame2: TFrame2;
Edit:
Further explenation form comment.
Ok I will be precise. I use anwser ardnew Frame2: TFrame; and I get ** access violation** and with out it I get Undeclared identifier: 'Frame2' now I'm more precise?
You did not show the contents of Unit2, so we can only speculate. It sounds like there is no Frame2 global variable declared in Unit2.pas. That would account for the undeclared identifier error. Declare the variable yourself, and instantiate an instance of the TFrame2 class before you can then Show() it, eg:
unit Unit1;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, StdCtrls, ComCtrls;
...
implementation
{$R *.dfm}
uses
Unit2;
var
Frame2: TFrame2 = nil;
...
procedure TForm1.Button4Click(Sender: TObject);
begin
if not Assigned(Frame2) then
begin
Frame2 := TFrame2.Create(Self);
Frame2.Parent := Self;
end;
Frame2.Show;
end;
You need to create the frame somehow, you can't access it without.
This example presumes you are creating 2 different "TFrame2" instances at once just temporarily, then closing (and freeing) them when done (in a try..finally block). There are many other ways of creating and freeing, but the general concept is if you create it, you have to free it...
procedure TForm1.Button4Click(Sender: TObject);
var
F1, F2: TFrame2;
begin
//You have to first create the instances of "TFrame2"...
F1:= TFrame2.Create(Self);
F2:= TFrame2.Create(Self);
try
F1.Left:= 0;
F2.Left:= Self.Width - F2.Width;
F1.Parent := Self;
F2.Parent := Self;
F1.Show;
F2.Show;
Application.ProcessMessages;
ShowMessage('There should be 2 instances of "TFrame2" showing on your main form');
finally
//And you have to free them when you're done...
F1.Free;
F2.Free;
end;
end;
Or if this "TFrame2" is elsewhere...
procedure TForm1.Create(Sender: TObject);
begin
//Create it first
Frame2:= TFrame2.Create(Self);
Frame2.Parent := Self;
Frame2.Left:= 0;
Frame2.Show;
end;
procedure TForm1.Destroy(Sender: TObject);
begin
if assigned(Frame2) then begin
Frame2.Free;
Frame2:= nil;
end;
end;
Be careful though, because you might already be creating this "TFrame2"... Go to Project > Options > Forms and look to see if "Frame2" is auto-create or not.
I guess it should be declared as
Frame2: TFrame;
Using: Delphi 2010 and the JEDI Windows API and JWSCL
I am trying to assign the Logon As A Service privilege to a user using LsaAddAccountRights function but it does not work ie. after the function returns, checking in Group Policy Editor shows that the user still does not have the above mentioned privilege.
I'm running the application on Windows XP.
Would be glad if someone could point out what is wrong in my code:
unit Unit1;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, StdCtrls, JwaWindows, JwsclSid;
type
TForm1 = class(TForm)
Button1: TButton;
procedure Button1Click(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
end;
var
Form1: TForm1;
implementation
{$R *.dfm}
function AddPrivilegeToAccount(AAccountName, APrivilege: String): DWORD;
var
lStatus: TNTStatus;
lObjectAttributes: TLsaObjectAttributes;
lPolicyHandle: TLsaHandle;
lPrivilege: TLsaUnicodeString;
lSid: PSID;
lSidLen: DWORD;
lTmpDomain: String;
lTmpDomainLen: DWORD;
lTmpSidNameUse: TSidNameUse;
lPrivilegeWStr: String;
begin
ZeroMemory(#lObjectAttributes, SizeOf(lObjectAttributes));
lStatus := LsaOpenPolicy(nil, lObjectAttributes, POLICY_LOOKUP_NAMES, lPolicyHandle);
if lStatus <> STATUS_SUCCESS then begin
Result := LsaNtStatusToWinError(lStatus);
Exit;
end;
try
lTmpDomainLen := DNLEN; // In 'clear code' this should be get by LookupAccountName
SetLength(lTmpDomain, lTmpDomainLen);
lSidLen := SECURITY_MAX_SID_SIZE;
GetMem(lSid, lSidLen);
try
if LookupAccountName(nil, PChar(AAccountName), lSid, lSidLen, PChar(lTmpDomain),
lTmpDomainLen, lTmpSidNameUse) then begin
lPrivilegeWStr := APrivilege;
lPrivilege.Buffer := PChar(lPrivilegeWStr);
lPrivilege.Length := Length(lPrivilegeWStr) * SizeOf(Char);
lPrivilege.MaximumLength := lPrivilege.Length;
lStatus := LsaAddAccountRights(lPolicyHandle, lSid, #lPrivilege, 1);
Result := LsaNtStatusToWinError(lStatus);
end
else
Result := GetLastError;
finally
FreeMem(lSid);
end;
finally
LsaClose(lPolicyHandle);
end;
end;
procedure TForm1.Button1Click(Sender: TObject);
begin
AddPrivilegeToAccount('Sam', 'SeServiceLogonRight');
end;
end.
Thanks in advance.
To be able to use LsaAddAccountRights you should open policy handle with additional POLICY_CREATE_ACCOUNT flag (POLICY_CREATE_ACCOUNT | POLICY_LOOKUP_NAMES) in LsaOpenPolicy or use MAXIMUM_ALLOWED instead of both flags.