Тема: Правильный вызов Excel из AutoCAD, чтобы дальше можно было работать с Excel

В своих программах я использую следующий вызов Excel из AutoCAD:

   On Error Resume Next
   Set ПрограммаAutoCAD = ThisDrawing.Application
   If ПриложениеExcell Is Nothing Then
ПроверитьПолучениеExcel:
   ' Функция GetObject, вызванная без указания первого аргумента,
   ' возвращает ссылку на экземпляр приложения. Если это приложение
   ' не запущено, возвращается ошибка. Обратите внимание на запятую,
   ' стоящую на месте отсутствующего первого аргумента.
      Set ПриложениеExcell = VBA.GetObject(, "Excel.Application")
      If ПриложениеExcell Is Nothing Then
         Set ПриложениеExcell = VBA.GetObject("", "Excel.Application")
         GoTo ПроверитьПолучениеExcel
      End If
   End If
   ' Проверка Excel. Если Excel выполняется,
   ' он вводится в таблицу выполняемых объектов (Running Object table).
   If DetectExcel = 0 Then
      Set ПриложениеExcell = Nothing
      GoTo ПроверитьПолучениеExcel
   End If
   Err.Clear
   ПриложениеExcell.Visible = True
'Описываем необходимые процедуры API:
Private Declare Function FindWindow Lib "user32" Alias _
"FindWindowA" (ByVal lpClassName As String, _
               ByVal lpWindowName As Long) As Long
Private Declare Function SendMessage Lib "user32" Alias _
"SendMessageA" (ByVal hWnd As Long, ByVal wMsg As Long, _
               ByVal wParam As Long, _
               ByVal lParam As Long) As Long
Public Function DetectExcel() As Long
' Процедура находит выполняемый Excel и регистрирует его.
   Const WM_USER = 1024
   Dim hWnd As Long
' Если Excel выполняется, этот вызов API возвращает его дескриптор.
   hWnd = FindWindow("XLMAIN", 0)
   If hWnd = 0 Then  ' 0 означает, что Excel не выполняется.
      DetectExcel = 0
      Exit Function
   Else
   ' Excel выполняется. Используйте функцию API SendMessage, чтобы ввести его в таблицу выполняемых объектов (Running Object Table).
'      SendMessage hWnd, WM_USER + 18, 0, 0
      DetectExcel = hWnd
   End If
End Function

Далее можно работать в Excel сколько угодно, а потом когда надо закрыть файл Excel и запустить другой файл происходит следующее:
1) Закрываем Excel при этом процесс Excel остаётся активным в диспетчере задач, а сам Exel при этом уже закрыт. Спрашивается: почему остался работающим процесс, когда программа уже закрылась? Может надо чтобы команду закрытия процесса дал AutoCAD, потому что Excel запускался из AutoCAD? Как это сделать? Или надо специальным образом запускать Excel чтобы следить за тем когда он закрывается?
2) Если попытаться открыть другой файл Excel то Excel работает без отображения ячеек. На экране видны только команды меню и панели инструментов. Ячеек не видно, вместо них чертёж AutoCAD или окно другой работающей программы. Доступа к ячейкам нет. Ясно, что с таким Excelем работать нельзя, неопытных пользователей это просто приводит в ужас! Появляются претензии, что я своим макросом испортил настройки в компьютере и Excel перестал правильно работать! Конечно, чтобы справиться с этой проблемой надо закрыть Excel, открыть диспетчер задач, найти там всё ещё работающий процесс Excel и остановить его. После этого можно запускать снова Excel и никаких проблем не будет. Но разьве объяснишь это обычным пользователям, для обычных пользователей это просто дико слушать и в итоге программа никому не нравится. Спрашивается зачем было делать эту ложку дёгтя в бочка мёда работы Excel из AutoCAD? Как надо правильно запускать Excel из AutoCAD, чтобы он правильно закрывался и нормально потом запускался снова и не было претензий от других пользователей программы?

Re: Правильный вызов Excel из AutoCAD, чтобы дальше можно было работать с Excel

Замечено, что если после закрытия Excel в п.1 закрыть AutoCAD лишний процесс Excel в диспетечере задач тоже останавливается. Может есть специальный метод который может остановить этот процесс без закрытия AutoCAD?
И ещё вопрос: Правильно ли я сделал что отключил вызов функции

'      SendMessage hWnd, WM_USER + 18, 0, 0

так как и без этой функции процесс Excel запускается в диспетчере задач? У меня программа и так работает. По логике, чтобы остановить процесс Excel надо запустить функцию противоположную функции "SendMessage", только что это за функция и как организовать запуск этой функции, чтобы она всегда была на готове и ждала когда закроется Excel? По моему это очень сложно и невозможно. Как вы думаете нет решения этой проблемы?

Re: Правильный вызов Excel из AutoCAD, чтобы дальше можно было работать с Excel

ИМХО (поскольку с VBA мало работаю) - наверное, проблема в том, что Excel при вызове подобным методом используется ACAD'ом как СОМ-сервер, соответственно его надо релизить. Но время "освобождения", во-первых, неизвестно, а, во-вторых, при работе через lisp Excel тоже далеко не сразу закрывается. Я думаю, что надо все же использовать другие методы вызова Excel'a и обращения с ним.

Re: Правильный вызов Excel из AutoCAD, чтобы дальше можно было работать с Excel

Закрываем Excel при этом процесс Excel остаётся активным в диспетчере задач, а сам Exel при этом уже закрыт. Спрашивается: почему остался работающим процесс, когда программа уже закрылась? Может надо чтобы команду закрытия процесса дал AutoCAD, потому что Excel запускался из AutoCAD? Как это сделать?

у меня проблема один в один была, просто ужас. освобождение через Nothing не помогало. интересно, что если выгрузить проект или выйти из автокада или (!) нажать alt+f11 и кнопочку Стоп(Reset) на панели VBAEditor'а, то этот фантомный процесс исчезает!! 8-(
Бился два дня, искал решения и через API и через LISP, и уже когда отчаялся и хотел забить... Вот как в итоге решился гемор. Знатоки, может это вам будет интересно.
в конце кода вставить End

Set xlSheet = Nothing
Set xlBook = Nothing
Set xlApp = Nothing
End
End With
End Sub

Видимо для VBA End помощнее, чем обыкновенное End Sub и этот End рубит концы и в памяти машины...

Re: Правильный вызов Excel из AutoCAD, чтобы дальше можно было работать с Excel

Тоже сталкивался с подобной проблемой. Кстати, подобная проблема возникает и при выгрузке файлов из Excel, в списке объектов, в VBA редакторе, они остаются.
Данная проблема связана как раз именно с освобождением используемых объектов.
Просто перед выгрузкой приложения, всем объектам, которые вы использовали (листы, ячейки, файлы, графические объекты и т. д.), необходимо присвоить Nothing. В противном случае, вызванное приложение само эти объекты не освобождает, поэтому будет активным в системе до освобождения всех объектов или до выгрузки вызывающего приложения, что автоматически ведёт к освобождению всех объектов.
Поэтому проверьте повнимательней свой программный код.
Что касается использования END, то это равноценно завершению работы вашего приложения, что автоматически ведёт к закрытию всех объектов.
На самом деле вопрос не однозначен. В чём конкретно суть задачи.
Запустить програмно Excel, выполнить код и оставить Excel открытым, но при этом ваше приложение свою работу завершает полностью или оно тоже остаётся активным и пользователь работает параллельно в Excel и вашем приложении, а проблема возникает когда пользователь сам закрывает Excel и ваше приложение повторно его открывает? Если в этом, то перед:

If ПриложениеExcell Is Nothing Then

вставьте

Set ПриложениеExcell = Nothing

плюс к этому освобождение всех используемых объектов.

Re: Правильный вызов Excel из AutoCAD, чтобы дальше можно было работать с Excel

Спасибо за ответ. Суть задачи - открыть эксель, заполнить таблицу данными из чертежа и завершить приложение, оставив Excel открытым в полное распоряжение пользователя. Все просто.
Приведу код, отвечающий за это:

........
'в наборе будут вхождения блоков с нужными атрибутами
sset.Select acSelectionSetAll, , , fT, fD
     Dim xlApp As Excel.Application
     Set xlApp = Nothing
     Dim xlBook As Workbook
     Dim xlSheet As Worksheet
     On Error Resume Next
     Err.Clear
     Set xlApp = GetObject(, "Excel.Application")
     If Err <> 0 Then
          Err.Clear
          Set xlApp = CreateObject("Excel.Application")
          If Err <> 0 Then
               MsgBox "Cannot start Excel", vbExclamation
               End
          End If
     End If
Set xlBook = xlApp.Workbooks.Add
Set xlSheet = xlBook.Worksheets(1)
xlApp.Visible = True
Cells(1, 1).Value = "Номер"
Cells(...).Value = "..."
...
Dim atts As Variant 'для атрибутов
For i = 0 To sset.Count - 1
    atts = sset(i).GetAttributes
    Cells(i + 2, 1).Value = atts(0).TextString
    Cells(...).Value = atts(...).TextString
...
Next i
'далее выбор и сортировка
Dim myRan As Excel.Range
Set myRan = Range("A2", Cells(sset.Count + 1, 5))
myRan.Sort Cells(2, 1)
Set myRan = Nothing
Set xlSheet = Nothing
Set xlBook = Nothing
Set xlApp = Nothing
End
End With
End Sub

Без End в конце кода, если пользователь закроет Excel, то процесс будет висеть и повтороно приложение не запустится... Не понимаю почему, но теперь, елси пользователь нЕ закроет Excel, и запустит приложение, то новая книга создастся, однако ничего не заполнится :(
Ясно, что что-то где-то забыл, но что? Может, Excel вызываю как-то криво?...

Re: Правильный вызов Excel из AutoCAD, чтобы дальше можно было работать с Excel

Cells(1, 1).Value = ...

Правильно

xlSheet.Cells(1, 1).Value = ...

Re: Правильный вызов Excel из AutoCAD, чтобы дальше можно было работать с Excel

2 brigval
Спасибо большое за исправление!
Удивительное рядом.
Теперь, когда все закрыто запускаю Excel, просто новый документ. Когда начинаю вводить что-нибудь в ячейки ЗАПУСКАЕТСЯ ПРОЦЕСС ACAD.EXE в невидимом режиме. Когда хочу открыть файл, из которого раньше списывал атрибуты, говорит, что ОН УЖЕ МНОЮ ОТКРЫТ! Наверное уже в реестре что-то засело. Замечено, что с "фантомным" автокадом запускается процесс ~e5d141.tmp и процесс WSCommCntr1.exe. А при удалении этих процессов мой макрос все-равно работает...
Подскажите, пожалуйста, как избежать лишних (СОМ?)связей между приложениями, в чем может быть проблема? dirol

Re: Правильный вызов Excel из AutoCAD, чтобы дальше можно было работать с Excel

В конце кода у тебя стоит код закрытия не существующего цикла (по крайней мере в приведённом куске):

End With

Странно, что VBA не выругался.
Удали данную строку и строку с END.
Приведённая задача простейшая и стандартная, проблем быть не должно.
Вызов Excel не причём, ошибка где-то в другом, но в приведённом коде, за исключением выше сказанного, всё в порядке.
По поводу:

xlSheet.Cells(1, 1).Value = ...

если работаешь с активным листом в активной книге, то указание листа или книги не обязательно.

Re: Правильный вызов Excel из AutoCAD, чтобы дальше можно было работать с Excel

Dron пишет:

Странно, что VBA не выругался.

Мы не знаем, что было выше в коде...

если работаешь с активным листом в активной книге, то указание листа или книги не обязательно.

Я так думаю, что это справедливо для VBA for Exсel, в других случаях (и для VBA For AutoCAD) я всегда указываю "родителя".

Re: Правильный вызов Excel из AutoCAD, чтобы дальше можно было работать с Excel

> brigval
Прошу прощения, на самом деле я имел ввиду, что это не должно вызывать описанную проблему.
Что касается End With, предположил что компилятор дойдя до END не реагирует на то, что после него. Проверил, оказался не прав. Где-то выше он видимо открыт. Значит ошибка в не достающей части кода.

Re: Правильный вызов Excel из AutoCAD, чтобы дальше можно было работать с Excel

Господа, очень признателен вам за внимание к моей проблеме. всех с наступающим праздником! )выложу весь код, если не охота сегодня работать, то можно покопаться в нем )).
2Dron & 2brigval
обращение с указанием "родителя" убрало ту ошибку, при которой, если Excel уже был открыт, то в ячейки ничего не записывалось.
Вот весь код модуля, но нем много "лишнего", не относящегося к проблеме:

Public Sub AttExtract()
With ThisDrawing
[i][b]'определим набор для необходимых вхождений блоков.[/i][/b]
Dim sset As AcadSelectionSet
DeclareSSet sset, "4attsset" [i]'это моя фунцкия для перебора и удаления существующего имени и записи в SelectionSets. просто облегчает жизнь и уж точно работает )) [/i]
[i]'далее описывается механизм выбора и фильтрации, принципиально к имеющимся проблемам не относится.
'создаем механизм фильтрации для набора. нужные вхождения характеризуются символами "_NB" в конце имени. переберем коллекцию чертежа Blocks, одновременно создавая механизм фильтрации по нужным именам.[/i]
Dim BlColl As AcadBlocks
Set BlColl = .Blocks
Dim Blk As AcadBlock
Dim fT() As Integer    [i]'FilterType[/i]
Dim fD() As Variant    [i]'FilterData[/i]
Dim i As Integer
i = 2                  [i]'для размеров дин.массивов. '2', потому что первые сейчас определим ->[/i]
ReDim fT(1): ReDim fD(1)
fT(0) = 0:  fD(0) = "INSERT"    [i]'по вхождениям блоков[/i]
fT(1) = -4: fD(1) = "<or"       [i]'отрываем логическое "ИЛИ" для разных имен[/i]
For Each Blk In BlColl
    If GetLastName(Blk.Name) = "NB" Then [i]'GetLastName моя функция, как раз достает из входящей строки символы после последнего '_'. если '_' вообще нет в строке, то возвращает "".[/i]
        ReDim Preserve fT(i + 1): ReDim Preserve fD(i + 1)
        fT(i) = 2: fD(i) = Blk.Name
        fT(i + 1) = -4: fD(i + 1) = "or>"
        i = i + 1
    End If
Next
[i][b]'теперь есть необходимый фильтр - выберем по всему чертежу то, что нужно[/i][/b]
sset.Select acSelectionSetAll, , , fT, fD
If sset.Count = 0 Then
    GoTo NoBlRef  [i]'в конец программы[/i]
End If
[i][b]'теперь нужно выводить атрибуты вхождений из набора в Excel.[/i][/b]
[i]'Начинается самое интересное ).[/i]
     Dim xlApp As Excel.Application
     Set xlApp = Nothing
     Dim xlBook As Workbook
     Dim xlSheet As Worksheet
     On Error Resume Next
     Err.Clear
     Set xlApp = GetObject(, "Excel.Application")
     If Err <> 0 Then
          Err.Clear
          Set xlApp = CreateObject("Excel.Application")
          If Err <> 0 Then
               MsgBox "Cannot start Excel", vbExclamation
               End
          End If
     End If
Set xlBook = xlApp.Workbooks.Add
Set xlSheet = xlBook.Worksheets(1)
xlApp.Visible = True
With xlSheet
[i][b]'Обозначим столбцы[/b]
'Один столбец пропущен для спец.символов номеров, потом удалим после сортировки[/i]
.Cells(1, 1).Value = "Номер кабеля"
.Cells(1, 3).Value = "Тип кабеля"
.Cells(1, 4).Value = "Длина кабеля"
.Cells(1, 5).Value = "Начало"
.Cells(1, 6).Value = "Конец"
[i]'определим Range для форматирования последующей сортировки[/i]
Dim myRan As Excel.Range
Set myRan = .Range("A2", .Cells(sset.Count + 1, 6))
myRan.NumberFormat = "@"    [i]'текстовый[/i]
Dim atts As Variant   [i]'для атрибутов[/i]
Dim sNum As String    [i]'для строки номера[/i]
Dim sRNum As String   [i]'для начальной "цифренной" части номера[/i]
Dim sDopNum As String [i]'для дополнительной "символьной" части номера[/i]
For i = 0 To sset.Count - 1   [i]'пройдемся по набору[/i]
    atts = sset(i).GetAttributes    [i]'достаем атрибуты[/i]
    sNum = atts(0).TextString
    sRNum = IsNum(sNum)   [i]'фунуция IsNum из строки возвращает Long из первых цифренных символов строки, если только текст, возвращает Long=0[/i]
    If sRNum = "0" Then  [i]'тогда это просто текст[/i]
        sRNum = sNum
        sDopNum = ""     [i]'и символьная часть не нужна[/i]
    Else
        sDopNum = Right(sNum, Len(sNum) - Len(sRNum))  [i]'отделили символьную часть номера[/i]
    End If
[i][b]'запись в ячейки[/i] [/b]
    .Cells(i + 2, 1).Value = sRNum      [i]'номер кабеля "реальный"[/i]
    .Cells(i + 2, 2).Value = sDopNum    [i]'символьная часть номера[/i]
    .Cells(i + 2, 3).Value = sset(i).Layer     [i]'тип кабеля[/i]
    .Cells(i + 2, 4).Value = CInt(atts(1).TextString)    [i]'длина кабеля[/i]
    .Cells(i + 2, 5).Value = atts(2).TextString          [i]'начало[/i]
    .Cells(i + 2, 6).Value = atts(3).TextString          [i]'конец[/i]
Next i
[i][b]'сортируем [/b] во-первых по номеру, во-вторых по "символьным" частям[/i]
myRan.Sort Key1:=.Cells(2, 1), Key2:=.Cells(2, 2), DataOption1:=xlSortTextAsNumbers
[i]'объединим номер с принадлежащей ему "символьной" частью[/i]
For i = 1 To sset.Count + 1
    sNum = .Cells(i, 1).Value + .Cells(i, 2).Value
    .Cells(i, 1).Value = sNum
Next
'удалим теперь вспомогательный столбец для символьных частей
.Columns(2).Delete
End With
Set myRan = Nothing
Set xlSheet = Nothing
Set xlBook = Nothing
Set xlApp = Nothing
End
NoBlRef:
MsgBox "Нет засечек кабелей"
Exit Sub
End With
End Sub

Re: Правильный вызов Excel из AutoCAD, чтобы дальше можно было работать с Excel

Единственное, что могу посоветовать, проверить самое начало:

Dim sset As AcadSelectionSet
DeclareSSet sset, "4attsset" 'это моя фунцкия для перебора и удаления существующего имени и записи в SelectionSets. просто облегчает жизнь и уж точно работает ))

Скорее всего проблема именно здесь.
В API и COM я не силён, поэтому не понял каким образом происходит декларация функции имеющей имя, только что определённой переменной как коллекция.
Может проблема именно в данной функции, именно она что-то не освобождает.
Могу предложить для отработки кода, только одно, перекинуть эту функцию в данный модуль, как процедуру или функцию, и обращаться к ней, а декларацию убрать.
Что касается END, обычно он используется во вложенных процедурах или фукциях, для выхода из программы. А здесь вроде как одна процедура, может стоит заменить на EXIT SUB. И зачем EXIT SUB в самом конце кода?