Who has files open on the network
The WhoHasFileOpen
utility displays the list of users who have specific file(s) open on network. Works on Windows NT Platform (Windows NT 4.0, Windows 2000, etc). This code detects only the files opened using a net shared path. It does not return the files opened by a user on the local computer using a local path.
The code is based on Ramon F. Jaquez's UT FAQ #7896 Who opened what files on the network? (modified to use only VFP code)
.
Thanks Kevin Delaney for code cleanup which made possible to post it here.
The code uses Windows API support class.
A user running this program must be a member of the Administrators or Account Operators local group on the file server where files are located.
This is sample code. Add error handling and adjust to your requirements as necessary. |
Examples
1 2 3 4 5 6 7 8 9 10 11 * Mapped drive, specific file 'Sometable.dbf' WhoHasFileOpen("X:\Somefolder\SumSubfolder\Sometable.dbf") * Mapped drive, all extensions for specific file name 'Sometable' WhoHasFileOpen("X:\Somefolder\SomeSubfolder\Sometable") * UNC path, specific file 'Sometable.dbf' WhoHasFileOpen("\\SomeServer\SomeShare\Somefolder\SomemSubfolder\Sometable.dbf")
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 * * WhoHasFileOpen.prg * *-- For Windows NT Platform (NT 4, NT 2000, e.t.c) * * Based of Ramon F. Jaquez UT FAQ # 7896 * Who opened what files on the network? (modified to use only VFP code) * *-- * The following program displays the open files, the users that * opened these files and other related information. * * This code detects only the files opened using a net shared * path. It does not return the files opened by a user on the * local computer using a local path (i.e. the computer where * the user is logged on). This is normal, because, otherwise, * the number of returned files would be huge. * * The user running this program must be a member of the * Administrators or Account Operators local group. * * In order to keep the code simple, the error handling only * displays the error code. You should integrate it in your * error handling mechanism. * *-- This function returns information about open files. * It returns the open files only if they were * opened using a share on that computer. * *-- It uses: * - The NetFileEnum Win32 API function to retrieve the wanted information from the OS. * *-- Parameters: * 1. The full file name including path. An extension can be ommited. LPARAMETERS tcFileName LOCAL lcDriveLetter, lcFileMask, llMask, lcRestName #DEFINE PERM_FILE_READ 0x1 && user has read access #DEFINE PERM_FILE_WRITE 0x2 && user has write access #DEFINE PERM_FILE_CREATE 0x4 && user has create access #DEFINE ACCESS_READ 0x01 #DEFINE ACCESS_WRITE 0x02 #DEFINE ACCESS_CREATE 0x04 #DEFINE ACCESS_EXEC 0x08 #DEFINE ACCESS_DELETE 0x10 #DEFINE ACCESS_ATRIB 0x20 #DEFINE ACCESS_PERM 0x40 #DEFINE ACCESS_GROUP 0x8000 #DEFINE ACCESS_ALL ( ACCESS_READ + ACCESS_WRITE + ACCESS_CREATE + ; ACCESS_EXEC + ACCESS_DELETE + ACCESS_ATRIB + ACCESS_PERM ) LOCAL lcServerName, lcBasePath, lcUserName, lnBufferPointer LOCAL lnPreferedMaxLength, lnEntriesRead, lnTotalEntries LOCAL lnResumeHandle, lnError, loPointersObject LOCAL lnI, lcDll, lnPermissions, lnID LOCAL llContinue, lnpFileInfo, lcFileName LOCAL lnLocks, loRec, lcPermissions, lcServerNameUC, lcBasePathUC, lcUserNameUC IF ("?" $ tcFileName) OR ("*" $ tcFileName) _msgbox("File Mask is not supported.") RETURN ENDIF IF EMPTY(SYS(2000, DEFAULTEXT(tcFileName,"*"))) _msgbox("File Name '" + tcFileName + "' not found") RETURN ENDIF IF LEFT(tcFileName,2) = "\\" lcNetName = LEFT(tcFileName, AT("\", tcFileName, 4)-1) lcRestName = SUBSTR(tcFileName, AT("\", tcFileName, 4)+1) lcDriveLetter = lcNetName ELSE lcDriveLetter = UPPER(JUSTDRIVE(tcFileName)) IF EMPTY(lcDriveLetter) _msgbox("Incorrect File Name '" + tcFileName + "'") RETURN ENDIF * Convert a driver letter to the UNC path lcNetName = _LocalName2UNC(lcDriveLetter) IF EMPTY(lcNetName) _msgbox(lcDriveLetter + " isn't a network drive - '" + tcFileName + "'") RETURN ENDIF lcRestName = SUBSTR(JUSTPATH(tcFileName),4) ENDIF * Convert share UNC path to the server local path lcServerName = "\\" + STREXTRACT(lcNetName, "\\", "\") lcLocalPath = _Share2LocalPath(lcNetName) IF ISNULL(lcLocalPath) RETURN ENDIF lcBasePath = ADDBS(lcLocalPath) + lcRestName lcUserName = "" lcFileMask = JUSTFNAME(tcFileName) DECLARE INTEGER NetFileEnum IN NETAPI32 ; STRING @ServerName, STRING @BasePath, ; STRING @UserName, INTEGER nLevel, ; INTEGER @BufferPointer, INTEGER PreferedMaxLength, ; INTEGER @EntriesRead, INTEGER @TotalEntries, ; INTEGER @ResumeHandle *-- This is the structure used by NetFileEnum to retrieve the information. *typedef struct _FILE_INFO_3 { * DWORD fi3_id; * DWORD fi3_permissions; * DWORD fi3_num_locks; * LPWSTR fi3_pathname; * LPWSTR fi3_username;} FILE_INFO_3 loWas = NEWOBJECT("WinApiSupport", "WinApiSupport.fxp") CREATE CURSOR crsWhoHas ( ; UserName C(10), ; Locks I, ; FileID I, ; Permissions C(24), ; FileName C(254), ; ServerFileName C(254)) SCATTER MEMO NAME loRec *-- The server name, the base path and the user name must be in Unicode format. lcServerNameUC = StrConv(StrConv(lcServerName + Chr(0), 1), 5) lcBasePathUC = StrConv(StrConv(lcBasePath + Chr(0), 1), 5) lcUserNameUC = StrConv(StrConv(lcUserName + Chr(0), 1), 5) *-- Allow for a very large buffer. * If this length is not enough, the info * will be retrieved in more than one step. lnPreferedMaxLength = 100000000 lnResumeHandle = 0 lnEntriesRead = 0 lnTotalEntries = 0 lnBufferPointer = 0 llContinue = .t. DO WHILE llContinue lnError = NetFileEnum( lcServerNameUC, lcBasePathUC, lcUserNameUC, 3, ; @lnBufferPointer, lnPreferedMaxLength, @lnEntriesRead, ; @lnTotalEntries, @lnResumeHandle) IF lnEntriesRead = 0 *-- There are no (more) open files. llContinue = .f. ENDIF IF lnError = 0 FOR lnI = 1 TO lnEntriesRead lnpFileInfo = lnBufferPointer + (lnI - 1) * 20 lcFileName = loWas.StrZFromBufferW(lnpFileInfo + 12) IF UPPER(JUSTFNAME(lcFileName)) <> UPPER(lcFileMask) LOOP ENDIF lnpFileInfo = lnBufferPointer + (lnI - 1) * 20 *-- Extract the file name loRec.FileName = lcDriveLetter + "\" + STREXTRACT(lcFileName, lcLocalPath, "",1,1) loRec.ServerFileName = lcFileName *-- Extract the number of locks on this file lnLocks = loWas.Long2NumFromBuffer(lnpFileInfo + 8) loRec.Locks = lnLocks *-- Extract the user name that opened the file lcUserName = loWas.StrZFromBufferW(lnpFileInfo + 16) loRec.UserName = lcUserName *-- Extract the permissions on this file lnPermissions = loWas.Long2NumFromBuffer( lnpFileInfo + 4) lcPermissions = "" IF BITAND(lnPermissions, PERM_FILE_READ) > 0 lcPermissions = lcPermissions + "Read+" ENDIF IF BITAND(lnPermissions, PERM_FILE_WRITE) > 0 lcPermissions = lcPermissions + "Write+" ENDIF IF BITAND(lnPermissions, PERM_FILE_CREATE) > 0 lcPermissions = lcPermissions + "Create+" ENDIF loRec.Permissions = LEFT(lcPermissions, LEN(lcPermissions)-1) *-- Extract the ID for this file. * This ID is generated when the file is opened and * can be used as parameter for the NetFileGetInfo * Win32 API function. lnID = loWas.Long2NumFromBuffer(lnpFileInfo) loRec.FileID = lnID INSERT INTO crsWhoHas FROM NAME loRec ENDFOR *-- Free the memory allocated by NetFileEnum IF lnBufferPointer <> 0 DeAllocNetAPIBuffer(lnBufferPointer) ENDIF ELSE _msgbox("NetFileEnum: Error " + _apierror(lnError)) llContinue = .f. ENDIF ENDDO IF RECCOUNT("crsWhoHas") = 0 _msgbox("No open files found for '" + tcFileName + "'") RETURN ENDIF SELECT crsWhoHas INDEX ON UserName TAG UserName LOCATE BROWSE LAST NOWAIT NAME oBr oBr.ReadOnly = .T. oBr.Columns(1).Header1.Caption = "User Name" oBr.Columns(3).Header1.Caption = "File ID" oBr.Columns(5).Header1.Caption = "File Name" oBr.Columns(6).Header1.Caption = "Server File Name" oBr.AutoFit() RETURN *---------------------------------------------------------------------------------- PROCEDURE _apierror LPARAMETERS tnErrorCode LOCAL lcErrBuffer, lcErrorMess, lnNewErr DECLARE Long FormatMessage In kernel32.dll ; Long dwFlags, String @lpSource, ; Long dwMessageId, Long dwLanguageId, ; String @lpBuffer, Long nSize, Long Arguments lcErrBuffer = REPL(CHR(0),1000) lnNewErr = FormatMessage(0x1000,.NULL., tnErrorCode, 0, @lcErrBuffer,500,0) lcErrorMess = TRANSFORM(tnErrorCode) + " " + LEFT(lcErrBuffer, AT(CHR(0),lcErrBuffer)- 1 ) RETURN lcErrorMess PROCEDURE _msgbox LPARAMETERS tcMessage =MESSAGEBOX(tcMessage,16) RETURN "OK" PROCEDURE _share2localpath LPARAMETERS tcNetName LOCAL loWas, lnBufferPointer, lcServer, lcShare, lnRC, lcPathRest, loWas, lcLocalPath IF EMPTY(tcNetName) OR TYPE("tcNetName") <> "C" ERROR 11 ENDIF DECLARE Long NetShareGetInfo IN Netapi32.dll ; String servername, String netname, Long level, Long @bufptr lcServer = STREXTRACT(tcNetName, "\\", "\") IF EMPTY(lcServer) RETURN "" ENDIF lcShare = STREXTRACT(tcNetName, "\\" + lcServer + "\", "\",1,1+2) lcPathRest = STREXTRACT(tcNetName, "\\" + lcServer + "\" + lcShare + "\", "",1,1) IF EMPTY(lcShare) RETURN "" ENDIF lnBufferPointer = 0 lnRC = NetShareGetInfo(STRCONV(lcServer+CHR(0),5), STRCONV(lcShare+CHR(0),5), 2, @lnBufferPointer) IF lnRC = 0 loWas = NEWOBJECT("WinApiSupport", "WinApiSupport.fxp") lcLocalPath = ADDBS(loWas.strzfrombufferw(lnBufferPointer + 24)) + lcPathRest ELSE lcLocalPath = Null _msgbox("NetShareGetInfo: Error accessing server '" + lcServer + "', share '" + lcShare + "'" + CHR(13) + _apierror(lnRC)) ENDIF *!* typedef struct _SHARE_INFO_2 { *!* 0 LPWSTR shi2_netname; *!* 4 DWORD shi2_type; *!* 8 LPWSTR shi2_remark; *!* 12 DWORD shi2_permissions; *!* 16 DWORD shi2_max_uses; *!* 20 DWORD shi2_current_uses; *!* 24 LPWSTR shi2_path; *!* 28 LPWSTR shi2_passwd; *!* } SHARE_INFO_2 RETURN lcLocalPath PROCEDURE _LocalName2UNC PARAMETERS tcLocalName LOCAL lcUNCBuffer, lnLength, lcLocalName DECLARE INTEGER WNetGetConnection IN WIN32API ; STRING @ lpLocalName, ; STRING @ lpRemoteName, ; INTEGER @ lplnLength IF EMPTY(tcLocalName) OR TYPE("tcLocalName") <> "C" ERROR 11 ENDIF lcLocalName = ALLTRIM(tcLocalName) IF LEN(lcLocalName) = 1 lcLocalName = lcLocalName + ":" ENDIF lcUNCBuffer = REPL(CHR(0),261) lnLength = LEN(lcUNCBuffer) IF WNetGetConnection(lcLocalName, @lcUNCBuffer, @lnLength) = 0 lcRemoteName = LEFT(lcUNCBuffer,AT(CHR(0),lcUNCBuffer)-1) ELSE lcRemoteName = "" ENDIF RETURN lcRemoteName FUNCTION DeAllocNetAPIBuffer * * Frees the NetAPIBuffer allocated at the address specified by nPtr. * The API call is not supported under Win9x LPARAMETER tnBufferPointer DECLARE INTEGER NetApiBufferFree IN NETAPI32.DLL ; INTEGER lpBuffer RETURN (NetApiBufferFree(INT(tnBufferPointer)) = 0)
Comments
Thank you! It's what I need.
It's what I need.
Error 5
Pls see the line with the _msgbox(...,64, Unable to continue), there seems to be too many parameters
I changed it to messagebox(), but always get error 5 from netfileenum (admin, file exists on a netshare, a dbf opened by me)
What am I doing wrong ?
BTW : The parameters in the debugger all have one space too much at the end, eg servername "server " while its "server"
Thanks and best regards
tom
Error 5 - Access is denied.
The Error 5 is 'Access is denied - The user does not have access to the requested information'. Something is not right with your permissions.
If there was a space at the end of server name, you would get error 123 not 5.
I fixed _msgbox problem an uploaded new version. Thanks.
WhoHasFileOpen.zip
Could you help - the WhoHasFileOpen.zip file has gone!
I would be very grateful if you could you send it to me please?
DDSquid@hotmail.com
Downloads are back
Error5
I downloaded the file NOW and I get messagebox 'netsharegetinfoerror acceses ... server(myserver name) share..'.
I get the message even I open this table from vfp and I try 'WhoHasFileOpen' AS FOLLOWING:
Thanks
The user running this program must have sufficient rights
Can I code it?
Can I access by code or by RunAS from my local?
Thanks
Permisions, permisions, permissions ...
Good New for my
Muchas Gracias y disculpe mi ingles. (very tank and sorry for my poor inglish)
Thanks for WhoHasFileOpen
Thank you very much for both your work and that you have decided to share your efforts.
Why i am doing wrong that it is not working for me
I am running this like this
whohasfileopen("S:\PRO50\APEX04\POTRAN04.dbf")
where S: drive is the letter mapped to the shared folder name "Accounting" and in that folder are folders "PRO50" and "APEX04", when i run the above command from the command window, i get this " function, argument value, type, or count is invalid", after i press "ignore" then i get the messagebox with "FILE MASK IS NOT SUPPORTED" , i am running this from VFP 9.0 SPK2, Please advise thanks
Re: Why i am doing wrong that it is not working for me
whohasfileopen
I wonder if you can help please.
Thanks
John
Re:P whohasfileopen
error 53