What exactly IUpdate::BundledUpdates retrieves? - windows

When we try to retrieve the Windows Update Information with WUA API, the following is the process I have followed. But I am bit confused with IUpdate::BundledUpdates property.
Create an IUpdateSearcher
Search based on a search criteria. I provided search criteria as "IsHidden=1 or IsInstalled=1"
You will have a IUpdateCollection as result of search.
Using get_Item in IUpdateCollection, I retrieved each update (IUpdate) and printed the required values (KB numbers in my case).
But again in IUpdate, you have a BundledUpdate property that gives IUpdateCollection with get_BundledUpdates() method. When I iterated over the results of BundledUpdates, I have got no results.
Am I missing something in retrieving bundled updates? (OR) does the criteria I specified includes Bundled Updates as part of first result set of IUpdateCollection?
Also in MSDN, examples are lacking for each interface in WUA API, Could someone provide any resources that explains clearly what each interface in WUA API does?
Added full source code of C++ console Application:
#include <wuapi.h>
#include <iostream>
#include <wuerror.h>
using namespace std;
int main()
{
HRESULT hr;
hr = CoInitialize(NULL);
IUpdateSession* iUpdate;
IUpdateSearcher* searcher;
ISearchResult* results;
BSTR criteria = SysAllocString(L"IsInstalled=1 or IsHidden=1 or IsPresent=1");
hr = CoCreateInstance(CLSID_UpdateSession, NULL, CLSCTX_INPROC_SERVER,
IID_IUpdateSession, (LPVOID*)&iUpdate);
hr = iUpdate->CreateUpdateSearcher(&searcher);
wcout << L"Searching for updates ..."<<endl;
hr = searcher->Search(criteria, &results);
SysFreeString(criteria);
switch(hr)
{
case S_OK:
wcout<<L"List of applicable items on the machine:"<<endl;
break;
case WU_E_LEGACYSERVER:
wcout<<L"No server selection enabled"<<endl;
return 0;
case WU_E_INVALID_CRITERIA:
wcout<<L"Invalid search criteria"<<endl;
return 0;
}
IUpdateCollection * updateList;
IUpdate *updateItem;
LONG updateSize;
BSTR updateName;
results->get_Updates(&updateList);
updateList->get_Count(&updateSize);
if (updateSize == 0)
{
wcout << L"No updates found"<<endl;
}
for (LONG i = 0; i < updateSize; i++)
{
IStringCollection *KBCollection;
LONG KBCount;
updateList->get_Item(i, &updateItem);
updateItem->get_KBArticleIDs(&KBCollection);
KBCollection->get_Count(&KBCount);
for(int k=0;k<KBCount;k++)
{
BSTR KBValue;
KBCollection->get_Item(k,&KBValue);
wcout << L"KB" << KBValue << endl;
}
//Retrieve the bundled updates
IUpdateCollection *updtCollection;
updateItem->get_BundledUpdates(&updtCollection);
LONG updtBundledCount;
updtCollection->get_Count(&updtBundledCount);
for(LONG j=0;j<updtBundledCount;j++)
{
cout<<"Bundled KBs" <<endl;
IUpdate *bundledUpdateItem;
updtCollection->get_Item(j,&bundledUpdateItem);
bundledUpdateItem->get_KBArticleIDs(&KBID);
if(KBID != NULL)
{
LONG KBCount;
BSTR KBIDValue;
KBID->get_Count(&KBCount);
for(LONG j=0;j<KBCount;j++)
{
wcout << "KB" <<(KBIDValue) << endl;
temp.setKBID(KBIDValue);
}
}
}
}
wcout << L"Total KB Count : " << updateSize << endl;
CoUninitialize();
return 0;
}

I just fix your code in order to retrieve the Bundled Updates.
#include "stdafx.h"
#include <wuapi.h>
#include <iostream>
#include <wuerror.h>
using namespace std;
int _tmain(int argc, _TCHAR* argv[])
{
HRESULT hr;
hr = CoInitialize(NULL);
IUpdateSession* iUpdate;
IUpdateSearcher* searcher;
ISearchResult* results;
BSTR criteria = SysAllocString(L"IsInstalled=1 or IsHidden=1 or IsPresent=1");
hr = CoCreateInstance(CLSID_UpdateSession, NULL, CLSCTX_INPROC_SERVER, IID_IUpdateSession, (LPVOID*)&iUpdate);
hr = iUpdate->CreateUpdateSearcher(&searcher);
wcout << L"Searching for updates ..."<<endl;
hr = searcher->Search(criteria, &results);
SysFreeString(criteria);
switch(hr)
{
case S_OK:
wcout<<L"List of applicable items on the machine:"<<endl;
break;
case WU_E_LEGACYSERVER:
wcout<<L"No server selection enabled"<<endl;
return 0;
case WU_E_INVALID_CRITERIA:
wcout<<L"Invalid search criteria"<<endl;
return 0;
}
IUpdateCollection *updateList;
IUpdate *updateItem;
LONG updateSize;
BSTR updateName;
results->get_Updates(&updateList);
updateList->get_Count(&updateSize);
if (updateSize == 0)
{
wcout << L"No updates found"<<endl;
}
for (LONG i = 0; i < updateSize; i++)
{
IStringCollection *KBCollection;
LONG KBCount;
updateList->get_Item(i,&updateItem);
updateItem->get_Title(&updateName);
wcout<<i+1<<" - "<<updateName<<endl;
IUpdateCollection *updtCollection;
LONG updtBundledCount;
//Retrieve the bundled updates
updateItem->get_BundledUpdates(&updtCollection);
hr = updtCollection->get_Count(&updtBundledCount);
if ((updtBundledCount>0) && (hr ==S_OK))
{
wcout << L"Bundled Updates " <<(updtBundledCount) << endl;
for(LONG j=0;j<updtBundledCount;j++)
{
IUpdate *bundledUpdateItem;
BSTR bundledName;
updtCollection->get_Item(j,&bundledUpdateItem);
bundledUpdateItem->get_Title(&bundledName);
wcout<<L" "<<j+1<<" - "<<bundledName<<endl;
}
}
}
::CoUninitialize();
wcin.get();
return 0;
}

Related

A disc's recursive path : Time to long C++

I want to build a class similar to "CFindDialog" with MFC Windows with color. I route the disk with a recursive function that uses "FindFirstFile". But when I switch to settings the system disk (C: . . .). The time is to long several seconds and so I'm on an SDD.
I have two questions:
Are there any low-level functions that allow you to go faster?
How does DLL comdlg32 go so fast?
Jean Bezet
I cannot find the "CFindDialog" class you mentioned, but you could try Windows Search, First is to index the drive you want to search(Only once):
Control Panel> Index Option> Modify, check the dirves.
There are some code samples in the document, and I also provide a sample without UI here. I made some simple modifications to this sample to specify the search content:
#include <windows.h>
#include <searchapi.h>
#include <iostream>
#include <atldbcli.h>
using namespace std;
class CMyAccessor
{
public:
WCHAR _szItemUrl[2048];
__int64 _size;
BEGIN_COLUMN_MAP(CMyAccessor)
COLUMN_ENTRY(1, _szItemUrl)
COLUMN_ENTRY(2, _size)
END_COLUMN_MAP()
};
HRESULT GetSQLStringFromParams(LCID lcidContentLocaleParam,
PCWSTR pszContentPropertiesParam,
LCID lcidKeywordLocaleParam,
LONG nMaxResultsParam,
PCWSTR pszSelectColumnsParam,
PCWSTR pszSortingParam,
SEARCH_QUERY_SYNTAX sqsSyntaxParam,
SEARCH_TERM_EXPANSION steTermExpansionParam,
PCWSTR pszWhereRestrictionsParam,
PCWSTR pszExprParam,
PWSTR* ppszSQL)
{
ISearchQueryHelper* pQueryHelper;
// Create an instance of the search manager
ISearchManager* pSearchManager;
HRESULT hr = CoCreateInstance(__uuidof(CSearchManager), NULL, CLSCTX_LOCAL_SERVER, IID_PPV_ARGS(&pSearchManager));
if (SUCCEEDED(hr))
{
// Get the catalog manager from the search manager
ISearchCatalogManager* pSearchCatalogManager;
hr = pSearchManager->GetCatalog(L"SystemIndex", &pSearchCatalogManager);
if (SUCCEEDED(hr))
{
// Get the query helper from the catalog manager
hr = pSearchCatalogManager->GetQueryHelper(&pQueryHelper);
if (SUCCEEDED(hr))
{
hr = pQueryHelper->put_QueryContentLocale(lcidContentLocaleParam);
if (SUCCEEDED(hr))
{
hr = pQueryHelper->put_QueryContentProperties(pszContentPropertiesParam);
}
if (SUCCEEDED(hr))
{
hr = pQueryHelper->put_QueryKeywordLocale(lcidKeywordLocaleParam);
}
if (SUCCEEDED(hr))
{
hr = pQueryHelper->put_QueryMaxResults(nMaxResultsParam);
}
if (SUCCEEDED(hr))
{
hr = pQueryHelper->put_QuerySelectColumns(pszSelectColumnsParam);
}
if (SUCCEEDED(hr))
{
hr = pQueryHelper->put_QuerySorting(pszSortingParam);
}
if (SUCCEEDED(hr))
{
hr = pQueryHelper->put_QuerySyntax(sqsSyntaxParam);
}
if (SUCCEEDED(hr))
{
hr = pQueryHelper->put_QueryTermExpansion(steTermExpansionParam);
}
if (SUCCEEDED(hr))
{
hr = pQueryHelper->put_QueryWhereRestrictions(pszWhereRestrictionsParam);
}
if (SUCCEEDED(hr))
{
hr = pQueryHelper->GenerateSQLFromUserQuery(pszExprParam, ppszSQL);
}
pQueryHelper->Release();
}
pSearchCatalogManager->Release();
}
pSearchManager->Release();
}
return hr;
}
void WindowsSearch(PCWSTR szPath, PCWSTR szFileName)
{
PWSTR pszSQL;
wstring script = L"AND SCOPE ='file:///";
script += szPath;
script += L"'";
wstring filename = L"FileName:";
filename += szFileName;
HRESULT hr = GetSQLStringFromParams(1033, L"", 1033, -1, L"System.ItemPathDisplay, System.Size",
L"", SEARCH_ADVANCED_QUERY_SYNTAX, SEARCH_TERM_NO_EXPANSION, script.c_str(),
filename.c_str(), &pszSQL);
if (SUCCEEDED(hr))
{
wcout << L"Generated query: " << pszSQL << endl;
CDataSource cDataSource;
hr = cDataSource.OpenFromInitializationString(L"provider=Search.CollatorDSO.1;EXTENDED PROPERTIES='Application=Windows'");
if (SUCCEEDED(hr))
{
CSession cSession;
hr = cSession.Open(cDataSource);
if (SUCCEEDED(hr))
{
// cCommand is derived from CMyAccessor which has binding information in column map
// This allows ATL to put data directly into apropriate class members.
CCommand<CAccessor<CMyAccessor>, CRowset> cCommand;
hr = cCommand.Open(cSession, pszSQL);
if (SUCCEEDED(hr))
{
__int64 maxValue = 0;
__int64 minValue = ULONG_MAX;
for (hr = cCommand.MoveFirst(); S_OK == hr; hr = cCommand.MoveNext())
{
wcout << cCommand._szItemUrl << L": " << cCommand._size << L" bytes" << endl;
maxValue = max(maxValue, cCommand._size);
minValue = min(minValue, cCommand._size);
}
wcout << L"Max:" << maxValue << L"Min:" << minValue << endl;
cCommand.Close();
}
cCommand.ReleaseCommand();
}
}
CoTaskMemFree(pszSQL);
}
}
int main()
{
HRESULT hr = CoInitializeEx(NULL, COINIT_MULTITHREADED | COINIT_DISABLE_OLE1DDE);
if (SUCCEEDED(hr))
{
WindowsSearch(L"C:\\", L"test*.txt");
CoUninitialize();
}
}
Thank you for your response. That doesn't quite address my problem. My function is recursive it broswse a hard drive. The travel time is too important when I throw it on c: (approximately 660,000 files). I memorize this description in a string array. If I remove this memorization it does not change the real time. Here's my function:
<<
bool Parcours_disque::EnumerateFolder(LPCWSTR lpcszFolder, DWORD nLevel)
{
// LPWIN32_FIND_DATAA fdd = __nullptr;
WIN32_FIND_DATAW fdd;
HANDLE hFind = INVALID_HANDLE_VALUE;
hFind = FindFirstFileW((lpcszFolder),(LPWIN32_FIND_DATAW) &fdd); // (LPCWSTR)
if (INVALID_HANDLE_VALUE == hFind)
{
return false;
}
do
{
if (fdd.dwFileAttributes & (FILE_ATTRIBUTE_DIRECTORY))
{
if (_T('.') != fdd.cFileName[0])
{
if (_T('$') != fdd.cFileName[0])
{
// Store in buffer
.....
EnumerateFolder(((CString)lpcszFolder).SpanExcluding(_T("*")) + (CString)fdd.cFileName + _T("\\*"), nLevel + 1); // Here's the recursive call!
}
}
}
else
{
// Store in buffer
.....
}
} while (FindNextFileW(hFind, (LPWIN32_FIND_DATAW) &fdd) != 0);
FindClose(hFind);
return true;
}
Microsoft does the same with CFileDialog object (MFC) and when you go through the disk it's immediate how does it do it? hidden functions?
Thank you

How to properly loop through / get text / select SysTreeView32 window item

I've spent a couple of hours pouring through Microsoft's Dev Center; however, I can't seem to figure out how to do the following two things:
Cycle through and view the names of each program under the 'Expert Advisors' section of the 'Navigator' sub window (for example 'MACD Sample' in screenshot below)
select and double click the program (e.g. 'MACD Sample').
Winspector(Left) | Application(Right)
My main problem seems to be that I don't know how to properly use HTREEITEM to access the information. I noticed there is a function ListView_GetItemText, but I've been unable to find a TreeView_GetItemText or equivalent function.
Any help would be greatly appreciated.
Below is the main function of my program:
int _tmain(int argc, _TCHAR* argv[])
{
wcout << TEXT("Enumerating Windows...") << endl;
HWND handle = NULL;
//--- Success: gets application handle
bool success1 = getHandle(L"MetaTrader", L"20", handle);
cout << "Success1: " << success1 << endl;
cout << "Result1: " << handle << endl;
//--- Success: gets navigator window
bool success2 = getChildHandle(handle, L"", L"Navigator", handle);
cout << "Success2: " << success2 << endl;
cout << "Result2: " << handle << endl;
//--- Success: gets "SysTreeView32" handle
handle = FindWindowEx(handle, 0, L"SysTreeView32", L"");
cout << "Result3: " << handle << endl;
//--- Success: get "SysTreeView32" root nod
HTREEITEM root = TreeView_GetNextItem(handle, NULL, TVGN_ROOT);
cout << "root: " << root << endl;
return 0;
}
The result of running the code seems to be working properly
Entire code for completeness:
// MT4Terminal-test.cpp : Defines the entry point for the console application.
//
#include "stdafx.h"
#pragma once
#include "targetver.h"
#include <iostream>
#include <map>
#include <string>
namespace std {
#if defined _UNICODE || defined UNICODE
typedef wstring tstring;
#else
typedef string tstring;
#endif
}
#include <stdio.h>
#include <tchar.h>
#include <Windows.h>
#include <psapi.h>
#include <Windows.h>
#include <Commctrl.h>
#include <windows.system.h>
using namespace std;
HWND glb_handle;
tstring glb_searchWindowTitle;
tstring glb_seachClassName;
BOOL CALLBACK enumWindowsChildProc(
__in HWND hWnd,
__in LPARAM lParam
) {
return TRUE;
}
BOOL CALLBACK enumWindowsProc(
__in HWND hWnd,
__in LPARAM lParam
) {
int length = ::GetWindowTextLength(hWnd);
if (0 == length) return TRUE;
TCHAR* bufferA;
bufferA = new TCHAR[length + 1];
memset(bufferA, 0, (length + 1) * sizeof(TCHAR));
TCHAR* bufferB;
bufferB = new TCHAR[100];
memset(bufferB, 0, 100 * sizeof(TCHAR));
GetWindowText(hWnd, bufferA, length + 1);
GetClassName(hWnd, bufferB, 100);
tstring windowTitle = tstring(bufferA);
tstring className = tstring(bufferB);
delete bufferA;
delete bufferB;
if (windowTitle.find(glb_searchWindowTitle) < string::npos &&
className.find(glb_seachClassName) < string::npos)
glb_handle = hWnd;
wcout.clear();
return TRUE;
}
bool getHandle(wstring searchClassName, wstring searchWindowTitle, HWND &handle)
{
handle = NULL;
glb_handle = NULL;
glb_searchWindowTitle = searchWindowTitle;
glb_seachClassName = searchClassName;
BOOL enumeratingWindowsSucceeded = EnumWindows(enumWindowsProc, NULL);
if (enumeratingWindowsSucceeded)
{
if (glb_handle != NULL)
{
handle = glb_handle;
return true;
}
}
glb_handle = NULL;
glb_searchWindowTitle = L"";
glb_seachClassName = L"";
return false;
}
bool getChildHandle(HWND parent_handle, wstring searchClassName, wstring searchWindowTitle, HWND &handle)
{
handle = NULL;
glb_handle = NULL;
glb_searchWindowTitle = searchWindowTitle;
glb_seachClassName = searchClassName;
BOOL enumeratingWindowsSucceeded = EnumChildWindows(parent_handle, enumWindowsProc, NULL);
if (enumeratingWindowsSucceeded)
{
if (glb_handle != NULL)
{
handle = glb_handle;
return true;
}
}
glb_handle = NULL;
glb_searchWindowTitle = L"";
glb_seachClassName = L"";
return false;
}
int _tmain(int argc, _TCHAR* argv[])
{
wcout << TEXT("Enumerating Windows...") << endl;
HWND handle = NULL;
//--- Success: gets application handle
bool success1 = getHandle(L"MetaTrader", L"20", handle);
cout << "Success1: " << success1 << endl;
cout << "Result1: " << handle << endl;
//--- Success: gets navigator window
bool success2 = getChildHandle(handle, L"", L"Navigator", handle);
cout << "Success2: " << success2 << endl;
cout << "Result2: " << handle << endl;
//--- Success: gets "SysTreeView32" handle
handle = FindWindowEx(handle, 0, L"SysTreeView32", L"");
cout << "Result3: " << handle << endl;
//--- Success: get "SysTreeView32" root nod
HTREEITEM root = TreeView_GetNextItem(handle, NULL, TVGN_ROOT);
cout << "root: " << root << endl;
return 0;
}
Selecting a SysTreeView32 item
(For clarification, when I say selecting a SysTreeView32 item, I'm referring to simulating a double-click operation on a tree node -- similar to how one can double click an icon on their Desktop to open a program)
After looking at the documentation, I'm convinced:
There doesn't exist an explicit message that will simulate double-clicking a node on a tree using the handle to the tree-view item
A possible work around would be to send the TVM_GETITEMRECT message to get the coordinates of the tree node, and then use SendInput() to send a click
Are the above two statements correct?
After implementing Barmak Shemirani's code, I tried to implement #2 above using the same methodology as in Barmak Shemirani's fix. Specifically, I attempted to allocate a Rect struct in the other Application program's memory with VirtualAllocEx(), call the TreeView_GetItemRect macro in my program with a pointer to the rectangle, and read the results with ReadProcessMemory().
However, my program crashes when I call TreeView_GetItemRect(), while passing the pointer to the Rect in the other Apps memory. Most likely, because TreeView_GetItemRect() is trying to write the Rect coordinates to an invalid memory address. This caused me to realize that I don't really understand what the macro is doing:
Hence, checking out the source, I found:
#define HELLO
#define TV_FIRST 0x1100 // TreeView messages
#define TVM_GETITEMRECT (TV_FIRST + 4)
#define TreeView_GetItemRect(hwnd, hitem, prc, code) \
(*(HTREEITEM *)(prc) = (hitem), (BOOL)SNDMSG((hwnd), TVM_GETITEMRECT, (WPARAM)(code), (LPARAM)(RECT *)(prc)))
I mostly understand everything except for the part before the SNDMSG function:
(*(HTREEITEM *)(prc) = (hitem),
What exactly does the above statement mean? Is this casting the rectangle pointer that I pass to a HTREEITEM pointer, which is somehow causing the program to crash?
Screenshot of console freezing
New code
int _tmain(int argc, _TCHAR* argv[])
{
wcout << TEXT("Enumerating Windows...") << endl;
HWND handle = NULL;
//--- Success: gets application handle
bool success1 = getHandle(L"MetaTrader", L"20", handle);
//--- Success: gets navigator window
bool success2 = getChildHandle(handle, L"", L"Navigator", handle);
//--- Success: gets "SysTreeView32" handle
handle = FindWindowEx(handle, 0, L"SysTreeView32", L"");
//--- Success: get "SysTreeView32" root nod
HTREEITEM root = TreeView_GetNextItem(handle, NULL, TVGN_ROOT);
unsigned long pid;
GetWindowThreadProcessId(handle, &pid);
HANDLE process = OpenProcess(PROCESS_VM_OPERATION | PROCESS_VM_READ | PROCESS_VM_WRITE |
PROCESS_QUERY_INFORMATION, FALSE, pid);
TVITEM item, *_item;
wchar_t buf[CHAR_BUF_LEN];
wchar_t *_buf;
memset(buf, 0, sizeof(buf) / sizeof(buf[0]));
_item = (TVITEM*)VirtualAllocEx(process, NULL, sizeof(TVITEM), MEM_COMMIT, PAGE_READWRITE);
_buf = (wchar_t*)VirtualAllocEx(process, NULL, CHAR_BUF_LEN, MEM_COMMIT, PAGE_READWRITE);
item.cchTextMax = CHAR_BUF_LEN;
item.pszText = _buf;
item.mask = TVIF_TEXT;
//--- find Experts Advisors branch in tree
HTREEITEM node = TreeView_GetNextItem(handle, root, TVGN_CHILD);
node = TreeView_GetNextItem(handle, node, TVGN_NEXT);
node = TreeView_GetNextItem(handle, node, TVGN_NEXT);
RECT rect, *_rect;
_rect = (RECT*)VirtualAllocEx(process, NULL, sizeof(RECT), MEM_COMMIT, PAGE_READWRITE);
rect = { 0 };
WriteProcessMemory(process, _rect, &rect, sizeof(RECT), NULL);
//--- step into Expert Advisors
node = TreeView_GetNextItem(handle, node, TVGN_CHILD);
//--- target program to open
wchar_t ea_name[] = L"MACD Sample";
while (node != NULL)
{
ZeroMemory(buf, CHAR_BUF_LEN);
item.hItem = node;
//Binds item and _item
WriteProcessMemory(process, _item, &item, sizeof(TVITEM), NULL);
TreeView_GetItem(handle, _item);
//Read buffer back to this program's process memory
ReadProcessMemory(process, _buf, buf, CHAR_BUF_LEN, NULL);
//Print program name
wcout << buf << endl;
if (wcscmp(ea_name, buf) == 0)
{
cout << "Found target program: " << ea_name << endl;
cout << "get rectangle coordinates: " << TreeView_GetItemRect(handle, node, _rect, TRUE) << endl;
}
node = TreeView_GetNextItem(handle, node, TVGN_NEXT);
}
VirtualFreeEx(process, _item, 0, MEM_RELEASE);
VirtualFreeEx(process, _buf, 0, MEM_RELEASE);
VirtualFreeEx(process, _rect, 0, MEM_RELEASE);
return 0;
}
This is the method you would normally use to read a TreeView item's text:
wchar_t buf[100];
memset(buf, 0, sizeof(buf));
TVITEM item = { 0 };
item.hItem = hitem;
item.cchTextMax = 100;
item.pszText = buf;
item.mask = TVIF_TEXT;
TreeView_GetItem(hwnd, &item);
This will not work in your program. TreeView_GetItem is a macro based on SendMessage, it copies data through LPARAM parameter. But this exchange is not allowed between different processes.
You could spend hours, possibly days, trying to hack it
(See this example)
Or you may want to research and see if the target program supports UI Automation
Edit, here is example to get HTREEITEM text. This won't work unless:
caller and target program are both 32-bit, or both 64-bit
caller and target program are both unicode
If target program is ANSI then change this function to ANSI.
HTREEITEM hitem = TreeView_GetSelection(hwndTree);
if (!hitem)
debug << "!hitem\n";
const int buflen = 512;
DWORD pid;
GetWindowThreadProcessId(hwndTree, &pid);
HANDLE process = OpenProcess(PROCESS_VM_OPERATION | PROCESS_VM_READ | PROCESS_VM_WRITE
| PROCESS_QUERY_INFORMATION, FALSE, pid);
TVITEMEX* ptv = (TVITEMEX*)VirtualAllocEx(process, NULL, sizeof(TVITEMEX),
MEM_COMMIT, PAGE_READWRITE);
wchar_t* pbuf = (wchar_t*)VirtualAllocEx(process, NULL, buflen,
MEM_COMMIT, PAGE_READWRITE);
TVITEMEX tv = { 0 };
tv.hItem = hitem;
tv.cchTextMax = buflen / 2;
tv.pszText = pbuf;
tv.mask = TVIF_TEXT | TVIF_HANDLE;
WriteProcessMemory(process, ptv, &tv, sizeof(TVITEMEX), NULL);
if (SendMessageW(hwndTree, TVM_GETITEM, 0, (LPARAM)(TVITEMEX*)(ptv)))
{
wchar_t buf[buflen / 2];
ReadProcessMemory(process, pbuf, buf, buflen, 0);
debug << "Result:" << buf << "\n";
}
else
debug << "!SendMessageW\n";
VirtualFreeEx(process, ptv, 0, MEM_RELEASE);
VirtualFreeEx(process, pbuf, 0, MEM_RELEASE);
CloseHandle(process); //*** I forgot this line before
The most voted answer has solved your problem, but I'd like to add some comment on the statement:
(*(HTREEITEM *)(prc) = (hitem),
TVM_GETITEMRECT has explained that :
When sending this message, the lParam parameter contains the handle of the item that the rectangle is being retrieved for.
In macro TreeView_GetItemRect, prc will be replaced by _rect, which is allocated in other process. So the program crashed.
For your situation, you can replace the code:
TreeView_GetItemRect(handle, node, _rect, TRUE)
by:
RECT rect, *_rect;
_rect = (RECT*)VirtualAllocEx(process, NULL, sizeof(RECT), MEM_COMMIT, PAGE_READWRITE);
*(HTREEITEM*)&rect = node;
WriteProcessMemory(process, _rect, &rect, sizeof(RECT), NULL);
SendMessage(handle, TVM_GETITEMRECT, true, (LPARAM)_rect);

ReadDirectoryChangesW not notifying when moving the files

I’m new to this windows API. I’m a GUI developer in my project i need to monitor a particular folder . I have followed every steps in the Windows API using ReadDirectoryChangeW but ReadDirectoryChangeW is not notifying me when the file is moved to other directory (cut/paste or delete to move to trash).at least it should notify as FILE_ACTION_RENAMED
It is windows 7 and ReadDirectorChangeW is working on normal copy,paset,shift+delete,rename
this code is written in Qt c++ where QString is char *
this is my code
#include <windows.h>
#include <Winbase.h>
#include <stdlib.h>
#include <stdio.h>
#include <tchar.h>
#include <qDebug>
#include <QThread>
#define MAX_DIRS 200
#define MAX_FILES 255
#define MAX_BUFFER 4096
#if 0
extern "C" {
WINBASEAPI BOOL WINAPI
ReadDirectoryChangesW( HANDLE hDirectory,
LPVOID lpBuffer, DWORD nBufferLength,
BOOL bWatchSubtree, DWORD dwNotifyFilter,
LPDWORD lpBytesReturned,
LPOVERLAPPED lpOverlapped,
LPOVERLAPPED_COMPLETION_ROUTINE lpCompletionRoutine
);
}
#endif
class WatcherThread : public QThread
{
Q_OBJECT
public:
WatcherThread(LPCWSTR dir)
{
path = dir;
}
void run() Q_DECL_OVERRIDE {
QString newDirName;
char buf[2048];
DWORD nRet;
BOOL result=TRUE;
char filename[MAX_PATH];
//path = L"K:/Demo/bb";
wchar_t* arr = (wchar_t*)path;
printf("\nThe file directory: [%s] \n", path);
qDebug() << "WatchDirectory Watcher Path " << QString::fromWCharArray(arr);
DirInfo[0].hDir = CreateFile (path, GENERIC_READ|FILE_LIST_DIRECTORY,
FILE_SHARE_READ|FILE_SHARE_WRITE|FILE_SHARE_DELETE,
NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS,
NULL);
if(DirInfo[0].hDir == INVALID_HANDLE_VALUE)
{
qDebug() << "Can not open";
return;
}
lstrcpy( DirInfo[0].lpszDirName, path);
OVERLAPPED PollingOverlap;
FILE_NOTIFY_INFORMATION pNotify[1024];
int offset;
PollingOverlap.OffsetHigh = 0;
PollingOverlap.hEvent = CreateEvent(NULL,TRUE,FALSE,NULL);
while(result)
{
result = ReadDirectoryChangesW(
DirInfo[0].hDir,// handle to the directory to be watched
(LPVOID)&pNotify,// pointer to the buffer to receive the read results
sizeof(pNotify),// length of lpBuffer
1,// flag for monitoring directory or directory tree
FILE_NOTIFY_CHANGE_DIR_NAME | FILE_NOTIFY_CHANGE_LAST_WRITE | FILE_NOTIFY_CHANGE_FILE_NAME | FILE_NOTIFY_CHANGE_SIZE ,
&nRet,// number of bytes returned
&PollingOverlap,// pointer to structure needed for overlapped I/O
NULL);
WaitForSingleObject(PollingOverlap.hEvent,INFINITE);
// if(result)
// {
offset = 0;
int rename = 0;
char oldName[260];
char newName[260];
do
{
//pNotify = (FILE_NOTIFY_INFORMATION*)((char*)buf + offset);
strcpy(filename, "");
int filenamelen = WideCharToMultiByte(CP_ACP, 0, pNotify[offset].FileName, pNotify[offset].FileNameLength/2, filename, sizeof(filename), NULL, NULL);
//filename[pNotify->FileNameLength/2] = ' ';
switch(pNotify[offset].Action)
{
case FILE_ACTION_ADDED:
qDebug() << "The FILE_ACTION_ADDED***********" << QString(filename).left(filenamelen);
emit onFileCopy(QString(filename).left(filenamelen));
break;
case FILE_ACTION_MODIFIED:
qDebug() << "The FILE_ACTION_MODIFIED" << QString(filename).left(filenamelen);
break;
case FILE_ACTION_REMOVED:
qDebug() << "The FILE_ACTION_REMOVED" << QString(filename).left(filenamelen);
emit onFileRemove(QString(filename).left(filenamelen));
break;
case FILE_ACTION_RENAMED_OLD_NAME:
qDebug() << "The FILE_ACTION_RENAMED_OLD_NAME" << QString(filename).left(filenamelen);
break;
case FILE_ACTION_RENAMED_NEW_NAME:
newDirName = QString(filename).left(filenamelen);
qDebug() << "The FILE_ACTION_RENAMED_NEW_NAME" << newDirName;
emit onDirRename(newDirName);
break;
default:
printf("\nDefault error.\n");
break;
}
//qDebug() << "pNotify->NextEntryOffset" << pNotify[offset].NextEntryOffset <<" offset "<< offset << nRet ;
offset += pNotify[offset].NextEntryOffset;
}while(pNotify[offset].NextEntryOffset); //(offset != 0)
ResetEvent(PollingOverlap.hEvent);
}
CloseHandle( DirInfo[0].hDir );
}
public:
signals:
void onDirRename(QString Dir);
void onFileRemove(QString name);
void onFileCopy(QString name);
private:
LPCWSTR path;
typedef struct _DIRECTORY_INFO {
HANDLE hDir;
TCHAR lpszDirName[MAX_PATH];
CHAR lpBuffer[MAX_BUFFER];
DWORD dwBufLength;
OVERLAPPED Overlapped;
}DIRECTORY_INFO, *PDIRECTORY_INFO, *LPDIRECTORY_INFO;
DIRECTORY_INFO DirInfo[MAX_DIRS];
TCHAR FileList[MAX_FILES*MAX_PATH];
DWORD numDirs;
};
It is a Qt based object. the path is the directory i will monitor and update any changes happens.

Convert a char * to inizialite an entity of type LPCTSTR for RegCreateKeyEx

I know I am a Windows programming nob, so I am just learning.
I am writing a Command Line tool to work with some of the Registry Functions of the Windows API, but I need to convert a char * that comes from an argv[] array to initialize a LPCTSTR variable with the content but I don't know how to do that.
This is the code I have so far:
#include <Windows.h>
#include <stdio.h>
#include <stdlib.h>
#include <iostream>
int main(int argc, char *argv [])
{
int count;
DWORD Reserved = 0;
LPTSTR lpClass = NULL;
DWORD dwOptions = REG_OPTION_NON_VOLATILE;
REGSAM samDesired = KEY_ALL_ACCESS;
LPSECURITY_ATTRIBUTES lpSecurityAttributes = NULL;
HKEY phkResult;
DWORD lpdwDisposition;
if (argv[1] == 0)
{
printf("There are no arguments, pleas type one at least. \n");
}
else if (std::string(argv[1]) == "-Clave")
{
if (std::string(argv[2]) == "HKCU")
{
printf("You are going to create a HKCU sub-key \n");
HKEY hKey = HKEY_CURRENT_USER;
if (std::string(argv[3]) != "")
{
printf("You are going to create this sub-key: %s \n",argv[3]);
//This is what I tried.
LPCTSTR lpSubKey = TEXT("%s",argv[3]);
RegCreateKeyEx(hKey, lpSubKey, Reserved, lpClass, dwOptions, samDesired, lpSecurityAttributes, &phkResult, &lpdwDisposition);
if (lpdwDisposition == REG_CREATED_NEW_KEY)
{
printf("The registry key has been created. \n");
}
}
else
printf("No one");
}
else
{
printf("No key has been specified \n");
}
}
system("Pause");
}
Can you help me out?
Thanks a lot.
Have a look at the MultiByteToWideChar function in Windows.h. Here's a nice and quick example:
const char * orig = "text1";
WCHAR buffer[6];
MultiByteToWideChar(0, 0, orig, 5, buffer, 6 );
LPCWSTR text = buffer;
Whoops, that's for LPCWSTR. For LPCTSTR, just use :
LPCTSTR text = _T("text1");
Another possible solution is to change main function declaration in this way :
int _tmain(int argc, TCHAR* argv[])
Quote from MSDN :
You can also use _tmain, which is defined in TCHAR.h. _tmain resolves
to main unless _UNICODE is defined. In that case, _tmain resolves to
wmain.
Second parameter on RegCreateKeyEx is on type* LPCTSTR.
lpSubKey is declared by the same type and will be initialized properly when passed as an argument to the RegCreateKeyEx function .
Here is your source code compiled with Visual C++, and uses Multi-byte Character Set (_MBCS macro is defined) :
// RegCreate.cpp : Defines the entry point for the console application.
// VC++ Compiler Options :
// cl /W3 /MT /O2 /D WIN32 /D _CONSOLE /D _MBCS /EHsc /TP RegCreate.cpp
#include <Windows.h>
#include <iostream>
#include <string>
#include <tchar.h>
#ifndef _MBCS
#define _MBCS
#endif
#pragma comment(lib, "Advapi32.lib")
using namespace std;
int _tmain(int argc, _TCHAR* argv[])
{
DWORD Reserved = 0;
LPTSTR lpClass = NULL;
DWORD dwOptions = REG_OPTION_NON_VOLATILE;
REGSAM samDesired = KEY_ALL_ACCESS;
LPSECURITY_ATTRIBUTES lpSecurityAttributes = NULL;
HKEY phkResult;
DWORD lpdwDisposition;
if(argc < 3)
{
std::cout << "There are no arguments, pleas type one at least. " << std::endl;
return 1;
}
if((std::string(argv[1]) == "-Clave") && (std::string(argv[2]) == "HKCU") && (argc == 4))
{
std::cout << "You are going to create a HKCU sub-key " << std::endl;
HKEY hKey = HKEY_CURRENT_USER;
if(std::string(argv[3]) != "")
{
std::cout << "You are going to create this sub-key: " << argv[3] << std::endl;
//This is what I tried.
LPCTSTR lpSubKey = argv[3];
if(ERROR_SUCCESS != RegCreateKeyEx(hKey, lpSubKey, Reserved, lpClass, dwOptions, samDesired,
lpSecurityAttributes, &phkResult, &lpdwDisposition))
{
return 1;
}
if(lpdwDisposition == REG_CREATED_NEW_KEY)
{
std::cout << "The registry key has been created. " << std::endl;
}
RegCloseKey(phkResult);
}
else
{
std::cout << "No one";
}
}
else
{
std::cout << "No key has been specified " << std::endl;
}
return 0;
}
We can use the TEXT macro to define a string as being Unicode or not, but in the above example this is not necessary .
If you use C++ I suggest you to change printf with std::cout, that works with ASCII characters .
The simplest solution is to explicitly call the Ansi versions of the Registry functions (RegCreateKeyExA, etc) and let Windows handle the conversions for you. You are currently calling the Unicode versions of the functions (RegCreateKeyExW, etc), or you wouldn't be having conversion problems in the first place:
#include <Windows.h>
#include <stdio.h>
#include <stdlib.h>
#include <iostream>
int main(int argc, char *argv [])
{
int count;
HKEY hkResult;
DWORD dwDisposition;
if (argc < 1)
{
printf("There are no arguments, pleas type one at least. \n");
}
else if (strcmp(argv[1], "-Clave") == 0)
{
if (argc < 3)
{
printf("There are not enough arguments typed in. \n");
}
else if (strcmp(argv[2], "HKCU") == 0)
{
if (strcmp(argv[3], "") != 0)
{
printf("You are going to create HKCU sub-key: %s \n", argv[3]);
if (RegCreateKeyExA(HKEY_CURRENT_USER, argv[3], 0, NULL, REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS, NULL, &hkResult, &dwDisposition) == 0)
{
if (dwDisposition == REG_CREATED_NEW_KEY)
{
printf("The registry key has been created. \n");
}
else
{
printf("The registry key already exists. \n");
}
RegCloseKey(hkResult);
}
else
{
printf("Unable to create the registry key. \n");
}
}
else
{
printf("No HKCU sub-key has been specified \n");
}
}
else
{
printf("No root key has been specified \n");
}
}
system("Pause");
return 0;
}
Update: If you want to be politically correct, most Win32 APIs that deal with text data actually deal with TCHAR (which is what you were attempting to use, but not successfully) so that they can be compiled for both Ansi and Unicode with a single codebase, eg:
#include <Windows.h>
#include <stdio.h>
#include <stdlib.h>
#include <iostream>
#include <tchar.h>
int _tmain(int argc, TCHAR *argv [])
{
int count;
HKEY hkResult;
DWORD dwDisposition;
if (argc < 1)
{
_tprintf(_T("There are no arguments, pleas type one at least. \n"));
}
else if (_tcscmp(argv[1], _T("-Clave")) == 0)
{
if (argc < 3)
{
_tprintf(_T("There are not enough arguments typed in. \n"));
}
else if (_tcsicmp(argv[2], _T("HKCU")) == 0)
{
if (_tcscmp(argv[3], _T("")) != 0)
{
_tprintf(_T("You are going to create HKCU sub-key: %s \n"), argv[3]);
if (RegCreateKeyEx(HKEY_CURRENT_USER, argv[3], 0, NULL, REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS, NULL, &hkResult, &dwDisposition) == 0)
{
if (dwDisposition == REG_CREATED_NEW_KEY)
{
_tprintf(_T("The registry key has been created. \n"));
}
else
{
_tprintf(_T("The registry key already exists. \n"));
}
RegCloseKey(hkResult);
}
else
{
_tprintf(_T("Unable to create the registry key. \n"));
}
}
else
{
_tprintf(_T("No HKCU sub-key has been specified \n"));
}
}
else
{
_tprintf(_T("No root key has been specified \n"));
}
}
_tsystem(_T("Pause"));
return 0;
}
With that said, since you are starting a new project, you are best off forgetting that Ansi even exists and just use Unicode for everything:
#include <Windows.h>
#include <stdio.h>
#include <stdlib.h>
#include <iostream>
int wmain(int argc, WCHAR *argv [])
{
int count;
HKEY hkResult;
DWORD dwDisposition;
if (argc < 1)
{
wprintf(L"There are no arguments, pleas type one at least. \n");
}
else if (wcscmp(argv[1], L"-Clave") == 0)
{
if (argc < 3)
{
wprintf(L"There are not enough arguments typed in. \n");
}
else if (_wcsicmp(argv[2], L"HKCU") == 0)
{
if (wcscmp(argv[3], L"") != 0)
{
wprintf(L"You are going to create HKCU sub-key: %s \n", argv[3]);
if (RegCreateKeyExW(HKEY_CURRENT_USER, argv[3], 0, NULL, REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS, NULL, &hkResult, &dwDisposition) == 0)
{
if (dwDisposition == REG_CREATED_NEW_KEY)
{
wprintf(L"The registry key has been created. \n");
}
else
{
wprintf(L"The registry key already exists. \n");
}
RegCloseKey(hkResult);
}
else
{
wprintf(L"Unable to create the registry key. \n");
}
}
else
{
wprintf(L"No HKCU sub-key has been specified \n");
}
}
else
{
wprintf(L"No root key has been specified \n");
}
}
_wsystem(L"Pause");
return 0;
}

SAPI: Speech to Text example

I am new to SAPI, and I would really appreciate if any of you can provide me a speech to text Hello World example in SAPI. I know MS got some examples like "Dictation" etc, but I would like to start with a very small one. Glad if you can help.
I played a bit with Windows Voice Recognition using SAPI, it really isn't user friendly. Here is an example of code I wrote (in C++) :
#include <sphelper.h>
#include <sapi.h>
#include <iostream>
#include <string>
const ULONGLONG grammarId = 0;
const wchar_t* ruleName1 = L"ruleName1";
int start_listening(const std::string& word);
ISpRecoGrammar* init_grammar(ISpRecoContext* recoContext, const std::string& command);
void get_text(ISpRecoContext* reco_context);
void check_result(const HRESULT& result);
int main(int argc, char** argv)
{
start_listening("Hello");
return EXIT_SUCCESS;
}
// This function exits when the word passed as parameter is said by the user
int start_listening(const std::string& word)
{
// Initialize COM library
if (FAILED(::CoInitialize(nullptr))) {
return EXIT_FAILURE;
}
std::cout << "You should start Windows Recognition" << std::endl;
std::cout << "Just say \""<< word << "\"" << std::endl;
HRESULT hr;
ISpRecognizer* recognizer;
hr = CoCreateInstance(CLSID_SpSharedRecognizer,
nullptr, CLSCTX_ALL, IID_ISpRecognizer,
reinterpret_cast<void**>(&recognizer));
check_result(hr);
ISpRecoContext* recoContext;
hr = recognizer->CreateRecoContext(&recoContext);
check_result(hr);
// Disable context
hr = recoContext->Pause(0);
check_result(hr);
ISpRecoGrammar* recoGrammar = init_grammar(recoContext, word);
hr = recoContext->SetNotifyWin32Event();
check_result(hr);
HANDLE handleEvent;
handleEvent = recoContext->GetNotifyEventHandle();
if(handleEvent == INVALID_HANDLE_VALUE) {
check_result(E_FAIL);
}
ULONGLONG interest;
interest = SPFEI(SPEI_RECOGNITION);
hr = recoContext->SetInterest(interest, interest);
check_result(hr);
// Activate Grammar
hr = recoGrammar->SetRuleState(ruleName1, 0, SPRS_ACTIVE);
check_result(hr);
// Enable context
hr = recoContext->Resume(0);
check_result(hr);
// Wait for reco
HANDLE handles[1];
handles[0] = handleEvent;
WaitForMultipleObjects(1, handles, FALSE, INFINITE);
get_text(recoContext);
std::cout << "Hello user" << std::endl;
recoGrammar->Release();
::CoUninitialize();
system("PAUSE");
return EXIT_SUCCESS;
}
/**
* Create and initialize the Grammar.
* Create a rule for the grammar.
* Add word to the grammar.
*/
ISpRecoGrammar* init_grammar(ISpRecoContext* recoContext, const std::string& command)
{
HRESULT hr;
SPSTATEHANDLE sate;
ISpRecoGrammar* recoGrammar;
hr = recoContext->CreateGrammar(grammarId, &recoGrammar);
check_result(hr);
WORD langId = MAKELANGID(LANG_FRENCH, SUBLANG_FRENCH);
hr = recoGrammar->ResetGrammar(langId);
check_result(hr);
// TODO: Catch error and use default langId => GetUserDefaultUILanguage()
// Create rules
hr = recoGrammar->GetRule(ruleName1, 0, SPRAF_TopLevel | SPRAF_Active, true, &sate);
check_result(hr);
// Add a word
const std::wstring commandWstr = std::wstring(command.begin(), command.end());
hr = recoGrammar->AddWordTransition(sate, NULL, commandWstr.c_str(), L" ", SPWT_LEXICAL, 1, nullptr);
check_result(hr);
// Commit changes
hr = recoGrammar->Commit(0);
check_result(hr);
return recoGrammar;
}
void get_text(ISpRecoContext* reco_context)
{
const ULONG maxEvents = 10;
SPEVENT events[maxEvents];
ULONG eventCount;
HRESULT hr;
hr = reco_context->GetEvents(maxEvents, events, &eventCount);
// Warning hr equal S_FALSE if everything is OK
// but eventCount < requestedEventCount
if(!(hr == S_OK || hr == S_FALSE)) {
check_result(hr);
}
ISpRecoResult* recoResult;
recoResult = reinterpret_cast<ISpRecoResult*>(events[0].lParam);
wchar_t* text;
hr = recoResult->GetText(SP_GETWHOLEPHRASE, SP_GETWHOLEPHRASE, FALSE, &text, NULL);
check_result(hr);
CoTaskMemFree(text);
}
void check_result(const HRESULT& result)
{
if (result == S_OK) {
return;
}
std::string message;
switch(result) {
case E_INVALIDARG:
message = "One or more arguments are invalids.";
case E_ACCESSDENIED:
message = "Acces Denied.";
case E_NOINTERFACE:
message = "Interface does not exist.";
case E_NOTIMPL:
message = "Not implemented method.";
case E_OUTOFMEMORY:
message = "Out of memory.";
case E_POINTER:
message = "Invalid pointer.";
case E_UNEXPECTED:
message = "Unexpecter error.";
case E_FAIL:
message = "Failure";
default:
message = "Unknown : " + std::to_string(result);
}
throw std::exception(message.c_str());
}
As I said, it's a bit complicated. I think you should wrap all that code into a library to make it easier to use.

Resources