Long paths

K

Karl E. Peterson

krazymike said:
Thanks. That's a lot of help. You know it's people like you who make
it nearly impossible to get any help. Trying to make people look bad
and yourself good only serves to expose you as the jerk you clearly
are.

Bite me. I've been here far too long to care how I look to random newbs. And,
fwiw, it's almost certain that a majority of regulars here would agree it's people
like you who make it nearly impossible for, well, people like you to get any help
here. If you can't describe the problem, give it up.
"Doesn't work" = hSearch gets value of -1 no matter what I do.

What's Err.LastDllError report?
Prepending the \\?\ doesn't change anything.

There are *fundamental* differences in calling A/W functions from VB. As I stated,
you need to have a grasp on how pointers are passed to external libraries.
What I was told that my
code that works with the FindFirstFileA/FindNextFileA would work by
switching to the W implementation of these functions. I'm finding
that this is not the case.

Wrong. That is the case. But it's like switching to diesel to improve your milage.
There's "a bit more to it" than just that.
I read somewhere that you need to pass the parameters to the W
implementations as UTF-16 strings, but I have yet to have any luck
with that either. The UTF-16 converter I found was returning null
strings. I tried using MultiByteToWideChar to generate a UTF-8, but I
don't think that's really doing much. The handler still receives the
-1 value.

VB stores all strings as Unicode internally. How you pass them to external
libraries will determine what they recieve. That's *why* I asked you how you were
doing that. In particular, for your declares (function and structure) and the
actual call. If you can't provide those, we're just pounding sand here. <-- last
time I'm nice about that.
I know this has to be possible, but perhaps not from VBA?

Of course it is.
If I can get this working in vb6, that'd do fine, too.

From this perspective, they are functionally identical languages.
I'm just restricted
from using .Net in my department. Not my choice, just the way
"business" decisions are made sometimes.

It's a good decision. Trust me. :)

Btw, I'm curious why you entirely ignored my initial suggestion. Given your
description of the actual problem, it still seems to me like you're working far
harder than needed to find a solution to a problem you don't really have.
 
K

Karl E. Peterson

krazymike said:
Prepending the \\?\ doesn't change anything. What I was told that my
code that works with the FindFirstFileA/FindNextFileA would work by
switching to the W implementation of these functions. I'm finding
that this is not the case.

Further reading:

Naming a File (Windows)
http://msdn.microsoft.com/en-us/library/aa365247(VS.85).aspx

Specifically, the "Maximum Path Length" section which covers specifying Unicode and
UNC paths. I'm not sure, given what information you've provided, whether UNC paths
may also be part of what needs to be in consideration here?
 
H

Howard Kaikow

Ok, back to the original topic.

I used FindFirstFileA and FindNextFileA and was able to access a path
of 306 characters. Now, I need to cover the UNICODE characters which
may be in the file names. We get stuff from macs and linux machines
from time to time. As far as I understand, I should be able to change
the A to a W to use the unicode version. That doesn't work. I
prepended the "\\?\" to the path, and had no luck. I'm wondering if I
need some language packs installed to use unicode at all.

Any thoughts?

You have to do more than just change the A to a W.
E.g., In the Declare, for each String Type, change the type to Long, and
pass the string using StrPtr.

I hope that others will help further.
I'm having a serious dental problem and need to find a good oral surgeon
today.

Oh well, getting info is sometines like pulling teet ...!
 
E

expvb

krazymike said:
Prepending the \\?\ doesn't change anything. What I was told that my
code that works with the FindFirstFileA/FindNextFileA would work by
switching to the W implementation of these functions. I'm finding
that this is not the case.


Post your declaration for the function and how you are calling it. You may
be still using the A version especially if you didn't edit the "Alias" part.
Also, you have to use StrPtr() when using the W version.
 
K

krazymike

Alright, let's start over. I'm sorry I got everyone upset. I was
pretty fed up with my code not working, and my boss breathing down my
neck. This is not actually the last state of my code, but I was so
fed up that I didn't save the changes I had made since it had gotten
pretty ugly-looking.

Before I call FindFilesAPI, I test if the path is UNC or not, and set
the "\\?\" or "\\?\UNC\" accordingly. I also drop the double "\\"
before the UNC path so it's "\\?\UNC\server\share". I can't think
of what else to include here.

Oh, we can't necessarily map closer to the data because this database
gets fed into a document review system from a different machine, and
the address has to be universal. Both for the loading into the review
system, and for while the attorneys actually use the review system.
We're working on a solution to flatten the file structure, but we're
still in the approval process. Don't you just love bureaucracy?

===DECLARES===

Private Declare Function FindFirstFile Lib "kernel32" Alias
"FindFirstFileW" (ByVal lpFileName As String, _
lpFindFileData As WIN32_FIND_DATAW) As Long
Private Declare Function FindNextFile Lib "kernel32" Alias
"FindNextFileW" (ByVal hFindFile As Long, _
lpFindFileData As WIN32_FIND_DATAW) As Long
Private Declare Function GetFileAttributes Lib "kernel32" Alias
"GetFileAttributesA" (ByVal lpFileName As String) As Long
Private Declare Function FindClose Lib "kernel32" (ByVal hFindFile As
Long) As Long

Const MAX_PATH = 260
Const MAXDWORD = &HFFFF
Const INVALID_HANDLE_VALUE = -1
Const FILE_ATTRIBUTE_ARCHIVE = &H20
Const FILE_ATTRIBUTE_DIRECTORY = &H10
Const FILE_ATTRIBUTE_HIDDEN = &H2
Const FILE_ATTRIBUTE_NORMAL = &H80
Const FILE_ATTRIBUTE_READONLY = &H1
Const FILE_ATTRIBUTE_SYSTEM = &H4
Const FILE_ATTRIBUTE_TEMPORARY = &H100

Private Type FILETIME
dwLowDateTime As Long
dwHighDateTime As Long
End Type

Private Type WIN32_FIND_DATAW
dwFileAttributes As Long
ftCreationTime As FILETIME
ftLastAccessTime As FILETIME
ftLastWriteTime As FILETIME
nFileSizeHigh As Long
nFileSizeLow As Long
dwReserved0 As Long
dwReserved1 As Long
cFileName As String * MAX_PATH
cAlternateFileName As String * 14
End Type

===Call===

Function FindFilesAPI(ByVal path As String)
Dim FileName As String ' Walking filename variable...
Dim i As Integer ' For-loop counter...
Dim hSearch As Long ' Search Handle
Dim WFD As WIN32_FIND_DATAW
Dim Cont As Integer
If Right(path, 1) <> "\" Then path = path & "\"
Cont = True
hSearch = FindFirstFile(path & "*.*" & vbNullChar, WFD)
If hSearch <> INVALID_HANDLE_VALUE Then
.. . . rest of code.
 
M

Mike Williams

Before I call FindFilesAPI, I test if the path is UNC or not, and set
the "\\?\" or "\\?\UNC\" accordingly. I also drop the double "\\"
before the UNC path so it's "\\?\UNC\server\share". I can't think
of what else to include here.
Private Declare Function FindFirstFile Lib "kernel32" Alias
"FindFirstFileW" (ByVal lpFileName As String, _
lpFindFileData As WIN32_FIND_DATAW) As Long

VB6 stores all strings internally using two bytes per character and when you
pass a VB String to an API function then VB makes a "one byte per character"
copy of the "2 bytes per character VB String" and passes that instead. The
opposite happens when a String is returned. I never use "W" versions of the
API functions myself, so I'm well out of my depth here, but I think that VB
still behaves in the same way when you use the "W" versions of the function
as well. If that is the case, which I think it is, then to pass the "two
byte per character String" that the above function is expecting you would
need to change the declaration so as to specify lpFileName as Long (instead
of lpFileName as String) and then pass the VB string pointer (as a Long) to
the function.

Mike
 
K

krazymike

Awesome!! It works, save one issue. The filenames alternate chars
with a white square (which I can only see in debug mode). Also, if I
just let the code fly, Access crashes. I have to put in several
breakpoints to slow it down for it to work.

Any ideas?
 
K

Karl E. Peterson

krazymike said:
Alright, let's start over. I'm sorry I got everyone upset. I was
pretty fed up with my code not working, and my boss breathing down my
neck.

Been there. No problem.
This is not actually the last state of my code, but I was so
fed up that I didn't save the changes I had made since it had gotten
pretty ugly-looking.

Before I call FindFilesAPI, I test if the path is UNC or not, and set
the "\\?\" or "\\?\UNC\" accordingly. I also drop the double "\\"
before the UNC path so it's "\\?\UNC\server\share". I can't think
of what else to include here.

That looks like a good start.
Oh, we can't necessarily map closer to the data because this database
gets fed into a document review system from a different machine, and
the address has to be universal. Both for the loading into the review
system, and for while the attorneys actually use the review system.
We're working on a solution to flatten the file structure, but we're
still in the approval process. Don't you just love bureaucracy?

Bummer, yeah.
===DECLARES===

Private Declare Function FindFirstFile Lib "kernel32" Alias
"FindFirstFileW" (ByVal lpFileName As String, _
lpFindFileData As WIN32_FIND_DATAW) As Long

Change that to (indented to highlight wordwrap):

Private Declare Function FindFirstFile Lib "kernel32" Alias "FindFirstFileW"
(ByVal lpFileName As Long, lpFindFileData As WIN32_FIND_DATAW) As Long
Private Declare Function FindNextFile Lib "kernel32" Alias
"FindNextFileW" (ByVal hFindFile As Long, _
lpFindFileData As WIN32_FIND_DATAW) As Long
Fine.

Private Declare Function GetFileAttributes Lib "kernel32" Alias
"GetFileAttributesA" (ByVal lpFileName As String) As Long

Wouldn't you want to use the same filename? If so, ...

Private Declare Function GetFileAttributes Lib "kernel32" Alias
"GetFileAttributesW" (ByVal lpFileName As Long) As Long
Private Declare Function FindClose Lib "kernel32" (ByVal hFindFile As
Long) As Long

Const MAX_PATH = 260
Const MAXDWORD = &HFFFF
Const INVALID_HANDLE_VALUE = -1
Const FILE_ATTRIBUTE_ARCHIVE = &H20
Const FILE_ATTRIBUTE_DIRECTORY = &H10
Const FILE_ATTRIBUTE_HIDDEN = &H2
Const FILE_ATTRIBUTE_NORMAL = &H80
Const FILE_ATTRIBUTE_READONLY = &H1
Const FILE_ATTRIBUTE_SYSTEM = &H4
Const FILE_ATTRIBUTE_TEMPORARY = &H100

Private Type FILETIME
dwLowDateTime As Long
dwHighDateTime As Long
End Type

Private Type WIN32_FIND_DATAW
dwFileAttributes As Long
ftCreationTime As FILETIME
ftLastAccessTime As FILETIME
ftLastWriteTime As FILETIME
nFileSizeHigh As Long
nFileSizeLow As Long
dwReserved0 As Long
dwReserved1 As Long
cFileName As String * MAX_PATH
cAlternateFileName As String * 14
End Type

This one I'm not entirely sure about, and will actually need to play with. Off the
top of my head, you'd probably want to change:
cFileName As String * MAX_PATH
cAlternateFileName As String * 14

to:

cFileName As Byte(0 To MAX_PATH * 2 - 1)
cAlternateFileName As Byte(0 To 27)
===Call===

Function FindFilesAPI(ByVal path As String)
Dim FileName As String ' Walking filename variable...
Dim i As Integer ' For-loop counter...
Dim hSearch As Long ' Search Handle
Dim WFD As WIN32_FIND_DATAW
Dim Cont As Integer
If Right(path, 1) <> "\" Then path = path & "\"
Cont = True
hSearch = FindFirstFile(path & "*.*" & vbNullChar, WFD)
If hSearch <> INVALID_HANDLE_VALUE Then
. . . rest of code.

If Right$(path, 1) <> "\" Then path = path & "\"
path = path & "*.*"
Cont = True
hSearch = FindFirstFile(StrPtr(path), WFD)

That oughta get you going. You'll probably end up needing to tweak that structure.
Might find http://vb.mvps.org/samples/HexDump useful?
 
T

Todd Vargo

krazymike said:
Awesome!! It works, save one issue. The filenames alternate chars
with a white square (which I can only see in debug mode). Also, if I
just let the code fly, Access crashes. I have to put in several
breakpoints to slow it down for it to work.

Any ideas?

White squares?

You're just beggin' to get flamed now.
 
K

Karl E. Peterson

krazymike said:
Awesome!! It works, save one issue. The filenames alternate chars
with a white square (which I can only see in debug mode). Also, if I
just let the code fly, Access crashes. I have to put in several
breakpoints to slow it down for it to work.

Any ideas?

See my other post.

You're still suffering UniMess: http://vb.mvps.org/vfred/unimess.asp
 
K

Karl E. Peterson

krazymike said:
Awesome!! It works, save one issue. The filenames alternate chars
with a white square (which I can only see in debug mode). Also, if I
just let the code fly, Access crashes. I have to put in several
breakpoints to slow it down for it to work.

Any ideas?

This works:

' *************************************************************************
' Copyright ©2008 Karl E. Peterson
' All Rights Reserved, http://vb.mvps.org/
' *************************************************************************
' You are free to use this code within your own applications, but you
' are expressly forbidden from selling or otherwise distributing this
' source code, non-compiled, without prior written consent.
' *************************************************************************
Option Explicit

' Win32 API declarations
Private Declare Function FindFirstFile Lib "kernel32" Alias "FindFirstFileW"
(ByVal lpFileName As Long, lpFindFileData As WIN32_FIND_DATA) As Long
Private Declare Function FindNextFile Lib "kernel32" Alias "FindNextFileW" (ByVal
hFindFile As Long, lpFindFileData As WIN32_FIND_DATA) As Long
Private Declare Function FindClose Lib "kernel32" (ByVal hFindFile As Long) As
Long

Private Const INVALID_FILE_ATTRIBUTES As Long = -1&
Private Const INVALID_HANDLE_VALUE As Long = -1&
Private Const MAX_PATH As Long = 260

' API structures.
Public Type FILETIME
dwLowDateTime As Long
dwHighDateTime As Long
End Type

Public Type WIN32_FIND_DATA
dwFileAttributes As Long
ftCreationTime As FILETIME
ftLastAccessTime As FILETIME
ftLastWriteTime As FILETIME
nFileSizeHigh As Long
nFileSizeLow As Long
dwReserved0 As Long
dwReserved1 As Long
' cFileName As String * MAX_PATH
cFileName(0 To (MAX_PATH * 2) - 1) As Byte
' cAlternate As String * 14
cAlternate(0 To 27) As Byte
End Type

Public Sub Main()
Call DirW("c:\*.*")
End Sub

Public Function DirW(ByVal SearchPath As String) As WIN32_FIND_DATA()
Dim hFind As Long
Dim nFound As Long
Dim wfd() As WIN32_FIND_DATA
Const RedimStep As Long = 1000

' Prepend necessary long/UNC identifier.
If InStr(SearchPath, "\\") = 1 Then
SearchPath = "\\?\UNC\" & Mid$(SearchPath, 3)
Else
SearchPath = "\\?\" & SearchPath
End If

' Start with bunches of data.
ReDim wfd(0 To RedimStep - 1) As WIN32_FIND_DATA

' Start the search!
hFind = FindFirstFile(StrPtr(SearchPath), wfd(nFound))
If hFind <> INVALID_HANDLE_VALUE Then
Do
Debug.Print wfd(nFound).cFileName
' Increment counter and check/bump storage.
nFound = nFound + 1
If nFound > UBound(wfd) Then
ReDim Preserve wfd(0 To UBound(wfd) + RedimStep)
End If
' Get next match.
Loop Until FindNextFile(hFind, wfd(nFound)) = 0
End If
Call FindClose(hFind) ' Clean up.

' Prepare final results.
If nFound Then
ReDim Preserve wfd(0 To nFound - 1)
DirW = wfd()
End If
End Function

Note it isn't 100% the same as VB's Dir function! There's no disambiguation
included there. IOW, if you pass a search path like "c:", VB would assume you meant
(CurDir("c:") & "\*.*"), but this API call takes you literally. You need to be
totally explicit, possibly doing an IsDirectory test on the passed path, and adding
a "\*.*" if so.
 
E

expvb

krazymike said:
Awesome!! It works, save one issue. The filenames alternate chars
with a white square (which I can only see in debug mode). Also, if I
just let the code fly, Access crashes. I have to put in several
breakpoints to slow it down for it to work.

Any ideas?

That's a display issue. Because it's 2 bytes per char, the second one is
usually 0, and an ANSI window would show a strange character for it
depending on the font. If you have to display Unicode characters, then use a
control that supports Unicode. MS Office applications use Unicode
controls(Not sure which version started this support), so it shouldn't be
difficult to display it in a label or a text box provided that you change
the font to one that implements the characters that you want to display.
Fonts with the word Unicode in them do not necessarily implement all
characters.

http://en.wikipedia.org/wiki/Unicode#Fonts
 

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

Top