Сейчас у меня есть функция, которая запускает запрос, используя ADO, и возвращает набор записей :
Recordset Execute(Connection connection, String commandText)
{
//[pseudo-code]
Recordset rs = new Recordset();
rs.CursorLocation = adUseClient;
rs.CursorType = adOpenForwardOnly;
rs.Open(commandText, connection,
adOpenForwardOnly, //CursorType; the default
adLockReadOnly, //LockType
adCmdText);
return rs;
}
И это нормально. Он работает синхронно и возвращает набор записей запроса.
Теперь я хочу подобную версию, которая показывает ProgressDialog , предоставляя пользователю возможность отменить длительный запрос:
Recordset Execute(HWND parenthWnd, String caption, Connection connection, String commandText)
{
//[pseudo-code]
//Construct a progressDialog and show it
IProgressDialog pd = new ProgressDialog();
pd.SetTitle(caption); //e.g. "Annual Funding Report"
pd.SetCancelMsg("Please wait while the operation is cancelled");
pd.StartProgressDialog(parenthWnd, null, PROGDLG_MODAL | PROGDLG_NOTIME | PROGDLG_NOMINIMIZE, null);
pd.SetLine(1, "Querying server", False, null);
try
{
//Query the server
Recordset rs = new Recordset();
rs.Open(commandText, connection,
adOpenForwardOnly, //CursorType
adLockReadOnly, //LockType
adCmdText | adAsyncExecute);
while (rs.State and (adStateConnecting+adStateExecuting+adStateFetching) <> 0)
{
if pd.HasUserCancelled()
throw new EUserCancelledOperationException();
Sleep(100);
};
finally
{
//Hide and destroy the progress dialog
pd.StopProgressDialog();
pd = null;
}
//Now we have our results for the client
return rs;
}
Чтобы проверить, отменил ли пользователь операцию, нужно периодически запрашивать диалог прогресса, если пользователь нажал кнопку отмены:
pd.HasUserCancelled(); //returns true if user has clicked Cancel
Теперь я сталкиваюсь с тем, как периодически проверять, отменил ли пользователь (и знать, завершился ли запрос или произошла ошибка), и быть хорошим программистом и делать это без опроса.
Единственный способ узнать, что произошла ошибка, - это обработчик набора записей FetchCompleteEvent :
pError
Объект Error. Он описывает ошибку, которая произошла, если значением adStatus является adStatusErrorsOccurred; в противном случае он не установлен.
adStatus
Значение состояния EventStatusEnum. Когда вызывается это событие, для этого параметра устанавливается значение adStatusOK, если операция, вызвавшая событие, было успешным, или значение adStatusErrorsOccrogen, если операция завершилась неудачно.
Перед возвратом этого события установите для этого параметра значение adStatusUnwantedEvent, чтобы предотвратить последующие уведомления.
pRecordset
Объект Recordset. Объект, для которого были получены записи.
Так что это будет означать, что мне понадобится, чтобы моя функция создала вспомогательный объект, чтобы я мог иметь обработчик FetchComplete
. Но тогда я должен предотвратить немедленное возвращение моей синхронной функции. И тогда мы попадаем в MsgWaitForSingleObject
, который, как известно, трудно правильно использовать.
Так что я прошу помощи или законный код.
Я должен быть более точным: я ищу реализацию функции с сигнатурой этого метода:
Recordset ExecuteWithCancelOption(Connection connection, String commandText)
, который показывает диалог с кнопкой отмены.
Задача состоит в том, что теперь функция должна создавать все, что требуется для этого. Если это связано со скрытой формой, на которой есть таймер и т. Д. - хорошо.
Но я ищу синхронную функцию , которая отображает кнопку Отмена .
И функция будет почти (или точной) заменой для
Recordset Execute(Connection connection, String commandText)
Учитывая практические соображения по Windows, мне нужно было бы снабдить функцию дескриптором родительского окна, к которому она будет привязывать свой диалог:
Recordset ExecuteWithCancelOption(HWND parentHwnd, Connection connection, String commandText)
И учитывая, что эта функция будет использоваться повторно, я позволю вызывающей стороне предоставить текст, который будет отображаться:
Recordset ExecuteWithCancelOption(HWND parenthWnd, String caption, Connection connection, String commandText)
И, учитывая, что это обе функции класса в моем классе TADOHelper
, я могу дать им одно и то же имя, и они будут перегрузками друг друга:
Recordset Execute(HWND parenthWnd, String caption, Connection connection, String commandText)
Я думаю, что на языках, отличных от Delphi, анонимные делегаты полезны. Но я все еще боюсь иметь дело с MsgWaitForMultipleObjects
.