Тема: Выбор директории

Написан модуль, который определяет границы файла и обводит его рамкой. Подскажите, как таким образом обвести все файлы в одной папке. Нужно, чтобы появлялось окошко с выбором директории.

Re: Выбор директории

Вопрос не совсем понятен но вот пример как вызвать диалог выбора директории

Private Type BrowseInfo
  hWndOwner As Long
  pIDLRoot As Long
  pszDisplayName As Long
  lpszTitle As Long
  ulFlags As Long
  lpfnCallback As Long
  lParam As Long
  iImage As Long
End Type
'
' просмотр каталогов
'
' для выбора каталога,
' чтобы начать поиск документа
Private Const BIF_RETURNONLYFSDIRS = &H1
' для запуска команды Find
Private Const BIF_DONTGOBELOWDOMAIN = &H2
Private Const BIF_STATUSTEXT = &H4
Private Const BIF_RETURNFSANCESTORS = &H8
' просмотр компьютеров
Private Const BIF_BROWSEFORCOMPUTER = &H1000
' просмотр принтеров
Private Const BIF_BROWSEFORPRINTER = &H2000
' просмотр всего
Private Const BIF_BROWSEINCLUDEFILES = &H4000
'
Private Const MAX_PATH = 260
Private Declare Sub CoTaskMemFree Lib "ole32.dll" _
  (ByVal hMem As Long)
Private Declare Function lstrcat Lib "kernel32" Alias "lstrcatA" _
  (ByVal lpString1 As String, ByVal lpString2 As String) As Long
Private Declare Function SHBrowseForFolder Lib "shell32" _
  (lpbi As BrowseInfo) As Long
Private Declare Function SHGetPathFromIDList Lib "shell32" _
  (ByVal pidList As Long, ByVal lpBuffer As String) As Long
Public Function BrowseForFolder(hWndOwner As Long, _
  sPrompt As String) As String
  '==================================================
  ' Открывает системное диалоговое окно для просмотра каталогов
  '==================================================
  Dim iNull As Integer
  Dim lpIDList As Long
  Dim lResult As Long
  Dim sPath As String
  Dim udtBI As BrowseInfo
  '
  With udtBI
    .hWndOwner = hWndOwner
    .lpszTitle = lstrcat(sPrompt, "")
    .ulFlags = BIF_RETURNONLYFSDIRS
  End With
  lpIDList = SHBrowseForFolder(udtBI)
  If lpIDList Then
    sPath = String$(MAX_PATH, 0)
    lResult = SHGetPathFromIDList(lpIDList, sPath)
    Call CoTaskMemFree(lpIDList)
    iNull = InStr(sPath, vbNullChar)
    If iNull Then sPath = Left$(sPath, iNull - 1)
    'End If
  End If
  BrowseForFolder = sPath
End Function

Вызов диалога

  Dim MyStr As String
  MyStr = BrowseForFolder(hWnd, "Привет всем!")
  MsgBox MyStr

Re: Выбор директории

> Петр
Если установленны экспрессы - можно лиспом через командную строку

(ACET-UI-PICKDIR)

Re: Выбор директории

Мне нужен именно VBA. А по поводу того, что вопрос не понятен: нужно выполнить в большом количестве файлов одинаковые действия, для этого написан соответсвующий модуль. Файлов много и нужно указать директорию, в которой они находятся, чтобы написанный модуль отработал в каждом из этих файлов. Спасибо

Re: Выбор директории

> Петр
Так я и имел в виду, что из VBA посылаешь выражение в командную строку, а потом читаешь возвращенное значение...
Кстати, вариант

> SmeL
очень хороший, я сам использую аналог на лиспе (чтоб не привязываться к экспрессам).

Re: Выбор директории

Выбор директории работает, но получается применить мой модуль только для одного файла из этой директории /*.dwg. Можно ли по очереди открывать каждый файл в директории, применять к нему модуль и закрывать файл?

Re: Выбор директории

> Петр
Самый простой способ выполнить серию команд в большом количестве файлов - выполнить скрипт (*.scr)...
Я, в свое время, писал програмку, которая генерировала скрипт:
1 открытие файла (с уникальным именем)
2 выполнение моей программы, которая стояла в автозагрузке
3 сохранение изменений
4 закрытие файла
...
и так для каждого файла.
Этот подход самый простой в реализации.
Проблемма в том, что при активации нового файла, программа прерывается до активации чертежа, в котором была запущенна программа. Скрипты не подверженны этим проблеммам - они выполняются не в документе, а в приложении... Если же необходимо рулить другим документом напрямую, можно запустить новое приложение, выполнить в нем необходимые действия и закрыть его.
Извини, что все на уровне алгоритмов - мое знание VBA не позволяет давать готовые решения...

Re: Выбор директории

На VBA иногда удобно воспользоваться функцией Dir. Вот пример, который выводит все полные имена файлов в дирректории.

Sub AppDir()
   Dim sFile As String
   Dim ptDir As String
   ptDir = Application.Path & "\"
   sFile = Dir$(ptDir)
   Debug.Print ptDir & sFile
   Do While Len(sFile) > 0
      sFile = Dir$
      Debug.Print ptDir & sFile
   Loop
End Sub

Re: Выбор директории

Мое знание VBA тоже не позволяет этого сделать. Спасибо

Re: Выбор директории

> Евгений Елпанов
Меня привлекают скрипты. Жаль, что в Autocad- скриптах нет операторов. Не могли бы Вы показать этот скрипт. Может быть я смогу его использовать как шаблон. У меня много таких операций. Т.е. открыть файл, что то в нем сделать и закрыть с записью. Не могу найти литературу с синтаксисом скриптов :(

Re: Выбор директории

> brigval
Спасибо большое! Все работает. Теперь смогу закончить задачу!

Re: Выбор директории

> Forma
Аргументы можно вставить двумя методами:
1 при генерации скрипта можно вставить в текст любые аргументы, даже уникальные для каждого из запущенных фалов
2 вычислять в запущенной из скрипта программе, т.е. пишется программа специально для работы в скрипте и ее имя добавляется в скрипт для запуска...
Например, недавно пришлось писать подобную программу и скрипт, для обработки огромного количества файлов - после сбоя винчестера были восстановленны файлы, но в одной из деректорий были потерянны все названия и расширения...
Расширения были восстановленны спец-софтом, но 10000 файлов без названий - это просто мусор, если их не обозвать осмысленно!
Был сгенерированн скрипт, который открывал последовательно файлы и запускал программу. Программа искала основную надпись, вычленяла из нее номер договора, имя менеджера заказа и заказчика, далее все это помещалось в имя файла. Из проблем с которыми столкнулся:
1 после обработки каждых трех - четырех тысяч файлов акад вылетал...
2 среди файлов были битые, но  Александр Ривилис любезно написал для меня програмку на ObjectARX проверяющую файл на возможность открытия... Мой вариант подобной программы на лиспе слишком сильно пожирал оперативную память.

Re: Выбор директории

> Евгений Елпанов
Да. Очень интересно. Я всегда говорил, что пакетный режим не умер. А не могли бы дать несколько ссылок где бы почитать более подробно. Желательно с законченными примерами. Я не программист. Мне трудно. Но по образу и подобию мог бы  сделать. Если не затруднит, конечно.

Re: Выбор директории

> Forma
Скрипт - это самое простое в освоении.
Просто попробуйте выполнить несколько команд только из командной строки (без использования мышки), а далее вспомните всю последовательность и запишите не пропустив ни одного пробела или знака переноса...

Re: Выбор директории

а далее вспомните всю последовательность и запишите не пропустив ни одного пробела или знака переноса...

Не надо ничего вспоминать, надо просто протокол посмотреть (текстовое окно, F2).

Re: Выбор директории

Снова проблема. После того, как открывается первый из файлов в директории, к нему применяется написанный макрос. После этого я добавил команду  , чтобы в окне Акада не открывались все 50 файлов, которые необходимо изменить макросом. Однако, теперь открытия следующего файла для применения к нему макроса не происходит. Возникает ошибка Failed to get the Document object. До добавления команды ThisDrawing.Close открывался файл, изменялся под действием макроса, затем поверх первого открывался второй и так далее до 50-го. Было слишком громоздко...

Re: Выбор директории

> BP
В текстовом окне видно только команды, а введенные пробелы не отследишь, хотя я тоже пользуюсь текстовым окном, но в предыдущем посте я объяснял алгоритм создания скриптов...

> Forma
Кстати, чтоб проверить скрипт не обязательно его загружать - достаточно скопировать его (или кусок) из текстового редактора в буфер обмена и вставить сразу в командную строку акада - смысл один и тот-же.

Re: Выбор директории

А как сделать так, чтобы при вызове окна:

lpIDList = SHBrowseForFolder(udtBI)

указывался путь к заранее назначеной папке, и эта папка была развёрнутой в дереве катологов. И ещё чтобы была кнопка на форме для создания новой папки?

Re: Выбор директории

Миша пишет:

указывался путь к заранее назначеной папке, и эта папка была развёрнутой в дереве катологов, + еще вкусные навароты

Public Type RECT
    Left As Long
    Top As Long
    Right As Long
    Bottom As Long
End Type
Public Declare Function MoveWindow Lib "user32.dll" ( _
     ByVal hWnd As Long, _
     ByVal X As Long, _
     ByVal y As Long, _
     ByVal nWidth As Long, _
     ByVal nHeight As Long, _
     ByVal bRepaint As Long) As Long
Public Declare Function GetWindowRect Lib "user32.dll" ( _
     ByVal hWnd As Long, _
     lpRect As RECT) As Long
Public Declare Function WaitForInputIdle Lib "user32" ( _
    ByVal hProcess As Long, _
    ByVal dwMilliseconds As Long) As Long
Public Declare Function FindWindowEx Lib "user32.dll" Alias "FindWindowExA" ( _
     ByVal hWnd1 As Long, _
     ByVal hWnd2 As Long, _
     ByVal lpsz1 As String, _
     ByVal lpsz2 As String) As Long
Public Declare Function EnumThreadWindows Lib "user32" _
    (ByVal dwThreadId As Long, _
    ByVal lpfn As Long, _
    ByVal lParam As Long) As Long
Public Declare Function GetWindowThreadProcessId Lib "user32.dll" ( _
     ByVal hWnd As Long, _
     lpdwProcessId As Long) As Long
Public Declare Function SetWindowPos Lib "user32.dll" ( _
     ByVal hWnd As Long, _
     ByVal hWndInsertAfter As Long, _
     ByVal X As Long, _
     ByVal y As Long, _
     ByVal cx As Long, _
     ByVal cy As Long, _
     ByVal wFlags As Long) As Long
Public Declare Function SetWindowText Lib "user32.dll" Alias "SetWindowTextA" ( _
     ByVal hWnd As Long, _
     ByVal lpString As String) As Long
Public Const SWP_SHOWWINDOW = &H40
Public Const HWND_TOPMOST = -1
Public g_CurrentDirectory As String
Public g_FileMasks() As String
Public g_ChangeSize As Boolean
Public g_DialogTitle As String
Public g_RatioX As Double, g_RatioY As Double
Public g_CenterOnScreen As Boolean
Public g_TopMost As Boolean
Public g_newLeft As Long, g_newTop As Long
Public g_deltaH As Long, g_deltaW As Long
Public Const WM_USER As Long = &H400
Public Const BFFM_INITIALIZED = 1
Public Const BFFM_SELCHANGED = 2
Public Const BFFM_SETSTATUSTEXT = (WM_USER + 100)
Public Const BFFM_ENABLEOK = (WM_USER + 101)
Public Const BFFM_SETSELECTION = (WM_USER + 102)
Public Type BrowseInfo
    hWndOwner As Long
    pIDLRoot As Long
    pszDisplayName As Long
    lpszTitle As Long
    ulFlags As Long
    lpfnCallback As Long
    lParam As Long
    iImage As Long
End Type
Public Enum WhatBrowse
    BIF_RETURNONLYFSDIRS = &H1
    BIF_BROWSEINCLUDEFILES = &H1 Or &H4000
    BIF_BROWSEFORCOMPUTER = &H1000
    BIF_BROWSEFORPRINTER = &H2000
    BIF_DONTGOBELOWDOMAIN = &H2
    BIF_STATUSTEXT = &H4
    BIF_NEWDIALOGSTYLE = &H40
    BIF_EDITBOX = &H10
    BIF_USENEWUI = (BIF_NEWDIALOGSTYLE Or BIF_EDITBOX)
    BIF_RETURNFSANCESTORS = &H8
End Enum
Public Const MAX_PATH = 260&
Public Declare Function SendMessage Lib "user32" Alias "SendMessageA" _
  (ByVal hWnd As Long, _
   ByVal wMsg As Long, _
   ByVal wParam As Long, _
   lParam As Any) As Long
Public Declare Sub CoTaskMemFree Lib "ole32.dll" (ByVal hMem As Long)
Public Declare Function lstrcat Lib "kernel32" Alias "lstrcatA" _
    (ByVal lpString1 As String, _
    ByVal lpString2 As String) As Long
Public Declare Function SHBrowseForFolder Lib "shell32" (lpbi As BrowseInfo) As Long
Public Declare Function SHGetPathFromIDList Lib "shell32" _
    (ByVal pidList As Long, _
    ByVal lpBuffer As String) As Long
Public Function fBrowseForFolder(ByVal hWnd_Owner As Long, _
                                 ByVal sPrompt As String, _
                                 ByVal WhatBr As Long, _
                                 ByVal DialogTitle As String, _
                                 Optional ByVal initDir As String = "", _
                                 Optional ByRef fileMasks As String = "", _
                                 Optional ByVal ChangeSize As Boolean = False, _
                                 Optional ByVal RatioX As Double = 1, _
                                 Optional ByVal RatioY As Double = 1, _
                                 Optional ByVal CenterOnScreen As Boolean = False, _
                                 Optional ByVal PromptColor As Long = 0&, _
                                 Optional ByVal PathColor As Long = 0&, _
                                 Optional ByVal TopMost As Boolean) As String
    Dim iNull As Integer
    Dim lpIDList As Long
    Dim lResult As Long
    Dim sPath As String
    Dim udtBI As BrowseInfo
    g_DialogTitle = DialogTitle
    g_ChangeSize = ChangeSize
    g_RatioX = RatioX
    g_RatioY = RatioY
    g_CenterOnScreen = CenterOnScreen
    g_TopMost = TopMost
    If initDir = "" Then
        g_CurrentDirectory = ""
    Else
        g_CurrentDirectory = initDir & vbNullChar
    End If
    If fileMasks = "" Then
        ReDim g_FileMasks(0 To 0)
        g_FileMasks(0) = ""
    Else
        g_FileMasks = Split(fileMasks, "|")
    End If
    With udtBI
        .hWndOwner = hWnd_Owner
        .lpszTitle = lstrcat(sPrompt, "")
        .ulFlags = WhatBr
        .lpfnCallback = GetAddressofFunction(AddressOf BrowseCallbackProc)  'get address of function.
    End With
    lpIDList = SHBrowseForFolder(udtBI)
    If lpIDList Then
        sPath = String$(MAX_PATH, 0)
        lResult = SHGetPathFromIDList(lpIDList, sPath)
        Call CoTaskMemFree(lpIDList)
        iNull = InStr(sPath, vbNullChar)
        If iNull Then sPath = Left$(sPath, iNull - 1)
    End If
    fBrowseForFolder = sPath
End Function
Public Function BrowseCallbackProc(ByVal hWnd As Long, ByVal uMsg As Long, ByVal lp As Long, ByVal pData As Long) As Long
    Dim lpIDList As Long
    Dim ret As Long
    Dim sBuffer As String
    Dim i As Integer, flag As Boolean, sPath As String
    On Error Resume Next
    Select Case uMsg
        Case BFFM_INITIALIZED
            If g_CurrentDirectory <> "" Then
                Call SendMessage(hWnd, BFFM_SETSELECTION, 1, ByVal g_CurrentDirectory)
            End If
            SetWindowText hWnd, g_DialogTitle
            If g_ChangeSize Then
                Call ResizeDialog(hWnd)
            Else
                If g_CenterOnScreen Then Call CenterDialog(hWnd)
            End If
        Case BFFM_SELCHANGED
            sBuffer = Space$(MAX_PATH)
            ret = SHGetPathFromIDList(lp, sBuffer)
            If ret = 1 Then
                Call SendMessage(hWnd, BFFM_SETSTATUSTEXT, 0, ByVal sBuffer)
                If g_FileMasks(0) <> "" Then
                    sPath = Left$(sBuffer, InStr(1, sBuffer, vbNullChar) - 1)
                    If Right$(sPath, 1) <> "\" Then sPath = sPath + "\"
                    flag = False
                    For i = 0 To UBound(g_FileMasks)
                        If Dir(sPath + g_FileMasks(i)) <> "" Then flag = True
                    Next i
                    If flag Then
                        Call SendMessage(hWnd, BFFM_ENABLEOK, 0, ByVal 1&)
                    Else
                        Call SendMessage(hWnd, BFFM_ENABLEOK, 0, ByVal 0&)
                    End If
                End If
            End If
        Case Else
    End Select
    BrowseCallbackProc = 0
End Function
Public Function GetAddressofFunction(add As Long) As Long
    GetAddressofFunction = add
End Function
Private Sub ResizeDialog(ByVal hWnd As Long)
    Dim pid As Long, thread_id As Long
    Dim screenWidth As Long, screenHeight As Long
    Dim winWidth As Long, winHeight As Long
    Dim R As RECT
    screenWidth = Screen.Width / Screen.TwipsPerPixelX
    screenHeight = Screen.Height / Screen.TwipsPerPixelY
    GetWindowRect hWnd, R
    winWidth = (R.Right - R.Left) * g_RatioX
    winHeight = (R.Bottom - R.Top) * g_RatioY
    g_deltaH = winHeight - (R.Bottom - R.Top)
    g_deltaW = winWidth - (R.Right - R.Left)
    If g_CenterOnScreen Then
        g_newLeft = (screenWidth - winWidth) / 2
        g_newTop = (screenHeight - winHeight) / 2
    Else
        g_newLeft = R.Left
        g_newTop = R.Top
    End If
    If g_TopMost Then
        SetWindowPos hWnd, HWND_TOPMOST, g_newLeft, g_newTop, winWidth, winHeight, SWP_SHOWWINDOW
    Else
        SetWindowPos hWnd, 0, g_newLeft, g_newTop, winWidth, winHeight, SWP_SHOWWINDOW
    End If
    thread_id = GetWindowThreadProcessId(hWnd, pid)
    Call WaitForInputIdle(pid, 1000&)
    EnumThreadWindows thread_id, AddressOf EnumThreadWndProc, 0&
End Sub
Public Function EnumThreadWndProc(ByVal hWnd As Long, ByVal lParam As Long) As Long
    Dim treeHwnd As Long, okButtonHwnd As Long, cancelButtonHwnd As Long, promptHwnd As Long, folderHwnd As Long
    Dim R As RECT
    Static allControlsAdjusted As Integer
    okButtonHwnd = FindWindowEx(hWnd, ByVal 0&, "Button", vbNullString)
    If okButtonHwnd <> 0 Then
        GetWindowRect okButtonHwnd, R
        MoveWindow okButtonHwnd, (R.Left - g_newLeft - 3) + g_deltaW, (R.Top - g_newTop - 22) + g_deltaH, R.Right - R.Left, R.Bottom - R.Top, 1
        allControlsAdjusted = allControlsAdjusted + 10000
        cancelButtonHwnd = FindWindowEx(hWnd, okButtonHwnd, "Button", vbNullString)
        If cancelButtonHwnd <> 0 Then
            GetWindowRect cancelButtonHwnd, R
            MoveWindow cancelButtonHwnd, (R.Left - g_newLeft - 3) + g_deltaW, (R.Top - g_newTop - 22) + g_deltaH, R.Right - R.Left, R.Bottom - R.Top, 1
            allControlsAdjusted = allControlsAdjusted + 1000
        End If
    End If
    treeHwnd = FindWindowEx(hWnd, ByVal 0&, "SysTreeView32", vbNullString)
    If treeHwnd <> 0 Then
        GetWindowRect treeHwnd, R
        MoveWindow treeHwnd, R.Left - g_newLeft - 3, R.Top - g_newTop - 22, (R.Right - R.Left) + g_deltaW, (R.Bottom - R.Top) + g_deltaH, 1
        allControlsAdjusted = allControlsAdjusted + 100
    End If
    promptHwnd = FindWindowEx(hWnd, ByVal 0&, "Static", vbNullString)
    If promptHwnd <> 0 Then
        GetWindowRect promptHwnd, R
        MoveWindow promptHwnd, R.Left - g_newLeft - 3, R.Top - g_newTop - 22, (R.Right - R.Left) + g_deltaW, R.Bottom - R.Top, 1
        allControlsAdjusted = allControlsAdjusted + 10
        folderHwnd = FindWindowEx(hWnd, promptHwnd, "Static", vbNullString)
        If folderHwnd <> 0 Then
            GetWindowRect folderHwnd, R
            MoveWindow folderHwnd, R.Left - g_newLeft - 3, R.Top - g_newTop - 22, (R.Right - R.Left) + g_deltaW, R.Bottom - R.Top, 1
            allControlsAdjusted = allControlsAdjusted + 1
        End If
    End If
    EnumThreadWndProc = Not (allControlsAdjusted = 11111)
End Function
Private Sub CenterDialog(ByVal hWnd As Long)
    Dim screenWidth As Long, screenHeight As Long
    Dim winWidth As Long, winHeight As Long
    Dim R As RECT
    screenWidth = Screen.Width / Screen.TwipsPerPixelX
    screenHeight = Screen.Height / Screen.TwipsPerPixelY
    GetWindowRect hWnd, R
    winWidth = (R.Right - R.Left)
    winHeight = (R.Bottom - R.Top)
    g_newLeft = (screenWidth - winWidth) / 2
    g_newTop = (screenHeight - winHeight) / 2
    SetWindowPos hWnd, 0, g_newLeft, g_newTop, winWidth, winHeight, SWP_SHOWWINDOW
End Sub

Вариант вызова

    Dim st As String
    st = fBrowseForFolder(hWnd_Owner:=0&, _
                          sPrompt:="Выберите папку, содержащую файлы *.DWG и *.TIF:", _
                          WhatBr:=BIF_RETURNONLYFSDIRS Or BIF_STATUSTEXT, _
                          DialogTitle:="Супер диалог выбора папок", _
                          initDir:="C:\", _
                          fileMasks:="*.DWG|*.TIF", _
                          ChangeSize:=True, _
                          RatioX:=1.4, _
                          RatioY:=1.7, _
                          CenterOnScreen:=True, _
                          TopMost:=True)
    If Len(st) > 0 Then MsgBox "Вы выбрали папку:" + vbCr + st, vbInformation, "Информация"

Насчет кнопочки сделать дирректории не стал заморачиваться ;)
Можно прикрепить кнопку и навязать ей евент

 lngHandle = CreateWindowExA(0, "button", "Новая папка", &H40000000, 10, .Top - R.Top - (.Bottom - .Top) + 1, 95, .Bottom - .Top, hwnd, 0, App.hInstance, ByVal 0)

Либо поместить созданную ранее кнопку с формы на данный диалог, а кнопку до этого Вы обучите создавать папку

Re: Выбор директории

Вот примерчик, как можно прикрепить кнопку на форму и обработать клик

  hwnd2 = CreateWindowEx(0, "Button", "My button", WS_CHILD, 50, 55, 100, 25, hWnd, 0, App.hInstance, ByVal 0&)
  new_proc = GetMyWndProc(AddressOf ButtonProc)
  old_proc = SetWindowLong(hwnd2, GWL_WNDPROC, new_proc)
----
Private Function ButtonProc(ByVal hWnd As Long, ByVal message As Long, ByVal wParam As Long, ByVal lParam As Long) As Long
    Dim x As Integer
    If (message = 533) Then
        x = MsgBox("You clicked on the button", vbOKOnly)
    End If
    ButtonProc = CallWindowProc(old_proc, hWnd, message, wParam, lParam)
End Function

Re: Выбор директории

Спасибо большое!

Re: Выбор директории

Уважаемый Smel! Спасибо! И ещё маленькая просьба. Нельзя ли из приведённого кода вычистить то, что не касается VBA и поместить его на страничку: https://www.caduser.ru/forum/topic21141.html ? Там бы это неплохо смотрелось.
Всего хорошего.

Re: Выбор директории

Ладно! Кое что я и сам умею. Но вот с чем мне не удаётся справиться, так это запустить из VBA этот диалог в модальном режиме. На VB это примитивно просто. А на VBA? Как добраться до hWnd_Owner?

Re: Выбор директории

Я далеко не спец ни по VBA, ни по VB, но, по-моему, можно сделать при вызове формы нечто типа

<frmDisk>.Show vbModal

SmeL, поправь, если я не прав.