tags:

views:

255

answers:

2

I need to create a directory inside COMMONAPPDATA (if it doesnt exists) with full rights to every user of that computer (everyone will be able to read or write to that dir). I'm looking for native simple code to do this in Delphi, without using third part libs or components.

+4  A: 

I wish I could comment and ask "why?".

The executable would have to be run in administrator mode (so most of your users would see elevation prompt)

Why not use the APPDATA or LOCALAPPDATA folders, or the public share?

Here's a link to a similar question: http://stackoverflow.com/questions/508240/delphi-2009-classes-components-to-read-write-file-permissions

MarkRobinson
Exactly. Your application would have to be run with full rights, and elevated rights on Win7 I think are generally not granted even to Administrators, except after they approve an installation. Thus, I think you're going to have to make a special "tool" with this code, which the user must run elevated.
Warren P
+1. Now you can. :) But I think this is the correct answer, anyway...
Craig Stuntz
What is the correct answer? This doesn't even try to answer the question. There are reasons to want such a directory. Both `APPDATA` and `LOCALAPPDATA` are unsuitable for application data shared between users (like database files). As is the public share, it's meant for other things.
mghie
Actually, the public share is the perfect place for shared public database files, although in this circumstance you might also want to create a root level folder for your app and use a subdirectory for the database files.
MarkRobinson
@mghie, just because you want it doesn't mean you can have it. You have to obey the rules Windows imposes. The correct answer to "how do I do this thing which Windows doesn't allow (set permissions without elevating)?" is "You don't." I interpreted his question as an attempt to understand what WarmBooter *really* needed to do, not a general solution.
Craig Stuntz
@Craig: This may be the correct answer, but to another question. This question doesn't **at all** mention "set permissions without elevating". Maybe the OP simply wants to know what API calls to use to enable write access for everyone? Maybe he's fine with the elevation request? The question is a technical one, while this answer isn't.
mghie
@mgie, yes, the question is incredibly vague. That's not the fault of the guy who answered it.
Craig Stuntz
Craig, I dont care about elevation prompt. That's why I even didn't mention it in the original question. I just want to know how to create the directory with full access by everyone. I think the question was clear about this.
WarmBooter
@MarkRobinson, afaik, the directories you suggest are local to the currently user, that's why I need COMMONAPPDATA.
WarmBooter
+5  A: 

@WarmBooter, you can use the CreateDirectory function to accomplish this task.

see this example :

program Project645;

{$APPTYPE CONSOLE}

uses
  AccCtrl,
  AclApi,
  Windows,
  SysUtils;


type

  PTrusteeW = ^TTrusteeW;
  TTrusteeW = record
    pMultipleTrustee: PTrusteeW;
    MultipleTrusteeOperation: DWORD;  { MULTIPLE_TRUSTEE_OPERATION }
    TrusteeForm: DWORD;  { TRUSTEE_FORM }
    TrusteeType: DWORD;  { TRUSTEE_TYPE }
    ptstrName: PWideChar;
  end;
  TExplicitAccessW = record
    grfAccessPermissions: DWORD;
    grfAccessMode: DWORD;  { ACCESS_MODE }
    grfInheritance: DWORD;
    Trustee: TTrusteeW;
  end;



Function  CreateDirectoryFullAccess(NewDirectory:String) :Boolean;
var
    SecurityAttributes  : TSecurityAttributes;
    SecurityDescriptor  : PSecurityDescriptor;
    ExplicitAccess      : array[0..0] of TExplicitAccessW;
    easize              : integer;
    pACL                : Windows.PACL;
begin
    ExplicitAccess[0].grfAccessPermissions:= STANDARD_RIGHTS_ALL or SPECIFIC_RIGHTS_ALL;
    ExplicitAccess[0].grfAccessMode:=Ord(SET_ACCESS);
    ExplicitAccess[0].grfInheritance:=SUB_CONTAINERS_AND_OBJECTS_INHERIT;
    ExplicitAccess[0].Trustee.TrusteeForm:=Ord(TRUSTEE_IS_NAME);
    ExplicitAccess[0].Trustee.TrusteeType:=Ord(TRUSTEE_IS_USER);
    ExplicitAccess[0].Trustee.ptstrName:='Everyone';//Access for all users
    SetEntriesinAclW(1,@ExplicitAccess,nil,pACL);//creates a new access control list

    SecurityDescriptor:= AllocMem(sizeof(SECURITY_DESCRIPTOR_MIN_LENGTH));
    InitializeSecurityDescriptor(SecurityDescriptor,SECURITY_DESCRIPTOR_REVISION);
    SetSecurityDescriptorDacl(SecurityDescriptor,true,pacl,false);//sets information in a discretionary access control list (DACL).

    FillChar(SecurityAttributes,sizeof(SECURITY_ATTRIBUTES),#0);
    SecurityAttributes.nLength:=sizeof(SECURITY_ATTRIBUTES);
    SecurityAttributes.lpSecurityDescriptor:=SecurityDescriptor;
    SecurityAttributes.bInheritHandle:=false;
    CreateDirectory(PChar(NewDirectory),@SecurityAttributes);
    Result:=GetLastError=0;// if all ok, GetLastError = 0
end;

begin
  if CreateDirectoryFullAccess('C:\MyNewDir') then
   Writeln('Ok')
  else
   Writeln('Failed');

  Readln;
end.
RRUZ
To set permissions on an already existing directory use the `SetNamedSecurityInfo()` function (http://msdn.microsoft.com/en-us/library/aa379579%28VS.85%29.aspx) - Google should turn up sample code.
mghie
@RRUZ, your code looks simple and easy! I'll try it. Thanks!
WarmBooter