Read file metadata via Windows Search from MFC program - visual-c++

I would like to read metadata of a DWG/AutoCAD file via Windows Search indexing service. I'm talking about properties that can be accessed with the right click in explorer without opening AutoCAD.
I have an MFC dialog based application written in Visual C++ 2005 and from inside this app I would like to access metadata (such as author, creation date etc.) of the given file. This was done by iFilter but it is deprecated since Windows XP and will be gone in Windows 8 (and LoadIFilter is not present in VS2005). Now from what I understand, it can be done with windows search - correct me if I'm wrong. Every example I found (msdn included) shows how to give data about your own files to windows search for indexing though. What I need is to know how to ask Windows Search about metadata for a given file.
Thanks
t.g.wilk
EDIT:
Here's what I've come up with so far:
BOOL WSQ_DoQuery( const wchar_t *constr, const wchar_t *querystr, VARIANT &result ) {
HRESULT hr = 0;
BOOL ret;
// Get the ADO connection
_Connection *con = NULL;
hr = CoCreateInstance( CLSID_Connection, NULL, CLSCTX_ALL, IID__Connection, (LPVOID *)&con );
if ( SUCCEEDED(hr) ) {
_Recordset *rs = NULL;
// Convert wide strings to BSTR as required by ADO APIs
BSTR bconstr = SysAllocString( constr );
BSTR bquerystr = SysAllocString( querystr );
if ( bconstr && bquerystr ) {
// Open the connection
hr = con->Open( bconstr, NULL, NULL, 0 );
if ( SUCCEEDED(hr) ) {
// Execute the query
hr = con->Execute( bquerystr, NULL, 0, &rs );
if ( SUCCEEDED(hr) ) {
// Display the results
ret = WSQ_GetCDate( rs ,result);
rs->Release();
} else {
TRACE( "Failed to execute query, %08x\r\n", hr );
} // if
} else {
TRACE( "Failed to open ADO connection, %08x\r\n", hr );
} // if
} else {
TRACE("Failed to convert wide to BSTR\r\n" );
} // if
con->Release();
if ( bconstr ) {
SysFreeString( bconstr );
}
if ( bquerystr ) {
SysFreeString( bquerystr );
}
} else {
TRACE("Failed to get connection, %08x\r\n", hr );
} // if
return ret;
} // DoQuery
The connection string (constr) is
provider=Search.CollatorDSO.1;EXTENDED PROPERTIES="Application=Windows"
as returned by ISearchQueryHelper.
And the query (querystr) is
SELECT System.Document.DateCreated FROM SystemIndex WHERE System.FileName LIKE 'filename%' AND DIRECTORY='file:C:\path\to\file'
The problem now is that I get an exception:
First-chance exception at 0x77c5fc56 in fraudTest.exe: Microsoft C++ exception: CNLBaseException at memory location 0x0012d6d0..
on this line
hr = con->Open( bconstr, NULL, NULL, 0 );
followed by the empty result from the query (this code is from WSQ_GetCDate):
rs->get_EOF( &eor );
while ( eor != VARIANT_TRUE ) { //this never executes }
Suprisingly SUCCEEDED(hr) returns true after the exception.
Where have I made en error and how to try and find it?
Thanks
t.g.wilk

I didn't solve this particular problem, but I learned that I don't need Windows Search to get the file metadata. The keyword to look for is "properties" instead of meta-data. I got my piece of code from Windows SDK v7.0 sample application named PropertyEdit.

Related

How to read field from setting/.cfg file in QuickFIX C++

I am writing quickfix call for NewOrderSingle/NewOrderCancelRequest/NewOrderStatusRequest.
Inorder to execute this request we need to fill one attribute that is AccountID.
As of now I am filling this attribute in each request individually like this-
So in each request i need to fill it. So i added this Account varibale in .cfg/settings file.
void Application::newOrderSingle44()
{
FIX::Account account("shad_tmp);
FIX44::NewOrderSingle newOrderSingle(
queryClOrdID(), querySide() ,
FIX::TransactTime(),queryOrdType());
newOrderSingle.set( FIX::HandlInst('1') );
newOrderSingle.set( querySymbol() );
newOrderSingle.set( queryOrderQty() );
newOrderSingle.set( queryTimeInForce() );
newOrderSingle.set(account);
queryHeader( newOrderSingle.getHeader() );
if ( queryConfirm( "Send order" ) )
FIX::Session::sendToTarget( newOrderSingle );
}
[SESSION]
BeginString=FIX.4.4
SenderCompID=TD_shad_dev_fix
TargetCompID=SHADSERVER_FIX
SocketConnectPort=10011
SocketConnectHost=10.250.2.8
FileStorePath=store
FileLogPath=log
ConnectionType=initiator
ResetOnLogon=N
ResetOnLogout=N
ResetOnDisconnect=N
ResetSeqNumFlag=N
DataDictionary=/mnt/d/quickfix/spec/FIX44.xml
Account=shad_tem
This is my client code to read from the settings file.
FIX::Initiator * initiator = 0;
try
{
FIX::SessionSettings settings( file );
const FIX::Dictionary& dict = settings.get();
std::string Account;
for (auto it = dict.begin(); it!=dict.end(); it++)
{
if(it->first.find("Account"))
Account = it->second;
}
std::cout<<"Account ID----------->"<<Account<<std::endl;
Application application;
FIX::FileStoreFactory storeFactory( settings );
FIX::ScreenLogFactory logFactory( settings );
if (isSSL.compare("SSL") == 0)
initiator = new FIX::ThreadedSSLSocketInitiator( application, storeFactory, settings, logFactory );
else
initiator = new FIX::SocketInitiator( application, storeFactory, settings, logFactory );
initiator->start();
application.run();
initiator->stop();
delete initiator;
return 0;
}
But this is not working. Can any one please let me know how to read this Account value from setting file??
Let me know is there any other to read this Account value from setting file and save it in any varibale and use it in any requrest . I mean wherever required.

Generating a unique 8-character hex name from input binary?

in Source multiplayer games, with settings that allow saving sprays, you can automatically save images that other users post in-game just by seeing them. They save with names like this:
18ce2add.vtf
ce77eb5b.vtf
7dbd03ec.vtf
a24cb0d3.vtf
When looking through the SDK code, I found this part which I think is part of the function that creates the names for each image.
https://github.com/ValveSoftware/source-sdk-2013/blob/master/mp/src/game/client/c_te_playerdecal.cpp#L168
IMaterial *CreateTempMaterialForPlayerLogo( int iPlayerIndex, player_info_t *info, char *texname, int nchars )
{
// Doesn't have a logo?
if ( !info->customFiles[0] )
return NULL;
IMaterial *logo = materials->FindMaterial( VarArgs("decals/playerlogo%2.2d", iPlayerIndex), TEXTURE_GROUP_DECAL );
if ( IsErrorMaterial( logo ) )
return NULL;
char logohex[ 16 ];
Q_binarytohex( (byte *)&info->customFiles[0], sizeof( info->customFiles[0] ), logohex, sizeof( logohex ) );
// See if logo has been downloaded.
Q_snprintf( texname, nchars, "temp/%s", logohex );
char fulltexname[ 512 ];
Q_snprintf( fulltexname, sizeof( fulltexname ), "materials/temp/%s.vtf", logohex );
if ( !filesystem->FileExists( fulltexname ) )
{
char custname[ 512 ];
Q_snprintf( custname, sizeof( custname ), "download/user_custom/%c%c/%s.dat", logohex[0], logohex[1], logohex );
// it may have been downloaded but not copied under materials folder
if ( !filesystem->FileExists( custname ) )
return NULL; // not downloaded yet
// copy from download folder to materials/temp folder
// this is done since material system can access only materials/*.vtf files
if ( !engine->CopyLocalFile( custname, fulltexname) )
return NULL;
}
return logo;
}
What I can't figure out is the format the binary is recieved in (array?) exactly why the name is limited to just 8 characters, and how it decides to use the hex to create the filename. If there is any code that does something like this, I'd be happy to see it. I started learning C++ to try and solve this exact problem.

Starting Microsoft Edge from MFC with web file paramater

If I open up a console prompt I can type this command:
start msedge "d:\HTML\Verticle Alignment.HTM"
It starts Microsoft Edge and opens the web page.
So I tried to do this programmatically in a test program using MFC:
void CMFCApplication8Dlg::OnBnClickedButton1()
{
ExecuteProgram(_T("start"), _T("msedge d:\\HTML\\Verticle Alignment.HTM"));
}
BOOL CMFCApplication8Dlg::ExecuteProgram(CString strProgram, CString strArguments)
{
SHELLEXECUTEINFO se = { 0 };
MSG sMessage;
DWORD dwResult;
se.cbSize = sizeof(se);
se.lpFile = strProgram;
se.lpParameters = strArguments;
se.nShow = SW_SHOWDEFAULT;
se.fMask = SEE_MASK_NOCLOSEPROCESS;
ShellExecuteEx(&se);
if (se.hProcess != nullptr)
{
do
{
dwResult = ::MsgWaitForMultipleObjects(1, &(se.hProcess), FALSE,
INFINITE, QS_ALLINPUT);
if (dwResult != WAIT_OBJECT_0)
{
while (PeekMessage(&sMessage, nullptr, NULL, NULL, PM_REMOVE))
{
TranslateMessage(&sMessage);
DispatchMessage(&sMessage);
}
}
} while ((dwResult != WAIT_OBJECT_0) && (dwResult != WAIT_FAILED));
CloseHandle(se.hProcess);
}
if ((DWORD_PTR)(se.hInstApp) < 33)
{
// Throw error
AfxThrowUserException();
return FALSE;
}
return TRUE;
}
But when I run it I get this error message:
So how can I launch my file in Microsoft Edge? I am using the latest Windows 10 so it is Microsoft Edge Chromium.
I have seen other questions which refer to making Edge the default browser and then just "opening" the data file and it working it all out but this is not OK in this case. In my editor I have a menu flyout which lists all the installed browsers (excluding Edge for now). But i want to add Edge so need to be able to programatically start it with my file to view.
Based on the comments provided to me, and to factor in for spaces in the file name, this works:
ExecuteProgram(_T("msedge"), _T("\"d:/HTML/Verticle Alignment.HTM\""));
The program to execute needs to be msedge and not start.
The parameter needs to be wrapped in " quotes.

IDirectoryObject::SetObjectAttributes returns no error, but no attributes is updated

I'm trying to update the attribute of a user in Active Directory. I've logged in as Domain Admins and executed the following code locally:
LPCWSTR pwszADsPath = L"LDAP://CN=MyUserName,CN=Users,DC=XXX,DC=YYY,DC=com";
// Bind to the root of the current domain.
hr = ADsOpenObject(pwszADsPath,
NULL,
NULL,
ADS_SECURE_AUTHENTICATION,
IID_IDirectorySearch,
(void**)&pDirObject);
// Bind to the object
if (SUCCEEDED(hr))
{
DWORD dwReturn = 0;
ADSVALUE givenName;
ADSVALUE phoneValue[2];
ADS_ATTR_INFO attrInfo[] = {
{L"givenName", ADS_ATTR_UPDATE, ADSTYPE_CASE_IGNORE_STRING, &givenName, 1},
{L"otherTelephone", ADS_ATTR_UPDATE, ADSTYPE_CASE_IGNORE_STRING, phoneValue, 2}
};
givenName.dwType = ADSTYPE_CASE_IGNORE_STRING;
givenName.CaseIgnoreString = L"Janet";
phoneValue[0].dwType = ADSTYPE_CASE_IGNORE_STRING;
phoneValue[0].CaseIgnoreString = L"425 844 1234";
phoneValue[1].dwType = ADSTYPE_CASE_IGNORE_STRING;
phoneValue[1].CaseIgnoreString = L"425 924 4321";
hr = pDirObject->SetObjectAttributes(attrInfo, sizeof(attrInfo) / sizeof(ADS_ATTR_INFO), &dwReturn);
if (SUCCEEDED(hr))
{
wprintf(L"%d Attributes updated\n", dwReturn);
}
else
{
wprintf(L"SetObjectAttributes failed. hr=0x%x\n", hr);
}
pDirObject->Release();
}
When I run the program, I'm always getting 0 Attributes updated. And I confirm in AD Users And Computers, the givenName is not updated.
The pwszADsPath is correctly copied from ADSI edit, and I have write privileges, what did I miss?
OK, So I tried compiling a part of your code and got the following error (MSVC, in Visual Studio 2019):
ADS_ATTR_INFO attrInfo[] = {
{L"givenName", ADS_ATTR_UPDATE, ADSTYPE_CASE_IGNORE_STRING, &givenName, 1},
};
^^^ error C2440: 'initializing': cannot convert from 'const wchar_t [15]' to 'LPWSTR'
Maybe your compiler version is different, and allows this implicit conversion, but that would seem to be what's upsetting the later call.
Try this, instead:
wchar_t gnString[32] = L"givenName";
ADS_ATTR_INFO attrInfo[] = {
{gnString, ADS_ATTR_UPDATE, ADSTYPE_CASE_IGNORE_STRING, &givenName, 1},
};
You'll probably have similar issues in your initialisation of the ADSVALUE structure(s)!
Change:
givenName.dwType = ADSTYPE_CASE_IGNORE_STRING;
givenName.CaseIgnoreString = L"Janet";
To:
wchar_t forename[16] = L"Janet";
givenName.dwType = ADSTYPE_CASE_IGNORE_STRING;
givenName.CaseIgnoreString = foreName;

ZeroMemory on PROCESSENTRY32 local variable?

I needed to enumerate running processes and wondered for a while why my code wasn't working:
PROCESSENTRY32 ProcEntry;
ZeroMemory (&ProcEntry, sizeof (PROCESSENTRY32)); //problem
ProcEntry.dwFlags = sizeof(PROCESSENTRY32);
HANDLE Snapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, NULL);
if (Snapshot == INVALID_HANDLE_VALUE)
return false;
if (Process32First(Snapshot, &ProcEntry))
....
Problem was that Process32First always returned FALSE because of ERROR_BAD_LENGTH error.
Once I removed ZeroMemory line, everything started working fine. So the question is, why ZeroMemory caused it? It should just fill memory at the address of X for Z bytes. I use it a lot for winapi pointer-like structures, this time I didnt realise its a local variable but that doesn't explain the problem or does it?
Thanks,
Kra
EDIT: plus I found out code works fine only in Debug version, once I compile it as Release version, its bugged again :/
You should set dwSize, not dwFlags.
ProcEntry.dwFlags = sizeof(PROCESSENTRY32);
should be
ProcEntry.dwSize = sizeof(PROCESSENTRY32);
You cannot zero out the entire PROCESSENTRY32 structure as it is self-describing - you have to set dwSize. From the sample here:
HANDLE hProcessSnap;
HANDLE hProcess;
PROCESSENTRY32 pe32;
DWORD dwPriorityClass;
// Take a snapshot of all processes in the system.
hProcessSnap = CreateToolhelp32Snapshot( TH32CS_SNAPPROCESS, 0 );
if( hProcessSnap == INVALID_HANDLE_VALUE )
{
printError( TEXT("CreateToolhelp32Snapshot (of processes)") );
return( FALSE );
}
// Set the size of the structure before using it.
pe32.dwSize = sizeof( PROCESSENTRY32 );
// Retrieve information about the first process,
// and exit if unsuccessful
if( !Process32First( hProcessSnap, &pe32 ) )
{
printError( TEXT("Process32First") ); // show cause of failure
CloseHandle( hProcessSnap ); // clean the snapshot object
return( FALSE );
}

Resources