Win32api: how to access NetFileEnum results?

I'm trying to programmatically determine any network opened files on a Windows
2003 Server system using the NETAPI32.NetFileEnum API.

    http://msdn.microsoft.com/library/default.asp?url=/library/en-us/netmgmt/netmgmt/netfileenum.asp

describes this API as

    NET_API_STATUS NetFileEnum(
      LMSTR servername,
      LMSTR basepath,
      LMSTR username,
      DWORD level,
      LPBYTE* bufptr,
      DWORD prefmaxlen,
      LPDWORD entriesread,
      LPDWORD totalentries,
      PDWORD_PTR resume_handle
    );

My code looks like this:

    require "Win32API"

    MAX_PREFERRED_LENGTH = -1
    level = 3
    entriesread = "\0" * 4
    totalentries = "\0" * 4
    resume_handle = "\0" * 4

    NetFileEnum = Win32API.new("netapi32", "NetFileEnum", 'PPPNPNPPP', 'I')
    ret = NetFileEnum.call(0,0,0,level,bufptr,MAX_PREFERRED_LENGTH,entriesread,totalentries,resume_handle)

    if 0 != ret
      puts "Failed:#{ret}"
      exit
    end

    rbuf = "\0" * 10240 # testing, should be *entriesread * sizeof(FILE_INFO_3)
    memcpy = Win32API.new('msvcrt','memcpy','PPL','P')
    memcpy.call(rbuf,bufptr,rbuf.size)

    p rbuf

When level == 3, rbuf will contain an array of FILE_INFO_3 structures as
described in

    http://msdn.microsoft.com/library/default.asp?url=/library/en-us/netmgmt/netmgmt/file_info_3_str.asp

which looks like this:

    typedef struct _FILE_INFO_3 {
      DWORD fi3_id;
      DWORD fi3_permissions;
      DWORD fi3_num_locks;
      LMSTR fi3_pathname;
      LMSTR fi3_username;
    } FILE_INFO_3;

My problem is that although the NetFileEnum.call succeeds, *bufstr aka rbuf
doesn't appear to have the expected contents, i.e. the fi3_id value(s) seen
don't correspond with those returned by the `net file' command. The result is
consistent though, leading me to believe I'm either using the wrong parameter
template or misinterpreting rbuf.

What could I be doing wrong?

···

--
Jos Backus _/ _/_/_/ Sunnyvale, CA
                                _/ _/ _/
                               _/ _/_/_/
                          _/ _/ _/ _/
jos at catnook.com _/_/ _/_/_/ require 'std/disclaimer'

Jos Backus wrote:

I'm trying to programmatically determine any network opened files on a Windows
2003 Server system using the NETAPI32.NetFileEnum API.

    LPBYTE* bufptr,
    rbuf = "\0" * 10240 # testing, should be *entriesread * sizeof(FILE_INFO_3)

    memcpy = Win32API.new('msvcrt','memcpy','PPL','P')
    memcpy.call(rbuf,bufptr,rbuf.size)

[...] believe I'm either using the wrong parameter template or misinterpreting rbuf.

What could I be doing wrong?

Hi Jos,

I just learnt from this so I'll go for the longer answer that you can check.

memcpy in msvcrt.dll takes POINTER to dest, POINTER to src and LONG length
which leads to the template you gave:

    memcpy = Win32API.new('msvcrt','memcpy','PPL','P')

However bufptr (from NetFileEnum) is a POINTER_to_address (POINTER_POINTER),
so Win32API is *not* required to convert this parameter into a POINTER.
Passed as a L(ONG), it fulfils the requirement that it's a POINTER already.

What this often leads to is multiple definitions such as:

    Pmemcpy1 = Win32API.new('msvcrt', 'memcpy', 'PLL'], 'L')
    Pmemcpy2 = Win32API.new('msvcrt', 'memcpy', 'LPL'], 'L')

for use in cases such as yours.

Call with:

    Pmemcpy1.call(dest ruby string, src POINTER_POINTER, length)
    Pmemcpy2.call(dest POINTER_POINTER, src ruby string, length)

Hope that's OK. Haven't tried ... (I'm not NT)

daz

Hi Jos,

I'm trying to programmatically determine any network
opened files on a Windows 2003 Server system using
the NETAPI32.NetFileEnum API.

I cannot claim to have any insight into this API,
but when I ran your code "as is" I got an error:

undefined local variable or method `bufptr'

so I changed the script slightly

<snip>

My code looks like this:

    require "Win32API"

    MAX_PREFERRED_LENGTH = -1
    level = 3
    entriesread = "\0" * 4
    totalentries = "\0" * 4
    resume_handle = "\0" * 4

      bufptr = "\0" * 4 # <<<<< added this assignment

    NetFileEnum = Win32API.new("netapi32",
"NetFileEnum", 'PPPNPNPPP', 'I')
    ret =

NetFileEnum.call(0,0,0,level,bufptr,MAX_PREFERRED_LENGTH,entriesread,totalentries,resume_handle)

    if 0 != ret
      puts "Failed:#{ret}"
      exit
    end

    rbuf = "\0" * 10240 # testing, should be
*entriesread * sizeof(FILE_INFO_3)
    memcpy =
Win32API.new('msvcrt','memcpy','PPL','P')
    memcpy.call(rbuf,bufptr,rbuf.size)

    p rbuf

<snip>

and I got a long string stuffed with some binary data.
Of course, like you said, it did not match the output
of:

C:\>net file
There are no entries in the list.

C:\>

I am not sure if this helps.
-- shanko

I tried this on Win 2K Professional using

ruby 1.8.2 (2004-06-04) [i386-mswin32]

···

--- Jos Backus <jos@catnook.com> wrote:

__________________________________
Do you Yahoo!?
Yahoo! Mail - 50x more storage than other providers!
http://promotions.yahoo.com/new_mail

I was renaming a file in Archno Ruby IDE when it crashed and my file has gone. Does anyone have any Idea where it went?

many thanks in advance

Keith

Hi daz,

[snip]

However bufptr (from NetFileEnum) is a POINTER_to_address (POINTER_POINTER),
so Win32API is *not* required to convert this parameter into a POINTER.
Passed as a L(ONG), it fulfils the requirement that it's a POINTER already.

What this often leads to is multiple definitions such as:

    Pmemcpy1 = Win32API.new('msvcrt', 'memcpy', 'PLL'], 'L')
    Pmemcpy2 = Win32API.new('msvcrt', 'memcpy', 'LPL'], 'L')

for use in cases such as yours.

Call with:

    Pmemcpy1.call(dest ruby string, src POINTER_POINTER, length)
    Pmemcpy2.call(dest POINTER_POINTER, src ruby string, length)

Hope that's OK. Haven't tried ... (I'm not NT)

Thanks for the info. I did indeed overlook the `*' after the LPBYTE typename.

Next question is: using Pmemcpy1, how do I initialize the src POINTER_POINTER
argument (i.e. bufptr)? Setting it to 0 causes ruby to segfault (both 1.8.1
and today's 1.8.2 snapshot installed using the one-click installer) and
setting it to "\0" * 4 causes a TypeError as the template specifies a Long
and I'm supplying a String.

Thanks,

···

On Tue, Jul 13, 2004 at 04:32:23PM +0900, daz wrote:
--
Jos Backus _/ _/_/_/ Sunnyvale, CA
                                _/ _/ _/
                               _/ _/_/_/
                          _/ _/ _/ _/
jos at catnook.com _/_/ _/_/_/ require 'std/disclaimer'

Jos Backus wrote:

Next question is: using Pmemcpy1, how do I initialize the src POINTER_POINTER
argument (i.e. bufptr)? Setting it to 0 causes ruby to segfault (both 1.8.1
and today's 1.8.2 snapshot installed using the one-click installer) and
setting it to "\0" * 4 causes a TypeError as the template specifies a Long
and I'm supplying a String.

Re-reading the API suggests a change of tack.
I think that memcpy isn't required.

bufptr needs to be POINTER to return_struct, so:

## rbuf = "\0" * 10240 # return_struct
## bufptr = [rbuf].pack('P') # POINTER to rbuf (as packed string)

## NetFileEnum needs a POINTER to bufptr # Win32API 'P' template will handle that

So my (UNTESTED) interpretation is:

···

On Tue, Jul 13, 2004 at 04:32:23PM +0900, daz wrote:

#----------------------------------
require 'Win32API'

MAX_PREFERRED_LENGTH = -1
level = 3
entriesread = "\0" * 4
totalentries = "\0" * 4
resume_handle = "\0" * 4

rbuf = "\0" * 10240 # testing, should be *entriesread * sizeof(FILE_INFO_3)
bufptr = [rbuf].pack('P') # POINTER to rbuf (as packed string)

NetFileEnum = Win32API.new('netapi32', 'NetFileEnum', 'PPPNPNPPP', 'I')
ret = NetFileEnum.call(0,0,0,level,bufptr,MAX_PREFERRED_LENGTH,entriesread,totalentries,resume_handle)

if 0 != ret
  puts "Failed:#{ret}"
  exit
end

p rbuf
#----------------------------------

Good or bad ?

daz

Re-reading the API suggests a change of tack.
I think that memcpy isn't required.

[snip]

p rbuf
#----------------------------------

Good or bad ?

This yields 10k worth of zeroes on my screen :slight_smile: I think I understand what you
are trying to do (have win32api follow the pointer automagically) but I can't
seem to get it to work. bufptr does have the right value in this case too, so
that part works fine, but I opted to use the simpler initialization instead.
So I ended up doing some memcpy's which I need anyway to access the pathname
and username fields in the returned FILE_INFO_3 structs. The attached code
works for me; cleanup suggestions welcomed!

Thanks for your help guys.

Cheers,

openfiles.rb (2.13 KB)

···

On Wed, Jul 14, 2004 at 04:57:21AM +0900, daz wrote:
--
Jos Backus _/ _/_/_/ Sunnyvale, CA
                                _/ _/ _/
                               _/ _/_/_/
                          _/ _/ _/ _/
jos at catnook.com _/_/ _/_/_/ require 'std/disclaimer'