Отмена внешнего запроса в Excel VBA - PullRequest
3 голосов
/ 11 ноября 2009

Я создал электронную таблицу Excel, которая помогает анализировать данные из базы данных Oracle.

Пользователь вводит, затем нажимает кнопку «Обновить запрос», которая генерирует запрос для выполнения Oracle. Запрос занимает минуту или около того, чтобы завершить. Хотя код VBA не висит на «.Refresh», все окна Excel остаются замороженными до завершения запроса.

Sub refreshQuery_click()
    Dim queryStr as String

    ' Validate parameters and generate query   
    ' ** Code not included **
    '

    ' Refresh Query
    With ActiveWorkbook.Connections("Connection").OLEDBConnection
        .CommandText = queryStr
        .Refresh
    End With
End Sub

Есть ли способ для пользователя вручную отменить запрос (вызов .CancelRefresh), пока пользовательский интерфейс Excel заморожен?

РЕДАКТИРОВАТЬ Я не знаю, стоит ли отмечать следующее или обычное поведение. Во время выполнения запроса все открытые окна Excel (включая редактор VBA) становятся «Не отвечающими» в диспетчере задач. Ни нажатие Esc, ни Ctrl + Break не отменит скрипт. Кроме того, вызов DoEvents (до или после .Refresh) не меняет это поведение.

Ответы [ 4 ]

4 голосов
/ 12 ноября 2009

Вот метод, который, я знаю, будет работать. Однако есть некоторые осложнения.

Вот как это делается:

  • Поместите электронную таблицу с данными в отдельную рабочую книгу. Этот лист должен выполнить запрос на обновление при открытии, а затем закрыть после обновления данных.
  • Создайте командный файл для вызова файла Excel «Данные».
  • В другой рабочей книге создайте процедуру (макрос) для вызова пользователем. Эта процедура вызывает пакетный файл, который впоследствии вызывает файл Excel. Поскольку вы вызываете командный файл, а не Excel, процедура Excel будет продолжена, потому что командная оболочка запускается так быстро и открывает другой файл Excel в другом потоке. Это позволяет продолжить работу в основном файле Excel.

Вот некоторые осложнения:

  • Я включил метод предупреждения пользователя об обновлении данных. Существуют проблемы с синхронизацией, когда можно попытаться проверить, обновлялись ли данные, когда рабочая книга недоступна, что вынуждает пользователя пытаться обновить значения. Я включил метод под названием «мое время», который приостанавливает выполнение кода, поэтому он проверяет только каждые столько секунд.
  • Обновленная рабочая таблица появится в новом окне, поэтому пользователю нужно будет щелкнуть ее исходную рабочую таблицу и продолжить работу. Вы могли бы научиться скрывать это, если вы знакомы со сценариями Windows (я этого еще не узнал).

Вот некоторые файлы и код. Обязательно прочитайте комментарии в коде, чтобы узнать, почему некоторые вещи есть.

ФАЙЛ: C: \ DataUpdate.xls

Мы создадим рабочую книгу с именем «DataUpdate.xls» и поместим ее в нашу папку C: \. В ячейку A1 листа Sheet1 мы добавим таблицу QueryTable, которая получает внешние данные.

Option Explicit

Sub UpdateTable()

Dim ws As Worksheet
Dim qt As QueryTable

Set ws = Worksheets("Sheet1")
Set qt = ws.Range("A1").QueryTable

qt.Refresh BackgroundQuery:=False

End Sub

Sub OnWorkbookOpen()
Dim wb As Workbook
Set wb = ActiveWorkbook

'I put this If statement in so I can change the file's
'name and then edit the file without code
'running.  You may find a better way to do this.
If ActiveWorkbook.Name = "DataUpdate.xls" Then


    UpdateTable
    'I update a cell in a different sheet once the update is completed.
    'I'll check this cell from the "user workbook" to see when the data's been updated.
    Sheets("Sheet2").Range("A1").Value = "Update Table Completed    " & Now()
    wb.Save

    Application.Quit

End If


End Sub

В объекте ThisWorkbook в Excel есть процедура Workbook_Open (). Он должен выглядеть следующим образом, чтобы при запуске он выполнял код обновления.

Private Sub Workbook_Open()
OnWorkbookOpen

End Sub

ПРИМЕЧАНИЕ. Я обнаружил ошибку при закрытии этого файла, если 1) вы получили доступ к файлу из командной строки или оболочки и 2) у вас установлена ​​надстройка Office Live. Если у вас установлена ​​надстройка Office Live, при выходе она выдаст исключение.

ФАЙЛ: C: \ RunExcel.bat

Далее мы собираемся создать командный файл, который откроет файл Excel, который мы только что создали. Причина, по которой файл Excel вызывается из пакетного файла, а не напрямую из другого файла Excel с помощью Shell, заключается в том, что Shell не продолжит работу, пока не закроется другое приложение (по крайней мере, при использовании Excel.exe "c:\File.xls"). Пакетный файл, однако, запускает свой код и затем немедленно закрывается, что позволяет исходному коду, который его вызвал, продолжить. Это то, что позволит вашим приложениям продолжать работать в Excel.

Все, что нужно для этого файла:

cd "C:\Program Files\Microsoft Office\Office10\" 
Excel.exe "C:\DataUpdate.xls"

Если вам удобно использовать Windows Scripting, вы делаете такие вещи, как открытие окна в скрытом режиме или передача параметра имени файла или местоположения Excel. Я держал это простым с командным файлом.

ФАЙЛ: C: \ UserWorkbook.xls

Это файл, который пользователь откроет, чтобы «выполнить свою работу». Они будут вызывать код для обновления другой рабочей книги из этой рабочей книги, и они по-прежнему смогут работать с этой рабочей книгой, пока обновляется эта.

Вам нужна ячейка в этой книге, в которой вы проверите ячейку «Обновление таблицы завершено» из книги DataUpdate. Я выбрал ячейку G1 в Sheet1 для моего примера.

Добавьте следующий код в модуль VBA в этой книге:

Option Explicit

Sub UpdateOtherWorkbook()
Dim strFilePath As String
Dim intOpenMode As Integer
Dim strCallPath As String
Dim strCellValue As String
Dim strCellFormula As String
Dim ws As Worksheet
Dim rng As Range

Set ws = Worksheets("Sheet1")
Set rng = ws.Range("G1")
strCellFormula = "='C:\[DataUpdate.xls]Sheet2'!A1"
'This makes sure the formula has the most recent "Updated" value
'from the data file.
rng.Formula = strCellFormula

strFilePath = "C:\RunExcel.bat"
intOpenMode = vbHide

'This will call the batch file that calls the Excel file.
'Since the batch file executes it's code and then closes,
'the Excel file will be able to keep running.
Shell strFilePath, intOpenMode

'This method, defined below, will alert the user with a
'message box once the update is complete. We know that
'the update is complete because the "Updated" value will
'have changed in the Data workbook.
AlertWhenChanged

End Sub

Sub AlertWhenChanged()

Dim strCellValue As String
Dim strUpdatedCellValue As String
Dim strCellFormula As String
Dim ws As Worksheet
Dim rng As Range

Set ws = Worksheets("Sheet1")
Set rng = ws.Range("G1")
strCellFormula = "='C:\[DataUpdate.xls]Sheet2'!A1"

strCellValue = rng.Value
strUpdatedCellValue = strCellValue

'This will check every 4 seconds to see if the Update value of the
'Data workbook has been changed.  MyWait is included to make sure
'we don't try to access the Data file while it is inaccessible.
'During this entire process, the user is still able to work.
Do While strCellValue = strUpdatedCellValue
    MyWait 2
        rng.Formula = strCellFormula
    MyWait 2
        strUpdatedCellValue = rng.Value
    DoEvents
Loop

MsgBox "Data Has Been Updated!"

End Sub

Sub MyWait(lngSeconds As Long)
Dim dtmNewTime As Date

dtmNewTime = DateAdd("s", lngSeconds, Now)

Do While Now < dtmNewTime
    DoEvents
Loop


End Sub

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

Весь этот процесс работает, потому что вы используете пакетный файл для вызова Excel в другой поток из исходного файла. Это позволяет вам работать с исходным файлом и получать уведомления при обновлении другого файла.

Удачи!

1 голос
/ 11 ноября 2009

РЕДАКТИРОВАТЬ: Вместо того, чтобы включать более полный ответ в этот же ответ, я создал отдельный ответ, полностью посвященный этому решению. Проверьте это ниже (или выше, если за него проголосуют)

Ваши пользователи могут отключить функцию VBA, нажав Ctrl + Break на клавиатуре. Тем не менее, я обнаружил, что это может привести к случайному сбою ваших функций до тех пор, пока не будет запущена любая функция. Когда компьютер перезагружается, он исчезает.

Если вы откроете этот файл в новом экземпляре Excel (то есть перейдите в «Пуск»> «Программы» и откроете Excel оттуда), я думаю, что единственная книга, которая будет заморожена, будет та, которая выполняет код. Другие экземпляры Excel не должны быть затронуты.

Наконец, вы можете исследовать функции DoEvents, которые возвращают выполнение обратно в операционную систему, чтобы она могла обрабатывать другие события. Я не уверен, что это сработает в вашем случае, но вы можете посмотреть на это. Таким образом, вы можете делать другие вещи во время завершения процесса (это опасно, потому что пользователь может изменить состояние вашего приложения во время работы процесса).


Мне кажется, я знаю способ, который действительно будет работать, но он сложный, и у меня нет кода передо мной. Он включает создание отдельного экземпляра приложения Excel в коде и присоединение обработчика к выполнению этого экземпляра. Вы включаете часть кода DoEvents в цикл, который освобождается после закрытия приложения. Другое конкретное приложение Excel имеет единственную цель - открыть файл для выполнения скрипта, а затем закрыть себя. Я делал что-то подобное раньше, поэтому я знаю, что это работает. Я посмотрю, смогу ли я найти код завтра и добавить его.

0 голосов
/ 10 февраля 2010

Вы можете попробовать XLLoop . Это позволяет вам писать функции Excel (UDfs) на внешнем сервере. Он включает реализации серверов на многих языках (например, Java, Ruby, Python, PHP).

Затем вы можете подключиться к вашей базе данных Oracle (и, возможно, добавить слой кэширования) и таким образом подать данные в вашу электронную таблицу.

XLL также имеет функцию для всплывающего «занятого» графического интерфейса, который позволяет пользователю отменить вызов функции (который отключается от сервера).

Кстати, я работаю над проектом, поэтому дайте мне знать, если у вас есть какие-либо вопросы.

0 голосов
/ 12 ноября 2009

Ну, вы могли бы рассмотреть старомодный способ - разбить запрос на более мелкие партии и использовать Do Events между партиями.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...