К сожалению, похоже, что нет встроенной опции для установки времени ожидания подключения к сокету. как минимум я не вижу этого и основываясь на этом вопросе - Как настроить время ожидания подключения сокета - никто не просматривает тоже.
одно возможное решение передает дескриптор события в запрос ввода / вывода, и если мы получили ERROR_IO_PENDING
- вызовите RegisterWaitForSingleObject
для этого события. если этот вызов будет успешным - будет вызвана наша функция обратного вызова WaitOrTimerCallback
- или потому что ввод / вывод будет завершен (с любым окончательным статусом) и в этот момент событие (которое мы передаем как I / Запрос O и RegisterWaitForSingleObject
) будут установлены или потому что истекло время ожидания ( dwMilliseconds ) - в этом случае нам нужно вызвать функцию CancelIoEx
.
, так скажем, у нас есть class IO_IRP : public OVERLAPPED
, у которого есть подсчет ссылок (нам нужно сохранить указатель на OVERLAPPED
, используемый в запросе ввода-вывода для передачи его в CancelIoEx
. уверен, что этот OVERLAPPED
все еще не используется в другом новом вводе / выводе - поэтому еще не свободен). в этом случае возможна реализация:
class WaitTimeout
{
IO_IRP* _Irp;
HANDLE _hEvent, _WaitHandle, _hObject;
static VOID CALLBACK WaitOrTimerCallback(
__in WaitTimeout* lpParameter,
__in BOOLEAN TimerOrWaitFired
)
{
UnregisterWaitEx(lpParameter->_WaitHandle, NULL);
if (TimerOrWaitFired)
{
// the lpOverlapped unique here (because we hold reference on it) - not used in any another I/O
CancelIoEx(lpParameter->_hObject, lpParameter->_Irp);
}
delete lpParameter;
}
~WaitTimeout()
{
if (_hEvent) CloseHandle(_hEvent);
_Irp->Release();
}
WaitTimeout(IO_IRP* Irp, HANDLE hObject) : _hEvent(0), _Irp(Irp), _hObject(hObject)
{
Irp->AddRef();
}
BOOL Create(PHANDLE phEvent)
{
if (HANDLE hEvent = CreateEvent(NULL, FALSE, FALSE, NULL))
{
*phEvent = hEvent;
_hEvent = hEvent;
return TRUE;
}
return FALSE;
}
public:
static WaitTimeout* Create(PHANDLE phEvent, IO_IRP* Irp, HANDLE hObject)
{
if (WaitTimeout* p = new WaitTimeout(Irp, hObject))
{
if (p->Create(phEvent))
{
return p;
}
delete p;
}
return NULL;
}
void Destroy()
{
delete this;
}
// can not access object after this call
void SetTimeout(ULONG dwMilliseconds)
{
if (RegisterWaitForSingleObject(&_WaitHandle, _hEvent,
(WAITORTIMERCALLBACK)WaitOrTimerCallback, this,
dwMilliseconds, WT_EXECUTEONLYONCE|WT_EXECUTEINWAITTHREAD))
{
// WaitOrTimerCallback will be called
// delete self here
return ;
}
// fail register wait
// just cancel i/o and delete self
CancelIoEx(_hObject, _Irp);
delete this;
}
};
и использовать что-то вроде
if (IO_IRP* Irp = new IO_IRP(...))
{
WaitTimeout* p = 0;
if (dwMilliseconds)
{
if (!(p = WaitTimeout::Create(&Irp->hEvent, Irp, (HANDLE)socket)))
{
err = ERROR_NO_SYSTEM_RESOURCES;
}
}
if (err == NOERROR)
{
DWORD dwBytes;
err = ConnectEx(socket, RemoteAddress, RemoteAddressLength,
lpSendBuffer, dwSendDataLength, &dwBytes, Irp)) ?
NOERROR : WSAGetLastError();
}
if (p)
{
if (err == ERROR_IO_PENDING)
{
p->SetTimeout(dwMilliseconds);
}
else
{
p->Destroy();
}
}
Irp->CheckErrorCode(err);
}
другое возможное решение установить таймер через CreateTimerQueueTimer
, а если таймер истек - позвоните [ 1112] или закройте ручку ввода-вывода отсюда. разница с решением события - если ввод / вывод будет завершен до истечения таймера - функция обратного вызова WaitOrTimerCallback
не будет вызываться автоматически. в случае события - устанавливается событие подсистемы ввода / вывода, когда завершается ввод / вывод (после первоначального состояния ожидания) и благодаря этому (событие в состоянии сигнала) будет вызываться обратный вызов. но в случае таймера - ни в коем случае не передавать его в запрос io как параметр (I / O принимает только дескриптор события). В результате нам нужно сохранить указатель на объект таймера самостоятельно и вручную освободить его, когда завершится ввод-вывод. так что здесь будет 2 указателя на объект таймера - один из пула (сохраненный CreateTimerQueueTimer
) и один из нашего класса объекта (сокета) (он нам нужен для объекта разыменования после завершения ввода / вывода). это требует подсчета ссылок на объект, который тоже инкапсулирует таймер. с другой стороны, мы можем использовать таймер не для одной операции ввода-вывода, а для нескольких операций ввода-вывода (поскольку это не прямое связывание с некоторыми операциями ввода-вывода)
Кроме того, какой сказанный lassevk, можно также использовать несколько других методов оценки компилятора (так как Delphi 6, я верю):
{$IF NOT DECLARED(SOME_SYMBOL)}
// Mind you : The NOT above is optional
{$ELSE}
{$IFEND}
, Чтобы проверить, имеет ли компилятор эту функцию, используйте:
{$IFDEF CONDITIONALEXPRESSIONS}
существует несколько использования для этого.
, Например, Вы могли проверить версию RTL; От справки Delphi:
можно использовать RTLVersion в выражениях $IF для тестирования уровня версии библиотеки времени выполнения независимо от уровня версии компилятора.
Пример: {$IF RTLVersion> = 16.2}... {$IFEND}
кроме того, сама версия компилятора может быть проверена, снова из кода:
CompilerVersion присваивает значение компилятор, когда системная единица компилируется. Это указывает на уровень пересмотра функций компилятора / синтаксис языка, который может совершенствоваться независимо от RTLVersion. CompilerVersion может быть протестирован в выражениях $IF и должен использоваться вместо того, чтобы тестировать на условное выражение VERxxx, определяют. Всегда тестируйте на больший, чем или меньше, чем известный уровень пересмотра. Это - плохая идея протестировать на определенный уровень пересмотра.
Другая вещь, которую я регулярно делаю, определяют символ, когда она еще не определяется (хороший для вперед-compatiblity), как это:
{$IF NOT DECLARED(UTF8String)}
type
UTF8String = type AnsiString;
{$IFEND}
Hope это помогает!
Эти директивы управления доступны:
{$IFDEF}
{$ELSE}
{$ENDIF}
{$IFNDEF} //if *not* defined
и они могут использоваться как показано здесь:
procedure TfrmMain.Button1Click(Sender: TObject);
begin
{$IFDEF MY_CONDITIONAL}
ShowMessage('my conditional IS defined!');
{$ELSE}
ShowMessage('my conditional is NOT defined!');
{$ENDIF}
{$IFNDEF MY_CONDITIONAL}
ShowMessage('My conditional is explicitly NOT defined');
{$ENDIF}
end;
DebugHook устанавливается, если приложение работает под отладчиком IDE. Не то же самое, что директива компилятора, но все же довольно полезная. Например:
ReportMemoryLeaksOnShutdown := DebugHook <> 0; // show memory leaks when debugging