please, how to code start-up behaviour in Delphi6? - delphi

I have an application, and I put a shortcut to it inside win+startup folder, and all is ok.
Now I wish to change this approach, by coding it, and so I have used the code listed at the bottom of this post.
The code inputs a key inside HKLM, but there is a windows error when system starts:
Access violation at address 004815EB in module 'ap1.exe'. Read of address 00000000.
This error is similar on 3 different computers, running win xp or win 7.
procedure SetAutoStart(AppName, AppTitle: string; bRegister: Boolean);
const RegKey = '\Software\Microsoft\Windows\CurrentVersion\Run'; // Run or
RunOnce
var Registry: TRegistry;
begin
Registry := TRegistry.Create;
try Registry.RootKey := HKEY_LOCAL_MACHINE;
if Registry.OpenKey(RegKey, False)
then begin
if bRegister = False then Registry.DeleteValue(AppTitle)
else Registry.WriteString(AppTitle,
AppName);
end;
finally Registry.Free;
end;
end;

The error is raised by the program that is executed on startup. It has nothing at all to do with the code in the question. You can verify that the code in the question behaves as expected by checking the registry entries using the Registry Editor.
You will need to debug the program that is being executed at startup. You won't be able to attach an interactive debugger. Instead you will need to use trace debugging.

Related

TMemIniFile.Create hanging when called in ServiceStart

In a service running under system account the code below hangs in the TMemIniFile.Create without errors.
If we replace it with TIniFile, it works fine.
It's a Delphi Tokyo 10.2.3 Win32 app running under Windows Server 2012R2. There's no concurrent access to the INI file.
This is the first time (first client) we see this, it has been running fine on many machines.
I have no idea what to look for further. Any ideas?
It 'works' now because we switched to TIniFile, but I'd like to find the cause. From other posts I read here, TINIfile seems to be more finicky than TMemINIfile, my situation is the reverse.
There are no special characters in the INI file and it is created with an ASCII editor.
// This is set in the ServiceCreate:
FIniFileNaam := ChangeFileExt(ParamStr(0),'.INI');
// This is called from the ServiceStart:
procedure TTimetellServiceBase.LeesINI;
var lIniFile : TMemIniFile;
begin
LogMessage(FIniFileNaam, EVENTLOG_INFORMATION_TYPE, cCatInfo, cReadINI); // Logs to event log, we see this
FStartDir := ExtractFilePath(ParamStr(0));
if assigned(FLaunchThreadLijst) then FreeAndNil(FLaunchThreadLijst);
FLaunchThreadLijst := TStringList.Create;
try
if FileExists(FIniFileNaam) then
begin
// Lees waarden uit INI file
lIniFile := TMemIniFile.Create(FIniFileNaam); // This is the call that hangs. The service is unresponsive now.
try
FLaunchThreadLijst.CommaText := lIniFile.ReadString(INISECTIE_SERVICETASKS,'RunIniFiles','');
FMaxTaskDuration := lIniFile.ReadInteger(INISECTIE_SERVICETASKS,'MaxTaskDuration',FMaxTaskDuration);
finally
FreeAndNil(lIniFile);
end;
end;
finally
if (FLaunchThreadLijst.Count = 0) and FileExists(FStartDir + FExeName) then
FLaunchThreadLijst.Add(SDEFAULTTHREADNAME);
LogMessage(Format('FLaunchThreadLijst.CommaText: %s (%d items)',[FLaunchThreadLijst.CommaText,FLaunchThreadLijst.Count]), EVENTLOG_INFORMATION_TYPE, cCatInfo, cLaunchList);
end;
end;
FWIW, INI file contents:
[TASKMANAGER]
RunIniFiles=TTTasks.ini
MaxTaskDuration=2
RestartIniFiles=
KillIniFiles=

Can't read key from registry

I heared that Windows creates a unique key for a PC which is called "MachineID". I found two locations in my registry. Only the location "HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Cryptography" should be correct. I try to read the value by this function:
Function GetMaschineID:string;
var
Reg : TRegistry;
//HKEY_CURRENT_USER\Software\Microsoft\MSNMessenger = {dd239a44-fa0d-43ff-a51c-5561d3e39de3}
//HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Cryptography = a06b0ee0-b639-4f55-9972-146776bcd5e4
begin
Reg := TRegistry.Create(KEY_READ);
try
Reg.Rootkey:=HKEY_LOCAL_MACHINE; //Hauptschlüssel
//Reg.RootKey:=HKEY_CURRENT_USER;
if Reg.OpenKey('SOFTWARE\Microsoft\Cryptography\',false) then //Unterschlüssel öffnen
//if Reg.OpenKey('Software\Microsoft\MSNMessenger\',false) then //Unterschlüssel öffnen
begin
Result:=Reg.ReadString('MachineGuid');
end;
finally
Reg.Free;
end;
end;
This version results in an empty string; you see as comment the result from the registry. The second version for "hkey_Current_user" brings the expected string result.
What is wrong with my code or are parts of the registry read protected?
Possible explanation 1
For HKLM you are subject to registry redirection. You have a 32 bit process and are trying to read a key from the 64 bit view of the registry. By default, your 32 bit process is redirected to the 32 bit view, which (implementation detail) lives under Wow6432Node.
Use the KEY_WOW64_64KEY access flag to read from the 64 bit view. As detailed here: How can I read 64-bit registry key from a 32-bit process?
Possible explanation 2
Your call to OpenKey fails for keys under HKLM because you are requesting write access and standard user does not have write access to HKLM. Use OpenKeyReadOnly instead.
Other advice
At the very least you should have debugged this a bit more. Does the call to Reg.OpenKey succeed or fail? You should have debugged enough to know that. Perhaps you did but did not say. If Reg.OpenKey failed then explanation 2 is most likely. Even then, you may subsequently suffer from the other problem.
Note also that your function does not assign to the function result variable, or raise an error, if the call to Reg.OpenKey fails. I would expect that the compiler would have warned you about that.
procedure TForm1.Button1Click(Sender: TObject);
var
registry: TRegistry;
begin
Registry := TRegistry.Create(KEY_READ OR KEY_WOW64_64KEY);
try
registry.RootKey := HKEY_LOCAL_MACHINE;
if (registry.KeyExists('\SOFTWARE\Microsoft\Microsoft SQL Server\Instance Names\SQL')) and
(registry.OpenKeyReadOnly('\SOFTWARE\Microsoft\Microsoft SQL Server\Instance Names\SQL')) then
begin
showmessage(registry.ReadString('SQLEXPRESS'));
registry.CloseKey;
end
else showmessage('failed');
finally
registry.Free;
end;
end;

run program with administrator access to write to registery

i tried to use this tutorial to work:
https://stackoverflow.com/a/14710803/1149172
first create a file with name uac.manifest with provided content
then create uac.rc width content of 1 24 "uac.manifest"
then copied the files to program folder (in delphi program sources folder) and changed the project file like this:
program Project4;
{.$R 'uac.res' 'uac.rc'} // UAC only
uses
Vcl.Forms,
Unit6 in 'Unit6.pas' {Form6};
{$R *.res}
begin
Application.Initialize;
Application.MainFormOnTaskbar := True;
Application.CreateForm(TForm6, Form6);
Application.Run;
end.
at last i put my registery code at the form
procedure AddEntryToRegistry;
var key: string;
Reg: TRegIniFile;
begin
key := 'Software\Microsoft\Windows\CurrentVersion\Run';
Reg := TRegIniFile.Create;
try
Reg.RootKey:=HKEY_LOCAL_MACHINE;
Reg.CreateKey(Key);
if Reg.OpenKey(Key,False) then Reg.WriteString(key, 'MyApp', 'c:MyApp.exe');
finally
Reg.Free;
end;
end;
every thing seems ok and i dont got any runtime rror
but after clicking the button nothing happened (dont shoe any error and dont add the key to registery)!!!
where is wrong with my works!?
my delphi is xe5 and working on win 8ul
Firstly, I'm going to take it as read that your program is running elevated. If that's not happening then there's no need to look at any code. You did not say otherwise, so let us proceed under the assumption that you are succeeding to elevate.
You are suffering from the registry redirector. Your 32 bit process is running on a 64 bit machine. And so HKLM\Software is redirected to the 32 bit view, stored at HKLM\Software\Wow6432Node.
You can, if you need, use the KEY_WOW64_64KEY flag to access the 64 bit view. Combine this with the flags in the registry object's Access property.
However, the system reads keys from both 32 and 64 bit views of the registry when enumerating the startup programs so you do not need to do this. For the sake of simplicity and predictability I would leave your 32 bit program writing to the 32 bit view.
Your call to CreateKey should be removed. The system creates that key and you can safely assume it exists. And you should not use TRegIniFile. Use TRegistry instead.
Your code should look like this:
procedure AddEntryToRegistry;
var
Reg: TRegistry;
begin
Reg := TRegistry.Create(KEY_ALL_ACCESS);
try
Reg.RootKey := HKEY_LOCAL_MACHINE;
if Reg.OpenKey('Software\Microsoft\Windows\CurrentVersion\Run', False) then
Reg.WriteString('MyApp', 'C:\MyApp.exe');
finally
Reg.Free;
end;
end;
Should you feel that you need to write to the 64 bit view then it is done like this:
procedure AddEntryToRegistry;
var
Reg: TRegistry;
begin
Reg := TRegistry.Create;
try
Reg.RootKey := HKEY_LOCAL_MACHINE;
Reg.Access := KEY_ALL_ACCESS or KEY_WOW64_64KEY;
if Reg.OpenKey('Software\Microsoft\Windows\CurrentVersion\Run', False) then
Reg.WriteString('MyApp', 'C:\MyApp.exe');
finally
Reg.Free;
end;
end;
Judging by your comments, it looks like you might be failing to elevate. The lack of a UAC dialog when your program starts is the tell-tale sign that this is happening. Once your program starts without a UAC dialog, there's no point continuing. You will not write to HKLM without elevation.
Regarding your manifest, you can link only one. So if you want to specify a manifest other than the Enable runtime themes manifest that the IDE can provide, you need to do it all yourself.
In the project options specify that you want to use a custom manifest. That's under Project | Options | Application | Runtime themes. Set the drop down to Use custom manifest. And then supply the file name of your manifest. You'll want to add in the comctl32 v6 part to make sure that you get runtime themes. But don't worry about that now. Just concentrate on getting elevation sorted, and the registry code working.
You are also silently ignoring any errors which does make things a little harder to debug. If it so happens that you are not elevating, then running the code is rather pointless. You know it must fail. But you could at least make it easier to diagnose the problem by throwing an error if OpenKey fails.
procedure AddEntryToRegistry;
var
Reg: TRegistry;
begin
Reg := TRegistry.Create(KEY_ALL_ACCESS);
try
Reg.RootKey := HKEY_LOCAL_MACHINE;
if not Reg.OpenKey('Software\Microsoft\Windows\CurrentVersion\Run', False) then
raise EMyExceptionClass.Create('Could not open registry key');
Reg.WriteString('MyApp', 'C:\MyApp.exe');
finally
Reg.Free;
end;
end;
One final point to make is that writing to this registry key is an admin task. You should require elevation once only, not every time your application starts. If you are planning to require elevation for your application just for this purpose, then you must re-design. This admin task should be performed outside your main application. The most natural place is inside your install program which users will accept requiring elevation.
You are saving your app path to the Run key of the HKEY_LOCAL_MACHINE hive. You should be using the HKEY_CURRENT_USER hive instead, then you will not need to use UAC elevation anymore (unless your app is doing other things that require admin rights).

ShellExecute not working from IDE but works otherwise

I want to create and then open a txt file using the ShellExecute command.
I have used this code for years with Delphi 7 and it worked:
function Execute(CONST ExeName, Parameters: string): Boolean;
begin
Result:= ShellExecute(0, 'open', PChar(ExeName), PChar(Parameters), nil, SW_SHOWNORMAL)> 32;
end;
Now, I switched to Windows 7 and the code is not working anymore when it runs from IDE. Delphi shows the CPU window with the caption "CPU-Process unknown (2352)". I close the CU windows and everything works fine until I close the application, when Delphi shows the CPU window one more time.
If I run the app from outside IDE, it works fine.
Looks like the debugger has something to say to me, but I don't know what.
Sounds to me like you have the "debug spawned processes" option turned on. When that's enabled, the debugger interrupts the new process at the earliest possible time. Press the "run" button to let it continue running.
You can confirm this hypothesis the next time you debug your program. Compare the process ID (2352, in your example) with the list of processes shown by Task Manager. Which process in that list matches the process ID reported by the debugger?
This is not the answer for your question (I vote for Rob Kennedy & Chris Thornton), but you can write your routine in a more compact way:
function Executa(const ExeName, Parameters: string): Boolean;
begin
Result :=
(ShellExecute(0, 'open', PChar(ExeName), Pointer(Parameters), nil, SW_SHOWNORMAL) > 32);
end;
Note Pointer() instead of PChar() for 4th argument. This is a documented behaviour of PChar/Pointer casts (see help).
I had a problem yesterday with the debugger crashing my application, but running it outside the IDE it would run fine. I was using packages in my development.
I used process explorer to verify I found I was loading a copy from another location than expected. I had two copies of the same BPL floating around. Once I removed the one I was not compiling I was fine.
Applying that to this problem, I would check to make sure you don't have any copies of compiled code that includes: .DCU, .DCP, .BPL, .EXE around. Then I would also make sure you you can ctrl-click on "ShellExecute" to and see the declaration. You may have your library path setup in a way that it can't find the source.
Shot in the dark here, but try running the IDE as administrator, and then not as administrator. That may be a factor. Some users make a shortcut with the administrator option set, so that the auto-update runs successfully. So you may be running the IDE as admin, if you've done that.
Same by me , i solved it by replacing ShellExecute with following:
function TformMain.CreateProcessSimple(
sExecutableFilePath : string )
: string;
function GetExeByExtension(sExt : string) : string;
var
sExtDesc:string;
begin
with TRegistry.Create do
begin
try
RootKey:=HKEY_CLASSES_ROOT;
if OpenKeyReadOnly(sExt) then
begin
sExtDesc:=ReadString('') ;
CloseKey;
end;
if sExtDesc <>'' then
begin
if OpenKeyReadOnly(sExtDesc + '\Shell\Open\Command') then
begin
Result:= ReadString('') ;
end
end;
finally
Free;
end;
end;
end;
var
pi: TProcessInformation;
si: TStartupInfo;
fapp: string;
begin
fapp:=GetExeByExtension(ExtractFileExt(sExecutableFilePath));
FillMemory( #si, sizeof( si ), 0 );
si.cb := sizeof( si );
if Pos('%1',fApp)>0 then begin
sExecutableFilePath:=StringReplace(fapp,'%1',sExecutableFilePath,[rfReplaceAll]);
end else begin
sExecutableFilePath:=fApp+' "'+sExecutableFilePath+'"';
end;
CreateProcess(
Nil,
// path to the executable file:
PChar( sExecutableFilePath ),
Nil, Nil, False,
NORMAL_PRIORITY_CLASS, Nil, Nil,
si, pi );
// "after calling code" such as
// the code to wait until the
// process is done should go here
CloseHandle( pi.hProcess );
CloseHandle( pi.hThread );
end;
ShellExecuteW solve my problems (XE2/Win7/32bit) with "debug spawned processes" option turned off
:)
mybe because strings and pchar are wide pointer from 2010

fmShareDenyWrite mode doesn't appear to work

I'm using a TFileSteam to open a log file. I would like to be able to read through this log file from other processes. I thought the fmShareDenyWrite mode would allow this.
However if I try to open the file from other processes, I get an error. For example, if I try and type the file from the command line, I get "the process can not access the file because it is being used by another process".
Here is the file initialization code:
if FileExists(AutoLogFileName) then
_ActivityLogStream := TFileStream.Create(AutoLogFileName,
fmOpenReadWrite or fmShareDenyWrite)
else
_ActivityLogStream := TFileStream.Create(AutoLogFileName,
fmCreate or fmShareDenyWrite);
NOTE:
I am using Delphi version 6.
Don't know whether this was already a bug in D6, but that is a distinct possibility. There is a QC report on this reported against D2007: QC 65767: http://qc.embarcadero.com/wc/qcmain.aspx?d=65767. This report is now closed, as it was resolved in D2010 (14.0.3467.22472 to be exact).
Update (prompted by Gabr's comment):
You can create your own TFileStream descendant that does honor the mode. Just override the Create(const AFileName: string; Mode: Word; Rights: Cardinal) constructor (there are two overloaded constructors) and handle the mode parameter yourself. Copy the code from the original constructor and change the
if Mode = fmCreate then
begin
inherited Create(FileCreate(AFileName, Rights));
to
if (Mode and fmCreate = fmCreate) then
begin
myMode := Mode and $FF;
if myMode = $FF then
myMode := fmShareExclusive;
inherited Create(FileCreate(AFileName, myMode, Rights));
where myMode is a local var of type Word.
mfCreate mode does not behave/work correctly with any share attribute. To work around, you must create file handle yourself and pass it to the constructor
Cheer

Resources