How can you test to see if a windows group is already setup? Can you somehow use the response from:
Exec('net.exe', 'localgroup', '', SW_SHOW, ewWaitUntilTerminated, Result);
which will list the groups to the command prompt? (I know that Result is an error code where 0 is success, just to be clear.)
Is there a way? Is there more than one way?
I haven't tested this, or had time to work out the exact process or syntax, but you could possibly use command redirection (see https://technet.microsoft.com/en-us/library/bb490982.aspx), using net localgroup as you suggested, to find the group name and output it to a text file. You could then read it into Inno Setup using LoadStringFromFile, then all you would need to do is compare the two strings.
So, having looked at this in more detail, to see if the Administrators group exists you could use:
[Run]
Filename: "{cmd}"; Parameters: "/c ""net localgroup | find /i ""Administrators"" > ""{tmp}\groupresult.txt"""""; StatusMsg: "Querying user groups..."; Flags: runhidden
[Code]
var
strGroupResult: String;
begin
LoadStringFromFile(ExpandConstant('{tmp}\groupresult.txt'), strGroupResult);
if strGroupResult = '*Administrators' then
begin
//Code to execute if group exists
...
end;
DeleteFile(ExpandConstant('{tmp}\groupresult.txt'));
end;
Note that net localgroup returns an asterisk (*) in front of the group name, so the string comparison needs to also include the asterisk.
If you need to do this before the [Run] section, you can write a similar Exec line in the [Code] section to run the same process.
Related
I'm trying to create a setup with Inno Setup.
In the run section, I want to launch a bat with two parameters but in the first parameters, there is a path file and a space is potentially inside. How to avoid the problem?
I found this topic in vain...
Inno Setup, spaces and double quote in [Run]
Thanks for your help.
[Run]
Filename: {code:GetDirSQL|0}\installSQL\createBase\launchCreateParam.bat;
Parameters: {code:GetDirSQL|0}\installSQL\createBase {code:GetDossier|0};
Description: {cm:LaunchProgram,LumisTraiteur};
StatusMsg: Création de la base de données...; Check: instalDossier
See Parameters in Sections:
Parameters: """{code:GetDirSQL|0}\installSQL\createBase"" ""{code:GetDossier|0}"""
It may get more complicated, when the application, you are running, treats the quotes and spaces specially:
How to handle path with spaces in Inno Setup?
Inno Setup, spaces and double quote in [Run]
How to append AppVersion to setup.exe file?
In other words, how to make output filename as sample-setup-1.4.2.0.exe?
[Setup]
AppName= {#GetStringFileInfo("Sample.exe", "ProductName")}
AppVersion= {#GetStringFileInfo("Sample.exe", "FileVersion")}
OutputBaseFilename=setup
Two valuable lessons are;
Lesson 1: Inline function should be used as {#FunctionName(...)}
Lesson 2: variables in [Setup] field are called by using SetupSetting function.
With above information, we can make sample-setup-1.0.0.0 as below;
OutputBaseFilename=sample-setup-{#SetupSetting("AppVersion")}
Likewise, we can append datetime;
OutputBaseFilename=sample-setup-{#SetupSetting("AppVersion") + GetDateTimeString('dd-mm-yyyy hh-nn-ss', '-', ':')}
I'm trying to use the silent mode of InnoSetup. I have to update the "License" key value if specified in silent mode installation.
This is how I configured the Inno file:
[INI]
Filename: define.ini; Section: "General"; Key: "License"; String: "{param:LICENSE}"; Check: WizardSilent;
It works good when the user installs the application this way:
setup.exe /SILENT /LICENSE=licensekey
The problem occurs when I want to skip the /LICENSE parameter (since it's already configured and I don't want to change this. For instance: when installing a patch kit).
Using the following way overrides "License" key on INI file and just leave it empty:
setup.exe /SILENT
How to avoid this? I just want NOT to change the "License" value in this case.
Thank you!
I can think of two options since you are using a custom parameter. Either you can define a default value for the {param} constant to be the existing value read from the same INI file:
[INI]
...; String: "{param:LICENSE|{ini:define.ini,General,License|}}"
The above statement writes to the given INI file value passed by the LICENSE command line parameter, or if it's not specified reads the same value from the same file. So it actually overwrites the value with the same value if the parameter is not specified, which is not much clean in my view.
Or, you may extend your existing Check parameter by the helper function like follows. This is the way I would prefer, since you just skip processing of the entry instead of overwriting the same value when the parameter is not specified:
[INI]
...; Check: WizardSilent and CmdLineParamExists('/LICENSE')
[Code]
function CmdLineParamExists(const Value: string): Boolean;
var
I: Integer;
begin
Result := False;
for I := 1 to ParamCount do
if CompareText(ParamStr(I), Value) = 0 then
begin
Result := True;
Exit;
end;
end;
In Inno Setup one can access texts from the language files e.g. the following way (where CreateDesktopIcon is the text entry to look for):
[Tasks]
Name: "TaskDesktopIcon"; Description: "{cm:CreateDesktopIcon}"; Flags:
My question is how to access texts from the language files from the code section of the Inno Setup script?
I have tried the following, but the compiler will not accept the syntax:
[code]
var
pageAutoLogon: TWizardPage;
procedure CreateAutoLogonPage;
begin
pageAutoLogon := CreateCustomPage(wpSelectTasks, "{cm:AutoLogonCredentialsTitle}", "{cm:AutoLogonCredentialsDescription}");
...
Any help is appreciated!
You need to call ExpandConstant (or ExpandConstantEx) function to evaluate a constant in script code. Like this way for instance:
procedure CreateAutoLogonPage;
begin
pageAutoLogon := CreateCustomPage(wpSelectTasks, ExpandConstant('{cm:AutoLogonCredentialsTitle}'), ExpandConstant('{cm:AutoLogonCredentialsDescription}'));
...
end;
You can use the CustomMessage() function to retrieve the values from the [CustomMessages] section.
pageAutoLogon := CreateCustomPage(wpSelectTasks, CustomMessage('AutoLogonCredentialsTitle'), CustomMessage('AutoLogonCredentialsDescription'));
For normal [Messages], you can use the SetupMessage() with one of the enum values.
I have a delphi (Win32) web application that can run either as a CGI app, ISAPI or Apache DLL. I want to be able to generate a unique filename prefix (unique for all current requests at a given moment), and figure that the best way to do this would be to use processID (to handle CGI mode) as well as threadID (to handle dll mode).
How would I get a unique Process ID and Thread ID in Delphi?
Will these be unique in a Multi-Core/Multi-Processor situation (on a single webserver machine)?
Edit: please note that I was advised against this approach, and thus the accepted answer uses a different method to generate temporary filenames
you have many good ideas presented here.
Does it also create an empty file to "get a lock on" the name?
no; i believe we rely on Windows to ensure the same temp file name is never given twice on the same computer since boot time.
is there any chance of a clash if there is a split second delay between generating the name and creating the file (if I need to create the file myself).
no; that'd be a pretty bad thing.
here's a routine i've been using for getting a temp file.
function GetTemporaryFileName:string;
var
Path, FileName: array[0..MAX_PATH] of Char;
begin
Win32Check(GetTempPath(MAX_PATH, Path) <> 0);
Win32Check(GetTempFileName(Path, '~EX', 0, FileName) <> 0);
Result:=String(Filename);
end;
you could instead use FileGetTempName( ) from JclFileUtils.pas in JCL.
Windows provides functionality for creating guaranteed unique file names. No need for creating your own:
Here's a Delphi wrapper around that functionality:
function CreateTempFileName(aPrefix: string): string;
var
Buf: array[0..MAX_PATH] of Char;
Temp: array[0..MAX_PATH] of Char;
begin
GetTempPath(MAX_PATH, Buf);
if GetTempFilename(Buf, PChar(aPrefix), 0, Temp) = 0 then
begin
raise Exception.CreateFmt(sWin32Error, [GetLastError, SysErrorMessage(GetLastError)]);
end;
Result := string(Temp);
end;
Could you not use a GUID instead?
Edit: Should have said first time around, check out the following two functions
CreateGuid
GuidToString
Process IDs are not guaranteed to be unique on windows. They are certainly unique for the life of the process, but once a process dies its id can be immediately reused. I am not certain about ThreadIDs. If these are temporary files you could use something equivalent to tmpfile or tmpnam (C functions, but I assume Delphi has an equivalent).
As Jamie posted a GUID may be better.
1) How to get a unique Process ID & ThreadID in Delphi:
Answer:
NOTE: Ensure to add 'windows' to your uses clause in the implementation section
NOTE: Cardinals are unsigned 32-bit integers ranging from 0 to 4294967295
implementation
uses Windows;
procedure MySolution();
var
myThreadID:Cardinal;
myProcessID:Cardinal;
begin
myThreadID := windows.GetCurrentThreadID;
myProcessID := windows.GetCurrentProcessId;
end;
2) Will these be unique in a Multi-Core/Multi-Processor situation (on a single webserver machine)?
Answer: Yes.
The process identifier is valid from
the time the process is created until
the process has been terminated and is
unique throughout the system. (Not
unique to processor)
Until the thread terminates, the
thread identifier uniquely identifies
the thread throughout the system.
(Again, system wide, not unique to
processor)
Better than either of of those options, you should be using the system function _tempnam. It returns a random file name in the directory for a file that does not exist. If you want to, you can supply a prefix to _tempnam so that the file you create is recognizably yours. If you are providing a unique prefix, there is shouldn't be any worry about someone opening your file. There is another solution, however.
_tempnam is only good if you want to put the file into an arbitrary directory. If you don't care that the directory is the system temporary directory, use tempfile_s instead. It will also create the file for you, so no worry about race conditions... Errors will only occur if you try to open more temp files than the system can handle. The big downside to tempfile_s is that the file will disappear once you fclose it.
EDIT: I've gotten a downvote because this is a C function. You have access to the C runtime by importing them into delphi. Have a look at some examples with msvcrt.dll here.
function _tempnam(const Dir: PChar, const Prefix: PChar): PChar; cdecl;
external 'msvcrt.dll' name '_tempnam';
Others all gave you a good and reasonable ideas, but still - if you're using files for temporary storage and if those files will always be created first (it doesn't matter if there is a leftover file with a same name already on the disk as you'll overwrite it anyway) then processid_threadid approach is completely valid.
Use GetCurrentProcessID and GetCurrentThreadID Win32 calls to access those two IDs.