I got the following code in which last statement I try to delete a pointer to dynamically created memory.
But as soon as I get to the instruction a Access Violation exception is raised saying :
Unhandled exception at 0x0094c91f in
Server.exe: 0xC0000005: Access
violation reading location 0x00000000.
But when I step through it with the debugger it contains a valid address with valid data in it... I don't realize what I'm doing fatally wrong here...
Any suggestions?
void CServer::HandleAcceptRequest(ACCEPT_REQUEST* pRequest)
{
//Add the new connection socket to the connection handler
m_pConnectionHandler->AddConnection(pRequest->m_NewConnection);
//Associate the new connections´ socket handle with the IOCP
if(!m_pCompletionPort->AssociateHandle((HANDLE)pRequest->m_NewConnection, 0))
{
MessageBox(NULL, "Could not associate a socket handle with the completion port", "", MB_ICONERROR | MB_OK);
DebugBreak();
}
//Create a new COMM_REQUEST and initialize a Recv-Request
COMM_REQUEST* pCommRequest = new COMM_REQUEST;
memset(pCommRequest, 0, sizeof(COMM_REQUEST));
pCommRequest->Socket = pRequest->m_NewConnection;
pCommRequest->m_RequestType = BASIC_REQUEST::RECV;
WSABUF* buf = new WSABUF;
buf->buf = pCommRequest->cBuf;
buf->len = Inc::COMMUNICATION_BUFFER_SIZE;
DWORD dwFlags = 0;
if(WSARecv(pCommRequest->Socket, buf, 1, NULL, &dwFlags, pCommRequest, NULL))
{
DWORD dwRet = WSAGetLastError();
if(dwRet != WSA_IO_PENDING)
{
MessageBox(NULL, "WSARecv() failed", "", MB_ICONERROR | MB_OK);
DebugBreak();
}
};
//Delete the old ACCEPT_REQUEST structure
delete pRequest;
}
EDIT: I did allocate the memory in another function in the main thread
bool CConnectionHandler::AcceptNewConnection(SOCKET ListenSocket, unsigned nCount)
{
DWORD dwBytesReceived = 0;
ACCEPT_REQUEST* pOverlapped = nullptr;
for(unsigned n = 0; n < nCount; n++)
{
dwBytesReceived = 0;
pOverlapped = new ACCEPT_REQUEST;
memset(pOverlapped, 0, sizeof(ACCEPT_REQUEST));
pOverlapped->m_RequestType = ACCEPT_REQUEST::ACCEPT;
//add the ListenSocket to the request
pOverlapped->m_ListenSocket = ListenSocket;
//invalidate the new connection socket
pOverlapped->m_NewConnection = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if(pOverlapped->m_NewConnection == INVALID_SOCKET)
{
delete pOverlapped;
return false;
}
// call 'AcceptEx'
if(m_lpfnAcceptEx(pOverlapped->m_ListenSocket, pOverlapped->m_NewConnection, pOverlapped->cOutputBuffer, 0, sizeof(sockaddr_in) + 16, sizeof(sockaddr_in) + 16, &dwBytesReceived, pOverlapped) == FALSE)
{
DWORD dwRet = WSAGetLastError();
if(dwRet == ERROR_IO_PENDING)
continue;
else
return false;
}
}
return true;
};
EDIT2: the threads code to do with giving the parameter to the function...
unsigned int WINAPI ServerThreadFunc(void* pvArgs)
{
CServer* pServer = (CServer*)pvArgs; // pointer to the server object
DWORD dwBytes = 0;
ULONG_PTR ulKey;
OVERLAPPED* pOverlapped = nullptr;
bool bLooping = true;
while(bLooping)
{
//TODO: Add code (ServerThreadFunc)
if(!pServer->m_pCompletionPort->GetCompletionStatus(&dwBytes, &ulKey, &pOverlapped, INFINITE))
{
//TODO: Set some error variable or flag an error event to notify the main thread
DebugBreak();
}
//check type of request
switch(((BASIC_REQUEST*)pOverlapped)->m_RequestType)
{
case BASIC_REQUEST::ACCEPT:
{
// TODO: Handle AcceptEx-request
pServer->HandleAcceptRequest(static_cast<ACCEPT_REQUEST*>(pOverlapped));
Sorry, there's no way to really figure anything out from bits and pieces you provided.
However, in the third piece of code you have this call
pServer->HandleAcceptRequest(static_cast<ACCEPT_REQUEST*>(pOverlapped));
This call will destroy the *pOverlapped object by calling delete on it. (Remember that at the very end of HandleAcceptRequest you do delete pRequest, where pRequest is the parameter of HandleAcceptRequest).
This will make pOverlapped a dangling pointer that points to dead memory. I don't see any place in your code where you would re-initialize that dangling pointer or set it to null.
If you don't re-initialize it, then the next access to *pOverlapped in the cycle will access dead memory (which might appear to "work") and the next attempt to delete it again will most likely crash. If the next delete attempt is the one at the end of HandleAcceptRequest again, then the behavior will probably be exactly as what you originally described.
OVERLAPPED* pOverlapped = nullptr;
..
pServer->m_pCompletionPort->GetCompletionStatus(&dwBytes, &ulKey, &pOverlapped, INFINITE);
Ok. It seems you are deleting the wrong pointer. The one you are deleting inside HandleAcceptRequest() is no longer a pointer to an OVERLAPPED, since the pointer has been casted from OVERLAPPED* to ACCEPT_REQUEST*.
Is GetCompletionStatus() your own?
Related
How can I use NotifyServiceStatusChange properly so I can get notified when the service specified is deleted? My current code successfully stops the service and marks it for deletion. However, I want to be notified when the service is fully deleted.
Here are the main points of my code:
SC_HANDLE SCManager = OpenSCManagerW(NULL, SERVICES_ACTIVE_DATABASE,
SC_MANAGER_CONNECT | SC_MANAGER_ENUMERATE_SERVICE);
HANDLE EventHandle = CreateEventW(NULL, TRUE, FALSE, NULL);
SERVICE_NOTIFY ServiceNotify;
ServiceNotify.dwVersion = SERVICE_NOTIFY_STATUS_CHANGE;
ServiceNotify.pszServiceNames = ServiceName;
ServiceNotify.pContext = &EventHandle;
ServiceNotify.pfnNotifyCallback = (PFN_SC_NOTIFY_CALLBACK)CallbackFunction;
DWORD status = NotifyServiceStatusChangeW(SCManager, SERVICE_NOTIFY_DELETED, &ServiceNotify);
WaitForSingleObject(EventHandle, INFINITE);
CloseServiceHandle(SCManager);
CloseHandle(EventHandle);
(ServiceName is WCHAR*)
CallbackFunction code:
VOID CALLBACK CallbackFunction(IN PVOID pParameter) {
SERVICE_NOTIFY* ServiceNotify = pParameter;
HANDLE EventHandle = *(HANDLE*)ServiceNotify->pContext;
SetEvent(EventHandle);
}
NotifyServiceStatusChange is returning ERROR_SUCCESS (0). However, my callback function is not being called at all. How can I fix this?
Edit:
Here is minimal reproducible code:
void ErrorExit(char* FunctionName, unsigned long ErrorCode) {
char* ErrorMessage;
FormatMessageA(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
NULL, ErrorCode, LANG_USER_DEFAULT, (LPTSTR)&ErrorMessage, 0, NULL);
int MessageSize = (strlen(ErrorMessage) + strlen(FunctionName) + 50) * sizeof(char);
char* FullMessage = malloc(MessageSize);
sprintf_s(FullMessage, MessageSize, "%s failed with error %d: %s", FunctionName, ErrorCode, ErrorMessage);
MessageBoxA(NULL, FullMessage, "Error", MB_OK);
ExitProcess(ErrorCode);
}
PFN_SC_NOTIFY_CALLBACK CallbackFunction(PVOID pParameter) {
printf("CallbackFunction has been called.\r\n");
SERVICE_NOTIFY* ServiceNotify = pParameter;
HANDLE EventHandle = ServiceNotify->pContext;
if (!SetEvent(EventHandle)) {
ErrorExit("SetEvent", GetLastError());
}
}
int main()
{
WCHAR* ServiceName = L"SERVICE NAME"; // Input service name here
SC_HANDLE SCManager = OpenSCManagerW(NULL, SERVICES_ACTIVE_DATABASE, SC_MANAGER_ALL_ACCESS);
if (!SCManager) {
ErrorExit("OpenSCManagerW", GetLastError());
}
SC_HANDLE ServiceHandle = OpenServiceW(SCManager, ServiceName,
SERVICE_ENUMERATE_DEPENDENTS | SERVICE_STOP | DELETE);
if (!ServiceHandle) {
ErrorExit("ServiceHandle", GetLastError());
}
if (!DeleteService(ServiceHandle)) {
ErrorExit("DeleteService", GetLastError());
}
if (!CloseServiceHandle(ServiceHandle)) {
ErrorExit("CloseServiceHandle", GetLastError());
}
HANDLE EventHandle = CreateEventW(NULL, TRUE, FALSE, NULL);
if (!EventHandle) {
ErrorExit("CreateEventW", GetLastError());
}
SERVICE_NOTIFY ServiceNotify;
ServiceNotify.dwVersion = SERVICE_NOTIFY_STATUS_CHANGE;
ServiceNotify.pszServiceNames = ServiceName;
ServiceNotify.pContext = EventHandle;
ServiceNotify.pfnNotifyCallback = CallbackFunction;
DWORD status = NotifyServiceStatusChangeW(SCManager, SERVICE_NOTIFY_DELETED, &ServiceNotify);
if (status != ERROR_SUCCESS) {
ErrorExit("NotifyServiceStatusChangeW", GetLastError());
}
status = WaitForSingleObjectEx(EventHandle, INFINITE, TRUE);
if (status == WAIT_FAILED) {
ErrorExit("WaitForSingleObjectEx", GetLastError());
}
printf("WaitForSingleObjectEx Result: %lu\r\n", status);
system("pause");
return 0;
}
When I run this, no other service depends on the service being deleted, and the service being deleted is already stopped. My error handling function "ErrorExit" is never called. Nothing is printed on the screen. My program simply pauses, which I assume is from WaitForSingleObjectEx.
I know the service is being deleted because I have ProcessHacker open, and it is giving me notifications that the service is being deleted.
NotifyServiceStatusChange is returning ERROR_SUCCESS (0). However, my callback function is not being called at all.
The NotifyServiceStatusChangeW documentation says:
When the service status changes, the system invokes the specified callback function as an asynchronous procedure call (APC) queued to the calling thread. The calling thread must enter an alertable wait (for example, by calling the SleepEx function) to receive notification. For more information, see Asynchronous Procedure Calls.
So, make sure you are actually processing APC notifications while you wait. WaitForSingleObject() will not do that for you.
Use WaitForSingleObjectEx() instead. It has a bAlertable parameter you can set to TRUE. You will have to call it in a loop, since it will return when any APC call is processed by the calling thread, which may not be the one you are expecting.
You also need to call NotifyServiceStatusChangeW() in a loop, too. The documentation does not mention this, but the callback will be called only 1 time per use. Once the callback is called, you need to call NotifyServiceStatusChangeW() again to receive another notification if the current one is not the event you are expecting.
With that said, try something more like this:
struct MyCallbackInfo {
HANDLE EventHandle;
LPCWSTR pszServiceName;
bool bDeleted;
};
...
VOID CALLBACK CallbackFunction(PVOID pParameter) {
SERVICE_NOTIFYW* ServiceNotify = (SERVICE_NOTIFYW*) pParameter;
MyCallbackInfo *ci = (MyCallbackInfo*) ServiceNotify->pContext;
if (ServiceNotify->dwNotificationStatus == ERROR_SUCCESS) {
LPWSTR pServiceName = ServiceNotify->pszServiceNames;
while (*pServiceName != L'\0') {
if (lstrcmpW(pServiceName, ci->pszServiceName) == 0) {
ci.bDeleted = true;
break;
}
pServiceName += (lstrlenW(pServiceName) + 1);
}
LocalFree(ServiceNotify->pszServiceNames);
}
SetEvent(ci->EventHandle);
}
...
MyCallbackInfo ci;
ci.EventHandle = CreateEventW(NULL, TRUE, FALSE, NULL);
ci.pszServiceName = ServiceName;
ci.bDeleted = false;
if (!ci.EventHandle) {
// error handling...
}
SC_HANDLE SCManager = OpenSCManagerW(NULL, SERVICES_ACTIVE_DATABASE,
SC_MANAGER_CONNECT | SC_MANAGER_ENUMERATE_SERVICE);
if (!SCManager) {
// error handling...
}
SERVICE_NOTIFYW ServiceNotify = {};
ServiceNotify.dwVersion = SERVICE_NOTIFY_STATUS_CHANGE;
ServiceNotify.pContext = &ci;
ServiceNotify.pfnNotifyCallback = CallbackFunction;
DWORD status;
do {
status = NotifyServiceStatusChangeW(SCManager, SERVICE_NOTIFY_DELETED, &ServiceNotify);
if (status != ERROR_SUCCESS) {
// error handling...
}
while ((status = WaitForSingleObjectEx(ci.EventHandle, INFINITE, TRUE)) == WAIT_IO_COMPLETION);
if (status == WAIT_FAILED) {
// error handling...
}
if (ci.bDeleted) {
// service has been deleted ...
break;
}
ResetEvent(ci.EventHandle);
}
while (true);
CloseServiceHandle(SCManager);
CloseHandle(ci.EventHandle);
So far I tried to use GetWindow using GetForegroundWindow and this always returns NULL. when I use The GetLastError I have a msg saying 1400, Invalid windows Handle.
When I first debug I have Null as I said befor, but when I redo the execution I have a valid handle and all work fine.
What could be the problem.
static int CheckZOrder2Windows(HWND FirstWindow, HWND SecondWindow)
{
int zOrderWnd1 = -1, zOrderWnd2 =-1;
HWND tempHwnd = GetWindow(GetForegroundWindow(), GW_HWNDFIRST);
if (!tempHwnd)
ErrorExit(TEXT(""));
DWORD dwFGThread1 = GetWindowThreadProcessId(FirstWindow, NULL);
DWORD dwFGThread2 = GetWindowThreadProcessId(SecondWindow, NULL);
int currentOrder = 0;
DWORD dwFGThread = 0;
while(tempHwnd)
{
++currentOrder;
if(IsWindowVisible(tempHwnd))
{
dwFGThread = GetWindowThreadProcessId(tempHwnd, NULL);
if (dwFGThread == dwFGThread1)
zOrderWnd1 = currentOrder;
else if (dwFGThread == dwFGThread2)
zOrderWnd2 = currentOrder;
}
tempHwnd=GetWindow(tempHwnd,GW_HWNDNEXT);
}
if ((zOrderWnd1 == -1) || (zOrderWnd2 == -1))
return 0;
return (zOrderWnd1 - zOrderWnd2);
}
GetForegroundWindow is allowed to return NULL and you never check for that.
MSDN also says:
The Enum[Child]Windows function is more reliable than calling GetWindow in a loop. An application that calls GetWindow to perform this task risks being caught in an infinite loop or referencing a handle to a window that has been destroyed.
I'm writing a very simple debugger and I defined a class called BREAKPOINT_INFO that contains information about breakpoints set.
class BREAKPOINT_INFO
{
public:
HANDLE hProcess;
PCHAR lpBreakPoint;
CHAR instr;
BOOL justCalled;
//Set default values
BREAKPOINT_INFO()
{
hProcess = NULL;
lpBreakPoint = NULL;
instr = 0x00;
justCalled = FALSE;
}
//Destructor
~BREAKPOINT_INFO()
{
//Let me know the destructor is being called
MessageBox(NULL, "Destructor called", NULL, MB_OK);
DWORD dwError = 0;
LPCSTR szErrorRest = (LPCSTR)"Error restoring original instruction: ";
LPCSTR szErrorHanlde = (LPCSTR)"Error closing process handle: ";
std::ostringstream oss;
if(hProcess != NULL && lpBreakPoint != NULL)
{
//write back the original instruction stored in instr
if(!WriteProcessMemory(hProcess, lpBreakPoint, &instr, sizeof(CHAR), NULL))
{
dwError = GetLastError();
oss << szErrorRest << dwError;
MessageBox(NULL, oss.str().c_str(), "ERROR", MB_OK|MB_ICONERROR);
}
}
}
};
I need the destructor to clean up any breakpoints set however the deconstructor is never called and I'm not quite sure why that is in my particular case.
Here's main.cpp:
BREAKPOINT_INFO instrMov;
//GetProcModuleHandle is a function I made to get the handle of a
//of a module in a remote process
LPVOID lpServerDll = (LPVOID)GetProcModuleHandle(dwPid, szServerDll);
//the instructions address is relative to the starting address of the server dll. Hence the offset.
PCHAR lpInstr = (PCHAR)((DWORD)lpServerDll+instr_offset);
hProcess = OpenProcess(PROCESS_ALL_ACCESS, TRUE, dwPid);
//sets the breakpoint
instrMov.InitializeBreakPoint(hProcess, lpInstr);
while(1)
{
if(!instrMov.justCalled)
{
instrMov.SetBreakPoint();
}
if(instrMov.justCalled)
{
instrMov.justCalled = FALSE;
}
if(WaitForDebugEvent(&dbgEvent, 0))
{
ProcessDebugEvent(&dbgEvent, lpBreakPoints, 3);
ContinueDebugEvent(dbgEvent.dwProcessId, dbgEvent.dwThreadId, DBG_CONTINUE);
}
}
return 0; //<---never reaches return
It's a never ending loop so the program, at the moment, never actually reaches the return. It has to be terminated with either Ctrl+C or by closing the terminal. Not sure if this could be causing the destructor to not be called or not.
Any information, solutions, etc would be greatly appreciated. Thank you for your time.
you have to handle SIGINT signal, otherwise program is terminated abnormally and dtors are not called.
I'm trying to call DeviceIoControl(IOCTL_VOLUME_GET_VOLUME_DISK_EXTENTS) API, as shown here, but I need it to first "tell me" how much memory it needs (unlike the code I linked to.)
So I call it as such:
//First determine how much data do we need?
BYTE dummyBuff[1];
DWORD bytesReturned = 0;
if(!::DeviceIoControl(hDevice, dwIoControlCode, lpInBuffer, nInBufferSize,
dummyBuff, sizeof(dummyBuff), &bytesReturned, NULL))
{
//Check last error
int nError = ::GetLastError();
if(nOSError == ERROR_INSUFFICIENT_BUFFER ||
nOSError == ERROR_MORE_DATA)
{
//Alloc memory from 'bytesReturned' ...
}
}
but it always returns error code 87, or ERROR_INVALID_PARAMETER and my bytesReturned is always 0.
So what am I doing wrong?
The instructions for getting all disk volume extents are documented under the VOLUME_DISK_EXTENTS structure:
When the number of extents returned is greater than one (1), the error code ERROR_MORE_DATA is returned. You should call DeviceIoControl again, allocating enough buffer space based on the value of NumberOfDiskExtents after the first DeviceIoControl call.
The behavior, if you pass an output buffer, that is smaller than sizeof(VOLUME_DISK_EXTENTS) is also documented at IOCTL_VOLUME_GET_VOLUME_DISK_EXTENTS control code:
If the output buffer is less than sizeof(VOLUME_DISK_EXTENTS), the call fails, GetLastError returns ERROR_INSUFFICIENT_BUFFER, and lpBytesReturned is 0 (zero).
While this explains the returned value in lpBytesReturned, it doesn't explain the error code 87 (ERROR_INVALID_PARAMETER)1).
The following code will return the disk extents for all volumes:
VOLUME_DISK_EXTENTS vde = { 0 };
DWORD bytesReturned = 0;
if ( !::DeviceIoControl( hDevice, IOCTL_VOLUME_GET_VOLUME_DISK_EXTENTS, NULL, 0,
(void*)&vde, sizeof(vde), &bytesReturned, NULL ) )
{
// Check last error
int nError = ::GetLastError();
if ( nError != ERROR_MORE_DATA )
{
// Unexpected error -> error out
throw std::runtime_error( "DeviceIoControl() failed." );
}
size_t size = offsetof( VOLUME_DISK_EXTENTS, Extents[vde.NumberOfDiskExtents] );
std::vector<BYTE> buffer( size );
if ( !::DeviceIoControl( hDevice, IOCTL_VOLUME_GET_VOLUME_DISK_EXTENTS, NULL, 0,
(void*)buffer.data(), size, &bytesReturned, NULL ) )
{
// Unexpected error -> error out
throw std::runtime_error( "DeviceIoControl() failed." );
}
// At this point we have a fully populated VOLUME_DISK_EXTENTS structure
const VOLUME_DISK_EXTENTS& result =
*reinterpret_cast<const VOLUME_DISK_EXTENTS*>( buffer.data() );
}
else
{
// Call succeeded; vde is populated with single disk extent.
}
Additional references:
Why do some structures end with an array of size 1?
offsetof Macro
1) At a guess I would assume, that BYTE[1] begins at a memory address, that is not sufficiently aligned for the alignment requirements of VOLUME_DISK_EXTENTS.
Following #IInspectable's advice, here's what I came up with for a more general case:
BYTE* DeviceIoControl_Dynamic(HANDLE hDevice, DWORD dwIoControlCode, DWORD dwszCbInitialSuggested, LPVOID lpInBuffer, DWORD nInBufferSize, DWORD* pncbOutDataSz)
{
//Calls DeviceIoControl() API by pre-allocating buffer internally
//'dwIoControlCode' = control code, see DeviceIoControl() API
//'dwszCbInitialSuggested' = suggested initial size of the buffer in BYTEs, must be set depending on the description of 'dwIoControlCode'
//'lpInBuffer' = input buffer, see DeviceIoControl() API
//'nInBufferSize' = size of 'lpInBuffer', see DeviceIoControl() API
//'pncbOutDataSz' = if not NULL, receives the size of returned data in BYTEs
//RETURN:
// = Data obtained from DeviceIoControl() API -- must be removed with delete[]!
// = NULL if error -- check GetLastError() for info
BYTE* pData = NULL;
int nOSError = NO_ERROR;
DWORD ncbSzData = 0;
if((int)dwszCbInitialSuggested > 0)
{
//Initially go with suggested memory size
DWORD dwcbMemSz = dwszCbInitialSuggested;
//Try no more than 10 times
for(int t = 0; t < 10; t++)
{
//Reserve mem
ASSERT(!pData);
pData = new (std::nothrow) BYTE[dwcbMemSz];
if(!pData)
{
//Memory fault
nOSError = ERROR_NOT_ENOUGH_MEMORY;
break;
}
//And try calling with that size
DWORD bytesReturned = 0;
if(::DeviceIoControl(hDevice, dwIoControlCode, lpInBuffer, nInBufferSize,
pData, dwcbMemSz, &bytesReturned, NULL))
{
//Got it
ncbSzData = bytesReturned;
nOSError = NO_ERROR;
break;
}
//Check last error
nOSError = ::GetLastError();
//Knowing how badly Windows drivers are written, don't rely on the last error code!
//Alloc more memory (we'll just "wing it" on the amount)
dwcbMemSz += 1024;
//Free old mem
delete[] pData;
pData = NULL;
}
}
else
{
//Bad initial size
nOSError = ERROR_INVALID_MINALLOCSIZE;
}
if(pncbOutDataSz)
*pncbOutDataSz = ncbSzData;
::SetLastError(nOSError);
return pData;
}
and then to call it, say for IOCTL_VOLUME_GET_VOLUME_DISK_EXTENTS:
DWORD bytesReturned;
VOLUME_DISK_EXTENTS* p_vde = (VOLUME_DISK_EXTENTS*)DeviceIoControl_Dynamic(hDsk,
IOCTL_VOLUME_GET_VOLUME_DISK_EXTENTS, sizeof(VOLUME_DISK_EXTENTS), NULL, NULL, &bytesReturned);
which can be later used as such:
//Ensure that driver returned the correct data
if(p_vde &&
offsetof(VOLUME_DISK_EXTENTS, Extents[p_vde->NumberOfDiskExtents]) <= bytesReturned)
{
//All good
for(int x = 0; x < p_vde->NumberOfDiskExtents; x++)
{
DWORD diskNumber = p_vde->Extents[x].DiskNumber;
//...
}
}
//Remember to free mem when not needed!
if(p_vde)
{
delete[] (BYTE*)p_vde;
p_vde = NULL;
}
You are getting error code ERROR_INVALID_PARAMETER when you have invalid parameter, like its name says. In your case it should be bad handle because all others looks fine, if we expect that dwIoControlCode argument is IOCTL_VOLUME_GET_VOLUME_DISK_EXTENTS, lpInBuffer and nInBufferSize are ignored.
In insufficient buffer you will get another error code mentioned in above comments.
Lets check what is saying documentation:
DeviceIoControl can accept a handle to a specific device. For example, to open a handle to the logical drive A: with CreateFile, specify \.\a:. Alternatively, you can use the names \.\PhysicalDrive0, \.\PhysicalDrive1, and so on, to open handles to the physical drives on a system.
In other words, when you open handle with "C:\" instead of "\\.\c:" argument in CreateFile and use it in DeviceIoControl, the result is ERROR_INVALID_PARAMETER.
I'm trying to perform an HTTP GET using WinHTTP in C++, but it's crashing at some point after resolving the name (after getting the WINHTTP_CALLBACK_STATUS_NAME_RESOLVED in the status callback function). I'm getting an access violation in winhttp.dll. The callstack is:
winhttp.dll!HTTP_USER_REQUEST::_IndicateSocketAddress() + 0x221ed bytes
winhttp.dll!HTTP_USER_REQUEST::OnDnsNameResolved() + 0x24 bytes
winhttp.dll!WEBIO_REQUEST::_OnInformation() + 0x1c0c bytes
webio.dll!_WaIndicateHttpRequestInformation#16() + 0x15a bytes
webio.dll!_WaHttpInformationConnection#16() + 0x7a bytes
webio.dll!_WapTcpDnsQueryCompletionRoutine#12() + 0x2f bytes
webio.dll!_WapCallDnsQueryCompletion#12() + 0x6d bytes
webio.dll!_WaDnsResolutionWorker#8() + 0x157 bytes
ntdll.dll!_TppSimplepExecuteCallback#8() + 0x7b bytes
ntdll.dll!TppWorkerThread#4() + 0x5a4 bytes
kernel32.dll!#BaseThreadInitThunk#12() + 0x12 bytes
ntdll.dll!__RtlUserThreadStart#8() + 0x27 bytes
ntdll.dll!__RtlUserThreadStart#8() + 0x1b bytes
Relevant code is:
enum OnlineState
{
OnlineState_Idle,
OnlineState_Registering
};
static OnlineState g_OnlineState = OnlineState_Idle;
HINTERNET g_Request = 0;
HINTERNET g_Connection = 0;
unsigned char g_HTTPBuffer[1024];
void HTTPRequestComplete()
{
if(g_Request != 0)
{
WinHttpCloseHandle(g_Request);
g_Request = 0;
}
if(g_Connection != 0)
{
WinHttpCloseHandle(g_Connection);
g_Connection = 0;
}
g_OnlineState = OnlineState_Idle;
}
void HTTPAsyncCallback(HINTERNET hInternet, DWORD_PTR dwContext, DWORD dwInternetStatus, LPVOID lpvStatusInformation, DWORD dwStatusInformationLength)
{
switch(dwInternetStatus)
{
case WINHTTP_CALLBACK_STATUS_SENDREQUEST_COMPLETE:
{
// Get the response
if (!WinHttpReceiveResponse(g_Request, 0))
{
// Failed to get the response
HTTPRequestComplete();
return;
}
}
break;
case WINHTTP_CALLBACK_STATUS_HEADERS_AVAILABLE:
{
DWORD statusCode = 0;
DWORD statusCodeSize = sizeof(DWORD);
if (!WinHttpQueryHeaders(g_Request,
WINHTTP_QUERY_STATUS_CODE | WINHTTP_QUERY_FLAG_NUMBER,
WINHTTP_HEADER_NAME_BY_INDEX,
&statusCode,
&statusCodeSize,
WINHTTP_NO_HEADER_INDEX))
{
// Failed to query headers
HTTPRequestComplete();
return;
}
if (HTTP_STATUS_OK != statusCode)
{
// Error status
HTTPRequestComplete();
return;
}
if (!WinHttpReadData(g_Request,
g_HTTPBuffer,
sizeof(g_HTTPBuffer),
0))
{
// Error reading data
HTTPRequestComplete();
return;
}
}
break;
case WINHTTP_CALLBACK_STATUS_READ_COMPLETE:
{
if (dwStatusInformationLength > 0)
{
// Store the data
// Read the next data
if (!WinHttpReadData(g_Request,
g_HTTPBuffer,
sizeof(g_HTTPBuffer),
0))
{
// Error
HTTPRequestComplete();
return;
}
}
else
{
// Request completed OK
HTTPRequestComplete();
}
}
break;
default:
break;
}
}
// Online functionality
void Online_UpdateServer()
{
switch(g_OnlineState)
{
case OnlineState_Idle:
{
// Get our local ip address by connecting a local socket to a web address and reading the socket name
// Look up the address to connect to
addrinfo hints;
addrinfo* res = 0;
memset(&hints, 0, sizeof hints);
hints.ai_family = AF_UNSPEC;
hints.ai_socktype = SOCK_DGRAM;
getaddrinfo("www.google.com", "80", &hints, &res);
// Create the socket
int tempSocket = socket(res->ai_family, res->ai_socktype, res->ai_protocol);
unsigned int localAddress = 0;
if (tempSocket >= 0)
{
// Connect the socket
connect(tempSocket, res->ai_addr, res->ai_addrlen);
// Get the socket name (our local ip address)
sockaddr_in localName;
memset(&localName, 0, sizeof(localName));
int bufferSize = sizeof(localName);
if(getsockname(tempSocket, (sockaddr*)&localName, &bufferSize) == 0)
{
// Get the IP address
localAddress = localName.sin_addr.S_un.S_addr;
}
closesocket(tempSocket);
}
// Connect
g_Connection = WinHttpConnect(g_Internet, L"www.google.com", INTERNET_DEFAULT_PORT, 0);
// Open the request
std::wstringstream urlString;
urlString << L"/";
std::wstring tempString = urlString.str();
const wchar_t* wurlString = tempString.c_str();
g_Request = WinHttpOpenRequest(g_Connection, 0, wurlString, 0, WINHTTP_NO_REFERER, WINHTTP_DEFAULT_ACCEPT_TYPES, 0);
// Install the status callback function.
if(WINHTTP_INVALID_STATUS_CALLBACK == WinHttpSetStatusCallback(g_Request, (WINHTTP_STATUS_CALLBACK)HTTPAsyncCallback, WINHTTP_CALLBACK_FLAG_ALL_NOTIFICATIONS, NULL))
{
OutputDebugString(L"Error");
}
// Send the request
if(!WinHttpSendRequest(g_Request, WINHTTP_NO_ADDITIONAL_HEADERS, 0, WINHTTP_NO_REQUEST_DATA, 0, 0, 0))
{
OutputDebugString(L"Error");
}
// Log that we're registering
g_OnlineState = OnlineState_Registering;
}
break;
case OnlineState_Registering:
{
// Don't do anything, as we're currently registering
}
break;
default:
break;
}
}
g_Internet is initialised like this:
// Initialise HTTP
g_Internet = WinHttpOpen(L"Boomba", WINHTTP_ACCESS_TYPE_DEFAULT_PROXY, WINHTTP_NO_PROXY_NAME, WINHTTP_NO_PROXY_BYPASS, WINHTTP_FLAG_ASYNC);
As far as I can tell, I'm initialising and using WinHTTP correctly, and everything seems to come back OK without errors. The callback is called twice, first time with WINHTTP_CALLBACK_STATUS_RESOLVING_NAME, then with WINHTTP_CALLBACK_STATUS_NAME_RESOLVED, then after that I get an access violation:
0xC0000005: Access violation reading location 0x00000014
The location changes, to various things, so I'm thinking it looks like it could possibly be a threading issue, but I'm not sure what could be the problem. Any ideas? (I'm running on Windows 7 64-bit).
if (WINHTTP_INVALID_STATUS_CALLBACK == WinHttpSetStatusCallback(
g_Request,
(WINHTTP_STATUS_CALLBACK)HTTPAsyncCallback, // <=== evil
WINHTTP_CALLBACK_FLAG_ALL_NOTIFICATIONS,
NULL))
The compiler originally complained about a mismatch between your function and the function pointer type that is required. You fixed the problem by shutting up the compiler with that cast. But that didn't actually fix the problem. Now you bought yourself a real problem, a corrupted stack. Very hard to diagnose.
The callback function must be declared as __stdcall, not the default __cdecl calling convention. The Windows headers use the CALLBACK macro for that. Fix:
void CALLBACK HTTPAsyncCallback(/* etc*/)
And of course remove that cast.