Related
I'm trying to add audio capability to a capture source filter in order to make a virtual cam with audio. Beginning with the TMH's and rdp's code I extended it with another pin, called "Audio":
CUnknown * WINAPI CVCam::CreateInstance(LPUNKNOWN lpunk, HRESULT *phr)
{
ASSERT(phr);
CUnknown *punk = new CVCam(lpunk, phr);
return punk;
}
CVCam::CVCam(LPUNKNOWN lpunk, HRESULT *phr) : CSource(LPCSTR(FILTER_NAME), lpunk, CLSID_VirtualCam)
{
ASSERT(phr);
CAutoLock cAutoLock(&m_cStateLock);
m_paStreams = (CSourceStream **) new CVCamStream*[2];
m_paStreams[0] = new CVCamStream(phr, this, L"Video");
m_paStreams[1] = new CVAudioStream(phr, this, L"Audio");
}
HRESULT CVCam::QueryInterface(REFIID riid, void **ppv)
{
if (riid == _uuidof(IAMStreamConfig) || riid == _uuidof(IKsPropertySet))
{
HRESULT hr;
hr = m_paStreams[0]->QueryInterface(riid, ppv);
if (hr != S_OK) return hr;
hr = m_paStreams[1]->QueryInterface(riid, ppv);
if (hr != S_OK) return hr;
}
else return CSource::QueryInterface(riid, ppv);
return S_OK;
}
CVAudioStream::CVAudioStream(HRESULT *phr, CVCam *pParent, LPCWSTR pPinName) : CSourceStream(LPCSTR(pPinName), phr, pParent, pPinName), m_pParent(pParent)
{
GetMediaType(0, &m_mt);
}
CVAudioStream::~CVAudioStream()
{
}
HRESULT CVAudioStream::QueryInterface(REFIID riid, void **ppv)
{
if (riid == _uuidof(IAMStreamConfig)) *ppv = (IAMStreamConfig*)this;
else if (riid == _uuidof(IKsPropertySet)) *ppv = (IKsPropertySet*)this;
else if (riid == _uuidof(IAMBufferNegotiation)) *ppv = (IAMBufferNegotiation*)this;
else return CSourceStream::QueryInterface(riid, ppv);
AddRef();
return S_OK;
}
HRESULT CVAudioStream::FillBuffer(IMediaSample *pms)
{
// fill buffer with Windows audio samples
return NOERROR;
}
STDMETHODIMP CVAudioStream::Notify(IBaseFilter * pSender, Quality q)
{
return E_NOTIMPL;
}
HRESULT CVAudioStream::SetMediaType(const CMediaType *pmt)
{
HRESULT hr = CSourceStream::SetMediaType(pmt);
return hr;
}
HRESULT setupPwfex(WAVEFORMATEX *pwfex, AM_MEDIA_TYPE *pmt) {
pwfex->wFormatTag = WAVE_FORMAT_PCM;
pwfex->cbSize = 0;
pwfex->nChannels = 2;
HRESULT hr;
pwfex->nSamplesPerSec = 11025;
pwfex->wBitsPerSample = 16;
pwfex->nBlockAlign = (WORD)((pwfex->wBitsPerSample * pwfex->nChannels) / 8);
pwfex->nAvgBytesPerSec = pwfex->nSamplesPerSec * pwfex->nBlockAlign;
hr = ::CreateAudioMediaType(pwfex, pmt, FALSE);
return hr;
}
/*HRESULT CVAudioStream::setAsNormal(CMediaType *pmt)
{
WAVEFORMATEX *pwfex;
pwfex = (WAVEFORMATEX *)pmt->AllocFormatBuffer(sizeof(WAVEFORMATEX));
ZeroMemory(pwfex, sizeof(WAVEFORMATEX));
if (NULL == pwfex) return E_OUTOFMEMORY;
return setupPwfex(pwfex, pmt);
}*/
HRESULT CVAudioStream::GetMediaType(int iPosition, CMediaType *pmt)
{
if (iPosition < 0) return E_INVALIDARG;
if (iPosition > 0) return VFW_S_NO_MORE_ITEMS;
if (iPosition == 0)
{
*pmt = m_mt;
return S_OK;
}
WAVEFORMATEX *pwfex = (WAVEFORMATEX *)pmt->AllocFormatBuffer(sizeof(WAVEFORMATEX));
setupPwfex(pwfex, pmt);
return S_OK;
}
HRESULT CVAudioStream::CheckMediaType(const CMediaType *pMediaType)
{
int cbFormat = pMediaType->cbFormat;
if (*pMediaType != m_mt) return E_INVALIDARG;
return S_OK;
}
const int WaveBufferChunkSize = 16 * 1024;
HRESULT CVAudioStream::DecideBufferSize(IMemAllocator *pAlloc, ALLOCATOR_PROPERTIES *pProperties)
{
CheckPointer(pAlloc, E_POINTER);
CheckPointer(pProperties, E_POINTER);
WAVEFORMATEX *pwfexCurrent = (WAVEFORMATEX*)m_mt.Format();
pProperties->cBuffers = 1;
pProperties->cbBuffer = expectedMaxBufferSize;
ALLOCATOR_PROPERTIES Actual;
HRESULT hr = pAlloc->SetProperties(pProperties, &Actual);
if (FAILED(hr)) return hr;
if (Actual.cbBuffer < pProperties->cbBuffer) return E_FAIL;
return NOERROR;
}
HRESULT CVAudioStream::OnThreadCreate()
{
//GetMediaType(0, &m_mt);
//HRESULT hr = LoopbackCaptureSetup();
//if (FAILED(hr)) return hr;
return NOERROR;
}
HRESULT STDMETHODCALLTYPE CVAudioStream::SetFormat(AM_MEDIA_TYPE *pmt)
{
if (!pmt) return S_OK;
if (CheckMediaType((CMediaType *)pmt) != S_OK) return E_FAIL;
m_mt = *pmt;
IPin* pin;
ConnectedTo(&pin);
if (pin)
{
IFilterGraph *pGraph = m_pParent->GetGraph();
pGraph->Reconnect(this);
}
return S_OK;
}
HRESULT STDMETHODCALLTYPE CVAudioStream::GetFormat(AM_MEDIA_TYPE **ppmt)
{
*ppmt = CreateMediaType(&m_mt);
return S_OK;
}
HRESULT STDMETHODCALLTYPE CVAudioStream::GetNumberOfCapabilities(int *piCount, int *piSize)
{
*piCount = 1;
*piSize = sizeof(AUDIO_STREAM_CONFIG_CAPS);
return S_OK;
}
HRESULT STDMETHODCALLTYPE CVAudioStream::GetStreamCaps(int iIndex, AM_MEDIA_TYPE **pmt, BYTE *pSCC)
{
if (iIndex < 0) return E_INVALIDARG;
if (iIndex > 0) return S_FALSE;
if (pSCC == NULL) return E_POINTER;
*pmt = CreateMediaType(&m_mt);
if (*pmt == NULL) return E_OUTOFMEMORY;
DECLARE_PTR(WAVEFORMATEX, pAudioFormat, (*pmt)->pbFormat);
AM_MEDIA_TYPE * pm = *pmt;
setupPwfex(pAudioFormat, pm);
AUDIO_STREAM_CONFIG_CAPS* pASCC = (AUDIO_STREAM_CONFIG_CAPS*)pSCC;
ZeroMemory(pSCC, sizeof(AUDIO_STREAM_CONFIG_CAPS));
pASCC->guid = MEDIATYPE_Audio;
pASCC->MaximumChannels = pAudioFormat->nChannels;
pASCC->MinimumChannels = pAudioFormat->nChannels;
pASCC->ChannelsGranularity = 1; // doesn't matter
pASCC->MaximumSampleFrequency = pAudioFormat->nSamplesPerSec;
pASCC->MinimumSampleFrequency = pAudioFormat->nSamplesPerSec;
pASCC->SampleFrequencyGranularity = 11025; // doesn't matter
pASCC->MaximumBitsPerSample = pAudioFormat->wBitsPerSample;
pASCC->MinimumBitsPerSample = pAudioFormat->wBitsPerSample;
pASCC->BitsPerSampleGranularity = 16; // doesn't matter
return S_OK;
}
HRESULT CVAudioStream::Set(REFGUID guidPropSet, DWORD dwID, void *pInstanceData, DWORD cbInstanceData, void *pPropData, DWORD cbPropData)
{
return E_NOTIMPL;
}
HRESULT CVAudioStream::Get(
REFGUID guidPropSet,
DWORD dwPropID,
void *pInstanceData,
DWORD cbInstanceData,
void *pPropData,
DWORD cbPropData,
DWORD *pcbReturned
)
{
if (guidPropSet != AMPROPSETID_Pin) return E_PROP_SET_UNSUPPORTED;
if (dwPropID != AMPROPERTY_PIN_CATEGORY) return E_PROP_ID_UNSUPPORTED;
if (pPropData == NULL && pcbReturned == NULL) return E_POINTER;
if (pcbReturned) *pcbReturned = sizeof(GUID);
if (pPropData == NULL) return S_OK;
if (cbPropData < sizeof(GUID)) return E_UNEXPECTED;
*(GUID *)pPropData = PIN_CATEGORY_CAPTURE;
return S_OK;
}
HRESULT CVAudioStream::QuerySupported(REFGUID guidPropSet, DWORD dwPropID, DWORD *pTypeSupport)
{
if (guidPropSet != AMPROPSETID_Pin) return E_PROP_SET_UNSUPPORTED;
if (dwPropID != AMPROPERTY_PIN_CATEGORY) return E_PROP_ID_UNSUPPORTED;
if (pTypeSupport) *pTypeSupport = KSPROPERTY_SUPPORT_GET;
return S_OK;
}
My first issue is when I insert the filter in GraphStudioNext and open its properties page. The Audio pin shows the following (incorrect) information:
majorType = GUID_NULL
subType = GUID_NULL
formattype = GUID_NULL
Of course I cannot connect nothing to that pin because is not valid.
I was expecting something like MEDIATYPE_Audio because I set up it:
DEFINE_GUID(CLSID_VirtualCam, 0x8e14549a, 0xdb61, 0x4309, 0xaf, 0xa1, 0x35, 0x78, 0xe9, 0x27, 0xe9, 0x33);
const AMOVIESETUP_MEDIATYPE AMSMediaTypesVideo =
{
&MEDIATYPE_Video,
&MEDIASUBTYPE_NULL
};
const AMOVIESETUP_MEDIATYPE AMSMediaTypesAudio =
{
&MEDIATYPE_Audio,
&MEDIASUBTYPE_NULL
};
const AMOVIESETUP_PIN AMSPinVCam[] =
{
{
L"Video", // Pin string name
FALSE, // Is it rendered
TRUE, // Is it an output
FALSE, // Can we have none
FALSE, // Can we have many
&CLSID_NULL, // Connects to filter
NULL, // Connects to pin
1, // Number of types
&AMSMediaTypesVideo // Pin Media types
},
{
L"Audio", // Pin string name
FALSE, // Is it rendered
TRUE, // Is it an output
FALSE, // Can we have none
FALSE, // Can we have many
&CLSID_NULL, // Connects to filter
NULL, // Connects to pin
1, // Number of types
&AMSMediaTypesAudio // Pin Media types
}
};
const AMOVIESETUP_FILTER AMSFilterVCam =
{
&CLSID_VirtualCam, // Filter CLSID
FILTER_NAME, // String name
MERIT_DO_NOT_USE, // Filter merit
2, // Number pins
AMSPinVCam // Pin details
};
CFactoryTemplate g_Templates[] =
{
{
FILTER_NAME,
&CLSID_VirtualCam,
CVCam::CreateInstance,
NULL,
&AMSFilterVCam
},
};
int g_cTemplates = sizeof(g_Templates) / sizeof(g_Templates[0]);
STDAPI RegisterFilters( BOOL bRegister )
{
HRESULT hr = NOERROR;
WCHAR achFileName[MAX_PATH];
char achTemp[MAX_PATH];
ASSERT(g_hInst != 0);
if( 0 == GetModuleFileNameA(g_hInst, achTemp, sizeof(achTemp))) return AmHresultFromWin32(GetLastError());
MultiByteToWideChar(CP_ACP, 0L, achTemp, lstrlenA(achTemp) + 1, achFileName, NUMELMS(achFileName));
hr = CoInitialize(0);
if(bRegister)
{
hr = AMovieSetupRegisterServer(CLSID_VirtualCam, FILTER_NAME, achFileName, L"Both", L"InprocServer32");
}
if( SUCCEEDED(hr) )
{
IFilterMapper2 *fm = 0;
hr = CreateComObject( CLSID_FilterMapper2, IID_IFilterMapper2, fm );
if( SUCCEEDED(hr) )
{
if(bRegister)
{
IMoniker *pMoniker = 0;
REGFILTER2 rf2;
rf2.dwVersion = 1;
rf2.dwMerit = MERIT_DO_NOT_USE;
rf2.cPins = 2;
rf2.rgPins = AMSPinVCam;
hr = fm->RegisterFilter(CLSID_VirtualCam, FILTER_NAME, &pMoniker, &CLSID_VideoInputDeviceCategory, NULL, &rf2);
}
else
{
hr = fm->UnregisterFilter(&CLSID_VideoInputDeviceCategory, 0, CLSID_VirtualCam);
}
}
if(fm) fm->Release();
}
if( SUCCEEDED(hr) && !bRegister ) hr = AMovieSetupUnregisterServer( CLSID_VirtualCam );
CoFreeUnusedLibraries();
CoUninitialize();
return hr;
}
Second issue: there's also a "Latency" tab but when I click on it GraphStudioNext hangs forever and the VS debugger (which is attached to that process) says nothing. What piece of code control this tab?
UPDATE
Solved first issue:
HRESULT CVAudioStream::GetMediaType(int iPosition, CMediaType *pmt)
{
if (iPosition < 0) return E_INVALIDARG;
if (iPosition > 0) return VFW_S_NO_MORE_ITEMS;
WAVEFORMATEX *pwfex = (WAVEFORMATEX *)pmt->AllocFormatBuffer(sizeof(WAVEFORMATEX));
setupPwfex(pwfex, pmt);
pmt->SetType(&MEDIATYPE_Audio);
pmt->SetFormatType(&FORMAT_WaveFormatEx);
pmt->SetTemporalCompression(FALSE);
pmt->SetSubtype(&MEDIASUBTYPE_PCM);
pmt->SetSampleSize(pwfex->nBlockAlign);
return S_OK;
}
Short version: Microsoft does not really offer an API to supply virtual audio device so that it's nicely accepted by the applications as if it is a real audio capture device.
If virtual video capture filters often work for historical reasons, it is not the case with audio. A kernel level driver that implements an audio device is the way to add an audio device that applications would recognize.
Latency tab shows up because you pretended that you are implementing IAMBufferNegotiation interface:
if (riid == _uuidof(IAMBufferNegotiation)) *ppv = (IAMBufferNegotiation*)this;
The implementation is likely to be incorrect, which results in certain unexpected behavior (freeze, crash etc).
Adding audio pin on the same filter is possible but might be not the best idea, if you expect the stream to be picked as an artificial source. It makes sense in general but real devices almost never expose audio streams like this.
Long story short, the only application which could utilize audio stream like this is the one you develop yourself: no well known application attempts to locate audio pin on the video source filter. For this reason implementation of
IAMStreamConfig and especially IKsPropertySet on such pin is useless.
You will not be able to register the filter under Audio Capture Sources category because you register a filter, and this filter exposes video output pin first, and only then there is some secondary audio. If you target an application that consumes audio via DirectShow (which is already pretty rare for the reasons beyond the scope of this question), you should rather develop a separate source filter. You of course can have the two filters talk to each other behind the scenes to deliver certain feed collaboratively, but in terms of DirectShow it is typical that the filters appear as independent.
...also real webcams expose two different filters and this is why in application like Skype we have to select both under video and audio devices.
Should it be better to create two completely different projects and filters: one for video and one for audio?
Real and typical camera:
Since "real" physical cameras are typically provided with kernel level drivers, their presence in DirectShow takes place through WDM Video Capture Filter which acts as a proxy and enumerates "DirectShow wrappers" of camera drivers under the same category Video Capture Sources where you would register virtual cameras.
That is, such design enables you to mix real and virtual cameras in the list of available devices, which DirectShow based application use when it comes to video capture. This approach has its limitations, which I described earlier e.g. in this question and referenced post Applicability of Virtual DirectShow Sources.
As DirectShow's successor Media Foundation have not had good reception in general, and in addition Media Foundation offers neither good backward compatibility nor video capture extensibility, a multitude of applications including Microsoft's own are still consuming video capture via DirectShow. Vice versa those who look into video capture API for Windows are also often interested in DirectShow and not the "current" API because of availability of samples and related information, API extensibility, application integration options.
It is not the case with audio, however. DirectShow audio capture was not top-notch already at the time DirectShow development stopped. Windows Vista introduces new API for audio WASAPI and DirectShow did not receive a respective connection to the new API, neither for audio capture nor for playback. Audio is simpler itself, and WASAPI was powerful and developer friendly, so developers started switching to the new API for audio related tasks. Much fewer applications use DirectShow for audio capture and your implementing virtual audio source is likely to be a miss: your device will remain "invisible" for applications consuming audio capture via WASAPI. Even if an application has a fallback code patch for Windows XP to do audio capture via DirectShow, it will hardly be a relief for you in newer OSes.
Follow up reading on audio on StackOverflow:
Directshow.net don't detect all mics in Windows 7
Write an audio source filter for use as Lync microphone
Windows Audio and Video Capture Software Paradigm
Also, you don't have to have separate projects for video and audio filters. You can mix them in the same project, they can just be independent filters registered separately.
I want to obtain a monitor handle (HMONITOR) that can be used with the Windows multi-monitor APIs for a specific monitor attached to the system by index. For example, say I have three monitors attached to my system and forming part of my desktop; I want to get a handle to monitor 3.
I already know how to get the device name for a specific monitor by index by calling the EnumDisplayDevices function. For example:
HMONITOR MonitorFromIndex(int index /* (zero-indexed) */)
{
DISPLAY_DEVICE dd;
dd.cb = sizeof(dd);
if (EnumDisplayDevices(NULL, index, &dd, 0) != FALSE)
{
// We found a match; make sure that it's part of the desktop.
if ((dd.StateFlags & DISPLAY_DEVICE_ATTACHED_TO_DESKTOP) == DISPLAY_DEVICE_ATTACHED_TO_DESKTOP)
{
// Yup. Now we've got the name of the device:
std::cout << dd.DeviceName << std::endl;
// But how do I obtain an HMONITOR for this device?
// ...
}
}
return NULL; // indicate failure
}
In the code above, we've found the name of the desired device (dd.DeviceName). I can use this name to create a DC for that monitor by calling CreateDC:
HDC hDC = CreateDC(dd.DeviceName, dd.DeviceName, NULL, NULL);
And I can obtain information about that monitor by calling EnumDisplaySettings:
DEVMODE dm;
dm.dmSize = sizeof(dm);
dm.dmDriverExtra = 0;
if (EnumDisplaySettings(dd.DeviceName, ENUM_CURRENT_SETTINGS, &dm) != FALSE)
{
std::cout << "The monitor supports " << dm.dmBitsPerPel << " bits per pixel." << std::endl;
}
Which is all great, but I want a handle to that monitor. How can I get it?
I tried to call EnumDisplayMonitors, passing a handle to the device context that I created using CreateDC, hoping to get a handle to the monitor passed to the callback function, but no such luck. The callback function was never called, and EnumDisplayMonitors returned FALSE (without setting an error code):
struct FoundMatch
{
BOOL found;
HMONITOR hMonitor;
};
BOOL CALLBACK MonitorEnumProc(HMONITOR hMonitor, HDC, LPRECT, LPARAM dwData)
{
FoundMatch* pfm = reinterpret_cast<FoundMatch*>(dwData);
pfm->found = TRUE;
pfm->hMonitor = hMonitor;
return FALSE; // stop enumerating
}
// elsewhere, after getting the device name and using it to create a DC
FoundMatch fm;
fm.found = FALSE;
fm.hMonitor = NULL;
BOOL result = EnumDisplayMonitors(hDC, NULL, MonitorEnumProc, reinterpret_cast<LPARAM>(&fm));
Sorry for such a late reply but maybe someone can find this useful.
The multi-monitor API is really minimalist to say the least. Once you've got your dd.DeviceName, it appears you have to go through EnumDisplayMonitors() enumeration until you find a match of dd.DeviceName against MONITORINFOEX.szDevice.
The MONITORINFOEX structure can be obtained by calling GetMonitorInfo().
Here is a non-compilable C++11 pseudo code:
struct DataBag
{
HMONITOR hmon;
TCHAR* devname;
} bag;
bag.hmon = NULL;
bag.devname = &dd.DeviceName;
BOOL bRes = EnumDisplayMonitors(
NULL, NULL,
[](HMONITOR hMonitor, HDC hDC, LPRECT rc, LPARAM data) -> BOOL {
auto& bag = *reinterpret_cast<DataBag*>(data);
MONITORINFOEX mi;
mi.cbSize = sizeof(mi);
if (/* match bag.devname against mi.szDevice */ && GetMonitorInfo(hMonitor, &mi))
{
bag.hmon = hMonitor;
return FALSE;
}
return TRUE;
},
reinterpret_cast<LPARAM>(&bag));
if (bRes && bag.hmon)
{
// Monitor found!
}
I have a application running on windows, which will send data over serial port.
Here is the code:
m_hCommPort= ::CreateFile(L"\\\\.\\COM3",
GENERIC_READ|GENERIC_WRITE, 0, 0, OPEN_EXISTING,0,0);
if(m_hCommPort == INVALID_HANDLE_VALUE)
{
printf("COM error: %d\n", GetLastError());
}
config.DCBlength = sizeof(config);
if((GetCommState(m_hCommPort, &config) == 0))
{
CloseHandle(m_hCommPort);
printf("Get configuration port has a problem.\n");
return FALSE;
}
config.BaudRate = 9600;
config.StopBits = ONESTOPBIT;
config.Parity = PARITY_NONE;
config.ByteSize = DATABITS_8;
config.fDtrControl = 0;
config.fRtsControl = 0;
if (!SetCommState(m_hCommPort, &config))
{
CloseHandle(m_hCommPort);
printf( "Failed to Set Comm State Reason: %d\n",GetLastError());
return E_FAIL;
}
Here is the code for Send only (Working) (continuously sending )
while(1)
{
Sleep(5000);
int isWritten = WriteFile(m_hCommPort, txData, 9/*(DWORD)sizeof(txData)*/, &dwBytesWritten, NULL);
printf("isWritten: %d, dwBytesWritten: %d \n", isWritten, dwBytesWritten);
}
After this I have added code for Receive data too, then send is NOT WORKING. I mean not able to send data over UART. WriteFile() seems not executed, its stuck.
Here I have added a thread to receive data, is thread causing the problem ? or do I need to do something else ?
void ReceiverThread(void *param)
{
DWORD dwRead=0;
BOOL fWaitingOnRead = FALSE;
OVERLAPPED osReader = {0};
osReader.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
if (osReader.hEvent == NULL)
printf("Error creating overlapped event; abort.\n");
while(1)
{
if (!ReadFile(m_hCommPort, &Byte, 1, &dwRead, &osReader)) {
if (GetLastError() != ERROR_IO_PENDING) // read not delayed?
printf("Error in communications; report it.\n");
else
fWaitingOnRead = TRUE;
}
else {
rxData[rxHeadIndex++]= Byte;
rxHeadIndex = (rxHeadIndex) & QUEUE_MASK;
}
}
}
SetCommMask (m_hCommPort, EV_RXCHAR/ | EV_ERR); //receive character event
_beginthread(ReceiverThread,0,NULL);
while(1)
{
Sleep(5000);
int isWritten = WriteFile(m_hCommPort, txData, 9/*(DWORD)sizeof(txData)*/, &dwBytesWritten, NULL);
printf("isWritten: %d, dwBytesWritten: %d \n", isWritten, dwBytesWritten);
}
Thanks in advance.
Ashok
I've encountered a similar problem a while ago.
I found out that WriteFile(..) blocks if a ReadFile(..) is currently in progress for a serial port. So that's a problem if ReadFile(..) blocks if there is no data to be read.
I've solved the problem by checking if there is data available within the serial buffer to be read by using the function ClearCommError(..). This ensures that ReadFile(..) can read something immediately and does not unnecessarily block the device. Try changing your ReceiverThread into something like this:
void ReceiverThread(void *param)
{
DWORD dwRead=0;
COMSTAT comStat;
char buffer[1024];
while (m_hCommPort != INVALID_HANDLE_VALUE)
{
if ((ClearCommError(m_hCommPort, NULL, &comStat) != FALSE) && (comStat.cbInQue > 0))
{
/* todo: ensure buffer is big enough to contain comStat.cbInQue bytes! */
if (ReadFile(m_hCommPort, buffer, comStat.cbInQue, &dwRead, NULL) != FALSE)
{
/* do something with data in buffer */
}
}
/* avoid busy-wait */
if (comStat.cbInQue == 0)
{
SleepEx(1, FALSE);
}
}
}
This way ReadFile(..) is only called if data is available and meanwhile WriteFile(..) can send data without being blocked.
Unfortunately I've not been able to make ClearCommError(..) blocking so I used the SleepEx(1, FALSE); work-around to avoid a busy-wait and therefore prefenting the ReceiverThread to eat up the CPU.
config.fDtrControl = 0;
config.fRtsControl = 0;
These settings turn the DTR and RTS handshake lines off. Most serial devices pay attention to these signals. They won't send anything when your DTR signal is off, assuming that the machine is not powered up. And won't send anything when your RTS signal is off, assuming that the machine is not ready to receive any data.
So what you observed is entirely normal.
Since the device appears to be "normal" and does pay attention to the handshake lines, you'll want to configure the DCB to let the device driver automatically control these signals. Fix:
config.fDtrControl = DTR_CONTROL_ENABLE;
config.fRtsControl = RTS_CONTROL_HANDSHAKE;
Also the default for terminal emulators like Putty and HyperTerminal. Use such a program first to ensure that the wiring and device are functional. If you can't get any device data to show up in such a program then it won't work with your program either. If this all checks out then also set the fDsrSensitivity, fOutxCtsFlow and fOutxDsrFlow properties to TRUE so that you will, in turn, pay attention to the handshake signals of the device.
I wish to create a startup job that every time that my Windows starts, it will rearrange some shortcut icons from my desktop to another location, such as right-bottom for example.
Can I make it with VBScript, Powershell, bat command script or even with C\C++\C#\Java?
Desktop is an ordinary listview so you can use windows api to move items to different locations. Have a look at this similar question: How can I programmatically manipulate Windows desktop icon locations?
I come late, but this piece of code works for me and I hope it may help somebody. It's in c++17.
#include <windows.h>
#include <commctrl.h>
#include <ShlObj.h>
int desktop_shuffle() {
// You must get the handle of desktop's listview and then you can reorder that listview (which contains the icons).
HWND progman = FindWindow(L"progman", NULL);
HWND shell = FindWindowEx(progman, NULL, L"shelldll_defview", NULL);
HWND hwndListView = FindWindowEx(shell, NULL, L"syslistview32", NULL);
int nIcons = ListView_GetItemCount(hwndListView);
POINT* icon_positions = new POINT[nIcons];
// READ THE CURRENT ICONS'S POSITIONS
if (nIcons > 0) {
// We must use desktop's virtual memory to get the icons positions, otherwise you won't be able to
// read their x, y positions.
DWORD desktop_proc_id = 0;
GetWindowThreadProcessId(hwndListView, &desktop_proc_id);
HANDLE h_process = OpenProcess(PROCESS_VM_OPERATION | PROCESS_VM_READ, FALSE, desktop_proc_id);
if (!h_process)
{
printf("OpenProcess: Error while opening desktop UI process\n");
return -1;
}
LPPOINT pt = (LPPOINT)VirtualAllocEx(h_process, NULL, sizeof(POINT), MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);
if (!pt)
{
CloseHandle(h_process);
printf("VirtualAllocEx: Error while allocating memory in desktop UI process\n");
return -1;
}
for (int i = 0; i < nIcons; i++)
{
if (!ListView_GetItemPosition(hwndListView, i, pt))
{
printf("GetItemPosition: Error while retrieving desktop icon (%d) position\n", i);
continue;
}
if (!ReadProcessMemory(h_process, pt, &icon_positions[i], sizeof(POINT), nullptr))
{
printf("ReadProcessMemory: Error while reading desktop icon (%d) positions\n", i);
continue;
}
}
VirtualFreeEx(h_process, pt, 0, MEM_RELEASE);
CloseHandle(h_process);
}
// UPDATE THE ICONS'S POSITIONS
for (int i = 0; i < nIcons; i++) {
ListView_SetItemPosition(hwndListView, i, rand(), rand());
}
return 0;
}
In my C++ Windows application, I have a function that is supposed to allow the end-user select a folder. I'm using SHBrowseForFolder and it is working fine except that folder shortcuts are not being displayed in the dialog.
Does anyone know if it is possible to configure SHBrowseForFolder so that the end-users will be able to navigate folder shortcuts?
Edit: May 24th, 2010, 1:10 EST:
Okay, I'm going to show the code of what I have so far. I have tried using the suggestion of putting a BFFM_IUNKNOWN callback in my callback procedure, but have been struggling to figure out how to provide a IFolderFilter descendent that works.
1. The code that gets called:
Error CFolderChooserDialog::RunDialog()
{
Error runResult = kError_NotInitialized;
if (VERIFYN(kLyndsey, m_ReferenceCount > 0)) {
runResult = kError_Unexpected;
m_AllFoldersFilter = new TAllFoldersFilter();
if (VERIFYN(kLyndsey, m_AllFoldersFilter))
{
char selectedDirectoryBuffer[MAX_PATH];
m_DirectoryPath.CopyInto(selectedDirectoryBuffer);
BROWSEINFO bi;
memset(&bi, 0, sizeof(bi));
bi.hwndOwner = MyGetMainHWND(m_CBP);
bi.pidlRoot = NULL;
bi.pszDisplayName = selectedDirectoryBuffer;
bi.lpszTitle = (const char*)m_Description;
bi.ulFlags |= BIF_RETURNONLYFSDIRS;
bi.ulFlags |= BIF_BROWSEINCLUDEFILES;
bi.lpfn = SHBrowseForFolderCallbackProc;
bi.lParam = (LPARAM)this;
bi.iImage = 0;
LPITEMIDLIST resultInfo = SHBrowseForFolder(&bi);
if (resultInfo) {
runResult = kError_NoError;
if (SHGetPathFromIDList(resultInfo, selectedDirectoryBuffer)) {
m_DirectoryPath = selectedDirectoryBuffer;
}
}
else {
runResult = kError_Failed;
}
delete m_AllFoldersFilter;
m_AllFoldersFilter = nil;
CoTaskMemFree(resultInfo);
}
}
return runResult;
}
2. The callback that gets called from SHBrowseForFolder:
int CALLBACK CFolderChooserDialog::SHBrowseForFolderCallbackProc(HWND window, UINT message, LPARAM messageValue, LPARAM clientData)
{
CFolderChooserDialog* thisPtr = (CFolderChooserDialog*)clientData;
if (VERIFYN(kLyndsey, thisPtr)) {
switch (message) {
case BFFM_INITIALIZED: {
if (!thisPtr->m_DialogTitle.IsEmpty()) {
::SetWindowText(window, (const char*) thisPtr->m_DialogTitle);
}
if (!thisPtr->m_DirectoryPath.IsEmpty()) {
LPCTSTR startDirectory = thisPtr->m_DirectoryPath;
::SendMessage(window, BFFM_SETSELECTION, TRUE, (LPARAM)startDirectory);
}
break;
}
case BFFM_IUNKNOWN:
{
IUnknown* theInterface = (IUnknown*)messageValue;
if (VERIFYN(kLyndsey, theInterface))
{
IFolderFilterSite* filter = NULL;
theInterface->QueryInterface(IID_IFolderFilterSite, (void**)&filter);
if (VERIFYN(kLyndsey, filter))
{
filter->SetFilter((IUnknown*)thisPtr->m_AllFoldersFilter);
filter->Release();
}
}
break;
}
default:
break;
}
}
return 0;
}
3. The IFolderFilter that should get called for each item for filtering it in or out of the dialog:
class TAllFoldersFilter : public IFolderFilter
{
public:
TAllFoldersFilter() { refCount = 0;}
HRESULT STDMETHODCALLTYPE QueryInterface(const IID& iid, void** obj)
{
if (!obj)
return E_INVALIDARG;
*obj = NULL;
if (iid == IID_IUnknown || iid == IID_IFolderFilter)
{
*obj = (void*)this;
AddRef();
return NOERROR;
}
return E_NOINTERFACE;
}
ULONG STDMETHODCALLTYPE AddRef()
{
refCount++;
return refCount;
}
ULONG STDMETHODCALLTYPE Release()
{
refCount--;
return refCount;
}
HRESULT STDMETHODCALLTYPE GetEnumFlags(IShellFolder* sf, LPCITEMIDLIST pidlFolder, HWND* window, DWORD* flags)
{
return 0;
}
HRESULT STDMETHODCALLTYPE ShouldShow(IShellFolder* sf, LPCITEMIDLIST pidlFolder, LPCITEMIDLIST pidlItem)
{
HRESULT resultCode = S_OK;
ULONG attributes = 0UL;
if (SUCCEEDED(sf->GetAttributesOf(1, &pidlItem, &attributes)))
{
if (attributes & SFGAO_FOLDER)
{
resultCode = S_OK; // Yes, I see the folders
}
else if (attributes & SFGAO_LINK)
{
resultCode = S_OK; // Yes, this shows the folder shortcut links, but I cannot explore them. When I "expand" them (click on the plus-sign-box), nothing happens.
}
}
return resultCode;
}
protected:
ULONG refCount;
};
So, where am I? Well, I can show the folders, I can show folder links, but I'm uncertain about the following:
How do I easily determine if the item I have is a shortcut link to a folder? The code I wrote is definitely not looking at that and is showing any shortcut link.
How do I easily allow the end-user delve into the folder represented by the shortcut link?
Is this code correct and as simple/clean as it can be?
Thanks for all of your help!
Edit: June 1st, 2010: 2:14 EST:
The answer was technically provided, so I'll mark that and I'm going to ask another question to help me fix this code.
I guess you could add the BIF_BROWSEINCLUDEFILES style and then filter the items to only display folders and .lnk's to folders (This is what you are after right?)
To filter the items, you need to add a callback func to BROWSEINFO, catch BFFM_IUNKNOWN and query for IFolderFilterSite and set a filter
The better answer is to use IFileOpenDialog with FOS_PICKFOLDERS as the option for Windows Vista and later.