Read 'Extended' file properties (C++) - c++

Is there a way to get all the properties and values of any file under the details tab using only c++ Code?
All extended file properties: link
I have seen solutions for c# but not c++. link
I have already looked into the fileapi.h function GetFileAttributesA() which gave me access to the file attribute constants like FILE_ATTRIBUTE_COMPRESSED, FILE_ATTRIBUTE_READONLY...
Even the GetFileAttributesExA() with GetFileExMaxInfoLevel was not able to return all the needed information.
if (FileAttributes & FILE_ATTRIBUTE_COMPRESSED) {
std::cout << "File is compressed.";
}
if (FileAttributes & FILE_ATTRIBUTE_READONLY) {
std::cout << "File is a readonly file.";
}
I thought there should be a similar thing for file properties something like GetFilePropertiesExA().
But could not find any similar function so far.
Also I was able to get information like Date created, modified and size using WIN32_FIND_DATA.

Here is some sample code from Microsoft for reading/writing file properties. It's using the WinAPI to read the file properties.
You can find a list of available properties here.
Depending on what you want to do, you may also take a look at these PROPVARIANT functions. For example, when you want to store the value of a property into a string.

Related

How to create a property store binary file

I'm trying to implement an icon-based property in Windows File Explorer, and my understanding from this post is that it requires returning a property store binary file from the property handler. Does anyone know how to create a property store binary file? After searching, I've come across some documentation on the specification, but I don't see any examples of how to create one. Thank you very much.
You don't need any binary file, you just need an implementation of IPropertyStore. You can create one using the PSCreateMemoryPropertyStore method.
IPropertyStore *ps;
if (SUCCEEDED(PSCreateMemoryPropertyStore(IID_PPV_ARGS(&ps))))
{
// do your work
ps->Release();
}

Boost Log: Interaction of log settings file and code configuration

I have a nice format and a console log:
auto fmtStream = expressions::stream
<< "LineID: " << expressions::attr<unsigned int>("LineID") << " "
<< "Message: " << expressions::message:
boost::log::add_console_log(std::cout, keywords::format = fmtStream);
The format stream is of course somewhat longer..
Now I want to give a user to configure the logging:
std::ifstream file("log.conf");
init_from_stream(file);
A lot of formatting used in fmtStream is not possible using the format string in the config file.
How can I give the user the possibility to modify the console sink, e.g. add a filter? But I want to keep the format string as a default.
Possibilities I see for that:
1) Give the console log that I define in my code a name. The user could now modify it with a sink of the same name.
2) Set a default format that is taken for all sinks. But according to Boost Log changing the default logging::core formatter? this is not possible.
3) Any other ideas?
Thanks!
init_from_stream and init_from_settings functions will initialize the library as specified in the settings. These functions are designed to configure the library from scratch, so they will add new sinks with the specified settings, including filters and formatters. If all you need is to customize formatter for your existing sink and not allow full logging configuration then you should interpret the settings file yourself.
You can parse the settings file with the parse_settings function. From it you will receive a settings (or wsettings) object which you can analyze and modify as described here and here (sorry for the bad formatting in the reference docs). Since you're probably not planning to support all sinks and parameters supported by Boost.Log, you are not bound to the semantics Boost.Log puts in the parameters and can interpret the settings in any way you want. For instance, you may choose to only read the sink formatter:
boost::log::settings setts = boost::log::parse_settings(file);
if (boost::optional<std::string> fmt = setts["MySink"]["Format"])
{
// Sink format is specified in the setting file
}
Now, to turn this format string into a formatter, you will need the parse_formatter function (the string format is described here). This function returns a formatter object that you can install into your sink, provided that you saved a pointer to it.
auto sink = boost::log::add_console_log(std::cout, keywords::format = fmtStream);
boost::log::settings setts = boost::log::parse_settings(file);
if (boost::optional<std::string> fmt = setts["MySink"]["Format"])
{
sink->set_formatter(boost::log::parse_formatter(fmt.get()));
}
There's one more thing to remember though. If you're using attribute values of custom types in your formatter, like enums for severity, for example, you will have to register those types in the library before parsing the formatter. This way the parser will be able to create a formatter that knows your types and uses appropriate formatting operators. There's a tutorial describing how to do that.

Write a Method like Vector.at()

I would like to know how is that the .at() method works, like the one in the Vector class of the C++, a method that both ways returns and/or assign a value to the member of the array.
i don't know if with a macro i can do it, or declaring 2 method with same name... any help?
i have been trying to find and open the file of vector, to see how it was written, that specific method, but i have not found it.
(its for a different structure i am building, but i would like to access to them with only one method)
Example of what i mean.
vec.at(x) = value;
newValue = vec.at(x);
You have to return a reference to the value. So instead of
int at(int idx)
You just do
int& at(int idx)
References are very similar to pointers with the difference that you cannot and dont have to dereference them in order to manipulate the value they are referencing
There should be an easy way to open any source file within the IDE. I use this feature all of the time to review the Standard Library, especially when wanting to review the signature of the member functions of std::vector, std::map, std::list, etc., without having to open a browser window.
Hopefully the IDE used has something similar to "open file at cursor". Since the IDE is not known by the question content, a more generic procedure is presented.
1) Make sure the relevant header name is in a #include line. For example:
#include <vector>
2) Move the editor cursor so that the cursor is clearly shown under a section of the header file name (e.g. vector).
3) Within the IDE run the "open file at cursor" command.
4) The IDE should now show the file highlighted by the cursor in its own window / tab, and there is no need to manually navigate to the area where all header files are located on the development system.
5) Search the newly opened file as desired.

Instantiating a Qt File-Based Logger for Debugging in a C++ Library

The following page provides a nice simple solution for file based logging in Qt for debugging without using a larger logging framework like the many that are suggested in other SO questions.
I'm writing a library and would like to instantiate a logger that the classes in the library can use (mostly for debugging purposes). There is no int main() function since it's a library. So would the best approach be to add the instantiation into a file like logger.h and have any classes include logger.h if it would like to do qDebug() << PREFIX << "Bla" as the link above suggests?
I pretty much agree with OrcunC but I'd recommend making that ofstream a little more accessible and capable of handling the Qt value types.
Here's my recommended process:
Create a global QIODevice that to which everything will be written. This will probably be a QFile.
Create a QTextStream wrapper around that QIODevice that you'll then use for all the logging.
If you want something slightly more complicated, create methods that do the filtering based on log level info.
For example:
// setup the global logger somewhere appropriate
QFile *file = new QFile("your.log");
file->open(QIODevice::ReadOnly);
QTextStream *qlogger = new QTextStream(file);
And once the global logger is initialized, you could reference it as a global:
#include "qlogger.h"
//... and within some method
*qlogger << "your log" << aQtValueType;
But you might want some filtering:
#include "qlogger.h"
// lower number = higher priority
void setCurrentLogLevel(int level) {
globalLogLevel = level;
}
QTextStream* qLog(int level) {
if (level <= globalLogLevel) {
return qlogger;
}
return getNullLogger(); // implementation left to reader
}
And then you'd likely create an enum that represented the LogLevel and do something like this:
#include "qlogger.h"
//...
setCurrentLogLevel(LogLevel::Warning);
*qLog(LogLevel::Debug) << "this will be filtered" << yourQMap;
*qLog(LogLevel::Critical) << "not filtered" << yourQString;
As you'd be dealing with globals, carefully consider memory management issues.
If you follow the method in that link, ALL messages of the application output with qCritical(), qDebug(), qFatal() and qWarning() will flow into your handler.
So be careful! You may get not only your library's trace messages but the entire QT framework's messages. I guess this is not what you really want.
Instead of this
as a simple solution define a global *ofstream* in your library and use it only within your library.
whenever you write a library in c++ or c , it is best practice to declare all your methods in a .h file and define the methods/classes in a .cpp/.c file. This serves 2 purposes.
The .h file needs to be used to compile a 3rd party application that is using your library, and the library itself is used at link time.
The developer who is using your library can use the .h file as a reference to your library since it contains all the declarations.
So ,yes , you need to declare methods in a .h file and have other classes include logger.h.

Create registry entry to associate file extension with application in C++

I would like to know the cleanest way of registering a file extension with my C++ application so that when a data file associated with my program is double clicked, the application is opened and the filename is passed as a parameter to the application.
Currently, I do this through my wix installer, but there are some instances where the application will not be installed on ths user's computer, so I also need the option of creating the registry key through the application.
Additionally, will this also mean that if the application is removed, unused entries in the registry will be left lying around?
Your basic overview of the process is found in this MSDN article. The key parts are at the bottom of the list:
Register the ProgID
A ProgID (essentially, the file type registry key) is what contains your important file type properties, such as icon, description, and context menu items including applications used when the file is double clicked. Many extensions may have the same file type. That mapping is done in the next step:
Register the file name extension for the file type
Here, you set a registry value for your extension, setting that extension's file type to the ProgID you created in the previous step.
The minimum amount of work required to get a file to open with your application is setting/creating two registry keys. In this example .reg file, I create a file type (blergcorp.blergapp.v1) and associate a file extension (.blerg) with it.
Windows Registry Editor Version 5.00
[HKEY_CURRENT_USER\Software\Classes\blergcorp.blergapp.v1\shell\open\command]
#="c:\path\to\app.exe \"%1\""
[HKEY_CURRENT_USER\Software\Classes\.blerg]
#="blergcorp.blergapp.v1"
Now, you probably want to accomplish this programmatically. To be absolutely kosher, you could check for the existence of these keys, and change your program behavior accordingly, especially if you're assuming control of some common file extension. However, the goal can be accomplished by setting those two keys using the SetValue function.
I'm not positive of the exact C++ syntax, but in C# the syntax looks something like this:
Registry.SetValue(#"HKEY_CURRENT_USER\Software\Classes\blergcorp.blergapp.v1\shell\open\command", null, #"c:\path\to\app.exe \"%1\"");
Registry.SetValue(#"HKEY_CURRENT_USER\Software\Classes\.blerg", null, "blergcorp.blergapp.v1");
Of course you could manually open each sub key, manually create the ProgID and extension subkey, and then set the key value, but a nice thing about the SetValue function is that if the keys or values don't exist, they will automatically be created. Very handy.
Now, a quick word about which hive to use. Many file association examples online, including ones on MSDN, show these keys being set in HKEY_CLASSES_ROOT. I don't recommend doing this. That hive is a merged, virtual view of HKEY_LOCAL_MACHINE\Software\Classes (the system defaults) and HKEY_CURRENT_USER\Software\Classes (the per-user settings), and writes to any subkey in the hive are redirected to the same key in HKEY_LOCAL_MACHINE\Software\Classes. Now, there's no direct problem doing this, but you may run into this issue: If you write to HKCR (redirected to HKLM), and the user has specified the same keys with different values in HKCU, the HKCU values will take precedence. Therefore, your writes will succeed but you won't see any change, because HKEY_CURRENT_USER settings take precedence over HKEY_LOCAL_MACHINE settings.
Therefore, you should take this into consideration when designing your application. Now, on the flip side, you can write to only HKEY_CURRENT_USER, as my examples here show. However, that file association setting will only be loaded for the current user, and if your application has been installed for all users, your application won't launch when that other user opens the file in Windows.
That should be a decent primer for what you want to do. For further reading I suggest
Best Practices for File Association
File Types and File Association, especially
How File Associations Work
And see also my similar answer to a similar question:
Associating file extensions with a program
This is a two step process:
1. Define a program that would take care of extension: (unless you want to use existing one)
1.1 create a key in "HKCU\\Software\\Classes\\" for example
"Software\\Classes\\YourProgramName.file.ext"
1.2 create subkey "Software\\Classes\\YourProgramName.file.ext\\DefaultIcon"
1.2.1 set default value ("") to your application full path to get
icon from resources
1.3 create a subkey "Software\\Classes\\YourProgramName.file.ext\\Shell\\OperationName\\Command"
OperationName = for example Open, Print or Other
1.3.1 set default value ("") to your application full path +optional runtime params (filename)
2.Associate file extension with program.
2.1 create a key HKCU\\Software\\Classes\\.ext - here goes your extension
2.2 set default value to the program definition key
("YourProgramName.file.ext")
Below is part of the program written in c# which associate file extension. It is not c++ but i think it is simple enought to explain itself and AFAIK it is verv simmilar if not identical to the code in c++
1.
RegistryKey keyPFCTExt0 = Registry.CurrentUser.OpenSubKey("Software\\Classes\\PFCT.file.enc", true);
if (keyPFCTExt0 == null)
{
keyPFCTExt0 = Registry.CurrentUser.CreateSubKey("Software\\Classes\\PFCT.file.enc");
keyPFCTExt0.CreateSubKey("DefaultIcon");
RegistryKey keyPFCTExt0ext = Registry.CurrentUser.OpenSubKey("Software\\Classes\\PFCT.file.enc\\DefaultIcon", true);
keyPFCTExt0ext.SetValue("", Application.ExecutablePath +",0");
keyPFCTExt0ext.Close();
keyPFCTExt0.CreateSubKey("Shell\\PFCT_Decrypt\\Command");
}
keyPFCTExt0.SetValue("", "PFCT.file.enc");
keyPFCTExt0.Close();
2.
RegistryKey keyPFCTExt1 = Registry.CurrentUser.OpenSubKey("Software\\Classes\\PFCT.file.enc\\Shell\\PFCT_Decrypt\\Command", true);
if (keyPFCTExt1 == null)
keyPFCTExt1 = Registry.CurrentUser.CreateSubKey("Software\\Classes\\PFCT.file.enc\\Shell\\PFCT_Decrypt\\Command");
keyPFCTExt1.SetValue("", Application.ExecutablePath + " !d %1"); //!d %1 are optional params, here !d string and full file path
keyPFCTExt1.Close();
I don't know why people keep saying that HKEY_CURRENT_USER\Software\Classes\<.ext>'s Default value (which will redirect you into another (software-created) class.
It does work, but it will be overridden by
HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Explorer\FileExts\<.ext>\UserChoice
And I believe Microsoft recommends the second practice- because it's what the built-in "open with" is doing. The value of Progid" key is equal to default value of HKEY_CURRENT_USER\Software\Classes\<.ext> in this case.
I found the following while trying to manipulate associations using C#:
hkcu\software\microsoft\windows\currentVersion\explorer\fileexts.reg\userchoice -> for user specific settings. The values in the openWithProgIds
key point to the keys in the hkcr.
hkcr\xfile\shell\open\muiVerb value or hkcr\xfile\shell\open\command\default value -> affects open handler. This is the value that contains the path to a program.
hkcr\ .x -> affects context menu (new x) among other things related to the menus.
I don't know the C++ code, but given these info you must be able to manipulate the registry using the registry API.