[VB6] bestanden groter dan 2 GB*

Pagina: 1
Acties:

  • Nocturno
  • Registratie: September 2001
  • Laatst online: 30-04 20:32
Hoi,

ik ben bezig met een programma om .ts (transport-streams) te joinen.
.ts bestanden zijn meestal in stukken van 105mb , deze bestanden hebben geen header en kunnen klakkeloos aan elkaar.
normaal gebruikt met vaak het dos commando : copy /B *.ts joined.ts
ik wil hier dus een mooie windows oplossing voor maken ,
op het moment maakt mijn code gebruik van get en put,
ik lees(get) stukjes van 20kb in van de bronbestanden en schrijf(put) deze weer weg naar 1 doelbestand.
dit gaat prima totdat mijn doelbestand groter dan 2gb is, dan tript het put statement op een invalid record number.
ik denk dat het put "recordnumber"(lees beginbyte) een long is en dus niet groter dan 2gb mag zijn.
en ik moet tot 15 a 20gb kunnen.

weet iemand een oplossing hiervoor?
google geeft weinig oplossingen waar ik iets mee kan.
wellicht de readfile / writefile api? maar ik kan nergens vinden of die ook een 2 gb limiet hebben.

  • NMe
  • Registratie: Februari 2004
  • Laatst online: 15-04 22:07

NMe

Quia Ego Sic Dico.

Met even Googlen kom ik op deze site uit, waar je volgens mij wel iets mee moet kunnen. Ik ben zelf geen VB-programmeur, maar het ziet er in elk geval veelbelovend uit. :)

Over welke versie van VB praten we eigenlijk? VB6? VB.NET? VBScript?

'E's fighting in there!' he stuttered, grabbing the captain's arm.
'All by himself?' said the captain.
'No, with everyone!' shouted Nobby, hopping from one foot to the other.


  • Nocturno
  • Registratie: September 2001
  • Laatst online: 30-04 20:32
sorry, VB6 bedoel ik.

die class had ik ook gevonden,maar ik kon daar niet iets duidelijks voor vb6 in vinden. nu kun je ook vbscript gebruiken, maar dan krijg je problemen met mensen die scripting af hebben staan voor security redenen.

maar ik zal het eens proberen.

  • Gerco
  • Registratie: Mei 2000
  • Laatst online: 04-05 22:29

Gerco

Professional Newbie

Ik denk dat je echt de Win32 API functies hiervoor moet gebruiken. Ik zie dat de nNumberOfBytesToRead/Write parameter een DWORD is, dus je zal niet meer dan 4GB in 1 keer kunnen lezen of schrijven, maar dat lijkt me niet zo bezwaarlijk.

- "Als ik zou willen dat je het begreep, legde ik het wel beter uit!" | All number systems are base 10!


  • MSalters
  • Registratie: Juni 2001
  • Laatst online: 09-04 22:08
Kun je niet simpelweg vanuit VB6 een CreateProcess CMD.EXE doen, met COPY ... als argumenten?

Man hopes. Genius creates. Ralph Waldo Emerson
Never worry about theory as long as the machinery does what it's supposed to do. R. A. Heinlein


  • RobIII
  • Registratie: December 2001
  • Niet online

RobIII

Admin Devschuur®

^ Romeinse Ⅲ ja!

(overleden)
Even from the top of my head:
Geef je wel een record lengte mee bij het openen van je bestand?
Gezien je een record-nummer moet doorgeven bij PUT zou je (theoretisch) dus, als je recordlengte 100 bytes is, een factor 100 groter moeten kunnen schrijven. Gebruik je records van 20Kb dan is dat natuurlijk nog vele malen groter.
Overigens geef ik (bij het "in 1 klap schrijven" van bestanden) nooit een recordnummer mee: Gewoon
Visual Basic 6:
1
Put #MyFile, , MyVar

In dat geval zou je een overflow vermijden omdat de gegevens gewoon achteraan worden geschreven.

Maar los van dat alles kan ik me goed voorstellen dat (native)VB6 beperkt is op 2Gb, maar dat zou dan ook in de documentatie moeten staan.

/edit:
Effe geprobeerd:
Visual Basic:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
    Dim sMyRec As String
    Dim FF As Integer
    Dim lRecords As Long
    Dim T As Long
    
    Const cRecSize = 20480              '20Kb records
    Const c2Gb = 2147483648#            '2Gb te gaan
    
    sMyRec = String(cRecSize, "*")      'Vul dummy string met cRecSize sterretjes
    lRecords = (c2Gb / cRecSize) + 10   '10 records over "de grens" heen te schrijven
    
    FF = FreeFile                       'File handle verkrijgen
    
    Open "D:\test.dat" For Binary As #FF Len = cRecSize
    For T = 1 To lRecords               'Records gaan knallen
        Put #FF, , sMyRec
        Debug.Print T, lRecords
    Next
    Close #FF

*POEF*
Alsnog een bad-record number :Y)
Native VB6 kan (volgens mij en deze snelle test dus) niet over de 2Gb heen :P
Overigens getest op NTFS (want op Fat32 kun je zowieso niet over de 2Gb heen) Correctie:4Gb toch?

Maar 2 minuten googlen leverde dit op: http://support.microsoft....aspx?scid=kb;en-us;189981 :Y)

En die heb ik even getest en die werkt wel prima (heb 't even wat opgeschoond, Microsoft heeft nogal een handje van raar inspringen en line_ continuations en daar heb ik een schurft aan als je op hoge resoluties draait :Y) ):
  1. Start een nieuw VB6 project
  2. Voeg een class toe en geef deze de naam "RandomAccessFile" (Ik vind Microsoft's suggestie "Random" te vaag).
  3. Paste daarin deze code:
    Visual Basic:
    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
    
    Option Explicit
    
    Public Enum W32F_Errors
        W32F_UNKNOWN_ERROR = 45600
        W32F_FILE_ALREADY_OPEN
        W32F_PROBLEM_OPENING_FILE
        W32F_FILE_ALREADY_CLOSED
        W32F_Problem_seeking
    End Enum
    
    Private Const W32F_SOURCE = "Win32File Object"
    
    Private Const GENERIC_WRITE = &H40000000
    Private Const GENERIC_READ = &H80000000
    Private Const FILE_ATTRIBUTE_NORMAL = &H80
    Private Const CREATE_ALWAYS = 2
    Private Const OPEN_ALWAYS = 4
    Private Const INVALID_HANDLE_VALUE = -1
    
    Private Const FILE_BEGIN = 0, FILE_CURRENT = 1, FILE_END = 2
    Private Const FORMAT_MESSAGE_FROM_SYSTEM = &H1000
    
    Private Declare Function FormatMessage Lib "kernel32" Alias "FormatMessageA" (ByVal dwFlags As Long, lpSource As Long, ByVal dwMessageId As Long, ByVal dwLanguageId As Long, ByVal lpBuffer As String, ByVal nSize As Long, Arguments As Any) As Long
    Private Declare Function ReadFile Lib "kernel32" (ByVal hFile As Long, lpBuffer As Any, ByVal nNumberOfBytesToRead As Long, lpNumberOfBytesRead As Long, ByVal lpOverlapped As Long) As Long
    Private Declare Function CloseHandle Lib "kernel32" (ByVal hObject As Long) As Long
    Private Declare Function WriteFile Lib "kernel32" (ByVal hFile As Long, lpBuffer As Any, ByVal nNumberOfBytesToWrite As Long, lpNumberOfBytesWritten As Long, ByVal lpOverlapped As Long) As Long
    Private Declare Function CreateFile Lib "kernel32" Alias "CreateFileA" (ByVal lpFileName As String, ByVal dwDesiredAccess As Long, ByVal dwShareMode As Long, ByVal lpSecurityAttributes As Long, ByVal dwCreationDisposition As Long, ByVal dwFlagsAndAttributes As Long, ByVal hTemplateFile As Long) As Long
    Private Declare Function SetFilePointer Lib "kernel32" (ByVal hFile As Long, ByVal lDistanceToMove As Long, lpDistanceToMoveHigh As Long, ByVal dwMoveMethod As Long) As Long
    Private Declare Function FlushFileBuffers Lib "kernel32" (ByVal hFile As Long) As Long
    
    Private hFile As Long
    Private sFName As String
    Private fAutoFlush As Boolean
    
    Public Property Get FileHandle() As Long
        If hFile = INVALID_HANDLE_VALUE Then RaiseError W32F_FILE_ALREADY_CLOSED
        FileHandle = hFile
    End Property
    
    Public Property Get FileName() As String
        If hFile = INVALID_HANDLE_VALUE Then RaiseError W32F_FILE_ALREADY_CLOSED
          FileName = sFName
    End Property
    
    Public Property Get IsOpen() As Boolean
        IsOpen = hFile <> INVALID_HANDLE_VALUE
    End Property
    
    Public Property Get AutoFlush() As Boolean
        If hFile = INVALID_HANDLE_VALUE Then RaiseError W32F_FILE_ALREADY_CLOSED
        AutoFlush = fAutoFlush
    End Property
    
    Public Property Let AutoFlush(ByVal NewVal As Boolean)
        If hFile = INVALID_HANDLE_VALUE Then RaiseError W32F_FILE_ALREADY_CLOSED
        fAutoFlush = NewVal
    End Property
    
    Public Sub OpenFile(ByVal sFileName As String)
        If hFile <> INVALID_HANDLE_VALUE Then RaiseError W32F_FILE_ALREADY_OPEN, sFName
        hFile = CreateFile(sFileName, GENERIC_WRITE Or GENERIC_READ, 0, 0, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, 0)
        If hFile = INVALID_HANDLE_VALUE Then RaiseError W32F_PROBLEM_OPENING_FILE, sFileName
        sFName = sFileName
    End Sub
    
    Public Sub CloseFile()
        If hFile = INVALID_HANDLE_VALUE Then RaiseError W32F_FILE_ALREADY_CLOSED
        CloseHandle hFile
        sFName = ""
        fAutoFlush = False
        hFile = INVALID_HANDLE_VALUE
    End Sub
    
    Public Function ReadBytes(ByVal ByteCount As Long) As Variant
        Dim BytesRead As Long, Bytes() As Byte
      
        If hFile = INVALID_HANDLE_VALUE Then RaiseError W32F_FILE_ALREADY_CLOSED
        ReDim Bytes(0 To ByteCount - 1) As Byte
        ReadFile hFile, Bytes(0), ByteCount, BytesRead, 0
        ReadBytes = Bytes
    End Function
    
    Public Sub WriteBytes(DataBytes() As Byte)
        Dim fSuccess As Long, BytesToWrite As Long, BytesWritten As Long
        
        If hFile = INVALID_HANDLE_VALUE Then RaiseError W32F_FILE_ALREADY_CLOSED
        BytesToWrite = UBound(DataBytes) - LBound(DataBytes) + 1
        fSuccess = WriteFile(hFile, DataBytes(LBound(DataBytes)), BytesToWrite, BytesWritten, 0)
        If fAutoFlush Then Flush
    End Sub
    
    Public Sub Flush()
        If hFile = INVALID_HANDLE_VALUE Then RaiseError W32F_FILE_ALREADY_CLOSED
        FlushFileBuffers hFile
    End Sub
    
    Public Sub SeekAbsolute(ByVal HighPos As Long, ByVal LowPos As Long)
        If hFile = INVALID_HANDLE_VALUE Then RaiseError W32F_FILE_ALREADY_CLOSED
        LowPos = SetFilePointer(hFile, LowPos, HighPos, FILE_BEGIN)
    End Sub
    
    Public Sub SeekRelative(ByVal Offset As Long)
        Dim TempLow As Long, TempErr As Long
      
        If hFile = INVALID_HANDLE_VALUE Then RaiseError W32F_FILE_ALREADY_CLOSED
        TempLow = SetFilePointer(hFile, Offset, ByVal 0&, FILE_CURRENT)
        If TempLow = -1 Then
            TempErr = Err.LastDllError
            If TempErr Then RaiseError W32F_Problem_seeking, "Error " & TempErr & "." & vbCrLf & CStr(TempErr)
        End If
    End Sub
    
    Private Sub Class_Initialize()
        hFile = INVALID_HANDLE_VALUE
        fAutoFlush = True
        sFName = ""
    End Sub
    
    Private Sub Class_Terminate()
        If hFile <> INVALID_HANDLE_VALUE Then CloseHandle hFile
    End Sub
    
    Private Sub RaiseError(ByVal ErrorCode As W32F_Errors, Optional sExtra)
        Dim Win32Err As Long, Win32Text As String
        Win32Err = Err.LastDllError
        
        If Win32Err Then Win32Text = vbCrLf & "Error " & Win32Err & vbCrLf & DecodeAPIErrors(Win32Err)
        Select Case ErrorCode
            Case W32F_FILE_ALREADY_OPEN
                Err.Raise W32F_FILE_ALREADY_OPEN, W32F_SOURCE, "The file '" & sExtra & "' is already open." & Win32Text
            Case W32F_PROBLEM_OPENING_FILE
                Err.Raise W32F_PROBLEM_OPENING_FILE, W32F_SOURCE, "Error opening '" & sExtra & "'." & Win32Text
            Case W32F_FILE_ALREADY_CLOSED
                Err.Raise W32F_FILE_ALREADY_CLOSED, W32F_SOURCE, "There is no open file."
            Case W32F_Problem_seeking
                Err.Raise W32F_Problem_seeking, W32F_SOURCE, "Seek Error." & vbCrLf & sExtra
            Case Else
                Err.Raise W32F_UNKNOWN_ERROR, W32F_SOURCE, "Unknown error." & Win32Text
        End Select
    End Sub
    
    Private Function DecodeAPIErrors(ByVal ErrorCode As Long) As String
        Dim sMessage As String, MessageLength As Long
        
        sMessage = Space$(256)
        MessageLength = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM, 0&, ErrorCode, 0&, sMessage, 256&, 0&)
        If MessageLength > 0 Then
            DecodeAPIErrors = Left(sMessage, MessageLength)
        Else
            DecodeAPIErrors = "Unknown Error."
        End If
    End Function
  4. Gooi in je lege form (Form1) deze code:
    Visual Basic:
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    
    Option Explicit
    
    Private Sub Form_Load()
        Dim oFile As RandomAccessFile
        Dim sMyRec As String
        Dim lRecords As Long
        Dim T As Long
        
        Const cRecSize = 20480              '20Kb records
        Const c2Gb = 2147483648#            '2Gb te gaan
        
        sMyRec = String(cRecSize, "*")      'Vul dummy string met cRecSize sterretjes
        lRecords = (c2Gb / cRecSize) + 10   '10 records over "de grens" heen te schrijven
        
        Set oFile = New RandomAccessFile
        oFile.OpenFile "D:\test.dat"
        For T = 1 To lRecords               'Records gaan knallen
            oFile.WriteBytes StrConv(sMyRec, vbFromUnicode)
            Debug.Print T, lRecords
            If T Mod 100 = 0 Then DoEvents
        Next
        oFile.CloseFile
    End Sub
  5. Eventueel D:\Test.dat even wijzigen in iets anders
  6. Mep F5 en hou je immediate window in de gaten
Al met al is het dus een kleine moeite om je probleem te omzeilen ;)
Met dank aan Microsoft en MSDN voor het voorkauwen _/-\o_
MSalters schreef op woensdag 22 juni 2005 @ 09:50:
Kun je niet simpelweg vanuit VB6 een CreateProcess CMD.EXE doen, met COPY ... als argumenten?
Dat is wel slim, maar tevens lastig als je er een progress-bar aan wil hangen ofzo ;)

[ Voor 200% gewijzigd door RobIII op 22-06-2005 17:25 ]

There are only two hard problems in distributed systems: 2. Exactly-once delivery 1. Guaranteed order of messages 2. Exactly-once delivery.

Je eigen tweaker.me redirect

Over mij


  • Nocturno
  • Registratie: September 2001
  • Laatst online: 30-04 20:32
dat was ik zelf nu ook aan het testen ,maar het werkt nog niet helemaal, ongetwijfeld door mijn eigen gepruts, hij leest meerdere invoerbestanden en moet deze aan 1 bestand joinen, of als er een split limiet ingesteld in meerdere bestanden joinen (bv 4,5gb voor op dvd te fikken), daar heb ik iets nog niet kloppend, want ik heb de api's die jullie als suggestie geven nu ook gebruikt, maar ik kom er nu wel uit.

ik gebruikte als put inderdaad ook Put #MyFile, , MyVar , en dat gaat prima tot 2 gb inderdaad.

bedankt voor jullie reacties, ik kom er nu wel uit. ;)

ps. idd copy.exe aanroepen had ook gekund, maar das geen sport, en ik gebruik idd ook progressbars. en joinen+splitten op een precieze filegrote word dan ook lastig.

  • Nocturno
  • Registratie: September 2001
  • Laatst online: 30-04 20:32
werkt nu prima, nogmaals dank.
heb mijn eigen class maar weggepleurt en deze gebruikt.
das er weer eentje voor mijn class verzameling ;)
Pagina: 1