Solved

Creating a shared folder

Posted on 2010-08-28
20
1,978 Views
Last Modified: 2012-05-10
Hi,

Currently I use the code below to compose the UNC-path of a folder.
Basically this is done once at initial setup of my application. The UNC-path is stored in the database and all other paths in the database point to this base-folder. Nothing can be saved/created outside this base-folder, so I only store the relative path to it.

So when the basefolder is set to: \\COMPUTERNAME\C$\FOLDERNAME
and a file is stored in folder:           \\COMPUTERNAME\C$\FOLDERNAME\MyFolder\MyFile.txt
I only store the latter part of the filename: \MyFolder\MyFile.txt

Now the problem. If the administrator who is setting up my application is selecting/creating a base-folder (from within my application). Then this folder might not be shared. Normally this would not be a problem as long as the path isn't transferred to the UNC-path. From that moment on the folder isn't accessible anymore.
Created base-folder: C:\Foldername\Test\
UNC-path:                  \\COMPUTERNAME\C$\Foldername\Test\
Here folder "C:\Foldername\Test\" would be accessible to the admin, but folder "\\COMPUTERNAME\C$\Foldername\Test\" isn't.
Apparently in order to access an UNC-path it must be shared first.

My customers are for some part, stand-alone users on one computer. They don't bother sharing folders (maybe even never heard of it). Others however need to work with multiple users in my application (they might even have a systemadministrator).
Another thing to keep in mind:
The computer of the customer needs the be set in a correct way, so sharing of folders is possible. I know f.e. that a missing registry-value called "IRPStackSize" can influence sharing folders.
HKLM\SYSTEM\CurrentControlSet\Services\lanmanserver\parameters\IRPStackSize

So much for the background.

One possible approach could be to share the base-folder from within my application. The moment the base-folder is selected, it will be shared among all users.

What I would like from you is to come up with some workable solutions. After we agree upon the approach, then see what code is needed to make it happen.
function BuildUNCPath(vFolder : String) : String;  

begin  

  if Copy(vFolder,1,1) <> '\' then  

    Result := '\\' + GetComputerName + '\' + StringReplace(vFolder,':','$', [rfReplaceAll])  

  else  

    Result := vFolder;  

end;

Open in new window

0
Comment
Question by:Delphiwizard
  • 11
  • 8
20 Comments
 
LVL 14

Expert Comment

by:systan
ID: 33551419
You don't have to share  "C:\Foldername\Test\"  because it is already in the UNC path of c$, when c$ is shared, then the rest of the folders inside drive c is shared too.
0
 

Author Comment

by:Delphiwizard
ID: 33551560
Yes, but normally an administrator will not share a whole drive, but only certain files and folders.
Small companies (1 person) with one stand-alone computer never have to share anything, because they have only one administrator Windows-user.
0
 
LVL 13

Expert Comment

by:aflarin
ID: 33552032
I think it may be the group of questions at initial setup:

1. Would you like to share data?

  No: nothing do
  Yes: user selects the base folder. then next question

  1.2 This path should be shared.
       (X) (recommended) Would you like to create shared folder automatically?
       (  ) If the share already exists, please select it [Select button]

When your program runs, it should check the basefolder and if it's empty or it doesn't exist (it may be deleted), the initial setup must be repeated

0
 

Author Comment

by:Delphiwizard
ID: 33552058
aflarin:
Exactly.
Sharing is required otherwise the application can't use the basefolder. Checking the existence is already in place, that's why I know that the folder required sharing :-)
But now how to do this.
  1. How to share the selected base-folder from within my application?
  2. How to select  an existing share (didn't know that was possible)?
0
 
LVL 13

Accepted Solution

by:
aflarin earned 500 total points
ID: 33552134
>> How to share the selected base-folder from within my application?

function NetShareAdd(ServerName: PChar; Level: Integer; Buf: Pointer; var parm_err: Integer): DWord; stdcall; external 'NETAPI32.DLL';

Type
  _SHARE_INFO_2 = Record
    shi2_NetName : lpWStr;
    shi2_Type : DWord;
    shi2_Remark : lpWStr;
    shi2_Permissions,
    shi2_MaxUsers,
    shi2_Current_Uses : DWord;
    shi2_Path : lpWStr;
    shi2_PassWord : lpWStr;
  End;
  TShareInfo = _SHARE_INFO_2;

const
  STYPE_DISKTREE = 0;
  ACCESS_ALL = $7F;


procedure TForm1.Button1Click(Sender: TObject);
var
  Buffer : TShareInfo;
  Err : Integer;
  NetName, Path: WideString;
Begin

  NetName:= 'NetName';
  Buffer.shi2_NetName := PWideChar(NetName);
  Buffer.shi2_Type:= STYPE_DISKTREE;
  Buffer.shi2_Remark:= nil;
  Buffer.shi2_Permissions:= ACCESS_ALL;
  Buffer.shi2_MaxUsers:= DWORD(-1);
  Buffer.shi2_Current_Uses:= DWORD(-1);
  Path:= 'C:\Temp';
  Buffer.shi2_Path:= PWideChar(Path);
  Buffer.shi2_PassWord:= nil;

  if NetShareAdd(nil,2,@Buffer, Err) <> 0 then
    ShowMessage( 'Error ' + IntToStr(Err) );
end;
0
 
LVL 13

Assisted Solution

by:aflarin
aflarin earned 500 total points
ID: 33552143
There is a function - NewShareEnum. You can enumerate all shares and let user to choose one from the list.

Here is a sample how to use NewShareEnum:
http://www.experts-exchange.com/Programming/Languages/Pascal/Delphi/Q_21185774.html
0
 

Author Comment

by:Delphiwizard
ID: 33552377
NetShareAdd
To whom will this code give access?
To ALL users or only to the logged in user? Or else?
NewShareEnum
In the example of NewShareEnum it states that it will also enumerate hidden shares (Like: C$).
Isn't such a share always available? I believe in the current situation I already use a hidden share (which doesn't work for me, as it will not allow access to the folder using this sharename). Or am I wrong?
Secondly:
How can I use the code that was presented? I never worked with a console application before. Can it easily be integrated into my application?
0
 
LVL 13

Assisted Solution

by:aflarin
aflarin earned 500 total points
ID: 33552431
>> To whom will this code give access?
for Everyone group. As far as I know it's only logged in users

>> Isn't such a share always available?

They are available by default but only for users who are Administrators.
Administrator can delete it - so, I can't say they are always available

They are called hidden because they are not visible into user dialogs.

If you are Administrator and the hidden shares are available, you can access all folder and files on the PC. (but you may stil be blocked at file-system level if it is NTFS)

>> How can I use the code that was presented? I never worked with a console application before. Can it easily be integrated into my application?

Sure, look at the attached code. I've redirected the output to the memo, but it may be a ComboBox in your case (Just change Memo1.Lines.Add to ComboBox1.Items.Add)

unit Unit1;



interface



uses

  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,

  Dialogs, StdCtrls;



type

  TForm1 = class(TForm)

    Button1: TButton;

    Memo1: TMemo;

    procedure Button1Click(Sender: TObject);

  private

    { Private declarations }

  public

    { Public declarations }

  end;



var

  Form1: TForm1;



implementation



{$R *.dfm}



type

  _SHARE_INFO_502         =  packed record

     shi502_netname:      PWideChar;

     shi502_type:         DWORD;

     shi502_remark:       PWideChar;

     shi502_permissions:  DWORD;

     shi502_max_uses:     DWORD;

     shi502_current_uses: DWORD;

     shi502_path:         LPWSTR;

     shi502_passwd:       LPWSTR;

     shi502_reserved:     DWORD;

     shi502_security_dsc: PSECURITY_DESCRIPTOR;

  end;

  SHARE_INFO_502          =  _SHARE_INFO_502;

  PSHARE_INFO_502         =  ^SHARE_INFO_502;

  LPSHARE_INFO_502        =  PSHARE_INFO_502;

  TShareInfo502           =  SHARE_INFO_502;

  PShareInfo502           =  PSHARE_INFO_502;



type

  TShareInfo502Array      =  Array [0..MaxWord] of TShareInfo502;

  PShareInfo502Array      =  ^TShareInfo502Array;



function   NetApiBufferFree(buffer: Pointer): DWORD; stdcall; external 'netapi32.dll';

function   NetShareEnum(servername: PWideChar;

                        level: DWORD;

                        bufptr: PByteArray;

                        prefmaxlen: DWORD;

                        entriesread: PDWORD;

                        totalentries: PDWORD;

                        resume_handle: PDWORD): DWORD; stdcall; external 'netapi32.dll';









procedure TForm1.Button1Click(Sender: TObject);

var

  p:             PShareInfo502Array;

  res,

  er, tr,

  resume,

  i:             DWORD;

  szServer:      String;

  lpwszServer:   Array [0..255] of WideChar;

  pwszServer:    PWideChar;

  line: string;

begin



   er:=0;

   tr:=0;

   resume:=0;





   szServer:= ''; // or you can use TEdit



   // Check parameter

   if (Length(szServer) = 0) then

     pwszServer:=nil

   else

   begin

     // Make sure the server starts with a \\

     if (Pos('\\', szServer) <> 1) then szServer:='\\'+szServer;

     StringToWideChar(szServer, @lpwszServer, SizeOf(lpwszServer));

     pwszServer:=@lpwszServer;

   end;



   Memo1.Lines.Clear;

   // Print a report header.

   Memo1.Lines.Add('Share:              Local Path:                                         Uses:   Descriptor:');

   Memo1.Lines.Add('-------------------------------------------------------------------------------------------');



   // Call the NetShareEnum function; specify level 502.

   repeat

     res:=NetShareEnum(pwszServer, 502, @p, DWORD(-1), @er, @tr, @resume);

     // If the call succeeds

     if (res = ERROR_SUCCESS) or (res = ERROR_MORE_DATA) then

     begin

        // Loop through the entries;

        // print retrieved data.

        for i:=1 to Pred(er) do

        begin

          line:= Format('%-20s%-70s%-0.8d   ', [WideCharToString(p^[i].shi502_netname),

                   WideCharToString(p^[i].shi502_path), p^[i].shi502_current_uses]);

           //

           //  Validate the value of the

           //  shi502_security_descriptor member.

           //

           if IsValidSecurityDescriptor(p^[i].shi502_security_dsc) then

               line:= line + 'Yes'

            else

               line:= line + 'No';

           Memo1.Lines.Add(line);

        end;

        //

        // Free the allocated buffer.

        //

        NetApiBufferFree(p);

     end

     else

         Memo1.Lines.Add('Error: ' + IntToStr( res) );



     // Continue to call NetShareEnum while

     //  there are more entries.



   until (res <> ERROR_MORE_DATA);



end;



end.

Open in new window

0
 

Author Comment

by:Delphiwizard
ID: 33552498
Your code works, but at the end of the line in the memo there is a column called "Descriptor:" filled with Yes or NO

  if IsValidSecurityDescriptor(p^[i].shi502_security_dsc) then  
    line:= line + 'Yes'  
  else  
    line:= line + 'No';
On my system only one share has YES.
The share that I added with NetShareAdd contains NO.
Also in Windows Explorer I can't see that this folder is shared (based on the looks of the foldericon). Is this a problem or will it work anyway?

What if the base-folder is a some subfolder.
f.e.: C:\MyFolder\Temp\MyBaseFolder
What happens if I share that folder? Will the user than also have access to the folders above?
0
 

Author Comment

by:Delphiwizard
ID: 33552510
You can forget the remark about:
"Also in Windows Explorer I can't see that this folder is shared (based on the looks of the foldericon). Is this a problem or will it work anyway?"
A refresh in Explorer did the trick.
0
How to run any project with ease

Manage projects of all sizes how you want. Great for personal to-do lists, project milestones, team priorities and launch plans.
- Combine task lists, docs, spreadsheets, and chat in one
- View and edit from mobile/offline
- Cut down on emails

 

Author Comment

by:Delphiwizard
ID: 33552531
Than another:
If I let the user select a share.
Do I in fact let him select a shared folder and will that need to become the base-folder?
Am I correct to assume selecting the base-folder first and share it, works exactly the other way around from this approach?
0
 
LVL 13

Expert Comment

by:aflarin
ID: 33552582
if the Descriptor field filled with NO - it means the share hasn't the associated descriptor. Unfortunatelly, I dodn't find the MSDN entry about that cause, but I think it means that such share has the default rights, i.e Everyone group + Full Control.

>> Will the user than also have access to the folders above?

NO

>> Do I in fact let him select a shared folder and will that need to become the base-folder?

Why not? It is the first way. I don't know if it suits to you

The second way if you already have the base folder and you don't want to change it. Then the Select button (from my first post) may be needed if user already have the above shared folder (or may be he would like to create the shared folder with some other rights).
When he press the Select button, you get all shares (NetShareEnum), leave only such shares which have LocalPath that includes your base folder and let him choose them.
If there isn't any shared folder that includes your base folder, you just show the warning message like:
"There is no any shares what includes BASE_FOLDER. Please create the shared folder or use the recommended way"
0
 

Author Comment

by:Delphiwizard
ID: 33552620
So far so good. Now using it all.
Computername = MYCOMP123
User selects a base-folder:
C:\Documents and Settings\All Users\Documenten\MyAppName
Suppose I always create my own share called 'MyApp'.
How would I access this shared folder from another computer in the network (using the computername)?
Something like: \\MYCOMP123\MyApp\  ??and then what??
Suppose some subfolders are created inside the base-folder.
C:\Documents and Settings\All Users\Documenten\MyAppName\Sub1
C:\Documents and Settings\All Users\Documenten\MyAppName\Sub2
Does the user have access to these subfolders?
0
 
LVL 13

Expert Comment

by:aflarin
ID: 33552640
>> How would I access this shared folder from another computer in the network (using the computername)?
>> Something like: \\MYCOMP123\MyApp\  ??and then what??

yes, you just use UNC path instead of local path. For example:

  Memo1.Lines.LoadFromFile( '\\MAIN\C$\test.txt' );

>> Does the user have access to these subfolders?

Yes:

Memo1.Lines.LoadFromFile( '\\MAIN\C$\temp\test.txt' );


BUT there is some problem with your example: C:\Documents and Settings\All Users\Documenten\
This folder has limited file-system rights by default (if your file-system is NTFS, of course). Only Power Users and Administrator can write it by default.

So if you need to access this folder from User account and for reading - there is no problem. otherwise you have a define other rights to
C:\Documents and Settings\All Users\Documenten\MyAppName

0
 

Author Comment

by:Delphiwizard
ID: 33553471
>> yes, you just use UNC path instead of local path.
So I still use the administrative sharename "C$" even though I created my own sharename "MyApp"?
>> C:\Documents and Settings\All Users\Documenten\
>> This folder has limited file-system rights by default  
I you would have to pick a folder on a NTFS, that needs to be shared by all user that use my application, which would it be?
0
 

Author Comment

by:Delphiwizard
ID: 33553662
And in addition:
If the base-folder is changed after a while, the old share needs to be replaced with the new one.
Is there a way to delete all Shares with a certain name (f.e. "MyApp")? Maybe from registry?
I've seen the entry in:
HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\lanmanserver\Shares
But I don't know if that is the only entry in registry to keep the shares?
How to remove a share?
0
 
LVL 13

Assisted Solution

by:aflarin
aflarin earned 500 total points
ID: 33553703
>> So I still use the administrative sharename "C$" even though I created my own sharename "MyApp"?

I used the hidden share name only as an example. You can use any share name

>> I you would have to pick a folder on a NTFS, that needs to be shared by all user that use my application, which would it be?

Good question :)
As I understand your application has to be able to write into this folder? And it will be launched under User account?

I think C:\Documents and Settings\All Users\Documenten\YourAppDir is Ok, but you have to change its default rights. Check this thread to learn how to do it:
http://www.experts-exchange.com/Programming/Languages/Pascal/Delphi/Q_22425981.html

>> How to remove a share?

I don't think the deleting regestry key is a good idea. It would be better to use NetShareDel:
http://www.experts-exchange.com/Programming/Languages/Pascal/Delphi/Q_23470466.html
0
 

Author Comment

by:Delphiwizard
ID: 33554496
NetShareDel: works perfectly.
The only thing that is troubleing me is giving access rights to the base-folder that will be selected by the user. (bye the way: only admin-users can change the base-folder in my application).
The code you point to is not at all clear to me. Also it mentions that advapi32.dll must be added.
Would it be much trouble for you to make me some working sample? And tell me which files need to be added to the uses clause, and maybe even distributed with my app.

0
 
LVL 13

Assisted Solution

by:aflarin
aflarin earned 500 total points
ID: 33556320
sure, here is the code to add Everyone with full access to your folder. And you don't need to add any files to your distribution package

uses AccCtrl, AclAPI;

procedure TForm1.Button1Click(Sender: TObject);
const
  FULL_CONTROL      = $001F01FF;
  READ_EXEC_CONTROL = $001200A9;
  LIMIT_CONTROL     = $001000A9;
  MODIFY_CONTROL    = $001301BF;
var
  path: string;
  pNewDACL : PACL;
  EA : array [0..1] of EXPLICIT_ACCESS;
begin
  path:='f:\Documents and Settings\All Users\Documents\MyApp';
  ZeroMemory(@EA,Sizeof(EA));
  BuildExplicitAccessWithName(@EA[0], PAnsiChar('Everyone'), FULL_CONTROL, SET_ACCESS, SUB_CONTAINERS_AND_OBJECTS_INHERIT);
  SetEntriesInAcl(1, @EA[0], nil, pNewDACL);
  SetNamedSecurityInfo(PChar(path), SE_FILE_OBJECT, DACL_SECURITY_INFORMATION, NIL, NIL, pNewDACL, NIL);
end;
0
 

Author Closing Comment

by:Delphiwizard
ID: 33556429
Thank you very much for your help. I realy appreciate it.
0

Featured Post

How to run any project with ease

Manage projects of all sizes how you want. Great for personal to-do lists, project milestones, team priorities and launch plans.
- Combine task lists, docs, spreadsheets, and chat in one
- View and edit from mobile/offline
- Cut down on emails

Join & Write a Comment

Objective: - This article will help user in how to convert their numeric value become words. How to use 1. You can copy this code in your Unit as function 2. than you can perform your function by type this code The Code   (CODE) The Im…
Creating an auto free TStringList The TStringList is a basic and frequently used object in Delphi. On many occasions, you may want to create a temporary list, process some items in the list and be done with the list. In such cases, you have to…
This video discusses moving either the default database or any database to a new volume.
Get a first impression of how PRTG looks and learn how it works.   This video is a short introduction to PRTG, as an initial overview or as a quick start for new PRTG users.

747 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question

Need Help in Real-Time?

Connect with top rated Experts

14 Experts available now in Live!

Get 1:1 Help Now