Discussion:
Installing an application on Vista/Win7 with VirtualStore ProgramData
(too old to reply)
P E Schoen
2010-12-14 04:37:34 UTC
Permalink
I have an application written in Delphi 4, originally on a Windows XP
platform. As it matured it needed an installation script which I developed
using Inno Setup, and for XP it used the proper "Program Files" and "Program
Data" folders. When I first installed it on Vista I had to deal with the
different location for data, which was solved by using the proper system
constants. But I found that any files that were created or changed by the
application were "virtualized" in a separate Program Data folder under the
user ID.

Now I am testing it on a Win7 platform, and the virtualization is similar.
It seemed innocuous enough because the required files were properly
installed in the Program Data folder, but when the program updated the
database files, they were virtualized and no longer where I had expected
them to be. And when I made changes to the database for new installations,
the program still used the unchanged files in the virtual folder.

The way I understand this is that the default user permissions, even for a
user with administrator privileges, are such that changing and writing to a
"protected" directory such as Program Data will automatically virtualize the
files. The administrator may change the user permissions for the directory,
and then the virtualization will not occur. However, if a file has already
been created in the virtual directory, that is what will be used unless it
is renamed or moved to the normal folder.

I'm trying to determine what is the best way to handle new installations
which may or may not be used on systems that have been used for a while in
the virtualized environment. I have found a utility called SetACL.exe, which
can programmatically change the permissions of the folder for the current
user. This seems to work well, but I'm trying to figure out the best way to
do this so it will work on XP, Vista, and Win7. Here is what I have for the
Inno setup script:

Filename: "{tmp}\SetACL.exe"; Parameters: "-on
{commonappdata}\Ortmaster -ot file
-actn ace -ace ""n:{username};p:change,write""
-log ""{commonappdata}\Ortmaster\setacl.log"" "

[code]
function MyProgCheck: Boolean;
var VirtualFileFolder: String;
var VirtualFileName: String;
var username: String;
begin
username := GetUserNameString();
VirtualFileFolder := 'C:\Users\'+ username+
'\AppData\Local\VirtualStore\ProgramData\Ortmaster\';
VirtualFilename := VirtualFileFolder +'Ortres.dbf';
If fileexists(VirtualFilename) then begin //1
if MsgBox('MyProg:' #13#13 'Do you want to rename virtual file '
+ VirtualFileName + '?', mbConfirmation, MB_YESNO)
= idYes then begin //2
if renamefile( VirtualFilename, VirtualFileFolder+'Ortres.dbk' )
then
result := True
else begin
MsgBox( 'File Rename Error', mbInformation, MB_OK );
result := False; end;
end //-2
else begin
MsgBox( 'File not found: ' + VirtualFileName, mbInformation,
MB_OK );
result := False;
end;
end;
end;

I will need to check for other files as well. This is not a final solution,
and I will probably move the file to the usual Program Data folder rather
than rename it. But then I will also need to make sure that no data is lost.
I have some ideas, but I was just hoping someone may have some experience
with this and offer some suggestions.

Thanks,

Paul
Mikey
2010-12-14 23:52:53 UTC
Permalink
I have an application written in Delphi 4, originally on a Windows XP platform. As it
matured it needed an installation script which I developed using Inno Setup, and for
XP it used the proper "Program Files" and "Program Data" folders. When I first
installed it on Vista I had to deal with the different location for data, which was
solved by using the proper system constants. But I found that any files that were
created or changed by the application were "virtualized" in a separate Program Data
folder under the user ID.
Now I am testing it on a Win7 platform, and the virtualization is similar. It seemed
innocuous enough because the required files were properly installed in the Program
Data folder, but when the program updated the database files, they were virtualized
and no longer where I had expected them to be. And when I made changes to the
database for new installations, the program still used the unchanged files in the
virtual folder.
The way I understand this is that the default user permissions, even for a user with
administrator privileges, are such that changing and writing to a "protected"
directory such as Program Data will automatically virtualize the files. The
administrator may change the user permissions for the directory, and then the
virtualization will not occur. However, if a file has already been created in the
virtual directory, that is what will be used unless it is renamed or moved to the
normal folder.
I'm trying to determine what is the best way to handle new installations which may or
may not be used on systems that have been used for a while in the virtualized
environment. I have found a utility called SetACL.exe, which can programmatically
change the permissions of the folder for the current user. This seems to work well,
but I'm trying to figure out the best way to do this so it will work on XP, Vista,
Filename: "{tmp}\SetACL.exe"; Parameters: "-on
{commonappdata}\Ortmaster -ot file
-actn ace -ace ""n:{username};p:change,write""
-log ""{commonappdata}\Ortmaster\setacl.log"" "
[code]
function MyProgCheck: Boolean;
var VirtualFileFolder: String;
var VirtualFileName: String;
var username: String;
begin
username := GetUserNameString();
VirtualFileFolder := 'C:\Users\'+ username+
'\AppData\Local\VirtualStore\ProgramData\Ortmaster\';
VirtualFilename := VirtualFileFolder +'Ortres.dbf';
If fileexists(VirtualFilename) then begin //1
if MsgBox('MyProg:' #13#13 'Do you want to rename virtual file '
+ VirtualFileName + '?', mbConfirmation, MB_YESNO)
= idYes then begin //2
if renamefile( VirtualFilename, VirtualFileFolder+'Ortres.dbk' ) then
result := True
else begin
MsgBox( 'File Rename Error', mbInformation, MB_OK );
result := False; end;
end //-2
else begin
MsgBox( 'File not found: ' + VirtualFileName, mbInformation, MB_OK );
result := False;
end;
end;
end;
I will need to check for other files as well. This is not a final solution, and I
will probably move the file to the usual Program Data folder rather than rename it.
But then I will also need to make sure that no data is lost. I have some ideas, but I
was just hoping someone may have some experience with this and offer some suggestions.
Thanks,
Paul
I suspect you'd have better luck for a correct solution on the Inno newsgroups.
Windows isn't making it easy on us......

Mike
P E Schoen
2010-12-15 02:34:14 UTC
Permalink
Post by Mikey
I suspect you'd have better luck for a correct solution on the Inno
newsgroups. Windows isn't making it easy on us......
I think I will do this in the application itself, rather than the setup
script. The setup script will run the SetACL.exe application to change the
permissions of the installed Application Data folder. In my install setup
script I am copying sample database files OrtRes.dbf, OrtRecl.dbf, and
others which are designed to be edited by the customer, as well as
OrtRes1.dbf, OrtRecl1.dbf, etc, which are blank databases (in case I need to
change the structure in future installs).

When the program is first installed, the sample databases are included,
since they do not exist. In subsequent installs, only the blank database
files are updated.

When the program first runs after a new install, it will see the OrtRes1.dbf
file and will use it to import the existing data into the OrtRes.dbf file
using the new format. This is repeated for other files. Then it will delete
the blank database files so the next time it runs it will skip this step.

The application will also check for virtualization and copy the virtualized
files from their folder and then probably delete them and the virtual store
folder, or maybe just rename them so there will be a backup copy in case
something goes wrong. This is a new version of this application and there
are only a few customers using it, but I need to make sure they don't lose
any of this data. And I'm not sure how well backup software handles the
virtual folder in Vista and Win7.

The explanation above is not how the present implementation works, and
fortunately I think the old data files are preserved. The install script
only updates the files in the actual Program Data folder, while the program
actually uses the virtual folder where the most recent database files are
located (and only if they were changed). However, this is not true for XP
users, and I think that is what most of them are using.

This application actually replaces one that was written in Borland C for an
MSDOS platform, and the hardware has also changed from using the parallel
port for data transfer, to the USB port. There are over 100 units in the
field and eventually most of them will be converted. For more information
see www.ortmaster.com.

If you have any comments or suggestions, they will be most appreciated.
Thanks.

Paul
P E Schoen
2010-12-15 08:16:02 UTC
Permalink
OK, now I'm trying to implement this and I'm getting something really
strange:

type
TUserDataFile = (Cust,Recl,Res,Tech);
var
UserFileName: Array[Cust..Tech] of String =
('OrtCust', 'OrtRecl', 'OrtRes', 'OrtTech');
fn: TUserDataFile;
username: Array[0..100] of char;
usersize: dWord;

For fn := Cust to Tech do
begin //1
if GetUserName( username, usersize ) then begin //2
VirtualFileFolder :=
'C:\Users\'+username+'\AppData\Local\VirtualStore\ProgramData\Ortmaster\';
VirtualFilename := VirtualFileFolder + UserFileName[fn]+'1.dbf';
If fileexists(VirtualFilename) then
DelayMsg( 1500, 'Found: ' + VirtualFilename )
else
DelayMsg( 1500, 'Not Found: ' + VirtualFilename );
end; //-2
end; //-1

I'm only getting three loops instead of four. It starts with fn:=Tech but
the VirtualFilename is OrtRecl1.dbf, then the next loop fn:=Res with
VirtualFilename OrtRes1.dbf (as expected), and then fn:=Recl with
VirtualFilename OrtTech1.dbf, but then it does not continue to fn:=Cust.

I tried using:
For fn := Tech downto Cust do ....

And the first iteration showed fn:= (Out of bound) -3 with VirtualFilename
OrtRes1.dbf The next iterations were -2 for OrtRecl1.dbf and -1 for
OrtCust1.dbf.

I was able to get it to work by using:

TUserDataFile = (Nothing,Cust,Recl,Res,Tech);
UserFileName: Array[Nothing..Tech] of String =
('', 'OrtCust', 'OrtRecl', 'OrtRes', 'OrtTech');
For fn := Nothing to Tech do....

This creates the expected file names from OrtCust to OrtTech.

I have a similar function elsewhere and it also appears to be acting
strangely. I don't know if it's always been that way or if somehow the extra
code I added somehow broke it. It's probably something stupid simple, but I
don't see it. Maybe my brain is frozen. It's down to about 16F again
tonight. Brrr!

TIA,

Paul
P E Schoen
2010-12-15 11:05:54 UTC
Permalink
I found that the similar code I have been using for a while also seems to be
acting strangely. This is what I have:

type TReclCurveFieldName = (fnReclType, fnReclCoil,
fnReclCurve, fnMPU, fnRef, fn2x, fn2x5,fn3x, fn4x,
fn5x, fn6x, fn7x, fn8x, fn10x, fn15x, fnTolMin, fnTolMax);

var
ReclCurveFieldName: Array[fnReclType..fnTolMax] of String =
('ReclType', 'ReclCoil', 'ReclCurve', 'MPU', 'Ref', '2x', '2x5',
'3x', '4x', '5x', '6x', '7x', '8x', '10x', '15x', 'TolMin',
'TolMax');
ReclCurveFieldValue: Array[fn2x..fn15x] of Real =
(2, 2.5, 3, 4, 5, 6, 7, 8, 10, 15);

var i: TReclCurveFieldName;

for i := fnMPU to fnTolMax do begin
S := Format('eb%sCurve%s', [Curve, ReclCurveFieldName[i]]);

The value for "i" starts as "fn15x" and continues to "fnMPU" and then goes
on to "fnReclCurve" and finally "fnReclCoil". If the enumeration goes from 0
to 16 as I understand it to be for the 17 values, somehow it is being
interpreted as starting with 14 and going to 1. What am I doing wrong?

Thanks,

Paul
Hans-Peter Diettrich
2010-12-16 14:42:47 UTC
Permalink
Post by P E Schoen
for i := fnMPU to fnTolMax do begin
S := Format('eb%sCurve%s', [Curve, ReclCurveFieldName[i]]);
The value for "i" starts as "fn15x" and continues to "fnMPU" and then
goes on to "fnReclCurve" and finally "fnReclCoil". If the enumeration
goes from 0 to 16 as I understand it to be for the 17 values, somehow it
is being interpreted as starting with 14 and going to 1. What am I doing
wrong?
You are fooled by compiler optimizations.

DoDi
Hans-Peter Diettrich
2010-12-15 15:58:14 UTC
Permalink
Post by P E Schoen
OK, now I'm trying to implement this and I'm getting something really
type
TUserDataFile = (Cust,Recl,Res,Tech);
var
UserFileName: Array[Cust..Tech] of String =
better: Array[TUserDataFile]
Post by P E Schoen
('OrtCust', 'OrtRecl', 'OrtRes', 'OrtTech');
fn: TUserDataFile;
username: Array[0..100] of char;
usersize: dWord;
For fn := Cust to Tech do
try: low(fn) to high(fn),
or low(TUserDataFile)...,
or low(UserFileName)...

You can add debug code, assigning the low/high values to dummy variables
or writing the values to the console.
Post by P E Schoen
begin //1
if GetUserName( username, usersize ) then begin //2
VirtualFileFolder :=
'C:\Users\'+username+'\AppData\Local\VirtualStore\ProgramData\Ortmaster\';
Shouldn't this be moved before the loop?
Post by P E Schoen
VirtualFilename := VirtualFileFolder + UserFileName[fn]+'1.dbf';
If fileexists(VirtualFilename) then
DelayMsg( 1500, 'Found: ' + VirtualFilename )
else
DelayMsg( 1500, 'Not Found: ' + VirtualFilename );
end; //-2
end; //-1
I'm only getting three loops instead of four. It starts with fn:=Tech
but the VirtualFilename is OrtRecl1.dbf, then the next loop fn:=Res with
VirtualFilename OrtRes1.dbf (as expected), and then fn:=Recl with
VirtualFilename OrtTech1.dbf, but then it does not continue to fn:=Cust.
Sounds like a type or element of the same name lurks somewhere else.
This could be revealed by checking with "Go To Declaration".
Post by P E Schoen
For fn := Tech downto Cust do ....
And the first iteration showed fn:= (Out of bound) -3 with
VirtualFilename OrtRes1.dbf The next iterations were -2 for OrtRecl1.dbf
and -1 for OrtCust1.dbf.
Turn off optimizations for debugging?
Do a full Build?

DoDi
P E Schoen
2010-12-18 18:15:07 UTC
Permalink
Post by Hans-Peter Diettrich
Turn off optimizations for debugging?
Do a full Build?
I finally found what was causing the strange behavior but I don't know why
it was a problem. Briefly, I had the following code for OnActivate of the
form:

type
TUserDataFile = (udfNothing,udfCust,udfRecl,udfRes,udfTech);
var
UserFileName: Array[udfNothing..udfTech] of String =
('', 'OrtCust', 'OrtRecl', 'OrtRes', 'OrtTech');

procedure TfmReclData.FormActivate(Sender: TObject);
var
username: Array[0..100] of char;
usersize: dWord;
udfn: TUserDataFile;
// tblResNew: TTable; // Compiler warning: Might not have been
initialized
begin
For udfn := udfCust to udfTech do
begin //1
if GetUserName( username, usersize ) then begin //2
{other code}
end; //-2
end; //-1
try //2
tblResNew := TTable.Create(self);
{other code to transfer records and update structure of tblRes using
tblResNew}
finally
tblRes.Close;
if tblResNew <> nil then //Here is where the warning was flagged
tblResNew.Free;
end; //-2

Before I commented out the tblResNew variable, the "for" loop variable udfn
would be out of bounds on the first loop, and also the first time
GetUserName() was called, it failed. On subsequent loops it worked normally.
I found this when I put that call outside the loop, and since it failed, the
loop was not executed.

I did not think this should cause a problem, at least not until possibly in
the try..finally section where it is first created. Now I wonder where I can
put the variable declaration without creating havoc with the following code?

Paul
P E Schoen
2010-12-18 19:15:09 UTC
Permalink
I thought I had found what was causing the strange behavior. Now I find that
I need to call GetUserName() twice:

// Removed udfNothing
type
TUserDataFile = (udfCust,udfRecl,udfRes,udfTech);
var
UserFileName: Array[udfCust..udfTech] of String =
( 'OrtCust', 'OrtRecl', 'OrtRes', 'OrtTech');

procedure TfmReclData.FormActivate(Sender: TObject);
var
username: Array[0..100] of char;
usersize: dWord;
udfn: TUserDataFile;
FileRef: Boolean;
tblResNew: TTable;
begin
// tblResNew := TTable.Create(self); //This takes care of the compiler
warning but is not needed
FileRef := GetUserName( username, usersize ); //Evaluates False
FileRef := GetUserName( username, usersize ); //Evaluates True
if FileRef then begin //1
For udfn := udfCust to udfTech do //udfn starts as udfTech
{other code}
end; //-1
try
if tblResNew = nil then
tblResNew := TTable.Create(self);
{other code}
finally
tblRes.Close;
if tblResNew <> nil then //Here is where the warning was flagged
tblResNew.Free;
end;

I wish I knew why this is happening. Could it have anything to do with
running Delphi4 on a Win7 64 bit machine? I don't feel comfortable putting a
bandaid on something just because it seems to work without knowing why. This
is the first time I've tried to use GetUserName().

Thanks...

Paul
P E Schoen
2010-12-18 19:52:39 UTC
Permalink
Duh, I found the problem. The function must have the usersize variable set.

var
username: Array[0..100] of char;
usersize: dWord;
FileRef: Boolean;

usersize := 100;
FileRef := GetUserName( username, usersize );

Sorry if I wasted your time. Thanks.

Paul
Jamie
2010-12-18 23:04:43 UTC
Permalink
Post by P E Schoen
Duh, I found the problem. The function must have the usersize variable set.
var
username: Array[0..100] of char;
usersize: dWord;
FileRef: Boolean;
usersize := 100;
FileRef := GetUserName( username, usersize );
Sorry if I wasted your time. Thanks.
Paul
so what you're saying is, this bug has been there before Win7 ?

:)

Isn't nice how older OS'es seem to hide bugs.

I had a bug in code once that I didn't even know was there
until the app was operated on a w2k machine and up (NT). It
had random issues. It turned out to be a handle I was freeing but
yet still using it for what it was originally intended for and
worked fine..

How the bug showed in W2k was due to how the memory manager changed.
In the past, when ever apps ask for memory, the OS was simply cycle the
memory from the other end of the stack and use the most recent memory
returned to the OS for last.. MS decided that it was more efficient to
give back a recent bit of memory since the chances were good that it'd
still be mapped in for immediate use.. This would cause the value of
returned memory to change almost instantaneous, due to other apps also
requesting memory bits here and there.

Oh well.
Maarten Wiltink
2010-12-20 09:03:05 UTC
Permalink
"P E Schoen" <***@pstech-inc.com> wrote in message news:pi8Po.19397$***@newsfe23.iad...

<Calling GetUserName fails the first time, succeeds after that>
Post by P E Schoen
Duh, I found the problem. The function must have the usersize variable set.
var
username: Array[0..100] of char;
usersize: dWord;
FileRef: Boolean;
usersize := 100;
FileRef := GetUserName( username, usersize );
Without bothering to actually look it up, I think you've found a bit of
classical C anti-buffer-overflow code. I'm guessing your usersize variable
happened to contain 0 the first time around, and is passed by reference.

The function may well respond to that by reporting a specific failure,
'output buffer too small', with the side-effect of changing the buffer
size parameter to the minimum required value.

For guessing what happens the next time around... no points.

It's a feature. Many older WinAPI functions work like that. (Now go read
MSDN.)

Groetjes,
Maarten Wiltink
P E Schoen
2010-12-20 16:26:27 UTC
Permalink
"Maarten Wiltink" wrote in message news:4d0f1bcc$0$81475$***@news.xs4all.nl...

"P E Schoen" <***@pstech-inc.com> wrote in message news:pi8Po.19397$***@newsfe23.iad...

<Calling GetUserName fails the first time, succeeds after that>
Post by P E Schoen
Post by P E Schoen
Duh, I found the problem. The function must have the usersize
variable set.
Post by P E Schoen
var
username: Array[0..100] of char;
usersize: dWord;
FileRef: Boolean;
usersize := 100;
FileRef := GetUserName( username, usersize );
Without bothering to actually look it up, I think you've found
a bit of classical C anti-buffer-overflow code. I'm guessing your
usersize variable happened to contain 0 the first time around, and
is passed by reference.
The function may well respond to that by reporting a specific
failure, 'output buffer too small', with the side-effect of changing
the buffer size parameter to the minimum required value.
For guessing what happens the next time around... no points.
It's a feature. Many older WinAPI functions work like that.
(Now go read MSDN.)
The MSDN reference is:
http://msdn.microsoft.com/en-us/library/ms724432(v=vs.85).aspx?ppud=4

The first time through, the size variable was set to 18, and the buffer
contained some text which started with the character 'v'. I assume this was
just uninitialized memory. The second time, using a buffer size of 18, it
was able to retrieve the user name properly with a size of 9. The lesson
here may be to look carefully at the function specifications. I had sort of
"borrowed" the code from my Inno Setup script and they had a similar
function which undoubtedly called the API function with its own properly
initialized set of parameters. I was in a hurry and somewhat distracted (and
tired) so I was not careful. Saving a few minutes cost me much more in time
and frustration, and then I was looking in the wrong places.

The "for" loop variable seems a bit strange when examined with the mouseover
debugger. Even when it works properly, it seems to start with an "out of
bounds" value one higher than expected, but it is probably decremented at
the time the function is called. The order of execution is reversed but the
resulting file name strings appear in the correct order, so I understand
that is likely the optimization.

Now I am working on a procedure to check for virtualization on startup, and
flagging a warning to reinstall the program. The install script changes the
user permissions for the data folder and also adds the file which signifies
a new installation and causes the startup code to copy the old database into
the new structure, even if it is identical. It may be cleaner to check for
this rather than doing an unnecessary copy. It's no problem when the
database is small, but at some point it may slow down the first run of the
program.

I'm also adding a "backup" procedure which copies the five database files
into a format dbFileName + '-yyyymmdd'. I need to see how the fileSaveDialog
works for a virtualized folder. When write/modify permission has not been
set, any writes to the data folder are performed in the virtualized
location, and I think it also reads from that folder so it appears to be
updating the files in the expected folder. I need to see if the folder path
appears as the actual virtualized folder or the normal path.

Also, I may need to deal with the possibility that a user without
administrative privileges may run the program, in which case the data will
be saved to the virtualized folder for that user name. If multiple users log
on separately, the data will be located in multiple folders, and that is not
what I want. I may need to check for that and either elevate the privileges
for any user, warn about the use of a virtualized folder, or perhaps use a
different unprotected folder for the data.

Thinking........

Paul
Maarten Wiltink
2010-12-21 09:04:21 UTC
Permalink
"P E Schoen" <***@pstech-inc.com> wrote in message news:6tLPo.12263$***@newsfe21.iad...
[...]
Post by P E Schoen
http://msdn.microsoft.com/en-us/library/ms724432(v=vs.85).aspx?ppud=4
The first time through, the size variable was set to 18, and the buffer
contained some text which started with the character 'v'. I assume this
was just uninitialized memory.
Local variables go on the stack. Stackspace is always uninitialised.
Post by P E Schoen
The second time, using a buffer size of
18, it was able to retrieve the user name properly with a size of 9. The
lesson here may be to look carefully at the function specifications. I
had sort of "borrowed" the code from my Inno Setup script and they had a
similar function which undoubtedly called the API function with its own
properly initialized set of parameters.
More 'managed' environments like that often clear memory before use. .Net
does, too.

I'm in two minds about that, maybe even more. Memory could be garbage-
initialised, zeroed, or filled with a guaranteed-unuseable pattern. The
first is more a security risk than anything else. None of the three is
problematic at all as long as programmers know and follow the rules.
None is safe when they don't. I strongly feel that all programmers should
know and follow the rules (and _want_ to know the rules), but I've seen
what companies will hire these days.
Post by P E Schoen
I was in a hurry and somewhat
distracted (and tired) so I was not careful. Saving a few minutes cost
me much more in time and frustration, and then I was looking in the
wrong places.
Ah, but you have learned something.
Post by P E Schoen
The "for" loop variable seems a bit strange when examined with the
mouseover debugger. Even when it works properly, it seems to start
with an "out of bounds" value one higher than expected, but it is
probably decremented at the time the function is called. The order of
execution is reversed but the resulting file name strings appear in
the correct order, so I understand that is likely the optimization.
Yes. Always turn off optimisation for debugging. And it doesn't really
hurt to leave it off in the release version. Another instance of something
being more trouble than it ever earns back.


[...]
Post by P E Schoen
Also, I may need to deal with the possibility that a user without
administrative privileges may run the program, in which case the data
will be saved to the virtualized folder for that user name. If multiple
users log on separately, the data will be located in multiple folders,
and that is not what I want. I may need to check for that and either
elevate the privileges for any user, warn about the use of a virtualized
folder, or perhaps use a different unprotected folder for the data.
I feel there should be an unvirtualised location somewhere that can be
used. It will in any case be in the profiles, not under Program Files.

Perhaps your installer needs to create it. If so, the security model has
well and truly jumped the shark. I'm not ruling it out.

Groetjes,
Maarten Wiltink
P E Schoen
2010-12-22 01:28:25 UTC
Permalink
Post by Maarten Wiltink
I feel there should be an unvirtualised location somewhere that
can be used. It will in any case be in the profiles, not under
Program Files.
Perhaps your installer needs to create it. If so, the security
model has well and truly jumped the shark. I'm not ruling it out.
I'm not familiar with that idiom, and I'm not sure the following applies:
http://en.wikipedia.org/wiki/Jumping_the_shark

I found that my install script uses the following:
Source: Ortmaster.exe; DestDir: {app}; Check: MyProgCheck; Flags:
ignoreversion
Source: Ortmaster.chm; DestDir: {app}; Flags: ignoreversion

So those go into the C:\Program Files\Ortmaster folder. The data files are
installed as follows:
DestDir: {commonappdata}\Ortmaster;

It seems that there is a folder called C:\Users\All Users\Ortmaster which is
also referenced as C:\ProgramData\Ortmaster. Any changes I make in one are
also seen in the other. They are just two different ways of referring to the
same disk space. In Windows Explorer the C:\ProgramData\Ortmaster folder has
a "Compatibility Files" button which redirects to the virtual store folder
of the user. The "All Users" folder does not have that button.

In my application I have used GetSystemFolder(CSIDL_COMMON_APPDATA) +
'\Ortmaster' as the data folder. According to my debug file, this resolves
to 'C:\ProgramData\Ortmaster' rather than 'C:\Users\All Users\Ortmaster' as
would be expected. The "All Users\Ortmaster" folder is shown as shared and
it has modify/write permission for my UserName but not the general Users
group. The 'C:\ProgramData\Ortmaster' folder is shown as Not Shared but
permissions are the same as the All Users folder.

This is getting confusing. If I create a new folder in the root C:\Ortmaster
the default is Not Shared but it has Modify/Write permissions for
"Authenticated Users" but not for "Users". I can share the folder by
changing the properties, but I'm not sure if I must do that if I create it
using the Inno Setup script. I have found some information in the following:

http://technet.microsoft.com/en-us/library/ee679793(WS.10).aspx
http://www.dslreports.com/forum/r23513600-Re-WIN7-Locked-Folders
http://support.microsoft.com/kb/927387
http://social.msdn.microsoft.com/forums/en-us/windowssecurity/thread/39F850E2-1FFC-414A-AECB-6D77B9FF84C6

The more I learn, the less sure I am of what I should do. I long for the
simpler days of XP, or maybe even MSDOS. Perhaps UNIX is the way to go. At
least it seems to have remained essentially stable over many years and the
changes are actual improvements rather than attempts to patch security leaks
with cotton candy.

I'm going to have a beer and spend some quality time with my dog!

Paul
www.muttleydog.com
Maarten Wiltink
2010-12-22 08:39:56 UTC
Permalink
Post by P E Schoen
Post by Maarten Wiltink
I feel there should be an unvirtualised location somewhere that
can be used. It will in any case be in the profiles, not under
Program Files.
Perhaps your installer needs to create it. If so, the security
model has well and truly jumped the shark. I'm not ruling it out.
I'm not familiar with that idiom, and I'm not sure the following
applies: http://en.wikipedia.org/wiki/Jumping_the_shark
Whether it applies without a generous helping of poetic licence may be
open to debate, but rest assured that that is _exactly_ what I meant.

The madness has overtaken the method, they've lost their marbles, gone
totally overboard in a final desperate bid to be seen doing what they
think they're expected to do, and it'll all be downhill from here on.
(Also called 'business as usual.')
Post by P E Schoen
It seems that there is a folder called C:\Users\All Users\Ortmaster
which is also referenced as C:\ProgramData\Ortmaster. Any changes I
make in one are also seen in the other. They are just two different
ways of referring to the same disk space.
It appears that c:\users\all users is a symlink to c:\programdata.

Groetjes,
Maarten Wiltink
P E Schoen
2010-12-22 23:34:02 UTC
Permalink
Post by Maarten Wiltink
Post by Maarten Wiltink
I feel there should be an unvirtualised location somewhere that
can be used. It will in any case be in the profiles, not under
Program Files.
Perhaps your installer needs to create it. If so, the security
model has well and truly jumped the shark. I'm not ruling it out.
Whether it applies without a generous helping of poetic licence
may be > open to debate, but rest assured that that is _exactly_
what I meant.
The madness has overtaken the method, they've lost their marbles,
gone totally overboard in a final desperate bid to be seen doing what
they think they're expected to do, and it'll all be downhill from here
on. (Also called 'business as usual.')
It appears that c:\users\all users is a symlink to c:\programdata.
Yes, it seems that symlinks are a recent addition with Vista and Win7. I
think it is unconscionable that there is no way to see that a file reference
is a symlink or the original true descriptor. It is resolved by the file
system and not higher level utilities like Explorer which can use shortcut
links. And apparently there is a security risk associated with symlinks:
http://en.wikipedia.org/wiki/Symlink_race

I am concerned that the GetSystemFolder(CSIDL_COMMON_APPDATA) resolves as
the same as CSIDL_APPDATA and possibly CSIDL_LOCAL_APPDATA as well. They
should be rendered as follows:

C:\Documents and Settings\All Users\Application Data
C:\Documents and Settings\username\Application Data
C:\Documents and Settings\username\Local Settings\Application Data

yet it is interpreted as:

C:\ProgramData\Ortmaster

And that does not even take into account the virtualized folder, which is
actually a separate storage location on the disk. I think there are
different sets of permissions for the three symlinks given above, and thus
it may be possible that the Authenticated Users group which would use the
AllUsers symlink might have modify/write permission while the username might
not, although the user should be included in the Authenticated Users group,
which I think just means having a username and password, as opposed to an
anonymous guest account.

At least it seems like there are just two possible locations for a given
file, which are the C:\ProgramData\Ortmaster folder and a virtualized folder
for any given username. I thought I had set this up to be available to All
Users, but the existence of the virtual folder under my user name
contradicts that assumption. Fortunately there are only about six of these
systems in the field and as a worst case scenario I could instruct each of
them to set up their systems as needed. And I think most of them are using
XP, so I may only need to change the setup to adjust the permissions if and
when the user upgrades or does a new installation on Vista or Win7.

In any case, I need to get this resolved soon, and hopefully "for all time",
but I'm sure Microsoft will be inflicting another "improved" OS sometime
soon. I think their main objective is to sell more software, so they only
upgrade freely with the latest SPs for a couple of years, and then unleash
the next latest and greatest for us to contend with, and which will
undoubtedly be even more bloated and require even more processor power and
features, and more memory, ensuring the obsolescence of existing PCs.

<Sigh>

Paul
Bart
2010-12-26 13:59:04 UTC
Permalink
Op Wed, 22 Dec 2010 18:34:02 -0500 schreef "P E Schoen"
Post by P E Schoen
Yes, it seems that symlinks are a recent addition with Vista and Win7. I
think it is unconscionable that there is no way to see that a file reference
is a symlink or the original true descriptor.
I think "symlinks" have FILE_ATTRIBUTE_REPARSE_POINT ($00000400) in
their fileattribute?

Bart
--
Bart Broersma
***@tiscali.nl
(ff _ANTISPAM_ wegpoetsen uit dit adres natuurlijk)
Jamie
2010-12-26 14:29:20 UTC
Permalink
Post by Bart
Op Wed, 22 Dec 2010 18:34:02 -0500 schreef "P E Schoen"
Post by P E Schoen
Yes, it seems that symlinks are a recent addition with Vista and Win7. I
think it is unconscionable that there is no way to see that a file reference
is a symlink or the original true descriptor.
I think "symlinks" have FILE_ATTRIBUTE_REPARSE_POINT ($00000400) in
their fileattribute?
Bart
Which is one of the problems that has made some very expensive software
we use at work in operable.. Even the compatibility settings do not
fully correct the problem because MS didn't really give you the full
step back compatibility function..

Some of the operations of this expensive software does not fully mask
out attributes that once were not even assigned anything and when doing
checks, use a simply boolean operation instead of masking only for a
particular bit.. Kind of breaks software..

On top of that, message filtering between apps which our tools seem
to use allot are blocked how ever, those can be fixed with some extra tools
operating on the task bar..

This software I speak of is a $2500 per year package and the authors
have simply stated that users should keep a XP machine Sp2 or less
around for it. And they still sell new packages of this software to any
one that wants to pay the price.. How's that for support!
P E Schoen
2010-12-26 16:48:41 UTC
Permalink
This post might be inappropriate. Click to display it.
Ali Svensson
2010-12-15 08:55:32 UTC
Permalink
Post by P E Schoen
I think I will do this in the application itself, rather than the setup
script.
...
When the program is first installed, the sample databases are included,
Usually it really is easier write familiar Delphi code to make complex
Win settings than try to make Inno setup to do them.

I use very much the same approach. As the last task the Installation
script calls my applicaton with some specified parameter, like
"C:\MyDir\MyApp -UPDATE"

When my app recognizes that it was started with -UPDATE parameter it
does not even show it's MainForm. It just silently does the wanted DB
upgrade tasks and other needed complicated Win settings etc.
Post by P E Schoen
If you have any comments or suggestions, they will be most appreciated.
Microsoft wants to make things complex by first requiring all
applications to be installed to \Program Files folder. Then they write
tons of complex restrictions to that folder and second they write tons
of complex Virtualizationes etc. so that it looks like apps are running
fine now.
Yet I have no seen the original MS idea about "Safe Windows use when
using centralized \Program Files folder use" to come true. Heavy Win
holes are everywhere.

My answer and solution is simple yet unorthodoxic: Do not install your
*DB apps* to Program Files folder at all.

I have done this for 10 years now, the DB folder is right below the
application folder itself, and everything is so simple. You can even
copy the C:\MyApp DB-application to USB-drive, and run the app directly
in there.

A couple of customers each year ask why my Installation Script suggests
to install the application to C:\MyApp folder. "Why does it not use
the standard Progam Files folder?"

I tell them that this is only a suggestion, but you can freely ask the
Installation Script to make the install to Progam Files folder also.

Then I give them a 10 item list about all the ACL settings, folder
settings, user rights settings etc. they have to do to make that work.
After seeing that list they say, well it does not sound so bad idea just
to install to C:\MyApp after all. And they live happily thereafter.

Quite unorthodoxic yes, and Christmas coming and everything.<g>
-as
P E Schoen
2010-12-15 10:50:12 UTC
Permalink
Post by Ali Svensson
Usually it really is easier write familiar Delphi code to make
complex Win settings than try to make Inno setup to do them.
I use very much the same approach. As the last task the
Installation script calls my applicaton with some specified
parameter, like "C:\MyDir\MyApp -UPDATE"
When my app recognizes that it was started with -UPDATE
parameter it does not even show it's MainForm. It just silently
does the wanted DB upgrade tasks and other needed complicated
Win settings etc.
That sounds like another good way to do this. I'll have to give it some
serious thought and see what makes the most sense.
Post by Ali Svensson
Microsoft wants to make things complex by first requiring
all applications to be installed to \Program Files folder.
Then they write tons of complex restrictions to that folder and
second they write tons of complex Virtualizationes etc. so that
it looks like apps are running fine now.
Yet I have no seen the original MS idea about "Safe Windows
use when using centralized \Program Files folder use" to come
true. Heavy Win holes are everywhere.
My answer and solution is simple yet unorthodoxic: Do not
install your *DB apps* to Program Files folder at all.
I have done this for 10 years now, the DB folder is right
below the application folder itself, and everything is so simple.
You can even copy the C:\MyApp DB-application to USB-drive,
and run the app directly in there.
A couple of customers each year ask why my Installation
Script suggests to install the application to C:\MyApp folder.
"Why does it not use the standard Progam Files folder?"
I tell them that this is only a suggestion, but you can freely
ask the Installation Script to make the install to Progam Files
folder also.
It is not too unreasonable to have the applications install in a protected
Program Files folder, but the Program Data folders are more of a problem.
For backup, one should be able to ignore the folder where the application
has been installed with its ancillary files, but the Program Data should be
available for user customization files and data. I don't see why it needs to
be virtualized. If data is supposed to be specific to a given user, then I
think it should be in the "My Documents" folder or possibly under the
Users\Username folder with the program name. I do think that is a better
option than a folder in the root directory.
Post by Ali Svensson
Then I give them a 10 item list about all the ACL settings,
folder settings, user rights settings etc. they have to do to
make that work. After seeing that list they say, well it does
not sound so bad idea just to install to C:\MyApp after all.
And they live happily thereafter.
I have recently installed some new applications that did that, and I
wondered why. Now that I have begun to understand the mechanics of
virtualization I can see why they might have used a non-MS-standard
installation folder. Until a little more than a year ago I did not have a
database for this application because it was supposed to interface to a
separate application (written in VB dot net) that took care of that. But the
programmers seemed to have endless problems and were taking forever to get
it working, so I dove into the task of adding native DBF capabilities to my
Delphi App. Originally I used XP but I tried it on my Vista laptop and it
seemed to work OK. But I soon discovered the virtual store and it was very
confusing. Early this year my XP machine crashed so I did my development on
Vista and checked it on an old XP laptop. Then a couple months ago my Vista
machine bit the dust and now I am using a new Win7 laptop.

I've also encountered other annoyances with newer Microsoft products, such
as their Windows Live Mail 2011 which has lost much of its newsgroup
functionality. And of course there has been the issue with the old help
files such as come with Delphi 4 and need a special installation. There are
other things that M$ says may not work in later releases. There are usually
work-arounds, such as DosBox which allowed me to use an old MSDOS program I
need to access old Futurenet schematic files. But eventually the battle
between security and compatibility will probably spell the death of legacy
programs.
Post by Ali Svensson
Quite unorthodoxic yes, and Christmas coming and
everything.<g>
Merry Christmas! Thanks.

Paul
Ali Svensson
2010-12-15 11:58:47 UTC
Permalink
This post might be inappropriate. Click to display it.
Loading...