I guess that the meset is not the only 1975ish artifact you have in the code
.
OVERLAPPED ov;
OVERLAPPED ov = { 0 };
memset(&ov, 0, sizeof(OVERLAPPED));
delete the above line
ov.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
if(ov->hEvent == NULL)
{
unsigned int code = GetLastError();
RTFM! The documentation CLEARLY states that the return type of
GetLastError is a DWORD!!! So why in the world would you declare an
āunsigned intā variable to hold it? What strange power entitles you to
use bae implementation types i stead of the specified result type?
char errmsg[256];
char is a nearly dead type left over from the 1960s. The correct type to
use would be TCHAR or WCHAR.
GetErrorMessage(code, errmsg, sizeof(errmsg));
The correct call would be
GetErrorMessage(code, errmsg, _countof(errmsg));
SET_LAST_ERROR_STRING(āCreateEvent failed, error: 0x%x, error
string: %sā, code, errmsg);
Thereās something seriously wrong here. Could it be that the āLast error
messageā variable is a GLOBAL variable? This isnāt a PDP-11; it is
Microsoft Windows, which means you should never use global variables to
pass back results! That is, if you have a global variable called
char LastErrorString[256];
then I would state that the code is a completely unmaintainable pile
ofā¦well anyway, itās damned poor code.
If the declaration is
__declspec(thread) chat LastErrorString[MAX_ERROR_STRING];
return false;
}
success = WriteFile(g_hDriver, data, sizeToWrite, (PULONG)
sizeWritten, &ov);
And where, prsy tell, is the declaration of sizeWriiten? And why are you
casting it to a PULONG? RTFM!!! The documentation CLEARLY states that
this should be an LPDWORD, or, more properly, a PDWORD. So what led you
to cast it as a PULONG? And why are you casting it? If you need a cast
to make it compile, then your code is already broken. The correct code
would be
DWORD sizeWritten;
ā¦
WriteFile(driver, data, sizeToWrite, &sizeWritten, &ov);
since you neglected showing the declaration of the variable, it is hard to
guess what you wrote.
And why in the world would the driver handle be a global? This looks like
1975 PDP-11 code. Have you heard of āparametersā? Are you aware that
passing scalar parameters is so close to free as to be largely unworthy of
debate?
if(success == 0)
{
unsigned int err = GetLastError();
Same comment as above. Why write a different type than the documentation
specifies?
if(err != ERROR_IO_PENDING)
{
return false;
}
}
while(0 == GetOverlappedResult(g_hDriver, ov, bytesReturned, FALSE))
All this says is to poll, thus consuming all available CPU cycles, until
GetOverlappedResult sucessfully reurns. But why poll? Why not make the
last parameter TRUE? RTFM! It clearly states that this operation returns
a BOOL, and of the many totally silly idioms I find in programming,
comparing a BOOL to a literal value to get, letās see, a BOOL, is among
the silliest. Itās already a BOOL, so you donāt need to compare it to
anything!
But why poll at all? Just call it; if it returns TRUE then the I/O has
complete, and if FALSE, something is seriously wrong.
{
code = GetLastError();
if(code != ERROR_IO_INCOMPLETE)
{
//error
GetErrorMessage(code,errmsg, sizeof(errmsg));
SET_LAST_ERROR_STRING(āGetOverlappedResult failed. error:
0x%x, error string: %s,Bytes returned %xā, code,
errmsg,*bytesReturned);
return false;
}
If you want a timeout, do a WaitForSingleObject on the ov.hEvent handle
and give a timeout. If the return value of WFSO is WAIT_TIMEOUT, you deal
with your timeout, otherwise, it will be WAIT_OBJECT_0. Rolling your own
timeout by polling makes no sense.
if(GetTickCount() > endtime)
{
if(CancelIo(g_hDriver) == 0)
{
code = GetLastError();
GetErrorMessage(code, errmsg, sizeof(errmsg));
SET_LAST_ERROR_STRING(āCancelIO failed. error: 0x%x,
error
string: %sā, code, errmsg);
return false;
}
SET_LAST_ERROR_STRING(āTimed out waiting for asynchronous IO
to finishā);
return false;
}
Every one of those return statements (except the one where CreateEvent
fails) leak an event object.
Note that if there is no error, you still have a handle leak!
The correct methodology is shown below:
***************************************************
/*
THIS CODE IS SUPPLIED āAS ISā. THE AUTHOR OF THIS CODE PROVIDES THIS
FOR REFERENCE AND DESCRIPTIVE PURPOSES ONLY. USERS OF THIS CODE ARE
RESPONSIBLE FOR VERIFYING THAT IT IS SUITABLE [Standard industry
disclaimers included by reference]
*/
BOOL WriteToMyDevice(HANDLE device, LPVOID data, DWORD count)
{
OVERLAPPED ov = { 0 };
ov.hEvent = CreateEvent(ā¦); //⦠means Iām not going to retype the
obvious
if(ov.hEvent == NULL)
{
⦠deal with error
return FALSE;
}
else
{
BOOL result = InnerWrite(handle, data, count, &ov);
CloseHandle(ov.hEvent);
return result;
} // WriteToMyDevice
BOOL InnerWrite(HANDLE device, LPVOID data, DWORD count, LPOVERLAPPED ov)
{
DWORD sizeWritten;
if(! WriteFile(device, data, count, &sizeWritten, ov)
{ /* WriteFile failed */
if(GetLastError() != ERROR_IO_PENDING)
{ /* Bad failure */
⦠deal with error
return FALSE;
} /* Bad failure */
else
{ /* I/O pending */
switch(WaitForSingleObject, ov->hEvent, MY_DEVICE_TIMEOUT)
{ /* WFSO */
case WAIT_OBJECT_0:
{ /* device complete */
If( ! GetOverlappedResult(device, ov,
&sizeWritten, TRUE))
{ /* GOR failed */
⦠deal with error
return FALSE;
} /* GOR failed */
return TRUE;
} /* device complete */
case WAIT_TIMEOUT:
⦠deal with timeout
return FALSE;
default:
⦠deal with WFSO failure
return FALSE;
} /* WFSO */
} /* I/O pending */
} /* WriteFile failed */
return TRUE;
} //InnerWrite;
Notes on why this code is better:
The two-tiered approach makes it impossible to leak handles
No global handle variable is required
No polling is done to check for completion; thread blocks until
completion
Data types are correct
No explicit zeroing of the OVERLAPPED structure is required
No ugly goto statements are required to avoid event handle leaks
Code is easier to understand
Things left as An Exercise For The Reader:
Eliminate any global variables, such as error message buffers
Write using 2012, not 1975, character set
If number of bytes written is required by caller, add LPDWORD/PDWORD
parameter to both functions and use this instead of the local
variable address, &sizeWritten
If an error buffer is used, pass in a pointer to a TCHAR, DWORD pair
(that is, a struct) that is the buffer. Better still, write in C++
and use ATL::CString or std::string reference parameter to the error
buffer, where this string is a local variable for the caller.
*********************************************************************************
}
NTDEV is sponsored by OSR
For our schedule of WDF, WDM, debugging and other seminars visit:
http://www.osr.com/seminars
To unsubscribe, visit the List Server section of OSR Online at
http://www.osronline.com/page.cfm?name=ListServer