Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

GetLocalShareName function

1 view
Skip to first unread message

Dan Thomas

unread,
May 19, 1998, 3:00:00 AM5/19/98
to

I finally figured out how to list all local "shares" for a computer. It's
taken me so long to find all this information that I decided I'd post it up
here in case anyone else wanted it.

FYI: I started this trek because I found out that accessing a local file via
a UNC name is slower than using the local name. Now that I have the
following function, I can convert a file name to the local name, if
possible.

unit GetShare;
(***************************************************************************
***
The following code is pieced together from a bunch of sources, and you're
free
to use it however you want. I don't take responsibility for anything,
however <g>.
****************************************************************************
**)

interface

uses windows,sysutils,classes;

{this procedure will fill "List" (which you supply) with a list of all local
disk drives and CDROMs that this computer has shared with the network}
procedure GetLocalShareNames(List: TStringList);


implementation


procedure GetLocalShareNames(List: TStringList);
type
PNetResourceArray=^TNetResourceArray;
TNetResourceArray=array[0..maxint div sizeof(TNetResource)-1] of
TNetResource;
var
dw : dword;
x,
Count,
BufSize,
Size,
NetResult : integer;
NetHandle : THandle;
NetResources : PNetResourceArray;
sComputerName : string;
begin

{clear the result list}
List.Clear;

{alloc some storage -- if this isn't enough, we'll re-alloc below}
BufSize := 10*sizeof(TNetResource);
GetMem(NetResources,BufSize);
try
{get the Computer Name}
dw := 255;
SetLength(sComputerName,dw);
if not GetComputerName(PCHAR(sComputerName),dw) then
raise exception.create('GetComputerName failed');
sComputerName := '\\'+sComputerName;

{initialize the resource array -- for the call to WNetOpenEnum, we will
put
some information in the first item of the array and use this as input
to
the function}
fillchar(NetResources^,BufSize,0);
NetResources^[0].lpRemoteName := PCHAR(sComputerName);
NetResources^[0].dwUsage := RESOURCEUSAGE_CONTAINER;
NetResources^[0].dwScope := RESOURCE_GLOBALNET;
NetResources^[0].dwType := RESOURCETYPE_DISK;

{start the enumeration process}
if
WNetOpenEnum(RESOURCE_GLOBALNET,RESOURCETYPE_DISK,0,PNetResourceA(NetResourc
es),NetHandle) <> NO_ERROR then
raise Exception.Create('WNetOpenEnum failed');

try

{Now we enumerate until we're all done
NOTE: There's a CONTINUE in this code}
while true do
begin

{setup and call the Enumeration function}
Count := -1;
Size := BufSize;
NetResult := WNetEnumResource(NetHandle,Count,NetResources,Size);

{if the function tells us there's more data available, we
re-allocate
our storage and call it again}
if NetResult = ERROR_MORE_DATA then
begin
BufSize := Size;
ReallocMem(NetResources,BufSize);
CONTINUE;
end;

{are we done?}
if NetResult <> NO_ERROR then
exit;

{pull out each share name}
for x := 0 to Count-1 do
List.Add(NetResources^[x].lpRemoteName);
end; {of while}
finally
WNetCloseEnum(NetHandle);
end;
finally
FreeMem(NetResources,BufSize);
end;
end; {GetLocalShareNames}


end.


Have fun!

-Dan

--
NOTE: Remove NOSPAM- from my e-mail address before responding directly to
me.

Dan Thomas

unread,
May 20, 1998, 3:00:00 AM5/20/98
to

I left out some important routines <sigh>. Here's the whole unit (is this
too big, Borland guys?):

unit GetShare;
(***************************************************************************
***
The following code is pieced together from a bunch of sources, and you're
free
to use it however you want. I don't take responsibility for anything,
however <g>.
****************************************************************************
**)

interface

uses windows,sysutils,classes;


{This procedure will fill "List" (which you supply) with a list of all local
disk drives and CDROMs that this computer has shared with the network. You
could pass these to GetLocalShareInfo, below.}
procedure GetLocalShareNames(List: TStringList);


{This will convert a Local UNC file ID to a Local file ID. Example:

Local machine name : dan
C: drive share name : cdrive
sLocalUNCFileID : \\dan\cdrive\autoexec.bat
Result : c:\autoexec.bat

If the passed file file ID can't be converted, it is returned without
modification.}
function ConvertLocalUNCFileIDToLocalFileID(sLocalUNCFileID: string):
string;


{This function will convert a local share name to a drive letter, and return
other information also. Example:

C: drive share name : cdrive
sLocalShareName : cdrive
Result sPath : "c:"}
function GetLocalShareInfo(sLocalShareName: string; var
sPath,sRemark,sRWPassword,sROPassword: string) : boolean;


{encapsulation of the windows API}
function GetComputerNameStr: shortstring;


{just a little easier to use this}
function IsWindowsNT: boolean;

implementation

const
SHI50F_RDONLY = $0001;
SHI50F_FULL = $0002;
SHI50F_DEPENDSON = SHI50F_RDONLY OR SHI50F_FULL;
SHI50F_ACCESSMASK = SHI50F_RDONLY OR SHI50F_FULL;

SHI50F_PERSIST = $0100;
SHI50F_SYSTEM = $0200;

STYPE_DISKTREE = 0;

SHI_USES_UNLIMITED = DWORD(-1);

MB_ERR_INVALID_CHARS = $00000008;


type
u_char = Char;
u_short = Word;
u_int = Integer;
u_long = Longint;
NET_API_STATUS = DWORD;

share_info_50 = record
shi50_netname : array[1..13] of char;
shi50_type : u_char;
shi50_flags : u_short;
shi50_remark : pchar;
shi50_path : pchar;
shi50_rw_password : array[1..9] of char;
shi50_ro_password : array[1..9] of char;
szWhatever : array[1..256] of char;
end; {share_info_50}

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_2}
pshare_info_2 = ^share_info_2;

TNetShareGetInfo_SVRAPI = function(servername : LPSTR;
netname : LPSTR;
level : DWORD;
var buf;
buflen : DWORD;
var totalavail: DWORD):
NET_API_STATUS; stdcall;
TNetShareGetInfo_NETAPI32 = function(servername : LPSTR;
netname : pwidechar;
level : DWORD;
var buf): NET_API_STATUS; stdcall;

function GetComputerNameStr: shortstring;
var
dwSize : dword;
begin
dwSize := sizeof(Result)-2;
GetComputerName(@Result[1],dwSize);
Result[0] := char(dwSize);
end; {GetComputerNameStr}


function IsWindowsNT: boolean;
var
osvi : TOSVersionInfo;
begin
fillchar(osvi,sizeof(osvi),0);
osvi.dwOSVersionInfoSize := sizeof(osvi);
if not GetVersionEx(osvi) then
raise Exception.Create('Error attempting to retrieve VersionInfo');
Result := osvi.dwPlatformId = VER_PLATFORM_WIN32_NT;
end; {IsWindowsNT}

try

{this is how you do it for Windows 95}
function W95GetShareInfo(sLocalShareName: string; var
sPath,sRemark,sRWPassword,sROPassword: string): boolean;
var
shi50 : share_info_50;
dwSize : dword;
hModule : THandle;
NetShareGetInfo : TNetShareGetInfo_SVRAPI;
begin
Result := false;
dwSize := sizeof(share_info_50);
hModule := LoadLibrary('SVRAPI.DLL');
if hModule = 0 then
raise Exception.Create('Could not LoadLibrary(''SVRAPI.DLL'')');
try
@NetShareGetInfo := GetProcAddress(hModule,'NetShareGetInfo');
if not Assigned(NetShareGetInfo) then
raise Exception.Create('Could not GetProcAddress(''NetShareGetInfo'')
in SVRAPI.DLL');
sLocalShareName := UpperCase(sLocalShareName);
if NetShareGetInfo(nil,pchar(sLocalShareName),50,shi50,dwSize,dwSize) =
0 then
begin
sPath := StrPas(shi50.shi50_path);
sRemark := StrPas(shi50.shi50_remark);
sRWPassword := StrPas(@shi50.shi50_rw_password);
sROPassword := StrPas(@shi50.shi50_ro_password);
Result := true;
end;
finally
FreeLibrary(hModule);
end; {try..finally}
end; {W95GetShareInfo}


{this is how you do it for NT}
function WNTGetShareInfo(sLocalShareName: string; var
sPath,sRemark,sRWPassword,sROPassword: string): boolean;
var
shi2 : share_info_2;
pShi2 : pshare_info_2;
hModule : THandle;
NetShareGetInfo : TNetShareGetInfo_NETAPI32;
bUnicodeShare,
bUnicodePath,
bUnicodeRemark,
bUnicodeRWPassword : pwidechar;
begin
bUnicodeShare := nil;
bUnicodePath := nil;
hModule := 0;
Result := false;
try

{allocate storage for unicode (double-byte) string}
bUnicodeShare := GlobalAllocPtr(GHND,(length(sLocalShareName)*2)+2);
if bUnicodeShare = nil then
raise Exception.Create('Out of memory');
bUnicodePath := GlobalAllocPtr(GHND,(256*2)*3);
bUnicodeRemark := bUnicodePath + 512;
bUnicodeRWPassword := bUnicodeRemark + 512;

// Convert sharename to Unicode
sLocalShareName := UpperCase(sLocalShareName);
MultiByteToWideChar(CP_ACP,MB_ERR_INVALID_CHARS,
pchar(sLocalShareName),length(sLocalShareName),
bUnicodeShare,(length(sLocalShareName)*2)+2);

fillchar(shi2,sizeof(shi2),0);
shi2.shi2_netname := bUnicodeShare;
shi2.shi2_remark := bUnicodeRemark;
shi2.shi2_passwd := bUnicodeRWPassword;
shi2.shi2_path := bUnicodePath;
sROPassword := '';

hModule := LoadLibrary('NETAPI32.DLL');
if hModule = 0 then
raise Exception.Create('Could not LoadLibrary(''NETAPI32.DLL'')');

@NetShareGetInfo := GetProcAddress(hModule,'NetShareGetInfo');
if not Assigned(NetShareGetInfo) then
raise Exception.Create('Could not GetProcAddress(''NetShareGetInfo''
in NETAPI32.DLL');
if NetShareGetInfo(nil,bUnicodeShare,2,shi2) = 0 then
begin
{This is weird...the netname member points to SHARE_INFO_2 instead of
a
name as expected. By casting a pointer, the whole thing works}
SetLength(sRemark,128);
SetLength(sPath,256);
SetLength(sRWPassword,24);
pShi2 := pshare_info_2(shi2.shi2_netname);
WideCharToMultiByte(CP_ACP,WC_COMPOSITECHECK,
pShi2^.shi2_remark,-1,
pchar(sRemark),length(sRemark),
nil,nil);
WideCharToMultiByte(CP_ACP,WC_COMPOSITECHECK,
pShi2^.shi2_path,-1,
pchar(sPath),length(sPath),
nil,nil);
WideCharToMultiByte(CP_ACP,WC_COMPOSITECHECK,
pShi2^.shi2_passwd,-1,
pchar(sRWPassword),length(sRWPassword),
nil,nil);
Result := true;
end;
finally
if hModule <> 0 then
FreeLibrary(hModule);
if bUnicodeShare <> nil then
GlobalFreePtr(bUnicodeShare);
if bUnicodePath <> nil then
GlobalFreePtr(bUnicodePath);
end; {try..finally}
end; {WNTGetShareInfo}


function GetLocalShareInfo(sLocalShareName: string; var
sPath,sRemark,sRWPassword,sROPassword: string) : boolean;
begin
if IsWindowsNT then
Result :=
WNTGetShareInfo(sLocalShareName,sPath,sRemark,sRWPassword,sROPassword)
else
Result :=
W95GetShareInfo(sLocalShareName,sPath,sRemark,sRWPassword,sROPassword);
end; {ConvertLocalUNCFileIDToLocalFileID}

function ConvertLocalUNCFileIDToLocalFileID(sLocalUNCFileID: string):
string;
var
n : integer;
s,
sShareName,
sPath,
sRemark,
sRWPassword,
sROPassword : string;
begin
Result := sLocalUNCFileID;

{does it start with "\\"?}
if (length(sLocalUNCFileID) < 4) or (copy(sLocalUNCFileID,1,2) <> '\\')
then
exit;

{work with a copy}
s := sLocalUNCFileID;

{delete the "\\"}
delete(s,1,2);

{find the next "\"}
n := pos('\',s);
if n < 2 then
exit;

{make sure the server portion of the file ID is our local computer}
if CompareText(copy(s,1,n-1),GetComputerNameStr) <> 0 then
exit;

{delete the computer name portion}
delete(s,1,n);

{find the trailing backslash, if any -- it is legal to pass "\\dan\vol1"}
n := pos('\',s);
if n < 1 then
n := length(s)+1;

{pull out the share name}
sShareName := copy(s,1,n-1);

{and remove it from the string}
delete(s,1,n);

{using this share name, get the local path if possible}
if (GetLocalShareInfo(sShareName,sPath,sRemark,sRWPassword,sROPassword))
and (sPath <> '') then
{return the local drive letter plus the file ID}
Result := sPath+s;
end; {ConvertLocalUNCFileIDToLocalFileID}


end.


Scott Samet

unread,
May 20, 1998, 3:00:00 AM5/20/98
to

In article <6jtit5$ol...@forums.borland.com>, Dan Thomas wrote:

> FYI: I started this trek because I found out that accessing a local file via
> a UNC name is slower than using the local name.

I never heard of this. Slower to open, slower to read/write?

What OS are you using?


Dan Thomas

unread,
May 20, 1998, 3:00:00 AM5/20/98
to

Scott Samet (TeamB) wrote in message ...


Slower to read/write.

Windows NT 4.0

After I started noticing this problem, I found the following on my latest
MSDN CD (which is Jan 1997):

"UNC names specify network-wide distinct locations which are useful to
provide configuration information for applications. However, when the UNC
name refers to the local computer, it introduces some overhead as the
requests are passed down the Redirector, looped back by the transport, and
sent to the server service instead of going directly to the local disk. "

Unfortunately, the sample program they mention "checklcl" does not include
the source to the routines that do all the work. So I started my journey,
which has resulted in the code I posted (in my second post).

If you care, this is how I got to where I am today:

I've been writing a "copy" program that runs on an NT server, but is started
from a workstation (using a C/S model I've developed). When I started
comparing the file times for copying large files to the normal COPY command
(at the server), I noticed that my copies were slower.

Let me add parenthetically that the reason my copies were slower was NOT due
to the UNC problem, but it started me down the path of checking for the
fastest copies I could come up with. More on the UNC problem in a minute.

Anyway, I started seeing times comparable to the COPY command when I learned
how to set the destination file's length to the final length, then copying
the blocks. This sped things up dramatically. If anyone needs to know, this
is how to extend the file to the final size:

if SetFilePointer(hDest,nFileSize,nil,FILE_BEGIN) = $FFFFFFFF then
raise Exception.Create('Error extending output file(1) (out of disk
space?): '+sDest);
if not SetEndOfFile(hDest) then
raise Exception.Create('Error extending output file(2) (out of disk
space?): '+sDest);

My final outcome is actually faster than the COPY command issued at the
server.

But when I put all this new COPY code into my "ntCopy" program and started
issuing copies from the workstation using this new C/S model, my times were
slower than the tests I had done earlier. I discovered by a fluke that it
was the difference between using a UNC name and a local drive letter.

Making a long story longer, that's the reason I developed this unit. And it
makes a difference!

0 new messages