win32api: how to access NetFileEnum results?

J

Jos Backus

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'
 
D

daz

Jos said:
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
 
S

Shashank Date

Hi Jos,

--- Jos Backus said:
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

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

PS> I tried this on Win 2K Professional using
ruby 1.8.2 (2004-06-04) [i386-mswin32]




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

Keith P Hodges

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
 
J

Jos Backus

Hi daz,

On Tue, Jul 13, 2004 at 04:32:23PM +0900, daz wrote:
[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,
--
Jos Backus _/ _/_/_/ Sunnyvale, CA
_/ _/ _/
_/ _/_/_/
_/ _/ _/ _/
jos at catnook.com _/_/ _/_/_/ require 'std/disclaimer'
 
D

daz

Jos said:
On Tue, Jul 13, 2004 at 04:32:23PM +0900, daz 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:
#----------------------------------
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
 
J

Jos Backus

--YiEDa0DAkWCtVeE4
Content-Type: text/plain; charset=us-ascii
Content-Disposition: inline

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 :) 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,
--
Jos Backus _/ _/_/_/ Sunnyvale, CA
_/ _/ _/
_/ _/_/_/
_/ _/ _/ _/
jos at catnook.com _/_/ _/_/_/ require 'std/disclaimer'

--YiEDa0DAkWCtVeE4
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment; filename="openfiles.rb"

#!/usr/bin/env ruby

# LMSTR =
# LPBYTE* = P
# DWORD = L
# LPDWORD = P
# PDWORD_PTR = L

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

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

require "Win32API"

NetFileEnum = Win32API.new('netapi32', 'NetFileEnum', 'PPPNPNPPP','I')
Memcpy = Win32API.new('msvcrt', 'memcpy', 'PPL', 'P')
Pmemcpy1 = Win32API.new('msvcrt', 'memcpy', 'PLL', 'L')
Pmemcpy2 = Win32API.new('msvcrt', 'memcpy', 'LPL', 'L')
Wcslen = Win32API.new('msvcrt', 'wcslen', 'P', 'L')

class OpenFile
attr_reader :id, :permissions, :num_locks, :pathname, :username
def initialize(id, permissions, num_locks, p_pathname, p_username)
@id, @permissions, @num_locks = id, permissions, num_locks
pathlen = Wcslen.call(p_pathname) * 2
userlen = Wcslen.call(p_username) * 2
pname = "\0" * pathlen
uname = "\0" * userlen
Pmemcpy1.call(pname, p_pathname, pathlen)
Pmemcpy1.call(uname, p_username, userlen)
@pathname = pname.delete("\0")
@username = uname.delete("\0")
end
def inspect
"File #{@pathname} opened by #{@username} (id #{@id}, #{@num_locks} lock(s))"
end
end

MAX_PREFERRED_LENGTH = -1
FILE_INFO_3_SIZE = 20
LEVEL = 3

bufptr = "\0" * 4
entriesread = "\0" * 4
totalentries = "\0" * 4
resume_handle = "\0" * 4

ret = NetFileEnum.call(nil, nil, nil, LEVEL, bufptr, MAX_PREFERRED_LENGTH,
entriesread, totalentries, resume_handle)

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

entriesread = entriesread.unpack("S")[0]
totalentries = totalentries.unpack("S")[0]

rbuf = "\0" * entriesread * FILE_INFO_3_SIZE

# bufptr now contains a ptr to a ptr
Pmemcpy1.call(rbuf, bufptr.unpack("L")[0], entriesread * FILE_INFO_3_SIZE)

open_files = []
entriesread.times do |i|
open_files << OpenFile.new(*rbuf[i * FILE_INFO_3_SIZE, FILE_INFO_3_SIZE].unpack("LLLLL"))
end

open_files.each do |file|
p file
end

exit

--YiEDa0DAkWCtVeE4--
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,982
Messages
2,570,186
Members
46,739
Latest member
Clint8040

Latest Threads

Top