Related
I connect to Arduino Uno R3 via a WinAPI handle.
std::string name = "COM5";
this->handle = CreateFile(name.c_str(), GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
How can I safely distinguish Arduino so I am not communicating with something different or at least get its name, preferably without third party libraries? After a research I found several APIs (QueryDosDevice and NtQueryObject), however I don't know how can I implement those in my code.
EDIT: I am now able to enumerate USB devices, however I have the exact opposite problem. I do not know how to CreateFile from these:
EXTERN_C const DEVPROPKEY DECLSPEC_SELECTANY DEVPKEY_Device_BusReportedDeviceDesc = { { 0x540b947e, 0x8b40, 0x45bc, { 0xa8, 0xa2, 0x6a, 0x0b, 0x89, 0x4c, 0xbd, 0xa2 } }, 4 };
typedef BOOL(WINAPI *FN_SetupDiGetDeviceProperty)(
__in HDEVINFO DeviceInfoSet,
__in PSP_DEVINFO_DATA DeviceInfoData,
__in const DEVPROPKEY *PropertyKey,
__out DEVPROPTYPE *PropertyType,
__out_opt PBYTE PropertyBuffer,
__in DWORD PropertyBufferSize,
__out_opt PDWORD RequiredSize,
__in DWORD Flags
);
std::vector<device> usbenumerator::ListDevices()
{
DWORD dwSize;
DEVPROPTYPE ulPropertyType;
CONFIGRET status;
HDEVINFO hDevInfo;
SP_DEVINFO_DATA DeviceInfoData;
char szDeviceInstanceID[MAX_DEVICE_ID_LEN];
WCHAR szBuffer[4096];
FN_SetupDiGetDeviceProperty GetDeviceProperty = (FN_SetupDiGetDeviceProperty)(GetProcAddress(GetModuleHandle("setupapi.dll"), "SetupDiGetDevicePropertyW"));
hDevInfo = SetupDiGetClassDevs(NULL, "USB", NULL, DIGCF_ALLCLASSES | DIGCF_PRESENT);
std::vector<device> output;
if (hDevInfo == INVALID_HANDLE_VALUE)
{
return output;
}
for (int i = 0; ; i++)
{
device dev;
DeviceInfoData.cbSize = sizeof(DeviceInfoData);
if (!SetupDiEnumDeviceInfo(hDevInfo, i, &DeviceInfoData))
{
break;
}
status = CM_Get_Device_ID(DeviceInfoData.DevInst, szDeviceInstanceID, MAX_PATH, 0);
if (status != CR_SUCCESS)
{
continue;
}
std::string deviceID = szDeviceInstanceID;
dev.id = deviceID;
if (GetDeviceProperty && GetDeviceProperty(hDevInfo, &DeviceInfoData, &DEVPKEY_Device_BusReportedDeviceDesc, &ulPropertyType, (BYTE*) szBuffer, sizeof(szBuffer), &dwSize, 0))
{
if (GetDeviceProperty(hDevInfo, &DeviceInfoData, &DEVPKEY_Device_BusReportedDeviceDesc, &ulPropertyType, (BYTE*)szBuffer, sizeof(szBuffer), &dwSize, 0))
{
_bstr_t b(szBuffer);
const char* cBusReportedDesc = b;
std::string busReportedDesc = cBusReportedDesc;
dev.busReportedDesc = busReportedDesc;
}
}
output.push_back(dev);
}
return output;
}
I think I got it: I am basically making my own copy of the SymbolicName key in the Windows registry (HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Enum\USB\**YOUR_DEVICE_ID**\Device Parameters, for example HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Enum\USB\VID_2341&PID_0001\75633313233351E0A1E1\Device Parameters)
// Error checking omitted
CM_Get_Device_ID(DeviceInfoData.DevInst, szDeviceInstanceID, MAX_PATH, 0);
std::string deviceID = szDeviceInstanceID;
std::string did = deviceID;
// Convert all backslashes to hash signs
std::replace(did.begin(), did.end(), '\\', '#');
// The GUID seems to stay the same: https://learn.microsoft.com/en-us/windows-hardware/drivers/install/guid-devinterface-usb-device
std::string rid = "\\??\\" + did + "#{a5dcbf10-6530-11d2-901f-00c04fb951ed}";
handle = CreateFile(rid, GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
the code is here
HANDLE hDrive = CreateFile(_T("\\\\.\\D:"), GENERIC_READ,
FILE_SHARE_READ|FILE_SHARE_WRITE|FILE_SHARE_DELETE,
NULL,
OPEN_ALWAYS,
FILE_ATTRIBUTE_NORMAL,///*FILE_FLAG_WRITE_THROUGH |*/FILE_FLAG_NO_BUFFERING,
NULL);
assert(hDrive != INVALID_HANDLE_VALUE);
I also tried CreateFile(_T("\\\.\D:",...) and OPEN_EXISTING, but also return INVALID_HANDLE_VALUE.
I called GetLastError(), and the error code is 5, ERROR_ACCESS_DENIED.
thanks to #Barmak Shemirani and #Hans Passant,
I got a workable code
HANDLE hDrive = CreateFile(_T("\\\\.\\PhysicalDrive0"), GENERIC_READ,
FILE_SHARE_READ|FILE_SHARE_WRITE|FILE_SHARE_DELETE,
NULL,
OPEN_EXISTING,
FILE_ATTRIBUTE_READONLY,///*FILE_FLAG_WRITE_THROUGH |*/FILE_FLAG_NO_BUFFERING,
NULL);
and
HANDLE hDrive = CreateFile(_T("\\\\.\\D:"), GENERIC_READ|GENERIC_WRITE,
FILE_SHARE_READ|FILE_SHARE_WRITE|FILE_SHARE_DELETE,
NULL,
OPEN_EXISTING,
FILE_ATTRIBUTE_READONLY,///*FILE_FLAG_WRITE_THROUGH |*/FILE_FLAG_NO_BUFFERING,
NULL);
DWORD dwError = GetLastError();
assert(hDrive != INVALID_HANDLE_VALUE);
cin.get();
need to run as administrator
I am trying to create a simple comunication between 2 processes in C++ ( Windows ) like FIFO in linux.
This is my server:
int main()
{
HANDLE pipe = CreateFile(TEXT("\\\\.\\pipe\\Pipe"), GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL);
ConnectNamedPipe(pipe, NULL);
while(TRUE){
string data;
DWORD numRead =1 ;
ReadFile(pipe, &data, 1024, &numRead, NULL);
cout << data << endl;
}
CloseHandle(pipe);
return 0;
}
And this is my client:
int main()
{
HANDLE pipe = CreateFile(TEXT("\\\\.\\pipe\\Pipe"), GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, NULL);
ConnectNamedPipe(pipe, NULL);
string message = "TEST";
DWORD numWritten;
WriteFile(pipe, message.c_str(), message.length(), &numWritten, NULL);
return 0;
}
The code does't work , how can i fixed it to like FIFO ?
You cannot create a named pipe by calling CreateFile(..).
Have a look at the pipe examples of the MSDN. Since these examples are quite complex I've quickly written a VERY simple named pipe server and client.
int main(void)
{
HANDLE hPipe;
char buffer[1024];
DWORD dwRead;
hPipe = CreateNamedPipe(TEXT("\\\\.\\pipe\\Pipe"),
PIPE_ACCESS_DUPLEX,
PIPE_TYPE_BYTE | PIPE_READMODE_BYTE | PIPE_WAIT, // FILE_FLAG_FIRST_PIPE_INSTANCE is not needed but forces CreateNamedPipe(..) to fail if the pipe already exists...
1,
1024 * 16,
1024 * 16,
NMPWAIT_USE_DEFAULT_WAIT,
NULL);
while (hPipe != INVALID_HANDLE_VALUE)
{
if (ConnectNamedPipe(hPipe, NULL) != FALSE) // wait for someone to connect to the pipe
{
while (ReadFile(hPipe, buffer, sizeof(buffer) - 1, &dwRead, NULL) != FALSE)
{
/* add terminating zero */
buffer[dwRead] = '\0';
/* do something with data in buffer */
printf("%s", buffer);
}
}
DisconnectNamedPipe(hPipe);
}
return 0;
}
And here is the client code:
int main(void)
{
HANDLE hPipe;
DWORD dwWritten;
hPipe = CreateFile(TEXT("\\\\.\\pipe\\Pipe"),
GENERIC_READ | GENERIC_WRITE,
0,
NULL,
OPEN_EXISTING,
0,
NULL);
if (hPipe != INVALID_HANDLE_VALUE)
{
WriteFile(hPipe,
"Hello Pipe\n",
12, // = length of string + terminating '\0' !!!
&dwWritten,
NULL);
CloseHandle(hPipe);
}
return (0);
}
You should replace the name of the pipe TEXT("\\\\.\\pipe\\Pipe") by a #define which is located in a commonly used header file.
(Sorry for my crazy English)
I want to get all the text in Rich Edit with RTF format, not plain text to a variable. I tried SendMessage() with EM_STREAMOUT to write directly Rich Edit to file, but I can't save the content to specific variables, for example LPWSTR. Please remember, only Win API, not MFC. Thanks for you help!
You can pass your variable to the EM_STREAMOUT callback so it can be updated as needed, eg:
DWORD CALLBACK EditStreamOutCallback(DWORD_PTR dwCookie, LPBYTE pbBuff, LONG cb, LONG *pcb)
{
std::stringstream *rtf = (std::stringstream*) dwCookie;
rtf->write((char*)pbBuff, cb);
*pcb = cb;
return 0;
}
.
std::stringstream rtf;
EDITSTREAM es = {0};
es.dwCookie = (DWORD_PTR) &rtf;
es.pfnCallback = &EditStreamOutCallback;
SendMessage(hRichEditWnd, EM_STREAMOUT, SF_RTF, (LPARAM)&es);
// use rtf.str() as needed...
Update: to load RTF data into the RichEdit control, use EM_STREAMIN, eg:
DWORD CALLBACK EditStreamInCallback(DWORD_PTR dwCookie, LPBYTE pbBuff, LONG cb, LONG *pcb)
{
std::stringstream *rtf = (std::stringstream*) dwCookie;
*pcb = rtf->readsome((char*)pbBuff, cb);
return 0;
}
.
std::stringstream rtf("...");
EDITSTREAM es = {0};
es.dwCookie = (DWORD_PTR) &rtf;
es.pfnCallback = &EditStreamInCallback;
SendMessage(hRichEditWnd, EM_STREAMIN, SF_RTF, (LPARAM)&es);
Using the EM_STREAMOUT message is the answer.
Here is the simplest example that I can construct to demonstrate. This will save the contents of a rich edit control to a file.
DWORD CALLBACK EditStreamOutCallback(DWORD_PTR dwCookie, LPBYTE pbBuff, LONG cb, LONG *pcb)
{
HANDLE hFile = (HANDLE)dwCookie;
DWORD NumberOfBytesWritten;
if (!WriteFile(hFile, pbBuff, cb, &NumberOfBytesWritten, NULL))
{
//handle errors
return 1;
// or perhaps return GetLastError();
}
*pcb = NumberOfBytesWritten;
return 0;
}
void SaveRichTextToFile(HWND hWnd, LPCWSTR filename)
{
HANDLE hFile = CreateFile(filename, GENERIC_WRITE, 0, NULL,
CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
if (hFile == INVALID_HANDLE_VALUE)
{
//handle errors
}
EDITSTREAM es = { 0 };
es.dwCookie = (DWORD_PTR) hFile;
es.pfnCallback = EditStreamOutCallback;
SendMessage(hWnd, EM_STREAMOUT, SF_RTF, (LPARAM)&es);
CloseHandle(hFile);
if (es.dwError != 0)
{
//handle errors
}
}
I have an application that needs to monitor the primary drive for file changes via ReadDirectoryChangesW. However, when UAC is enabled, it doesn't work.
All of the Windows API calls succeed, but I'm not notified of any changes.
I can work around this by individually monitoring each directory in the root, but this is a problem, because it can potentially cause a blue screen if there are too many directories.
Is there an acceptable way to get around UAC and receive file change notifications on the entire primary drive?
The relevant CreateFile and ReadDirectoryChangesW is below. In the case where it doesn't work, directory is C:\. If I monitor any secondary drive (i.e. E:\, F:\, G:\) it works as expected. None of the calls return errors.
HANDLE fileHandle = CreateFileW(directory.c_str(), FILE_LIST_DIRECTORY,
FILE_SHARE_READ | FILE_SHARE_DELETE, NULL, OPEN_EXISTING,
FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OVERLAPPED, NULL);
BOOL success = ReadDirectoryChangesW(fileHandle, watched.buffer.data(),
watched.buffer.size(), TRUE,
FILE_NOTIFY_CHANGE_FILE_NAME | FILE_NOTIFY_CHANGE_LAST_WRITE,
NULL, &watched.overlapped, NULL);
Interestingly, the .NET System.IO.FileSystemWatcher does work correctly, and it uses the exact same functions and parameters as I'm using, but it behaves correctly.
First it is best for applications that use the ReadDirectoryChangesW API to run elevated make a manifest file for you app and set requireAdministrator as the requestedExecutionLevel level. Check here for reference.
Try removing FILE_SHARE_WRITE from the CreateFile call if you are using it.
Another option is to make your program run as a service, im not sure how applicable this is to your needs. You could post some code as to how you are getting the file handle and what are you passing to ReadDirectoryChangesW
Here's some working test code, for future reference.
#include <Windows.h>
#include <stdio.h>
int main(int argc, char ** argv)
{
HANDLE filehandle;
BYTE buffer[65536];
DWORD dw;
FILE_NOTIFY_INFORMATION * fni;
OVERLAPPED overlapped = {0};
overlapped.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
if (overlapped.hEvent == NULL)
{
printf("CreateEvent: %u\n", GetLastError());
return 1;
}
filehandle = CreateFile(L"C:\\",
FILE_LIST_DIRECTORY,
FILE_SHARE_READ | FILE_SHARE_DELETE,
NULL,
OPEN_EXISTING,
FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OVERLAPPED,
NULL);
if (filehandle == INVALID_HANDLE_VALUE)
{
printf("CreateFile: %u\n", GetLastError());
return 1;
}
for (;;)
{
if (!ReadDirectoryChangesW(filehandle, buffer, sizeof(buffer),
TRUE,
FILE_NOTIFY_CHANGE_FILE_NAME | FILE_NOTIFY_CHANGE_LAST_WRITE,
NULL, &overlapped, NULL))
{
printf("ReadDirectoryChangesW: %u\n", GetLastError());
return 1;
}
printf("Queued OK.\n");
if (!GetOverlappedResult(filehandle, &overlapped, &dw, TRUE))
{
printf("GetOverlappedResult: %u\n", GetLastError());
return 1;
}
printf("%u bytes read.\n", dw);
fni = (FILE_NOTIFY_INFORMATION *)buffer;
for (;;)
{
printf("Next entry offset = %u\n", fni->NextEntryOffset);
printf("Action = %u\n", fni->Action);
printf("File name = %.*ws\n",
fni->FileNameLength / 2,
fni->FileName);
if (fni->NextEntryOffset == 0) break;
fni = (FILE_NOTIFY_INFORMATION *)
(((BYTE *)fni) + fni->NextEntryOffset);
}
}
printf("All done\n");
return 0;
}
You can adjust the privileges of your process yourself like this:
// enable the required privileges for this process
LPCTSTR arPrivelegeNames[] = { SE_BACKUP_NAME,
SE_RESTORE_NAME,
SE_CHANGE_NOTIFY_NAME
};
for (int i=0; i<(sizeof(arPrivelegeNames)/sizeof(LPCTSTR)); ++i)
{
CAutoGeneralHandle hToken;
if (OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES, hToken.GetPointer()))
{
TOKEN_PRIVILEGES tp = { 1 };
if (LookupPrivilegeValue(NULL, arPrivelegeNames[i], &tp.Privileges[0].Luid))
{
tp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
AdjustTokenPrivileges(hToken, FALSE, &tp, sizeof(tp), NULL, NULL);
}
}
}
This works also for non-privileged processes (a.k.a. normal user processes).