B4J Question B4J: Getting file detailed information [solved, sort of...]

Didier9

Well-Known Member
Licensed User
Longtime User
I need to get a file's detailed information (actual file size, creation date) equivalent to what we get from a DIR command at the Command Prompt, or from the File->Property menu in Explorer.
Any suggestion appreciated.
 

Mark Read

Well-Known Member
Licensed User
Longtime User
It may help a little to use the shell command (link) with the command:

B4X:
wmic datafile where Name="F:\\anyfile.txt" list /format:list 'change F:\\anyfile.txt accordingly

gives the output

1638534362019.png
 
Upvote 0

Didier9

Well-Known Member
Licensed User
Longtime User
That's a lot more information than I need but I'll take it :)
Thank you.
Is there a way to get the output from the command directly into B4J? At the moment, I am saving it to a file and opening the file in B4J, not the most elegant.
This is what I have at the moment:
B4X:
        Dim d As String = ProjectDir & "\"
        Dim MyFile as string = "test.txt"
        MyFile = ProjectDir & "\" & MyFile
        Dim script As String = $"
          Get-Item -Path ${MyFile} | fl * > ${d}details.txt
        "$
        Wait For (PowerShellScript(script)) Complete (Result As ShellSyncResult)
        If Result.ExitCode = 0 Then
            Dim lst As List = File.ReadList( ProjectDir, "details.txt" )
            Log( lst )
        End If
 
Upvote 0

Mark Read

Well-Known Member
Licensed User
Longtime User
You can give this a try, it needs some tidying up (notice I had to change the command format!):

B4X:
#Region Project Attributes 
    #MainFormWidth: 600
    #MainFormHeight: 600 
#End Region

Sub Process_Globals
    Private fx As JFX
    Private MainForm As Form
    Private xui As XUI 
    Private Button1 As B4XView
    Private Directory As String="E:"
    Private FileName As String="test.txt"
    
    'Get-Item -Path e:\test.txt | f1 *
    
    Private Script As String=$"Get-ItemProperty -Path ${Directory}\${FileName} | format-list"$
        
    Private res As ShellSyncResult
    End Sub

Sub AppStart (Form1 As Form, Args() As String)
    MainForm = Form1
    MainForm.RootPane.LoadLayout("Layout1")
    MainForm.Show
    PowerShellScript(Script)
End Sub

Sub Button1_Click
    MainForm.Close       
End Sub

Public Sub PowerShellScript(s As String) As ResumableSub
    's = s.Replace(CRLF, ";").Replace("""", "'")
    Dim shl As Shell
    shl.InitializeDoNotHandleQuotes("shl", "powershell.exe", Array("-Command", s))
    shl.Run(-1)
    Wait For shl_ProcessCompleted (Success As Boolean, ExitCode As Int, StdOut As String, StdErr As String)
    
    res.ExitCode = ExitCode
    res.StdErr = StdErr
    res.StdOut = StdOut
    res.Success = Success
    If StdErr <> "" Then
        Log(StdErr)
        If ExitCode = 0 Then res.ExitCode = 1
    End If    
    
    Dim InfoArray() As String=Regex.Split(CRLF, StdOut)
    Log(InfoArray(8))
    
    Return res
End Sub

Gives the output:

1638547653937.png
 
Last edited:
Upvote 0

Didier9

Well-Known Member
Licensed User
Longtime User
Thank you, but that's getting me to the next problem... The strings returned by the shell are encoded with 16 bit chars, and for some reason IndexOf() or Contain() do not work. I have spent an hour trying to understand why Contain( "LastWriteTime" ) did not work even though the Log was showing the record just fine. That is true for the StdOut version you have and for the file solution I have been using.

I have been trying to find what kind of encoding/decoding would return a "normal" string that IndexOf() and Contain() can deal with with little success.

I ended up converting the string to a byte array and creating a new string from the byte array using only the lower 8 bits.
It's been a long while since I had to do something that ugly, but what I am working on is a utility for my own usage that is supposed to save me time, not software that I will release to the masses. I am prepared to deal with some ugliness if it gets me where I need to go...

So far, not so good.

What kind of encoding is the shell using and how can I convert that to a string that the normal string functions can deal with?

The weird part if that the debugger (as your example shows) has no problem displaying the "string".
 
Upvote 0

Didier9

Well-Known Member
Licensed User
Longtime User
Here is an example of what I mean.
Debugging the code, the debugger indicates that the string s2 is not empty:

1638555188992.png


Yet, when I execute that line, I get an Empty String error:

1638555248320.png


The line above the error in the Log window is the ASCII decoding of s2, byte by byte:
32 32 32 32 68 105 114 101 99 116 111 114 121 58 32 67 58 92 69 120 112 108 111 116 114 97 105 110 92 88 45 83 65 70 84 73 92 70 53 56 48 92 100 101 118

Why is java saying it's an empty string?

Here is the entire code block:

B4X:
    Log( s2 )
    Dim sout As String
    For n=0 To s2.Length-1
         sout = sout & " " & Asc( s2.SubString( n ))
    Next
    Log( sout )
    If s2.IndexOf( "LastWriteTime" ) > -1 Then
        writeTime = s2.SubString( s2.IndexOf( ":" ))
        Exit
    End If
 
Last edited:
Upvote 0

Mark Read

Well-Known Member
Licensed User
Longtime User
Sorry I was not on my PC.

The strings returned by the shell are encoded with 16 bit chars, and for some reason IndexOf() or Contain() do not work

I cannot reproduce your error. I tried this code:

B4X:
Public Sub PowerShellScript(s As String) As ResumableSub
    's = s.Replace(CRLF, ";").Replace("""", "'")
    Dim shl As Shell
    shl.InitializeDoNotHandleQuotes("shl", "powershell.exe", Array("-Command", s))
    shl.Run(-1)
    Wait For shl_ProcessCompleted (Success As Boolean, ExitCode As Int, StdOut As String, StdErr As String)
    
    res.ExitCode = ExitCode
    res.StdErr = StdErr
    res.StdOut = StdOut
    res.Success = Success
    If StdErr <> "" Then
        Log(StdErr)
        If ExitCode = 0 Then res.ExitCode = 1
    End If    
    
    InfoArray=Regex.Split(CRLF, StdOut)
    Log($"Array size: ${InfoArray.Length}"$)
    Log(InfoArray(8))
    Log(" ")
    Log($"Position of year: ${InfoArray(8).IndexOf(2021)}"$)         '<-------- here i am using indexof!!!
    Return res
End Sub

which gave me:

1638626245838.png
 
Upvote 0

Mark Read

Well-Known Member
Licensed User
Longtime User
This is just an after thought as I cannot see your whole code.

The output from the powershell command is a long multiline string with CRLF as seperator. I have split this multiline string with Regex on the CRLF and put the answer into an array. To get the "LastWriteTime", I need to know the position in the array of course. Either I look for it or assume it is always in the same place. Remembering the the array index starts from 0. Using the string functions is no problem. See line 21 in my code above.
 
Upvote 0

Mark Read

Well-Known Member
Licensed User
Longtime User
This works fine with me to get the variable you want.

B4X:
Public Sub PowerShellScript(s As String) As ResumableSub
    's = s.Replace(CRLF, ";").Replace("""", "'")
    Dim shl As Shell
    shl.InitializeDoNotHandleQuotes("shl", "powershell.exe", Array("-Command", s))
    shl.Run(-1)
    Wait For shl_ProcessCompleted (Success As Boolean, ExitCode As Int, StdOut As String, StdErr As String)
    
    res.ExitCode = ExitCode
    res.StdErr = StdErr
    res.StdOut = StdOut
    res.Success = Success
    If StdErr <> "" Then
        Log(StdErr)
        If ExitCode = 0 Then res.ExitCode = 1
    End If    
    
    InfoArray=Regex.Split(CRLF, StdOut)
    Log($"Array size: ${InfoArray.Length}"$)
    Log(" ")
    Log(StdOut)
    Log(" ")
    Log($"Position of year: ${InfoArray(9).IndexOf( 2021 )}"$)
    
    writetime=InfoArray(9).SubString2(InfoArray(9).IndexOf(2021)-6,InfoArray(9).IndexOf(2021)+4)
    Log($"Last Write Time: ${writetime}"$)
    Return res
End Sub
 
Upvote 0

Mark Read

Well-Known Member
Licensed User
Longtime User
If you still cannot get your code to work, then I suggest you upload the project and I/we can take a look at it.
 
Upvote 0

stevel05

Expert
Licensed User
Longtime User
The small code module (in the project) may help it uses the java nio Files Class to get some file attributes:

B4X:
Waiting for debugger to connect...
Program started.
lastAccessTime 2020-05-19T09:58:55.583569Z
lastModifiedTime 2007-11-07T08:00:40Z
size 17734
creationTime 2007-11-07T08:00:40Z
isSymbolicLink false
isRegularFile true
fileKey null
isOther false
isDirectory false
***
Creation Time : 2007-11-07T08:00:40Z

For more information see the javadoc

You can get all attributes in a Map, or specify which one you want.
 

Attachments

  • FilesNio.zip
    1.6 KB · Views: 114
Last edited:
Upvote 0

jkhazraji

Active Member
Licensed User
Longtime User
B4X:
Sub AppStart (Form1 As Form, Args() As String)
    MainForm = Form1
    MainForm.RootPane.LoadLayout("Layout1")
    MainForm.Show
    Dim JO As JavaObject = Me
    dim FilePath as string ="C:\......."
    JO.RunMethod("FileDetails",Array(filePath))
   
End Sub

#if java
import java.io.File;
import java.io.IOException;
import java.text.ParseException;
import java.util.Date;
    public static void FileDetails(String filePath)    {
        File file = new File(filePath); //with \ escape pattern for backslashes
        System.out.println("Name: " + file.getName());
        System.out.println("Absolute path: " + file.getAbsolutePath());
        System.out.println("Size: " + file.length());
        System.out.println("Last modified: " + new Date(file.lastModified()));
    }
   


#End If
 
Upvote 0

EnriqueGonzalez

Well-Known Member
Licensed User
Longtime User
B4X:
Sub AppStart (Form1 As Form, Args() As String)
    MainForm = Form1
    MainForm.RootPane.LoadLayout("Layout1")
    MainForm.Show
    Dim JO As JavaObject = Me
    dim FilePath as string ="C:\......."
    JO.RunMethod("FileDetails",Array(filePath))
  
End Sub

#if java
import java.io.File;
import java.io.IOException;
import java.text.ParseException;
import java.util.Date;
    public static void FileDetails(String filePath)    {
        File file = new File(filePath); //with \ escape pattern for backslashes
        System.out.println("Name: " + file.getName());
        System.out.println("Absolute path: " + file.getAbsolutePath());
        System.out.println("Size: " + file.length());
        System.out.println("Last modified: " + new Date(file.lastModified()));
    }
  


#End If
You should learn javaobject you could save a lot of work. It is easier than it seems
 
Upvote 0
Top