I'm trying to get the "random delay" value for an arbitrary task scheduler task. I came up with the following C++ code:
//IRegisteredTask* pRegisteredTask for task:
// Name: "Regular Maintenance"
// Folder: "\Microsoft\Windows\TaskScheduler"
ITaskDefinition* pTaskDef = NULL;
HRESULT hr = pRegisteredTask->get_Definition(&pTaskDef);
if(SUCCEEDED(hr))
{
//Get triggers
ITriggerCollection* pTriggerCol = NULL;
hr = pTaskDef->get_Triggers(&pTriggerCol);
if(SUCCEEDED(hr))
{
//Get number of triggers
LONG nTriggerCnt = 0;
hr = pTriggerCol->get_Count(&nTriggerCnt);
if(SUCCEEDED(hr))
{
//Look through all triggers
for(LONG t = 0; t < nTriggerCnt; t++)
{
ITrigger* pTrigger = NULL;
hr = pTriggerCol->get_Item(t + 1, &pTrigger);
if(SUCCEEDED(hr))
{
//Get time trigger interface
ITimeTrigger *pTimeTrigger = NULL;
hr = pTrigger->QueryInterface(IID_ITimeTrigger, (void**)&pTimeTrigger);
if(SUCCEEDED(hr))
{
BSTR bstrRndDelay = NULL;
hr = pTimeTrigger->get_RandomDelay(&bstrRndDelay);
if(SUCCEEDED(hr))
{
//Check random delay
}
SysFreeString(bstrRndDelay);
}
else
{
//0x80004002 (E_NOINTERFACE) error happens here
}
if(pTimeTrigger)
pTimeTrigger->Release();
}
if(pTrigger)
pTrigger->Release();
}
}
}
if(pTriggerCol)
pTriggerCol->Release();
}
if(pTaskDef)
pTaskDef->Release();
I'm testing the code snippet above on the task that I know has "random delay" set up, i.e., \Microsoft\Windows\TaskScheduler->Regular Maintenance on my Windows 8.1:
but for some reason when I try to get the ITimeTrigger interface I'm getting error 0x80004002 or E_NOINTERFACE (as I marked in the code above.)
Any idea what am I doing wrong?
For accessing Random delay in Task scheduler, we can't do it directly you have to access by all interfaces.
For suppose I am using IDailyTigger or IWeeklyTigger Interface from this you have to access random delay.
IWeeklyTigger* lpWeeklyTrigger;
lpWeeklyTrigger->get_RandomDelay(&lcRandomDelay);
IDailyTigger* lpDailyTrigger;
lpDailyTrigger->get_RandomDelay(&lcRandomDelay);
This way you have to do for each trigger.
Related
I'm attempting to host a file preview handler within a dialog. I've set up an event sink for selection changes in Explorer. When the selection changes, I feed the selected shell item to the dialog, which in turn feeds it to a function that prepares the preview frame.
In general, it successfully loads the correct handler and displays the contents of the file, but for certain file types (namely, Excel and Word files), it runs into various problems like focus-loss or flashing. Here's a demo of the Excel preview handler messing up focus (and by mess up, I mean it wrongfully steals the focus from Explorer, which I'd like to maintain focus):
Word files may load successfully once, but they'll subsequently fail, especially if Word is opened.
As for the code:
For starters, here's my function for obtaining the preview handler from the file extension. This seems to work fine:
HRESULT PreviewHandlerFromExt(LPCWSTR pszExt, IPreviewHandler** ppph)
{
WCHAR szCLSID[CLSID_LEN] = { 0 };
DWORD cchOut = CLSID_LEN;
HRESULT hr = AssocQueryString( ASSOCF_INIT_DEFAULTTOSTAR,
ASSOCSTR_SHELLEXTENSION,
pszExt,
L"{8895b1c6-b41f-4c1c-a562-0d564250836f}",
szCLSID,
&cchOut );
if (FAILED(hr))
{
return hr;
}
CLSID clsid;
hr = CLSIDFromString(szCLSID, &clsid);
if (FAILED(hr))
{
return hr;
}
CComPtr<IUnknown> punk;
hr = punk.CoCreateInstance(clsid, NULL, CLSCTX_LOCAL_SERVER);
if (FAILED(hr))
{
return hr;
}
CComPtr<IPreviewHandler> pPrevHandler;
hr = punk->QueryInterface(&pPrevHandler);
if (FAILED(hr) || !pPrevHandler)
{
return hr;
}
return pPrevHandler.CopyTo(ppph);
}
And now here's the function in my dialog that prepares the preview, given a shell item (m_pPreviewHandler is the active preview handler, IDC_PREVIEWFRAME is a placeholder in the dialog for the preview pane, and m_mapExtsToPreviewHandlers is just a map for storing preview handlers as the user comes across them):
void CMyDialog::ShowPreview(IShellItem* pShItem)
{
HRESULT hr;
if (m_pPreviewHandler)
{
m_pPreviewHandler->Unload();
m_pPreviewHandler.Release();
}
CComHeapPtr<WCHAR> pszPath;
hr = pShItem->GetDisplayName(SIGDN_FILESYSPATH, &pszPath);
if (FAILED(hr))
{
return;
}
LPWSTR pszExt = CharLower(PathFindExtension(pszPath));
auto it = m_mapExtsToPreviewHandlers.find(pszExt);
if (it == m_mapExtsToPreviewHandlers.end())
{
hr = PreviewHandlerFromExt(pszExt, &m_pPreviewHandler);
if (FAILED(hr) || !m_pPreviewHandler)
{
return;
}
m_mapExtsToPreviewHandlers[pszExt] = m_pPreviewHandler;
}
else
{
m_pPreviewHandler = m_mapExtsToPreviewHandlers[pszExt];
}
CComPtr<IInitializeWithFile> pInitWithFile;
hr = m_pPreviewHandler->QueryInterface(&pInitWithFile);
if (SUCCEEDED(hr))
{
hr = pInitWithFile->Initialize(pszPath, STGM_READ);
if (FAILED(hr))
{
return;
}
}
else
{
CComPtr<IInitializeWithStream> pInitWithStream;
hr = m_pPreviewHandler->QueryInterface(&pInitWithStream);
if (SUCCEEDED(hr))
{
CComPtr<IStream> pStream;
hr = SHCreateStreamOnFile(pszPath, STGM_READ, &pStream);
if (FAILED(hr) || !pStream)
{
return;
}
hr = pInitWithStream->Initialize(pStream, STGM_READ);
if (FAILED(hr))
{
return;
}
}
}
CWindow wndPreviewFrame( GetDlgItem(IDC_PREVIEWFRAME) );
CRect rectPreviewFrame;
wndPreviewFrame.GetClientRect(&rectPreviewFrame);
hr = m_pPreviewHandler->SetWindow(wndPreviewFrame, &rectPreviewFrame);
if (FAILED(hr))
{
return;
}
hr = m_pPreviewHandler->DoPreview();
if (FAILED(hr))
{
return;
}
hr = m_pPreviewHandler->SetRect(&rectPreviewFrame);
if (FAILED(hr))
{
return;
}
}
Does anyone know what I'm doing wrong or what might fix these focus problems?
I've also tried placing LockSetForegroundWindow at various places, but no lock.
Also, this is what the dialog resource looks like:
I am porting my code to D3D12 from D3D11 and I'm trying to obtain display's refresh rate on D3D12. I use the refresh rate for precise animation timing (this is a hard requirement). This code works on D3D11:
HRESULT GetRefreshRate(IUnknown* device, IDXGISwapChain* swapChain, double* outRefreshRate)
{
Microsoft::WRL::ComPtr<IDXGIOutput> dxgiOutput;
HRESULT hr = swapChain->GetContainingOutput(&dxgiOutput);
if (FAILED(hr))
return hr;
Microsoft::WRL::ComPtr<IDXGIOutput1> dxgiOutput1;
hr = dxgiOutput.As(&dxgiOutput1);
if (FAILED(hr))
return hr;
DXGI_MODE_DESC1 emptyMode = {};
DXGI_MODE_DESC1 modeDescription;
hr = dxgiOutput1->FindClosestMatchingMode1(&emptyMode, &modeDescription, device);
if (SUCCEEDED(hr))
*outRefreshRate = (double)modeDescription.RefreshRate.Numerator / (double)modeDescription.RefreshRate.Denominator;
return hr;
}
Unfortunately, ID3D12Device does not implement IDXGIDevice interface, and FindClosestMatchingMode1 therefore fails with this error:
DXGI ERROR: IDXGIOutput::FindClosestMatchingMode: pConcernedDevice doesn't support the IDXGIDevice interface [ MISCELLANEOUS ERROR #69: ]
Is there a way to obtain IDXGIDevice when using D3D12? Alternatively, how do I determine display's refresh rate on D3D12?
I know about EnumDisplaySettings however it returns an integer and therefore lacks precision, causing drift in animations. I also found DwmGetCompositionTimingInfo, however, it seems to only support getting info for the main monitor.
I also need a solution that would work on both traditional Win32 and UWP applications. I am open to having to use two code paths for different application models if needed.
We can get the refresh rate using CCD api, here is the code for your reference:
HRESULT GetRefreshRate(IDXGISwapChain* swapChain, double* outRefreshRate)
{
ComPtr<IDXGIOutput> dxgiOutput;
HRESULT hr = swapChain->GetContainingOutput(&dxgiOutput);
// if swap chain get failed to get DXGIoutput then follow the below link get the details from remarks section
//https://learn.microsoft.com/en-us/windows/win32/api/dxgi/nf-dxgi-idxgiswapchain-getcontainingoutput
if (SUCCEEDED(hr))
{
ComPtr<IDXGIOutput1> dxgiOutput1;
hr = dxgiOutput.As(&dxgiOutput1);
if (SUCCEEDED(hr))
{
// get the descriptor for current output
// from which associated mornitor will be fetched
DXGI_OUTPUT_DESC outputDes{};
hr = dxgiOutput->GetDesc(&outputDes);
if (SUCCEEDED(hr))
{
MONITORINFOEXW info;
info.cbSize = sizeof(info);
// get the associated monitor info
if (GetMonitorInfoW(outputDes.Monitor, &info) != 0)
{
// using the CCD get the associated path and display configuration
UINT32 requiredPaths, requiredModes;
if (GetDisplayConfigBufferSizes(QDC_ONLY_ACTIVE_PATHS, &requiredPaths, &requiredModes) == ERROR_SUCCESS)
{
std::vector<DISPLAYCONFIG_PATH_INFO> paths(requiredPaths);
std::vector<DISPLAYCONFIG_MODE_INFO> modes2(requiredModes);
if (QueryDisplayConfig(QDC_ONLY_ACTIVE_PATHS, &requiredPaths, paths.data(), &requiredModes, modes2.data(), nullptr) == ERROR_SUCCESS)
{
// iterate through all the paths until find the exact source to match
for (auto& p : paths) {
DISPLAYCONFIG_SOURCE_DEVICE_NAME sourceName;
sourceName.header.type = DISPLAYCONFIG_DEVICE_INFO_GET_SOURCE_NAME;
sourceName.header.size = sizeof(sourceName);
sourceName.header.adapterId = p.sourceInfo.adapterId;
sourceName.header.id = p.sourceInfo.id;
if (DisplayConfigGetDeviceInfo(&sourceName.header) == ERROR_SUCCESS)
{
// find the matched device which is associated with current device
// there may be the possibility that display may be duplicated and windows may be one of them in such scenario
// there may be two callback because source is same target will be different
// as window is on both the display so either selecting either one is ok
if (wcscmp(info.szDevice, sourceName.viewGdiDeviceName) == 0) {
// get the refresh rate
UINT numerator = p.targetInfo.refreshRate.Numerator;
UINT denominator = p.targetInfo.refreshRate.Denominator;
double refrate = (double)numerator / (double)denominator;
*outRefreshRate = refrate;
break;
}
}
}
}
else
{
hr = E_FAIL;
}
}
else
{
hr = E_FAIL;
}
}
}
}
}
return hr;
}
More details about CCD API, you can refer the link below:
https://learn.microsoft.com/en-us/windows-hardware/drivers/display/ccd-apis
I am using the Windows Sensor API to get info from various sensors including accelerometer and gyroscope. (https://learn.microsoft.com/en-us/windows/desktop/sensorsapi/sensor-api-programming-guide)
My initial implementation of sensor driver for accelerometer worked - I asynchronously can get the values for that sensor.
The working initialization code for a single sensor (accelerometer) looks like the following:
void initialize1(AccelerometerCallBack callBack) {
HRESULT hr = S_OK;
CoInitializeEx(NULL, COINIT_APARTMENTTHREADED);
ISensorManager* pSensorManager = NULL;
ISensorCollection* pSensorColl = NULL;
ISensor* accelerometer = NULL;
hr = CoCreateInstance(CLSID_SensorManager,
NULL, CLSCTX_INPROC_SERVER,
IID_PPV_ARGS(&pSensorManager));
if (SUCCEEDED(hr))
{
printf("Succeeded getting Sensor...\n");
ULONG ulCount = 0;
// Verify that the collection contains
// at least one sensor.
hr = pSensorColl->GetCount(&ulCount);
if (SUCCEEDED(hr))
{
if (ulCount < 1)
{
wprintf_s(L"\nNo sensors of the requested category.\n");
hr = E_UNEXPECTED;
}
}
}
else {
printf("Failed to get Sensor...\n");
}
if (SUCCEEDED(hr))
{
// Get the first available sensor.
hr = pSensorColl->GetAt(0, &accelerometer);
BSTR name = 0;
hr = accelerometer->GetFriendlyName(&name);
wprintf(L"%s\n", name);
}
AccelerometerEvent* pEventClass = NULL;
ISensorEvents* pMyEvents = NULL;
if (SUCCEEDED(hr))
{
// Create an instance of the event class.
pEventClass = new(std::nothrow) AccelerometerEvent();
pEventClass->globalCallBack = callBack;
}
if (SUCCEEDED(hr))
{
// Retrieve the pointer to the callback interface.
hr = pEventClass->QueryInterface(IID_PPV_ARGS(&pMyEvents));
}
if (SUCCEEDED(hr))
{
// Start receiving events.
hr = accelerometer->SetEventSink(pMyEvents);
}
MSG msg;
BOOL bRet;
while ((bRet = GetMessage(&msg, NULL, 0, 0)) != 0)
{
if (bRet == -1)
{
// handle the error and possibly exit
}
else
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
}
Now I wanted to simultaneously get the gyroscope, so I add a similar initialization code:
void initialize2(GyroscopeCallBack callBack);, which does similar thing as above.
Now my C# layer triggers those code like this:
internal void Start()
{
AccelerometerCallBack myCallBack1 = new AccelerometerCallBack(onAccelerometerDataChanged);
initialize1(myCallBack1);
GyroscopeCallBack myCallBack2 = new GyroscopeCallBack(onGyroscopeDataChanged);
initialize2(myCallBack2);
}
However, only the accelerometer info is received through the callback, not the gyroscope's.
I have confirmed that my device has both sensors of the correct type
I have confirmed that my callback functions and C# to C++ interface all works properly
What is the correct way to "fetch" multiple sensors (instead of one) using the Windows Sensor API?
There are no examples on the official website about doing this, and the only related SO post (Exposing multiple sensors on a single device to Windows Sensor API) does not help. Thanks!
It worked! Solution was to remove the message pump and use COINIT_MULTITHREADED option for Coinitializine(). Thanks for all the help.
What i want to do is set the Windows Media Player's volume level. By default the volume is in-/decreased by 10% when e.g. clicking the down or up menu item (Play -> Volume -> Up), but this is, in my opinion, not fine enough (especially when e.g. skypeing with someone while listening to music).
The media player should stay an independent application.
Currently i'm using a little tool that sends app commands via SendMessage to the player with parameters as seen in spy++.
I thought of three ways to achieve my goal:
using WASAPI to get the media player's audio session and dynamically set the volume level
sending mouse down/up events to the volume slider of the media player host control by point
getting an instance of the media player control via IWMPPlayer4
including a media player control in my WPF application within a windows forms host (not preferred due to loss of independence)
Point 2 seems rather ugly due to the fact that the media player control is a COM element and has as far spy++ displays only one handle, meaning i would have to determine the exact position of the volume slider and send very precise mouse events. Additional i don't know whether this would work at all.
Point 3 has the presupposition that one can get an instance of a COM element by a handle. Since i have yet not worked with COM elements i don't know if this is possible.
Update: One can get an instance of a remote mediay player using the IWMPPlayer4 interface. Though i have to see whether one can change settings.
Point 1 has the impression on me that it would be possible without much effort.
Though i'd be facing the next problem: identifying the media players audio session. Enumerating them using IAudioSessionManager2 and displaying the name using
IAudioSessionControl2 ctrl2 = NULL;
// ...
hr = ctrl2->GetDisplayName(&name);
if (FAILED(hr))
{
SafeRelease(ctrl);
SafeRelease(ctrl2);
continue;
}
String ^sessionName = gcnew String(name);
Console::WriteLine("Session name: '" + sessionName + "'");
prints most the times an emtpy string except for Mozilla Firefox and System Sounds (the other processes might not have set a session name themselfes => a default name is chosen and GetDisplayName returns an empty string).
Update 2:
As Simon Mourier pointed out one can compare the process ids to get the right ISimpleAudioVolume instance and it works as far as it comes to WMP to adopt the changes. The said instance is aquired the following way:
IMMDeviceEnumerator *pEnumerator = NULL;
ISimpleAudioVolume *pVolume = NULL;
IMMDevice *pDevice = NULL;
IAudioSessionManager2 *pManager = NULL;
IAudioSessionEnumerator *pSessionEnumerator = NULL;
int sessionCount = 0;
CoCreateInstance(__uuidof(MMDeviceEnumerator), NULL,
CLSCTX_ALL, __uuidof(IMMDeviceEnumerator), (void**)&pEnumerator);
pEnumerator->GetDefaultAudioEndpoint(EDataFlow::eRender, ERole::eMultimedia, &pDevice);
pDevice->GetState(&deviceState);
pDevice->Activate(__uuidof(IAudioSessionManager2), CLSCTX_ALL, NULL, (void**)&pManager);
pManager->GetSessionEnumerator(&pSessionEnumerator);
pSessionEnumerator->GetCount(&sessionCount);
for (int i = 0; i < sessionCount; i++)
{
IAudioSessionControl *ctrl = NULL;
IAudioSessionControl2 *ctrl2 = NULL;
DWORD processId = 0;
hr = pSessionEnumerator->GetSession(i, &ctrl);
if (FAILED(hr))
{
continue;
}
hr = ctrl->QueryInterface(__uuidof(IAudioSessionControl2), (void**)&ctrl2);
if (FAILED(hr))
{
SafeRelease(ctrl);
continue;
}
hr = ctrl2->GetProcessId(&processId);
if (FAILED(hr))
{
SafeRelease(ctrl);
SafeRelease(ctrl2);
continue;
}
if (processId == wmpProcessId)
{
hr = ctrl2->QueryInterface(__uuidof(ISimpleAudioVolume), (void**)&pVolume);
SafeRelease(ctrl);
SafeRelease(ctrl2);
break;
}
SafeRelease(ctrl);
SafeRelease(ctrl2);
}
When aquiering an ISimpleAudioVolume instance via a IAudioClient one has to provide a session id to have volume changes reported to event subscribers. Is this possible using this approach?
Though i know that adding a media player control to my application would be the easiest way, i'd like to not use this option if possible.
I don't know what may happened during my initial try to set the media player's volume level, but the following code works (most exception handling excluded):
HRESULT hr;
IMMDeviceEnumerator *pEnumerator = NULL;
ISimpleAudioVolume *pVolume = NULL;
IMMDevice *pDevice = NULL;
IAudioSessionManager2 *pManager = NULL;
IAudioSessionEnumerator *pSessionEnumerator = NULL;
int sessionCount = 0;
int wmpProcess = GetWmpProcessId(); // Aquire WMPs process id
// Get the device enumerator and initialize the application for COM
hr = CoCreateInstance(__uuidof(MMDeviceEnumerator), NULL, CLSCTX_ALL,
__uuidof(IMMDeviceEnumerator), (void**)&pEnumerator);
// Get the default device
hr = pEnumerator->GetDefaultAudioEndpoint(EDataFlow::eRender,
ERole::eMultimedia, &pDevice);
// Get the session 2 manager
hr = pDevice->Activate(__uuidof(IAudioSessionManager2), CLSCTX_ALL,
NULL, (void**)&pManager);
// Get the session enumerator
hr = pManager->GetSessionEnumerator(&pSessionEnumerator);
// Get the session count
hr = pSessionEnumerator->GetCount(&sessionCount);
// Loop through all sessions
for (int i = 0; i < sessionCount; i++)
{
IAudioSessionControl *ctrl = NULL;
IAudioSessionControl2 *ctrl2 = NULL;
DWORD processId = 0;
hr = pSessionEnumerator->GetSession(i, &ctrl);
if (FAILED(hr))
{
continue;
}
hr = ctrl->QueryInterface(__uuidof(IAudioSessionControl2), (void**)&ctrl2);
if (FAILED(hr))
{
SafeRelease(ctrl);
continue;
}
//Identify WMP process
hr = ctrl2->GetProcessId(&processId);
if (FAILED(hr))
{
SafeRelease(ctrl);
SafeRelease(ctrl2);
continue;
}
if (processId != wmpProcess)
{
SafeRelease(ctrl);
SafeRelease(ctrl2);
continue;
}
hr = ctrl2->QueryInterface(__uuidof(ISimpleAudioVolume), (void**)&pVolume);
if (FAILED(hr))
{
Error(hr, "Failed to get ISimpleAudioVolume.");
SafeRelease(ctrl);
SafeRelease(ctrl2);
continue;
}
// Set the master volume
hr = pVolume->SetMasterVolume(1.0, NULL);
if (FAILED(hr))
{
Error(hr, "Failed to set the master volume.");
SafeRelease(ctrl);
SafeRelease(ctrl2);
SafeRelease(pVolume);
continue;
}
SafeRelease(ctrl);
SafeRelease(ctrl2);
SafeRelease(pVolume);
}
Edit: Are there any tutorials on how to use WIA or TWAIN in c++, that explain how to scan pages, adjust settings (DPI, using automatic feeder etc.) and save them as PNG files?
I'd like to use WIA to scan pages and store them as png files. If the scanner supports automatic feeding I'd also like to use that feature. Currently I am following the steps of this tutorial and am stuck at the section Transferring Image Data in WIA 2.0.
So far my scanner has been found and I am able to create the device, and an IWiaItem2* has been created. How can use it to scan at 300dpi and store the result as png file?
The tutorial is not clear about how to start the scan process or how to set dpi for scanning, so I hope someone can help me with the code.
This is essentially the code for getting all local devices:
bool init(IWiaDevMgr2* devMgr)
{
//creating the device manager
*devMgr = 0;
CoCreateInstance( CLSID_WiaDevMgr2, 0, CLSCTX_LOCAL_SERVER, IID_IWiaDevMgr2, (void**)&devMgr);
//enumerating wia devices
IEnumWIA_DEV_INFO* enumDevInfo = 0;
HRESULT hr = devMgr->EnumDeviceInfo( WIA_DEVINFO_ENUM_LOCAL, &enumDevInfo);
if(SUCCEEDED(hr))
{
//loop until an error occurs or end of list
while(hr == S_OK)
{
IWiaPropertyStorage* storage = 0;
hr = enumDevInfo->Next( 1, &storage, 0);
if(hr == S_OK)
{
readProperties(storage);
storage->Release();
storage = 0;
}
}
//set hr to ok, so no error code is returned
if(hr == S_FALSE) hr = S_OK;
enumDevInfo->Release();
enumDevInfo = 0;
}
return SUCCEEDED(hr);
}
void readProperties(IWiaPropertyStorage* storage)
{
PROPSPEC propSpec[2] = {0};
PROPVARIANT propVar[2] = {0};
const ULONG propCount = sizeof(propSpec) / sizeof(propSpec[0]);
propSpec[0].ulKind = PRSPEC_PROPID;
propSpec[0].propid = WIA_DIP_DEV_ID;
propSpec[1].ulKind = PRSPEC_PROPID;
propSpec[1].propid = WIA_DIP_DEV_NAME;
HRESULT hr = storage->ReadMultiple(propCount, propSpec, propVar);
if(SUCCEEDED(hr))
{
Device* dev = new Device(propVar[0].bstrVal, propVar[1].bstrVal);
devices.push_back( dev );
FreePropVariantArray( propCount, propVar );
}
}
Afterwards a device is initialized like this:
bool createDevice(BSTR id, IWiaItem2** item)
{
*item = 0;
HRESULT hr = devMgr->CreateDevice( 0, deviceId, item);
return SUCCEEDED(hr);
}
Then the items are enumerated:
bool enumerateItems(IWiaItem2* item)
{
LONG itemType = 0;
HRESULT hr = item->GetItemType(&itemType);
if(SUCCEEDED(hr))
{
if(itemType & WiaItemTypeFolder || itemType & WiaItemTypeHasAttachments)
{
IEnumWiaItem2* enumItem = 0;
hr = item->EnumChildItems(0, &enumItem );
while(hr == S_OK)
{
IWiaItem2* child = 0;
hr = enumItem->Next( 1, &child, 0 );
if(hr == S_OK)
{
hr = enumerateItems( child );
child->Release();
child = 0;
}
}
if(hr == S_FALSE) hr = S_OK;
enumItem->Release();
enumItem = 0;
}
}
return SUCCEEDED(hr);
}
Now that everything has been initialized I'd like to implement a scan function. However, the code provided at the tutorial is for transferring files and folders and not for scanning images.
void scanAndSaveAsPNG(IWiaItem2* item, unsigned int dpi, std::string targetPath)
{
}
EDIT:
I installed the latest version available of the scanner driver (WIA and TWAIN) and after checking the supported commands using this code
void printCommands(IWiaItem2* i)
{
IEnumWIA_DEV_CAPS* caps = 0;
HRESULT h = item->EnumDeviceCapabilities(WIA_DEVICE_COMMANDS, &caps);
if(SUCCEEDED(h))
{
ULONG count = 0;
caps->GetCount(&count);
if(count > 0)
{
WIA_DEV_CAP* cap = new WIA_DEV_CAP[ count ];
ULONG fetched;
caps->Next(count, cap, &fetched);
for(int i = 0; i < fetched; i++)
{
std::cout << bstr_t( cap[i].bstrName ) << "\n";
}
}
caps->Release();
}
}
I noticed it only lists WIA Synchronize command. I am not sure if I didn't initialize the device correctly, or if the device doesn't support all WIA commands although the driver is installed.
So unless this problem is solved I am alternatively also looking for the same code based on TWAIN.
You want to use IWiaItem2::DeviceCommand which sends a command to the image capture device. The list of commands you can send are listed here.