I have seen a few questions on this but they are not quite in my scenario. Unless I missed something.
This is how I revoke Google Access:
bool CCalendarSettingsGooglePage::RevokeGoogleAccess()
{
CInternetSession iSession;
CHttpFile *pWebFile = nullptr;
CWaitCursor wait;
TCHAR szError[_MAX_PATH];
DWORD dwStatusCode;
CString strError, strToken, strRevokeURL;
if (m_strGoogleToken == _T(""))
return true;
strRevokeURL.Format(_T("https://accounts.google.com/o/oauth2/revoke?token=%s"), (LPCTSTR)m_strGoogleToken);
// ask user to go online
if (InternetGoOnline((LPTSTR)(LPCTSTR)strRevokeURL, GetSafeHwnd(), 0))
{
TRY
{
// our session should already be open
// try to open up internet session to my URL
// AJT v10.4.0 Use flag INTERNET_FLAG_RELOAD
pWebFile = (CHttpFile*)iSession.OpenURL(strRevokeURL, 1,
INTERNET_FLAG_TRANSFER_BINARY |
INTERNET_FLAG_DONT_CACHE | INTERNET_FLAG_RELOAD);
if (pWebFile != nullptr)
{
if (pWebFile->QueryInfoStatusCode(dwStatusCode))
{
if (dwStatusCode == 200) // OK!
{
return true;
}
else
{
// There was a problem!
strError.Format(_T("Revoke error: %lu"), dwStatusCode);
AfxMessageBox(strError, MB_OK | MB_ICONERROR);
}
}
}
else
{
AfxMessageBox(_T("Revoke error"), MB_OK | MB_ICONERROR);
}
}
CATCH(CException, e)
{
e->GetErrorMessage(szError, _MAX_PATH);
AfxMessageBox(szError, MB_OK | MB_ICONERROR);
}
END_CATCH
// Tidy up
if (pWebFile != nullptr)
{
pWebFile->Close();
delete pWebFile;
}
iSession.Close();
}
return false;
}
My problem is that this approach does not always works. I find that a user has to actually go into their Google Account online, locate their apps, and manually revoke it.
Is there a more robust way to support revoking of an access token?
curl option to revoke token according to Google:
curl -H "Content-type:application/x-www-form-urlencoded" https://accounts.google.com/o/oauth2/revoke?token={token}
So your method should be okay. Maybe the token is already invalid and Google tells you that it doesn't know what to do with the request (the token may have a short life span)
OAuth usually expects "POST" method instead of "GET". You can try the "POST" method instead. In MFC it would be as follows:
DWORD flag = INTERNET_FLAG_SECURE | INTERNET_FLAG_DONT_CACHE |
INTERNET_FLAG_IGNORE_CERT_CN_INVALID | INTERNET_FLAG_IGNORE_CERT_DATE_INVALID;
CStringW header;
CStringA query;
{
CStringW queryW;
queryW.Format(L"token=%s", token);
query = CW2A(queryW, CP_UTF8);
}
header.Format(L"\
Content-Type: application/x-www-form-urlencoded\r\n\
Content-length: %d\r\n\
Connection: Close\r\n\
\r\n", query.GetLength());
CInternetSession session;
CHttpConnection *connection = session.GetHttpConnection(L"accounts.google.com",
(INTERNET_PORT)INTERNET_DEFAULT_HTTPS_PORT);
if(connection)
{
CHttpFile *file = connection->OpenRequest(L"POST",
L"o/oauth2/revoke", L"http://localhost", 1, NULL, NULL, flag);
if(file)
{
if(file->SendRequest(header,
query.GetBuffer(), query.GetLength()))
{
DWORD dwRet;
if (file->QueryInfoStatusCode(dwRet))
{
if (dwRet == 200 || dwRet == 400)
{
//success!
}
}
}
delete file;
}
delete connection;
}
Above code uses Unicode version, it has to convert UTF-16 query to UTF-8...
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);
I am finding that if I navigate to a file and then I try to delete the file the system tells me it is in use.
How can I stopped the files from being "in use" so I can delete it and recreate it to update the html display?
If I create a NEW XML data file every time and navigate to that then I get no problems. This is because there is no file to delete.
But the moment I use the same file I get the file in use issue.
I added code in my dialog OnDestroy method and added an array of temporary files that i create. Then I try to delete them:
for (auto i = 0; i < m_aryStrTempFiles.GetCount(); i++)
{
if (PathFileExists(m_aryStrTempFiles[i]))
{
if (!::DeleteFile(m_aryStrTempFiles[i]))
{
AfxMessageBox(theApp.GetLastErrorAsString(), MB_OK | MB_ICONERROR);
}
}
}
I find that ALL of the files are considered still in use.
The code that creates the temporary file names is not the issue:
CString CMeetingScheduleAssistantApp::GetFolderTempFilenameEx(CString strFolder, CString strToken, CString strSuffix /*_T("htm")*/)
{
CString strFile;
int i;
::GetTempFileName(strFolder, strToken, 0, strFile.GetBuffer(_MAX_PATH));
strFile.ReleaseBuffer();
// Because we will rename to .HTM we must delete old file
::DeleteFile(strFile);
// I can't be sure the suffix is .tmp so I manually
// replace the suffix, whatever it is, with .htm"
i = strFile.ReverseFind(_T('.'));
strFile = strFile.Left(i + 1);
strFile += strSuffix;
return strFile;
}
And this is the code that saves my XML files:
bool CMeetingScheduleAssistantApp::SaveToXML(CString strFileXML, tinyxml2::XMLDocument& rDocXML)
{
FILE *fStream = nullptr;
CString strError, strErrorCode;
errno_t eResult;
bool bDisplayError = false;
int iErrorNo = -1;
using namespace tinyxml2;
// Does the file already exist?
if (PathFileExists(strFileXML))
{
// It does, so try to delete it
if (!::DeleteFile(strFileXML))
{
// Unable to delete!
AfxMessageBox(theApp.GetLastErrorAsString(), MB_OK | MB_ICONINFORMATION);
return false;
}
}
// Now try to create a FILE buffer (allows UNICODE filenames)
eResult = _tfopen_s(&fStream, strFileXML, _T("w"));
if (eResult != 0 || fStream == nullptr) // Error
{
bDisplayError = true;
_tcserror_s(strErrorCode.GetBufferSetLength(_MAX_PATH), _MAX_PATH, errno);
strErrorCode.ReleaseBuffer();
}
else // Success
{
// Now try to save the XML file
XMLError eXML = rDocXML.SaveFile(fStream);
int fileCloseResult = fclose(fStream);
if (eXML != XMLError::XML_SUCCESS)
{
// Error saving
bDisplayError = true;
strErrorCode = rDocXML.ErrorName();
iErrorNo = rDocXML.GetErrorLineNum();
}
if (!bDisplayError)
{
if (fileCloseResult != 0)
{
// There was a problem closing the stream. We should tell the user
bDisplayError = true;
_tcserror_s(strErrorCode.GetBufferSetLength(_MAX_PATH), _MAX_PATH, errno);
strErrorCode.ReleaseBuffer();
}
}
}
if (bDisplayError)
{
if (iErrorNo == -1)
iErrorNo = errno;
strError.Format(IDS_TPL_ERROR_SAVE_XML, strFileXML, strErrorCode, iErrorNo);
AfxMessageBox(strError, MB_OK | MB_ICONINFORMATION);
return false;
}
return true;
}
As you can see, they all close the stream. Yet, even though in OnDestroy I delete the html view first the temporary files still can't be deleted. Why?
The issue was how I was testing for a file still being open:
bool CMeetingScheduleAssistantApp::WaitForFileToBeReady(CString strFile)
{
HANDLE hFile;
int delay = 10;
while ((hFile = CreateFile(strFile, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL)) == INVALID_HANDLE_VALUE)
{
if (GetLastError() == ERROR_SHARING_VIOLATION) {
Sleep(delay);
if (delay < 5120) // max delay approx 5.Sec
delay *= 2;
}
else
{
AfxMessageBox(theApp.GetLastErrorAsString(), MB_OK | MB_ICONINFORMATION);
return false; // some other error occurred
}
}
if (hFile != INVALID_HANDLE_VALUE)
CloseHandle(hFile);
return true;
}
I was missing the CloseHandle lines of code.
I need to send one request to server more quickly and repeatedly.
So I implemented code like this.
BOOL CTestDlg::OnInitDialog()
{
...
m_hInstance = InternetOpen(L"asynchttp", INTERNET_OPEN_TYPE_PRECONFIG,NULL,NULL,INTERNET_FLAG_ASYNC);
if(m_hInstance == NULL)
{
ErrorLog(L"InternetOpen Failed");
return TRUE;
}
if (InternetSetStatusCallback(m_hInstance,(INTERNET_STATUS_CALLBACK)&Callback) == INTERNET_INVALID_STATUS_CALLBACK)
{
ErrorLog(L"Set Callback Function Failed");
return TRUE;
}
m_hConnect = InternetConnect(m_hInstance, L"192.168.2.116", 8080,NULL,NULL,INTERNET_SERVICE_HTTP,INTERNET_FLAG_RELOAD | INTERNET_FLAG_NO_CACHE_WRITE | INTERNET_FLAG_KEEP_CONNECTION,1);
if(m_hConnect == NULL)
{
if(DWORD dwError = GetLastError() != ERROR_IO_PENDING)
{
ErrorLog(L"Fail to Connect Server");
return TRUE;
}
WaitForSingleObject(hConnectedEvent, INFINITE);
}
CreateThread(0 , 0 , (LPTHREAD_START_ROUTINE)SendThread , 0 , 0 , 0);
return TRUE;
}
void __stdcall Callback(HINTERNET hInternet,DWORD dwContext,DWORD dwInternetStatus,LPVOID lpStatusInfo,DWORD dwStatusInfoLen)
{
switch(dwContext)
{
case 1:
{
SetEvent(hConnectedEvent);
}
break;
}
}
DWORD SendThread(LPVOID lpParam)
{
TCHAR *szAceptType[] = {_T("*/*") , NULL};
CString szContentType = _T("Content-Type: application/x-www-form-urlencoded\r\n");
char szPostData[MAX_PATH];
sprintf(szPostData , "num=1234&str=3240");
HINTERNET hRequest = HttpOpenRequest(m_hConnect, L"POST", L"/TestWeb/index.jsp", HTTP_VERSION , NULL , (LPCTSTR*)szAceptType , 0, 2);
while(1)
{
try
{
if (!HttpSendRequest(hRequest, szContentType, (DWORD)szContentType.GetLength(), szPostData,(DWORD)strlen(szPostData)))
{
if (DWORD dwError = GetLastError() != ERROR_IO_PENDING)
ErrorLog(L"SendRequest: Error = %d" , dwError);
}
}
catch (CException* e)
{
UNREFERENCED_PARAMETER(e);
}
Sleep(100);
}
return 0;
}
When execute or debug program, I saw this "SendRequest; Error = 1" log frequently.
And Server does not record anymore to database , after one or two request data recorded
It seems like HttpSendRequest API doesn't work correctly after error occured.
I aim to send one request to server more fast and more correctly, without loss.
Please teach me what is wrong problem.
Or if you got another best way, then please tell me.
Thanks.
Error code 1 is "Invalid Parameter".
You appear to be mixing ASCII (char and sprintf) and Unicode strings in your sample above. Have you tried with all Unicode strings (making szPostData a WCHAR array)? From MSDN: "There two versions of HttpSendRequest —HttpSendRequestA (used with ANSI builds) and HttpSendRequestW (used with Unicode builds)". It goes on to talk about when ERROR_INVALID_PARAMETER is returned. http://msdn.microsoft.com/en-us/library/windows/desktop/aa384247(v=vs.85).aspx
Also, I haven't used MFC in a long time, but I remember that you should call e->Delete() in your exception handler (unless you re-throw). The CException::Delete method will delete any thrown exception created on the heap (and do nothing otherwise).
How can I get contents of a file online with C++?
There are a number of ways you can do this.
WinInet
First off Windows has a built-in API allowing you to make HTTP requests, which is reasonably simple to use. I use this simple wrapper class to download files using it:
/**
* Simple wrapper around the WinInet library.
*/
class Inet
{
public:
explicit Inet() : m_hInet(NULL), m_hConnection(NULL)
{
m_hInet = ::InternetOpen(
"My User Agent",
INTERNET_OPEN_TYPE_PRECONFIG,
NULL,
NULL,
/*INTERNET_FLAG_ASYNC*/0);
}
~Inet()
{
Close();
if (m_hInet)
{
::InternetCloseHandle(m_hInet);
m_hInet = NULL;
}
}
/**
* Attempt to open a URL for reading.
* #return false if we don't have a valid internet connection, the url is null, or we fail to open the url, true otherwise.
*/
bool Open(LPCTSTR url)
{
if (m_hInet == NULL)
{
return false;
}
if (url == NULL)
{
return false;
}
m_hConnection = ::InternetOpenUrl(
m_hInet,
url,
NULL /*headers*/,
0 /*headers length*/,
INTERNET_FLAG_NO_AUTH | INTERNET_FLAG_NO_CACHE_WRITE | INTERNET_FLAG_NO_COOKIES | INTERNET_FLAG_NO_UI,
reinterpret_cast<DWORD_PTR>(this));
return m_hConnection != NULL;
}
/**
* Read from a connection opened with Open.
* #return true if we read data.
*/
bool ReadFile(LPVOID lpBuffer, DWORD dwNumberOfBytesToRead, LPDWORD dwRead)
{
ASSERT(m_hConnection != NULL);
return ::InternetReadFile(m_hConnection, lpBuffer, dwNumberOfBytesToRead, dwRead) != 0;
}
/**
* Close any open connection.
*/
void Close()
{
if (m_hConnection != NULL)
{
::InternetCloseHandle(m_hConnection);
m_hConnection = NULL;
}
}
private:
HINTERNET m_hInet;
HINTERNET m_hConnection;
};
Usage of this is very simple:
Inet inet;
if (inet.Open(url))
{
BYTE buffer[UPDATE_BUFFER_SIZE];
DWORD dwRead;
while (inet.ReadFile(&buffer[0], UPDATE_BUFFER_SIZE, &dwRead))
{
// TODO: Do Something With buffer here
if (dwRead == 0)
{
break;
}
}
}
LibCurl
If you'd rather avoid Windows-specific APIs then you could do a lot worse than use the libcurl library to get files using a variety of protocols, including HTTP. There is a good sample showing how to retrieve a URL directly into memory (avoiding downloading to disk): getinmemory sample.
Use the following functions.
WinHttpConnect
WinHttpOpenRequest
WinHttpSendRequest
WinHttpReceiveResponse
WinHttpQueryDataAvailable
WinHttpReadData
Hey all i want to login onto my works webpage with wininet, this is my current code:
int main()
{
HINTERNET hInet = InternetOpenA("UserAgent/1.0", INTERNET_OPEN_TYPE_PRECONFIG,0, 0, 0 );
if(!hInet)
{
printf("hInet Failed!\n");
return -1;
}
HINTERNET hConnection = InternetConnectA( hInet,"app.tamigo.com",INTERNET_DEFAULT_HTTPS_PORT,"","", INTERNET_SERVICE_HTTP,0,0);
if (!hConnection)
{
InternetCloseHandle(hInet);
printf("InternetConnectA failed!\n");
return -1;
}
HINTERNET hRequest = HttpOpenRequestA( hConnection, "Accept","text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8",NULL,"https://app.tamigo.com/Home/Pages/Login.aspx", NULL, INTERNET_FLAG_KEEP_CONNECTION, 0 );
if (!hRequest)
{
printf("BuildRequestHeader failed %d!\n",GetLastError());
InternetCloseHandle(hConnection);
InternetCloseHandle(hInet);
return -1;
}
HttpSendRequestA(hRequest, NULL, 0, NULL, 0);
DWORD dwInfoLevel = HTTP_QUERY_RAW_HEADERS_CRLF;
DWORD dwInfoBufferLength = 10;
BYTE *pInfoBuffer = (BYTE *)malloc(dwInfoBufferLength+1);
while (!HttpQueryInfo(hRequest, dwInfoLevel, pInfoBuffer, &dwInfoBufferLength, NULL))
{
DWORD dwError = GetLastError();
if (dwError == ERROR_INSUFFICIENT_BUFFER)
{
free(pInfoBuffer);
pInfoBuffer = (BYTE *)malloc(dwInfoBufferLength+1);
}
else
{
fprintf(stderr, "HttpQueryInfo failed, error = %d (0x%x)\n",
GetLastError(), GetLastError());
break;
}
}
pInfoBuffer[dwInfoBufferLength] = '\0';
printf("%s", pInfoBuffer);
free(pInfoBuffer);
cin.get();
return 1;
}
if this code is right, i have to login with my username and pass,i got a cookie using "Firefox plugin Tamper Data". How can i set this cookie with wininet?
Thanks alot for reading and for your time
If the cookie already exists from a previous WinInet request, then WinInet will send it automatically. However, if the cookie does not exist in WinInet's cookie cache (if instance, if you got the cookie from another source), then you will have to use HttpAddRequestHeaders() to provide your own Cookie: request header before calling HttpSendRequest().