1

I am currently accessing the Windows Netapi32 using Netapi32.lib, currently I am using c++ to access the api. I am having trouble retrieving the computer name, currently one the NetFileEnum here on FILE_INFO_3 structure here. In the documentation it says,

fi3_username

Pointer to a string that specifies which user (on servers that have user-level security) or which computer (on servers that have share-level security) opened the resource. Note that Windows does not support share-level security.This string is Unicode if _WIN32_WINNT or FORCE_UNICODE are defined.

Now, the network that I am running this script in has indeed Share-level security, I am just not sure how to list the computer name.

Relevant code

Library includes:

#pragma comment(lib, "Netapi32.lib")
#include <stdio.h>
#include <assert.h>
#include <windows.h> 
#include <lm.h>

Initiate structure:

fstatus is defined in my code as, NET_API_STATUS fStatus, it is the I/O structure. Documentation here

if fstatus is successful the Return value NERR_Success.

If the function fails, the return value can be one of the following error codes.

  • ERROR_ACCESS_DENIED The user does not have access to the requested information.
  • ERROR_INVALID_LEVEL The value specified for the level parameter is not valid.
  • ERROR_INVALID_PARAMETER The specified parameter is not valid.
  • ERROR_MORE_DATA More entries are available. Specify a large enough buffer to receive all entries.
  • ....

more here

To handle that I use if ((fStatus == NERR_Success) || (fStatus == ERROR_MORE_DATA))

The user name could not be found.

fStatus = NetFileEnum(
                flServerName, //Pointer to a string that specifies the DNS or NetBIOS name of the remote server on which the function is to execute. If this parameter is NULL, the local computer is used. 
                flBasePath, //Pointer to a string that specifies a qualifier for the returned information. If this parameter is NULL, all open resources are enumerated.
                flUserName, //Pointer to a string that specifies the name of the user or the name of the connection.
                dfLevel, //Pointer to a string that specifies the name of the user or the name of the connection. Can be either 2 or 3, I am using 3
                (LPBYTE*)&pFile, //Pointer to the address of the buffer that receives the information. The format of this data depends on the value of the level parameter. 
                fwPrefMaxLen, //pecifies the preferred maximum length of returned data, in bytes.
                &fwEntriesRead, //Pointer to a value that receives the count of elements actually enumerated.
                &fwTotalEntries, //Pointer to a value that receives the total number of entries that could have been enumerated from the current resume position.
                &fwResumeHandle); //Pointer to a value that contains a resume handle which is used to continue an existing file search.

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

RAW Values for the above:

   NET_API_STATUS fStatus;
   LPFILE_INFO_3 pFile = NULL;
   LPFILE_INFO_3 pTmpFile;
   DWORD dfLevel = 3;
   LPTSTR flServerName = NULL;
   LPTSTR flUserName = NULL;
   LPTSTR flBasePath = NULL;
   DWORD fwPrefMaxLen = MAX_PREFERRED_LENGTH;
   DWORD fwEntriesRead = 0;
   DWORD fwTotalEntries = 0;
   DWORD fwResumeHandle = 0;

pTmpfile is the level 3 (documentation here) buffer object,

bufptr [out]

Pointer to the address of the buffer that receives the information. The format of this data depends on the value of the levelparameter.

This buffer returns data in this format,

typedef struct _FILE_INFO_3 {
  DWORD fi3_id;
  DWORD fi3_permissions;
  DWORD fi3_num_locks;
  LMSTR fi3_pathname;
  LMSTR fi3_username;
} FILE_INFO_3, *PFILE_INFO_3, *LPFILE_INFO_3;

Retrieve data:

printf("\n\tComputer: %S\n", pTmpFile->fi3_username); //how do I retrieve computer name???
printf("\n\tid: %D\n", pTmpFile->fi3_id);
printf("\n\tpath: %S\n", pTmpFile->fi3_pathname);

**It is important to note that I have tried this using vbnet and it works, but somehow cant figure it how to do it on c++.

Complete Test Program:

#ifndef UNICODE
#define UNICODE
#endif
//Initialize the NetAPI Library
#pragma comment(lib, "Netapi32.lib")
#include <stdio.h>
#include <assert.h>
#include <windows.h>
#include <lm.h>
int wmain(int argc, wchar_t *argv[])
{
    //NetFile Enum, using 3 Level.
    NET_API_STATUS fStatus;
    LPFILE_INFO_3 pFile = NULL;
    LPFILE_INFO_3 pTmpFile;
    DWORD dfLevel = 3;
    LPTSTR flServerName = NULL;
    LPTSTR flUserName = NULL;
    LPTSTR flBasePath = NULL;
    DWORD fwPrefMaxLen = MAX_PREFERRED_LENGTH;
    DWORD fwEntriesRead = 0;
    DWORD fwTotalEntries = 0;
    DWORD fwResumeHandle = 0;
    DWORD fi;
    //
    // Check command line arguments.
    // Dont need this currently.
    //
    do
    {
        fStatus = NetFileEnum(flServerName,
        flBasePath,
        flUserName,
        dfLevel,
        (LPBYTE*)&pFile,
        fwPrefMaxLen,
        &fwEntriesRead,
        &fwTotalEntries,
        &fwResumeHandle);
        if ((fStatus == NERR_Success) || (fStatus == ERROR_MORE_DATA))
        {
            if ((pTmpFile = pFile) != NULL)
            {
                for (fi=0; fi < fwEntriesRead; fi++)
                {
                    assert(pTmpFile != NULL);
                    if (pTmpFile == NULL)
                    {
                        fprintf(stderr, "An access violation has occurred\n");
                        break;
                    }
                    printf("\n\tComputer: %S", pTmpFile->fi3_username);
                    printf("\n\tid: %d", pTmpFile->fi3_id);
                    printf("\n\tpath: %s", pTmpFile->fi3_pathname);
                    printf("\n\tLocks: %d\n", pTmpFile->fi3_num_locks);
                    pTmpFile++;
                    fwTotalEntries++;
                }
            }
        }
        else
        fprintf(stderr, "A system error has occurred: %d\n", fStatus);
        //
        // Free the allocated memory.
        //
        if (pFile != NULL)
        {
            NetApiBufferFree(pFile);
            pFile = NULL;
        }
    }
    //
    // Continue to call NetFilEnum while
    //  there are more entries.
    //
    while (fStatus == ERROR_MORE_DATA);
    if (pFile != NULL)
    NetApiBufferFree(pFile);
    return 0;
}

Output:

From Build:

1>------ Build started: Project: Perfmon, Configuration: Release Win32 ------
1>Compiling...
1>file_enumerate.cpp
1>Linking...
1>Generating code
1>Finished generating code
1>Embedding manifest...
1>Build log was saved at "file://...."
1>Perfmon - 0 error(s), 0 warning(s)
========== Build: 1 succeeded, 0 failed, 0 up-to-date, 0 skipped ==========

Run:

 Computer: User1 //prints only username, not computername (in our system, each user has the same username)
 id: 1005687
 path: c:\fips\library

 Computer: User2 //prints only username, not computername (in our system, each user has the same username)
 id: 1005689
 path: c:\fips\library\util
  • 1
    What happens? What is `fStatus`? What is returned in `pTmpFile`? What is `pTmpFile`? Where do you get it from? Remember that we cannot see your screen. – David Heffernan Apr 08 '13 at 21:00
  • @DavidHeffernan Ok, I think the edits should explain everything now. –  Apr 08 '13 at 21:10
  • Not really. You didn't say what any of the values are. – David Heffernan Apr 08 '13 at 21:14
  • @DavidHeffernan I have posted the RAW values that I am passing/receiving from the `NetFileEnum` api. –  Apr 08 '13 at 21:20
  • I cannot see the status value – David Heffernan Apr 08 '13 at 21:22
  • @DavidHeffernan Its this `NET_API_STATUS fStatus;` and then I pass the values with `NetFileEnum` in the second code example. –  Apr 08 '13 at 21:25
  • What is the value of `fStatus` when the function returns? – David Heffernan Apr 08 '13 at 21:27
  • Just curious why you have the `c#` tag on this question? – Icemanind Apr 08 '13 at 21:32
  • @DavidHeffernan Ok, I updated my question with the info, it basically returns the state, either success or an exception. –  Apr 08 '13 at 21:41
  • 1
    Oh my. I know all of this. What value is returned when you run your program? Does it return `NERR_Success`? Or something else. I can read the docs on MSDN fine. No need to post them all here. Best if you could remove all of that and just show your complete test program, and its output. – David Heffernan Apr 08 '13 at 21:44
  • @DavidHeffernan I posted the code, its on the very bottom. The value returned is `NERR_Success` but then if more data is required, it returns `ERROR_MORE_DATA`. –  Apr 08 '13 at 22:10
  • We're at cross purposes. I'm asking what value is returned when you run your program on your machine. You seem to be telling me what value could be returned. – David Heffernan Apr 08 '13 at 22:22
  • @DavidHeffernan I am not so sure I understand what you mean, when I run the program, `NERR_Success` is returned. –  Apr 08 '13 at 22:27
  • `%D` isn't a valid format specifier, that's why you're getting `id: D` in your output instead of a number. Use `%d` or `%u`. – Ben Voigt Apr 08 '13 at 22:30
  • @BenVoigt I used an online code formatter prior to posting the code and for some reason it made quite a bit of changes, that one I did not catch. –  Apr 08 '13 at 22:32
  • @enginefree: The output shows me that the mistake is in your real code, not a result of the formatter. – Ben Voigt Apr 08 '13 at 22:33
  • @BenVoigt I was so sure it was the formatter. Well, however it got there (probably shift error), thanks for the tip. –  Apr 08 '13 at 22:37
  • I'm not clear why you expect the username field to contain a computer name. Couldn't you use NetSessionEnum instead to get the computer name? Can you post the VB.NET code that you already have working so that we can see what it does differently? – Harry Johnston Apr 09 '13 at 03:13

1 Answers1

1

If anyone else is wondering about the solution, I figured it out. To query the number of files assoicated with the Computer and not just the User, the NetFileEnum function has to be used, documentation here. The NetFileEnum syntax is shown below,

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

Where you have to pass the Computer Name as LMSTR username (you can retrieve the computer name by quering NetSessionEnum(502) which will return all computer names in the network, documentation here ) and the query returns the file details based on the DWORD level, either FILE_INFO_3 documentation here and FILE_INFO_2 documentation here.