So I am trying to create a function to download a file with a variable address depending on the user with a string input using URLDownloadToFile() however it does not seem to actually download the file to the disk. It skips S_OK, E_OUTOFMEMORY and INET_E_DOWNLOAD_FAILURE to UNKNOWN_ERROR. Using GetLastError() returns 2, checking up online said that meant "Not Found". I'm not sure whether this was the website I was using or not (it's my own server with Node.JS using res.download(path, filename)). Hope for help, the code is below.
Just a note, everything is properly included as needed and the process (safely) runs with SE_DEBUG_NAME so it hopefully should not be a permission error.
// Convert String To Wide String
wstring S2WS(const string& str) {
int length;
int slength = (int)str.length() + 1;
length = MultiByteToWideChar(CP_ACP, 0, str.c_str(), slength, 0, 0);
wchar_t* buffer = new wchar_t[length];
MultiByteToWideChar(CP_ACP, 0, str.c_str(), slength, buffer, length);
wstring out(buffer);
delete[] buffer;
return out;
}
// Download File
int download(string url) {
wstring wUrl = S2WS(url);
LPCWSTR lUrl = wUrl.c_str();
TCHAR path[MAX_PATH];
GetCurrentDirectory(MAX_PATH, path);
HRESULT response = URLDownloadToFile(NULL, lUrl, path, 0, NULL);
if (response == S_OK) {
cout << "S_OK" << endl;
} else if (response == E_OUTOFMEMORY) {
cout << "E_OUTOFMEMORY" << endl;
} else if (response == INET_E_DOWNLOAD_FAILURE) {
cout << "INET_E_DOWNLOAD_FAILURE" << endl;
} else {
cout << "UNKNOWN_ERROR" << endl;
}
return 0;
}
// Example Usage
int main() {
download("http://somewebsite.com/files/textfile.txt");
}
From URLDownloadToFile, the path should be file path not a directory path. You could append some file name to the path variable and it should work.
TCHAR path[MAX_PATH];
GetCurrentDirectory(MAX_PATH, path);
wcscat_s(path, L"\\filename.txt");
Related
This question already has answers here:
FindFirstChangeNotification is notifying about changes twice
(2 answers)
Closed 1 year ago.
I have to find out if there is a new file in a directory on Windows. Following this MSDN example (Obtaining Directory Change Notifications), I came up with the following test program:
#include <iostream>
#include <Windows.h>
#include <vector>
#include <string>
std::string FindNewFile(std::vector<std::string>& vsNewFileList, std::vector<std::string>& vsOldFileList)
{
std::string sNewFileName;
int nScore = 0;
for (auto& newFile : vsNewFileList)
{
nScore = 0;
for (auto& oldFile : vsOldFileList)
if(!newFile.compare(oldFile))
nScore++;
if (nScore!=1)
{
sNewFileName = newFile;
break;
}
}
return sNewFileName;
}
void GetCurrentFilesInDir(std::string sDir, std::vector<std::string>& vsFileList)
{
WIN32_FIND_DATA ffd;
sDir += "\\*";
std::wstring wStr = std::wstring(sDir.begin(), sDir.end());
LPCWSTR lpcwsDir = (LPCWSTR)wStr.c_str();
HANDLE hFind = FindFirstFile(lpcwsDir, &ffd);
if (hFind == INVALID_HANDLE_VALUE)
{
std::cout << "Nope\n";
return;
}
vsFileList.clear();
do
{
int nSize = WideCharToMultiByte(CP_ACP, 0, ffd.cFileName, -1, 0, 0, 0, 0);
char* pcStr = new char[nSize];
WideCharToMultiByte(CP_ACP, 0, ffd.cFileName, -1, pcStr, nSize, 0, 0);
//std::cout << pcStr << "\n";
vsFileList.push_back(std::string(pcStr));
delete[] pcStr;
} while (FindNextFile(hFind, &ffd) != 0);
}
int main()
{
// watch the foo directory for new files
std::string sDir = "C:\\foo";
std::vector<std::string> vsOldFileList, vsNewFileList;
GetCurrentFilesInDir(sDir, vsOldFileList);
std::wstring wStr = std::wstring(sDir.begin(), sDir.end());
LPCWSTR lpcwsDir = (LPCWSTR)wStr.c_str();
DWORD dwWaitStatus;
HANDLE dwChangeHandle;
dwChangeHandle = FindFirstChangeNotification(
lpcwsDir,
FALSE,
FILE_NOTIFY_CHANGE_FILE_NAME);
while (TRUE)
{
// returns multiple times before and after new file appears!!
dwWaitStatus = WaitForSingleObject(dwChangeHandle, INFINITE);
switch(dwWaitStatus)
{
case WAIT_OBJECT_0:
GetCurrentFilesInDir(sDir, vsNewFileList);
std::string sNewFileName = FindNewFile(vsNewFileList, vsOldFileList);
std::cout << sNewFileName << "\n";
GetCurrentFilesInDir(sDir, vsOldFileList);
FindNextChangeNotification(dwChangeHandle);
break;
}
}
}
The problem is that, when I save a new file in C:\foo (for instance, using Notepad++ to "Save As" an open .txt file in C:\foo), the call to WaitForSingleObject() in the while loop will return 0 multiple times. Since my FindNewFile() method returns an empty string if there is no new file in the directory, I will get output like:
a.txt
or:
b.txt
Or even:
c.txt
c.txt
Can someone explain what I am missing here?
Using FindNextChangeNotification can not tell you what actually happened, and the operation of the file may involve multiple changes.
You can try to use ReadDirectoryChangesW and here is a sample:
#include <windows.h>
#include <iostream>
using namespace std;
wstring getname(FILE_NOTIFY_INFORMATION* tmp)
{
wstring s = L"";
for (int i = 0; i < tmp->FileNameLength / 2; i++)
s += tmp->FileName[i];
return s;
}
int main(int argc, const char* argv[])
{
HANDLE hDir;
char notify[1024];
DWORD cbBytes;
LPTSTR path;
FILE_NOTIFY_INFORMATION* pnotify = (FILE_NOTIFY_INFORMATION*)notify;
FILE_NOTIFY_INFORMATION* tmp;
// GetCurrentDirectory(MAX_PATH,path.GetBuffer(MAX_PATH+1));
path = (LPTSTR)L"D:\\test";
hDir = CreateFile(path, FILE_LIST_DIRECTORY,
FILE_SHARE_READ |
FILE_SHARE_WRITE |
FILE_SHARE_DELETE, NULL,
OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS |
FILE_FLAG_OVERLAPPED, NULL);
wcout << L"===CreateFile complete===" << endl;
if (hDir == INVALID_HANDLE_VALUE)
{
wcout << L"invalid handle value" << endl;
return -1;
}
FILE_NOTIFY_INFORMATION buffer[1024];
FILE_NOTIFY_INFORMATION* pbuffer;
while (TRUE)
{
wcout << L"waiting..." << endl;
WaitForSingleObject(hDir, INFINITE);
if (ReadDirectoryChangesW(hDir, &buffer, sizeof(buffer),
TRUE, FILE_NOTIFY_CHANGE_FILE_NAME,
&cbBytes, NULL, NULL))
{
pbuffer = buffer;
do {
tmp = pbuffer;
switch (tmp->Action)
{
case FILE_ACTION_ADDED:
wcout << L"Directory/File added - " << getname(tmp) << endl;
break;
case FILE_ACTION_REMOVED:
wcout << L"Directory/File removed - " << getname(tmp) << endl;
break;
case FILE_ACTION_MODIFIED:
wcout << L"Directory/File modfied - " << getname(tmp) << endl;
break;
case FILE_ACTION_RENAMED_OLD_NAME:
wcout << L"Directory/File old name - " << getname(tmp) << endl;
break;
case FILE_ACTION_RENAMED_NEW_NAME:
wcout << L"Directory/File new name - " << getname(tmp) << endl;
break;
default:
wcout << L"unknown action\n" << endl;
break;
}
pbuffer += pbuffer->NextEntryOffset;
} while (pbuffer->NextEntryOffset);
}
else
{
wcout << "readChangesW failed now return" << endl;
return -1;
}
}
}
When you do the Save As operation, you will find:
Therefore, multiple file operations were triggered when actually saving as, and you also performed multiple comparisons when processing new file comparisons, so empty characters were output.
More reference: FindFirstChangeNotification is notifying about changes twice
I want to open a file in VS2017 with C++ by using fopen and relative path. But every try I got an error so I look at the absolute path of my relative input:
string ExePath() {
char buffer[MAX_PATH];
GetModuleFileName(NULL, buffer, MAX_PATH);
return buffer;
}
string AbsPath(string path) {
char buffer[MAX_PATH]; strcpy_s(buffer, path.c_str());
char buffer2[MAX_PATH];
GetFullPathName(buffer, MAX_PATH, buffer2, NULL);
return buffer2;
}
int main() {
cout << "Application\t " << ExePath() << "\n";
cout << "\\data.dat\t" << AbsPath(".\\data.dat") << "\n";
system("Pause");
}
I got the output:
Application C:\Users\User\source\repos\Project1\Debug\Project1.exe
\data.dat C:\Users\User\source\repos\Project1\Project1\data.dat
My Application is located in .\Debug\ and my relative path points to \Project1. Do I made an error?
How can i get current file name without path, i want to use it on WinExec,
example for what am trying to do,
WinExec("Do something to <mycurrentfilename.exe>", SW_HIDE);
In common case you have to use GetModuleFileName function.
Example:
#include <Windows.h>
#include <sstream>
int main(void) {
char myexepath[MAX_PATH] = { 0 };
DWORD returnCode = GetModuleFileNameA(NULL, myexepath, MAX_PATH);
if (returnCode != 0 && returnCode < MAX_PATH)
{
std::string filepath(myexepath);
filepath = filepath.substr(filepath.find_last_of('\\') + 1);
std::ostringstream ss;
ss << "Do something to \"" << filepath << "\"";
WinExec(ss.str().c_str(), SW_HIDE);
}
else
{
// process GetModuleFileName error.
}
return 0;
}
Example uses char encoding for filename, but it can be changed to wchar_t or universal TCHAR
UPDATE:
Looking through the protocol here, I can't figure out what goes into the Unsized Envelope Record. I can't find any examples online.
ORIGINAL:
I have the following WCF service
static void Main(string[] args)
{
var inst = new PlusFiver();
using (ServiceHost host = new ServiceHost(inst,
new Uri[] { new Uri("net.pipe://localhost") }))
{
host.AddServiceEndpoint(typeof(IPlusFive), new NetNamedPipeBinding(NetNamedPipeSecurityMode.None), "PipePlusFive");
host.Open();
Console.WriteLine("Service is Available. Press enter to exit.");
Console.ReadLine();
host.Close();
}
}
[ServiceContract]
public interface IPlusFive
{
[OperationContract]
int PlusFive(int value);
}
[ServiceBehavior(InstanceContextMode = InstanceContextMode.Single)]
public class PlusFiver : IPlusFive
{
public int PlusFive(int value)
{
Console.WriteLine("Adding 5 to " + value);
return value + 5;
}
}
I output the adding 5 line so I know if the server processed the
request or not.
I have a .NET client that I used to test this and everything works as
expected.
Now I want to make an unmanaged C++ client for this.
I figured out how to get the name of the pipe, and write to it.
I've downloaded the protocol from here
I can write to the pipe but I can't read to it. Whenever I try to read from it I get a ERROR_BROKEN_PIPE 109 (0x6D) The pipe has been ended. error. If I replace the read with a write, the write is successful, so I don't think that the pipe is closed, at least not until I try to do a read.
Here is how I'm connecting to the pipe.
HANDLE OpenPipe(OLECHAR* bstrGuid)
{
wstring pipeName = L"\\\\.\\pipe\\";
wstring strGuid = bstrGuid;
pipeName.append(strGuid.substr(1,36));
wcout << "Pipe Name " << endl;
wcout << pipeName.c_str() << endl;
HANDLE hPipe = CreateFile(pipeName.c_str(), GENERIC_WRITE | GENERIC_READ, FILE_SHARE_WRITE | FILE_SHARE_READ, NULL, OPEN_EXISTING, NULL, NULL);
if(hPipe == INVALID_HANDLE_VALUE)
{
wcout << "failed to create pipe" << endl;
system("pause");
return NULL;
}
return hPipe;
}
this is how i'm creating the first message that I'm sending
std::list<wchar_t> GetFirstMessage()
{
std::list<wchar_t> message;
message.push_back(0x00);// version record
message.push_back(0x01);// major version
message.push_back(0x00);// minor version
message.push_back(0x01);// mode record
message.push_back(0x01);// singleton-unsized mode
message.push_back(0x02);// via record
wstring url = L"net.pipe://localhost/PipePlusFive";
message.push_back(url.length());// via length
for(int x= 0;x<url.length();x++)
{
message.push_back(url[x]); // via
}
message.push_back(0x03);
message.push_back(0x08);
return message;
}
This is how I'm writing it to the file.
int WriteMessage(HANDLE hPipe, LPVOID message, int size)
{
DWORD bytesWritten;
BOOL bWrite = WriteFile(hPipe, &message, size, &bytesWritten, NULL);
wcout << "Bytes Written: " << bytesWritten << endl;
if(bWrite == false)
{
wcout << "fail"<<endl;
CloseHandle(hPipe);
system("pause");
return 1;
}
return 0;
}
list<wchar_t> full_message = GetFirstMessage();
int result = WriteMessage(hPipe, &full_message, full_message.size());
if (result == 1)
{ return 1;}
Here is how I'm writing the end message
wchar_t message = 12;
result = WriteMessage(hPipe, &message, 1);
if (result == 1)
{ return 1;}
here is how I'm trying to read the response
char buffer[10];
DWORD bytesRead;
BOOL bRead = ReadFile(hPipe, buffer, 1, &bytesRead, NULL);
if(bRead == false)
{
wcout << "fail read"<<endl;
wcout << "error: " << GetLastError() << endl;
CloseHandle(hPipe);
system("pause");
return 1;
}
I'm new to c++, so I don't know if I'm not following the protocol correctly or making a stupid mistake in the way I'm trying to do this?
UPDATE:
The problem was that I was writing the pointer address to the named pipe instead of the contents of the list. I've fixed that And I'm now able to read the Preamble Ack Record. Now I have to figure out what needs to be sent for the next part of the protocol.
Check if this works for you
Try to open a named pipe. (CreateFile)
Set the read mode and the blocking mode of the specified named pipe. (SetNamedPipeHandleState)
Send a message to the pipe server and receive its response. (WriteFile, ReadFile)
Close the pipe. (CloseHandle)
What code add to this function to work good? (ERROR_SUCCESS)
I have code, that check value in registry.
In function RegQueryValueEx is bug.
When oldValue is few letters longer than newValue, function shows ERROR_MORE_DATA, but I want want ERROR_SUCCESS
What code add to this function to do this?
void function(string newValue, string key, string name)
{
// string key - key in registry, ie Myapp\\Options
// string name - name in registry
// string newValue - data in REG_SZ
string oldValue;
DWORD keytype = REG_SZ;
HKEY keyHandle;
DWORD size = sizeof(string);
if(RegOpenKeyEx(HKEY_LOCAL_MACHINE, key.c_str(),0L,KEY_ALL_ACCESS,&keyHandle) == ERROR_SUCCESS)
{
LONG isgood = RegQueryValueEx(keyHandle, name.c_str(), 0, &keytype, (LPBYTE)&oldValue, &size);
if(isgood == ERROR_MORE_DATA)
{
cout << "Error more data\n";
}
if(isgood == ERROR_SUCCESS)
{
cout << "Old data is " << oldValue.c_str() << endl;
cout << "New data is " << newValue.c_str() << endl;
if(strcmp(newValue.c_str(), oldValue.c_str()) != 0) // compare 2 strings, if
{
cout << "String 1 and string 2 are different";
}
else
{
cout << "String 1 and string 2 are the same";
}
}
if(isgood == ERROR_FILE_NOT_FOUND)
{
cout << "Name in registry not found!";
}
}
}
ERROR_MORE_DATA means that you need to pass in a larger string buffer. The typical pattern you'll need to use is to call once to get the size, then allocate a properly-sized buffer, then call again. Or, alternatively, you can guess at a size, pass in that-sized buffer, and increase size if you get ERROR_MORE_DATA back.
BTW, you are also computing size incorrectly. And you're not closing the registry key. And you're not prepared to support being compiled under unicode or non-unicode modes.
Here's some revised code which addresses these issues.
#include <string>
#include <vector>
#include <iostream>
#include <windows.h>
using namespace std;
namespace std
{
#ifdef _UNICODE
#define tcout wcout
#define tcin wcin
typedef wstring tstring;
#else
#define tcout cout
#define tcin cin
typedef string tstring;
#endif
};
void function(tstring newValue, tstring key, tstring name)
{
// string key - key in registry, ie Myapp\\Options
// string name - name in registry
// string newValue - data in REG_SZ
HKEY keyHandle;
if(RegOpenKeyEx(HKEY_LOCAL_MACHINE, key.c_str(),0L,KEY_ALL_ACCESS,&keyHandle) == ERROR_SUCCESS)
{
DWORD size = 500; // initial size
vector<TCHAR> buf(size);
tstring oldValue;
DWORD keytype = REG_SZ;
LONG isgood = RegQueryValueEx(keyHandle, name.c_str(), 0, &keytype, (LPBYTE) &buf[0], &size);
if(isgood == ERROR_SUCCESS)
{
oldValue.assign (&buf[0], size);
}
else if(isgood == ERROR_MORE_DATA)
{
buf.reserve (size); // expand to however large we need
isgood = RegQueryValueEx(keyHandle, name.c_str(), 0, &keytype, (LPBYTE)&buf[0], &size);
if(isgood == ERROR_SUCCESS)
oldValue.assign (&buf[0], size);
}
RegCloseKey (keyHandle); // remember to close this!
if(isgood == ERROR_SUCCESS)
{
tcout << _T("Old data is ") << oldValue << endl;
tcout << _T("New data is ") << newValue << endl;
if(newValue.compare(oldValue) != 0) // compare 2 strings, if
{
tcout << _T("String 1 and string 2 are different");
}
else
{
tcout << _T("String 1 and string 2 are the same");
}
}
if(isgood == ERROR_FILE_NOT_FOUND)
{
tcout << _T("Name in registry not found!");
}
}
}
int _tmain(int argc, _TCHAR* argv[])
{
tstring val;
function (val, _T("SOFTWARE\\Microsoft\\Windows\\CurrentVersion"), _T("CommonFilesDir"));
return 0;
}
ERROR_MORE_DATA means the buffer you supplied to hold the data is not big enough.
Your problems are multiple:
When you say sizeof(string) you're getting the size of the string data type, not the length of the string. You should call string::size() to get the length of the string.
You can't just cast a string to an LPBYTE. That is going to FAIL miserably. The Registry APIs are not designed to work with strings, they are designed to work with char* and WCHAR* types. You need to declare a local character array (e.g. char *foo = new char[256]) and then pass that. If you get ERROR_MORE_DATA, declare a bigger one.