Compartir carpetas en Windows.

En principio es tan sencillo o tan complicado como usar la función de la API de Windows « NetShareAdd ». La preguntas es: ¿dónde está esta función? Pues bien, esta función se puede encontrar en la DLL « SrvApi.dll » en los sistemas Win9x y WinMe, o en la DLL « NetApi32.dll » en los sistemas Windows NT y 2000 (y supongo que XP).

Borland, ante este problema de que dicha función dependa del sistema operativo lo que ha hecho es pasar de todo y no incluir esta función (ni muchas otras de redes locales) en sus archivos de sistema de la VCL, por lo que para poder usar tendremos que declarar nosotros mismos esta función.

Aquí surge una duda de como cargar la DLL que vayamos a usar: estática o dinámicamente.
Si declaramos la función estáticamente, es decir, con un « external XXXXX » después de la declaración, nuestro ejecutable dependerá de dicha DLL y no nos funcionará en el otro tipo de plataforma. Si por ejemplo cargamos estáticamente NetApi32.dll porque estamos en NT ó 2000 no podremos ejecutar nuestra aplicación en Win9x ó Me.
Esto nos lleva a que la mejor forma de hacerlo es cargando la función dinámicamente (LoadLibrary, GetProcAddress, …) y evitar la dependencia del sistema operativo.

Por otra parte está el hecho de que esta función utiliza unas estructuras (records en Delphi) que tampoco están declaradas y tendremos que declararlas a mano. Estas son: « share_info_50 » para sistemas 9x y Me y « share_info_2 » para sistemas NT/2000.

Para declarar estas estructuras y otras constantes necesarias he creado el fichero NetShare.pas que es como sigue:

unit NetShare;

interface
uses Windows;

const
  LM20_NNLEN = 12;
  SHPWLEN = 8;

  SHI50F_RDONLY = 1;
  SHI50F_FULL   = 2;
  SHI50F_DEPENDSON = (SHI50F_RDONLY or SHI50F_FULL);
  SHI50F_ACCESSMASK     = (SHI50F_RDONLY or SHI50F_FULL);
  SHI50F_PERSIST = 256;
  SHI50F_SYSTEM = 512;

  STYPE_DISKTREE = 0;
  ACCESS_NONE = 0;
  ACCESS_READ = $01;
  ACCESS_WRITE = $02;
  ACCESS_CREATE = $04;
  ACCESS_EXEC = $08;
  ACCESS_DELETE = $10;
  ACCESS_ATRIB = $20;
  ACCESS_PERM = $40;
  ACCESS_GROUP = $8000;
  ACCESS_ALL = (ACCESS_READ or ACCESS_WRITE or ACCESS_CREATE or
    ACCESS_EXEC or ACCESS_DELETE or ACCESS_ATRIB or ACCESS_PERM);

type
  share_info_2= record
     shi2_netname : PWideChar;
     shi2_type : DWORD;
     shi2_remark : PWideChar;
     shi2_permissions : DWORD;
     shi2_max_uses : DWORD;
     shi2_current_uses : DWORD;
     shi2_path : PWideChar;
     shi2_passwd : PWideChar;
    end;

  share_info_50 = packed record
     shi50_netname    : array [0..LM20_NNLEN] of Char;
     shi50_type       : Byte;
     shi50_flags      : Short;
     shi50_remark     : PChar;
     shi50_path       : PChar;
     shi50_rw_password: array [0..SHPWLEN] of Char;
     shi50_ro_password: array [0..SHPWLEN] of Char;
    end;

implementation

end.

La llamada a la función para compartir una carpeta sería algo así:

procedure TForm1.Button1Click(Sender: TObject);

var
  hDll : THandle;
  NetShareAddWin9x : function(pszServer : PChar;
                          sLevel : Short;
                          pbBuffer : Pointer;
                          cbBuffer : Short):DWORD;stdcall;

  NetShareAddWinNT : function(servername : PWideChar;
                          level : DWORD;
                          buf : Pointer;
                          var parm_Err : DWORD):DWORD;stdcall;

  si50 : share_info_50;
  si2 : share_info_2;
  tamano : Short;
  res, err : DWORD;
begin
  // Habría que comprobar la versión de windows
  // con la función EsNT que tiene que devolver
  // true si estamos en NT ó 2000 y si no false.

  if (not EsNT) then
  begin
    // Probamos con la librería de Win 95/98/Me
    hDll := LoadLibrary('SvrApi.dll');
    if hDll > 32 then
    begin
      NetShareAddWin9x := GetProcAddress(hDll, 'NetShareAdd');
      tamano := sizeof(si50);
      FillChar(si50, tamano, 0);
      StrCopy(si50.shi50_netname, 'PRUEBA');
      si50.shi50_type := STYPE_DISKTREE;
      si50.shi50_flags := SHI50F_RDONLY;
      si50.shi50_path := 'D:\TEMP';
      res := NetShareAddWin9x(nil, 50, @si50, tamano);
      FreeLibrary(hDll);
    end;
  end
  else
  begin
    // Probamos con la librería de Win NT/2000
    hDll := LoadLibrary('NetApi32.dll');
    if hDll > 32 then
    begin
      NetShareAddWinNT := GetProcAddress(hDll, 'NetShareAdd');
      tamano := sizeof(si2);
      FillChar(si2, tamano, 0);
      si2.shi2_netname := 'PRUEBA';
      si2.shi2_type := STYPE_DISKTREE;
      si2.shi2_permissions := ACCESS_READ;
      si2.shi2_max_uses := 1;
      si2.shi2_current_uses := 1;
      si2.shi2_path := 'C:\WINNT';
      res := NetShareAddWinNT(nil, 2, @si2, err);
      FreeLibrary(hDll);
    end;
  end;
end;

NOTA: en Win9x/ME hay que poner el parámetro « shi50_path » en mayúsculas para que funcione.