Saturday, 11 February 2012

The greek account

Time for a bit more local politics:

This is going to be a very short post. There's no need to complicate on the damn subject.
I believe our fine (european) polititians totally screwed up on salvaging the Greek crisis - and they are still doing so.
The entire EU is generating over 12 trillion EUR GDP annually. Current Greek debt is around 580 billion. This sure is a lot of money. Nevertheless, to solve the Greek crysis a lot less cash is needed. The EU central bank could easily print the necessary cash before the thing got blown out of proportions. That together with no-further-loan policy would have cost us some single to low double digit percentage EUR devalvation and that would be it.
Instead we went and made a super drama out of the whole thing. The effect is that we already lost 20% of value against the U.S. dollar and there is still no sight of closure.

Not to mention that the americans are even more endebted, but currently they still manage to appear strong, especially through their lobbying (ACTA being a fine example of their ripping the rest of us idiots off). Looking at the situation it seems really hilarious. But I disgress. This is not a post on american debt issues.

The Greeks will have to learn that maintaining such high standards of living requires also generating the means for such standards. Currently many countries with a lot lower standards of living have to work harder to support their way of living and that is not right. I admire and commend the Slovaks for acknowledging the fact and refusing to support this.

Also French and German banks will have to swallow the bitter pill of knowledge that issuing bad loans can have negative effects on their business as well. I have no idea why supporting the very businesses that caused the current crysis (not talking just about Greece here) should be on top of our priority list right now. The EU as such should "nationalize" such idiot banks only to close them down gracefully and let the more responsible people / banks do business in their stead.

Total waste and utter incompetence. I don't see why they can't just do it already and be done with it.

Friday, 3 February 2012

Declaring and using C++ properties like in Delphi or C#

I find properties a marvelous concept. You gain so much more control over class members not to mention declaration of the stuff places everything in just a few lines of code. It is immediately visible just by looking at class declaration what the class offers you and what it doesn't.
Anybody who switched from Delphi to C++ will know what I'm talking about.

I have been looking for a way to support properties in C++ for a while now without much luck. Looking at the implementations found on the net, there are two types of implementation:
  1. Property class that stores getter / setter / lvalue pointers
  2. subclasses with pre-processor macro-style implementation
Each of the methods used has its merits and shortfalls. I personally don't like the first approach because it means that for each property declared you waste 3x4 bytes (32 bit) or even 3x8 bytes (64 bit). Plus there's the initialization code setting those pointers adding just about twice the memory consumption on top of the pointers allocated. And I don't like losing so much memory for something as simple as that.
The second approach is a lot more complex to nail down, but once you have the macros, usage is just as easy as the first approach and there is only one detail you need to watch out for: empty class members in C++ take 1 byte of storage (by standard) just for indexing purposes. You can gain this one byte back simply by storing the property l-value inside.

My original intent was to get this thing as far as Delphi's TObject was, but I fell short of implementing the storage / identification subsections. This way you can't do the following two things:
  1. myInstance.setProperty("propertyname", value)
  2. stream << myInstance, stream >> myInstance
So if you feel up to it, I'm all inbox for ya :) I will even explain how it can be done further down.

As explained I decided for the second approach with macros and subclasses because it is much more efficient memory usage-wize. Originally it was intended for there to be only one macro which would do it all, but it turns out C++ preprocessor is pretty dumb and doesn't really allow you to do such things. So I had to split the code in multiple macros, the differences in usage being explained around line 80. Please forgive me for the messed up code, this really wasn't easy to achieve.

At the end of the code there's also the TObject class declared showing most of the functionality off. You can use that class as an example for your usage or use it as the base class for your library. It's up to you.

As mentioned above, I failed to implement storage / load. Turns out my C++ still isn't up to par though this is the most complete property implementation I know of in the net. Only Qt library manages to surpass this implementation, but they have their own preprocessor. Anyway, this code will compile both on MS VC and gcc.

In order to implement that, a global std::map would ned to be declared into which properties would register themselves in their constructors. The key would be class + property and data would be pointers to getter, setter, default and store method pointers. Additionally, each property type would need conversion to string done in order to be able to store classes into XML or something. Having all this implemented, it is then trivial to write getProperty and setProperty functions as well as properly implement saveToXML / loadFromXML.

Anyway, I still feel this implementation of C++ property support is pretty awesome in comparison to other attempts and it should be posted to the almighty net for your pleasure under modified BSD license. The code compiles under MS VC++ and gcc. Have fun.


#include <string>
#include <sstream>
#include <iostream>
#include <typeinfo>
#include <list>

typedef std::wstring vString;

//Support macros begin
//getter macros
#define PROPERTY_GETTER(getter, prop_type, prop_my_struct) \
operator prop_type() { return getter; }                    \
friend std::wostream& operator << (std::wostream &out, prop_my_struct aValue) { out << (prop_type)aValue; return out; }

#define V_GETTER(varName)           getBase()->varName
#define F_GETTER(funcName)          getBase()->funcName()
#define S_GETTER(varName)           varName
#define T_GETTER(varName, funcName) funcName
#define N_GETTER 

//Setter macros
#define PROPERTY_SETTER(setter, prop_type, param_type, prop_my_struct, streamcode) \
prop_my_struct operator = (param_type aValue) { setter; return *this; }            \
friend std::wistream& operator >> (std::wistream &in, prop_my_struct aValue) { streamcode return in; }

#define V_SETTER(varName)  getBase()->varName = aValue
#define F_SETTER(funcName) getBase()->funcName(aValue)
#define S_SETTER(varName)  varName = aValue
#define T_SETTER(funcName) funcName(aValue)
#define N_SETTER 

//Default values
#define DEFAULT_EXPAND(prop_type, value) prop_type(value)
#define N_DEFAULT
//Internal storage (saves one byte for struct default allocation size)
//use N_STORE if you have external storage or no storage
#define STORAGE_EXPAND(prop_type, value) private: prop_type value; public:
#define N_STORE __intstore_func() {}
#define STORAGE_STD(type, defaultval, store)                                   \
bool isDefault() { return (type)(*this) == DEFAULT_EXPAND(type, defaultval); } \
bool stored() { return store; }
#define STORAGE_STDC(type, defaultval, store)      \
bool isDefault() { return (type)(*this) == NULL; } \
bool stored() { return store; }
#define STORAGE_STDS(type, defaultval, store) \
bool isDefault() { return value == NULL; }    \
bool stored() { return store; }

//Constructor / destructor
#define PROPERTY_CTOR(name, set_default) property_##name##_class() { getBase()->registerProperty(L#name); set_default}
#define PROPERTY_DTOR(name, del_obj) ~property_##name##_class() { del_obj }

#define PROPERTY_ALL(base, name, type, ctor, dtor, storage, getfunc, setfunc, defstore, addcode)  \
struct property_##name##_class                                                        \
{                                                                                     \
  ctor                                                                                \
  dtor                                                                                \
  storage                                                                             \
  getfunc                                                                             \
  setfunc                                                                             \
  base *getBase() { return ((base *) ((char *) this - offsetof( base, name ))); }     \
  defstore                                                                            \
  friend class base;                                                                  \
private:                                                                              \
  addcode                                                                             \
} name
//Support macros end

/*
  Note that I had to do some compromises because C++ preprocessor is so dumb
  I found it impossible to support various data types in one macro, so I had to use multiple
  The initial letter before PROPERTY designates a particular data type I supported with that macro
   PROPERTY - standard ordinal type property
  CPROPERTY - property that supports class data types. The macro will support -> operator which 
              accesses class members directly
  SPROPERTY - same as CPROPERTY, but manages allocation of the value (class instance)
  IPROPERTY - supports container types

  The letter trailing PROPERTY designates the use of getter / setter
  _PROPERTY  - property with both a getter and a setter
  _PROPERTYG - property with just a getter (read-only property)
  _PROPERTYS - property with just a setter (write-only property)
*/

//Standard basic type properties
#define PROPERTY(base, name, type, storage, getfunc, setfunc, defaultval, store, addcode)  \
PROPERTY_ALL(base, name, type,                                                             \
             PROPERTY_CTOR(name, ),                                                        \
             PROPERTY_DTOR(name, ),                                                        \
             STORAGE_EXPAND(type, storage),                                                \
             PROPERTY_GETTER(getfunc, type, property_##name##_class &),                    \
             PROPERTY_SETTER(setfunc, type, const type &, property_##name##_class &, in >> aValue;), \
             STORAGE_STD(type, defaultval, store), addcode)

#define PROPERTYG(base, name, type, storage, getfunc, addcode)                             \
PROPERTY_ALL(base, name, type,                                                             \
             PROPERTY_CTOR(name, ),                                                        \
             PROPERTY_DTOR(name, ),                                                        \
             STORAGE_EXPAND(type, storage),                                                \
             PROPERTY_GETTER(getfunc, type, property_##name##_class &),                    \
             ,, addcode)

#define PROPERTYS(base, name, type, storage, setfunc, addcode)                             \
PROPERTY_ALL(base, name, type,                                                             \
             PROPERTY_CTOR(name, ),                                                        \
             PROPERTY_DTOR(name, ),                                                        \
             STORAGE_EXPAND(type, storage),                                                \
             ,                                                                             \
             PROPERTY_SETTER(setfunc, type, const type &, property_##name##_class &, in >> aValue;), \
             , addcode)

//class / struct type properties (note that properties can only be pointers to actual l-values)
//!!!Note that below code assumes that CPROPERTY has storage :( Since I don't know better I may have to re-declare the below 3 macros such that I have one set for initialized storage and one for no storage
#define CPROPERTY(base, name, type, storage, getfunc, setfunc, store, addcode)                  \
PROPERTY_ALL(base, name, type *,                                                                \
             PROPERTY_CTOR(name, storage = NULL;),                                              \
             PROPERTY_DTOR(name, ),                                                             \
             STORAGE_EXPAND(type *, storage),                                                   \
             PROPERTY_GETTER(getfunc, type *, property_##name##_class)                          \
             type * operator ->() { return (type *)(*this); },                                  \
             PROPERTY_SETTER(setfunc, type *, type *, property_##name##_class, in >> aValue;),  \
             STORAGE_STDC(type *, NULL, store), addcode)

#define CPROPERTYG(base, name, type, storage, getfunc, addcode)                                \
PROPERTY_ALL(base, name, type *,                                                               \
             PROPERTY_CTOR(name, ),                                                            \
             PROPERTY_DTOR(name, ),                                                            \
             STORAGE_EXPAND(type *, storage),                                                  \
             PROPERTY_GETTER(getfunc, type *, property_##name##_class)                         \
             type * operator ->() { return (type *)(*this); },                                 \
             ,, addcode)

#define CPROPERTYS(base, name, type, storage, setfunc, addcode)                                \
PROPERTY_ALL(base, name, type *,                                                               \
             PROPERTY_CTOR(name, ),                                                            \
             PROPERTY_DTOR(name, ),                                                            \
             STORAGE_EXPAND(type *, storage),                                                  \
             ,                                                                                 \
             PROPERTY_SETTER(setfunc, type *, type *, property_##name##_class, in >> aValue;),, addcode)

#define SGET_FUNC(type) *(value == NULL?value = new type():value)
#define SSET_FUNC(type) if (value == NULL) value = new type(aValue); else *value = aValue
#define SPROPERTY(base, name, type, store, addcode)                          \
PROPERTY_ALL(base, name, type *,                                             \
             PROPERTY_CTOR(name, value = NULL;),                             \
             PROPERTY_DTOR(name, if (value != NULL) delete value;),          \
             STORAGE_EXPAND(type *, value),                                  \
             PROPERTY_GETTER(SGET_FUNC(type), type, property_##name##_class &) \
             type operator ->() { return (type)(*this); },                   \
             PROPERTY_SETTER(SSET_FUNC(type), type, type, property_##name##_class &, in >> aValue;), \
             STORAGE_STDS(type, NULL, store), addcode)


//indexed properties that would map to a get() or set() function (but not both) are impossible
//  since both [] and () operators can only be overloaded such that they return reference to the element in question
//  this would mean you actually have to have data behind the get() / set() function, which is not the purpose 
//  of such properties thus I decided not to support indexed properties! :(
//  instead I support:
//Container (array) properties
//  These are simplified class properties that store the container pointer. The pointer is allocated only upon access
//  Read only access is supported (can't assign a different instance to the property)
//  To avoid unnecessary allocation, a bool empty() method is provided which returns whether the object is instantiated
//  This is done so because std::map takes 28 bytes and std::list takes 24 bytes in 32-bit MSVC
//  I prefer 4 (32 bit) / 8 (64 bit) bytes of fixed storage per class instance
//  These properties are always read/write though meaning you can access ALL container methods / operators once instantiated. Sorry :)
//  In order to be able to load / store these properties (main intent for their support), you must supply the following:
//    begin() and end() iterator methods as implemented in std:: container classes
//    << and >> operator overloads for the container types
#define AGET_FUNC(type) (value == NULL?value = new type():value)
#define ASET_FUNC(type) if (value == NULL) value = new type(); value = aValue
#define ASTORAGE(store)            \
bool isDefault() { return false; } \
bool stored() { return store; }

#define IPROPERTY(base, name, type, store, addcode)                                               \
PROPERTY_ALL(base, name, type *,                                                                  \
             PROPERTY_CTOR(name, value = NULL;),                                                  \
             PROPERTY_DTOR(name, if (value != NULL) delete value; ),                              \
             STORAGE_EXPAND(type *, value),                                                       \
             PROPERTY_GETTER(AGET_FUNC(type), type *, property_##name##_class)                    \
             type * operator ->() { return (type *)(*this); },                                    \
             PROPERTY_SETTER(ASET_FUNC(type), type *, type *, property_##name##_class, in >> aValue;),  \
             ASTORAGE(store), addcode)

class Object
{
public:
  Object() {}
  Object(const Object &aValue) {}
  virtual ~Object() { parent = NULL; } 

  virtual Object *create() const = 0; //"virtual default constructor"
  Object *create(Object *aParent) { Object *res = create(); res->parent = aParent; return res; } 
  virtual Object *copy() { Object *obj = create(); obj->copyMembers(); return obj; } //"virtual" copy constructor
  virtual void copyMembers() {}

   PROPERTYG(Object, className, vString, N_STORE, F_GETTER(getClassName),);
  SPROPERTY (Object, name,      vString, true, );
  CPROPERTY (Object, parent,    Object,  mParent, S_GETTER(mParent), F_SETTER(setParent), false,);
  IPROPERTY (Object, children,  std::list<Object *>, true,);
   PROPERTY (Object, tag,       int, value, S_GETTER(value),   S_SETTER(value), 0, true, ); //for some reason MSVC 2008 increases the object by 20 bytes if I use an int64 here :(

protected:
  vString getClassName() 
  {
    std::string s(typeid(*this).name());
    return vString(s.begin(), s.end()); 
  }

  void children_append(const Object *aParent);

  void setParent(Object *aParent)
  {
    //First remove any existing parent
    if (parent != NULL)
      parent->children->remove(this);
    parent.mParent = aParent;
    //Add me to the new parent's children list
    if (aParent != NULL)
      parent->children->push_back(this);
  }

  static void registerProperty(vString name) {} //TODO: Do the code, man
  static void registerClass() {} //TODO: Do the code, man

  vString getProperty(vString aPropertyName) {} //TODO: Do the code, man
  void setProperty(vString aPropertyName) {} //TODO: Do the code, man

  friend std::wostream& operator << (std::wostream &out, Object &aValue) { /*out << aValue;*/ return out; }
  friend std::wistream& operator >> (std::wistream &in,  Object &aValue) {  }
};

Thursday, 2 February 2012

Enumerating and using partitions and volumes in Windows operating system

One of the things I wanted to do for my latest project was to be able to display disks and partition layouts on them to the user. Of course the whole thing must allow the software to later work on those partitions as well.

It turns out that Windows API isn't exactly programmer friendly when it comes to working with physical disks and their partitions. Actually, the API provided is a mess: There are plenty of functions with seemingly the same functionality, but ultimately all the programmer gets with them is plenty of confusion, not to mention finding any concise info on the stuff is one of the hardest things I ever had to go find as a programmer. Luckily for me (and possibly you since you're reading this) I've gotten a bit better with google lately so I was able to find some information that ultimately helped me achieve my goal. So thanks to all the anonymous forum / social / whatever site users that posted various bits of info on the functions.

I should note first that I wasn't using Windows Management Instrumentation (WMI) functions to get here. I think I never got to really explore what those functions do since I was so close to my desired result using plain Windows API all the time. So this solution uses Volume Management functions and subfunctions of the DeviceIoControl function.

The code that utilizes the functions and prints out some partition information follows later, but first I'd like to explain a few things about the various function groups since explanation of the terms is also not quite readily available.

In the Volume Management functions there are actually two groups of functions, each providing access to each own set of data.
The first group is volume enumeration and information group. You have FindFirstVolume, FindNextVolume, FindVolumeClose and GetVolumeInformation functions in this group among others. These functions let you enumerate end obtain information about volumes that Windows OS recognizes. Volumes are logical "drives" that Windows can use for its file system operations. Normally you will see a volume as a drive letter in the windows explorer, but a volume could also be mounted as a folder in another drive or not mounted at all. Note that volumes do not correspond to actual partitions on physical drives. Any partition that windows does not recognize will not be listed in volumes. That's where brute force approach described below helps.
The second group allows you to examine info on volume mount points. You will find functions such as FindFirstVolumeMountPoint, FindNextVolumeMountPoint, FindVolumeMountPointClose and GetVolumePathNamesForVolumeName. These functions let you find out information about drive letters or paths to which a particular volume is mounted. Note that some of these functions require administrative privilege to work. See code below for more info.
Then there are various DeviceIoControl functions which allow one to query the drivers directly about what's available. Fortunately, Windows sorts available storage devices in a nice list that makes the enumeration process a bit easier, though a nice set of Find_X_Drive / Find_X_Partition functions would most certainly help a lot here. Blabbing about the particular functions would help noone here so I suppose you'll just have to get the info on them from code below and with a little searching through MSDN. Let's just say that \\.\PhysicalDrive1\Partition1 is a relatively neat way of accessing each and every partition on your storage devices. See the code below how you can turn this into a bit more information about the particular partition.

The main problem with all these functions is finding the linking point among them. How to get from \\.\PhysicalDrive1\Partition1 (which is nigh but unusable) to \\?\Volume{18e59d60-e102-11e0-a667-806e6f6e6963}\ (which is the way to use the partition and get data on it) - that is what is achieved and described in the attached code.

I must apologize for not cleaning up the code properly, but I hope it should be sufficient to see how one can get the information required. If you still don't get it, feel free to post me a mail of a comment to this article. The license, as usual, is Modified BSD.

#include "windows.h"
#include <stdio.h>
#include <iostream>

using namespace std;

void volumeInfo(WCHAR *volName)
{
  {
    //First some basic volume info
    WCHAR volumeName[MAX_PATH + 1] = { 0 };
    WCHAR fileSystemName[MAX_PATH + 1] = { 0 };
    DWORD serialNumber = 0;
    DWORD maxComponentLen = 0;
    DWORD fileSystemFlags = 0;
    if (GetVolumeInformation(volName, volumeName, ARRAYSIZE(volumeName), &serialNumber, &maxComponentLen, &fileSystemFlags, fileSystemName, ARRAYSIZE(fileSystemName)))
    {
      wprintf(L"Label: [%s]  ", volumeName);
      wprintf(L"SerNo: %lu  ", serialNumber);
      wprintf(L"FS: [%s]\n", fileSystemName);
  //    wprintf(L"Max Component Length: %lu\n", maxComponentLen);
    }
    else
    {
      TCHAR msg[MAX_PATH + 1];
      FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, NULL, GetLastError(), MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), msg, MAX_PATH, NULL);
      wprintf(L"Last error: %s", msg);
    }
  }
  {
    //The following code finds all folders that are mount points on this volume (empty folder that has another volume mounted-in)
    //This requires administrative privileges so unless you run the app as an admin, the function will simply return nothing
    //It's pretty much useless anyway because the same info can be obtained in the following section where we get mount points for a volume - so reverse lookup is quite possible
    HANDLE mp;
    WCHAR volumeName[MAX_PATH + 1] = { 0 };
    bool success;
    mp = FindFirstVolumeMountPoint(volName, volumeName, MAX_PATH);
    success = mp != INVALID_HANDLE_VALUE;
    if (!success)
    { //This will yield "Access denied" unless we run the app in administrative mode
      TCHAR msg[MAX_PATH + 1];
      FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, NULL, GetLastError(), MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), msg, MAX_PATH, NULL);
      wprintf(L"Evaluate mount points error: %s", msg);
    }
    while (success)
    {
      wcout << L"Mount point: " << volumeName << endl;
      success = FindNextVolumeMountPoint(mp, volumeName, MAX_PATH) != 0;
    }
    FindVolumeMountPointClose(mp);
  }

  {
    //Now find the mount points for this volume
    DWORD charCount = MAX_PATH;
    WCHAR *mp = NULL, *mps = NULL;
    bool success;

    while (true)
    {
      mps = new WCHAR[charCount];
      success = GetVolumePathNamesForVolumeNameW(volName, mps, charCount, &charCount) != 0;
      if (success || GetLastError() != ERROR_MORE_DATA) 
        break;
      delete [] mps;
      mps = NULL;
    }
    if (success)
    {
      for (mp = mps; mp[0] != '\0'; mp += wcslen(mp))
        wcout << L"Mount point: " << mp << endl;
    }
    delete [] mps;
  }

  {
    //And the type of this volume
    switch (GetDriveType(volName))
    {
    case DRIVE_UNKNOWN:     wcout << "unknown"; break;
    case DRIVE_NO_ROOT_DIR: wcout << "bad drive path"; break;
    case DRIVE_REMOVABLE:   wcout << "removable"; break;
    case DRIVE_FIXED:       wcout << "fixed"; break;
    case DRIVE_REMOTE:      wcout << "remote"; break;
    case DRIVE_CDROM:       wcout << "CD ROM"; break;
    case DRIVE_RAMDISK:     wcout << "RAM disk"; break;
    }
    wcout << endl;
  }
  {
    //This part of code will determine what this volume is composed of. The returned disk extents are actual disk partitions
    HANDLE volH;
    bool success;
    PVOLUME_DISK_EXTENTS vde;
    DWORD bret;

    volName[wcslen(volName) - 1] = '\0';
    volH = CreateFile(volName, 0, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, 0);
    if (volH == INVALID_HANDLE_VALUE)
    {
      TCHAR msg[MAX_PATH + 1];
      FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, NULL, GetLastError(), MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), msg, MAX_PATH, NULL);
      wprintf(L"Open volume error: %s", msg);
      return;
    }
    bret = sizeof(VOLUME_DISK_EXTENTS) + 256 * sizeof(DISK_EXTENT);
    vde = (PVOLUME_DISK_EXTENTS)malloc(bret);
    success = DeviceIoControl(volH, IOCTL_VOLUME_GET_VOLUME_DISK_EXTENTS, NULL, 0, (void *)vde, bret, &bret, NULL) != 0;
    if (!success)
      return;
    for (unsigned i = 0; i < vde->NumberOfDiskExtents; i++)
      wcout << L"Volume extent: " << vde->Extents[i].DiskNumber << L" "<< vde->Extents[i].StartingOffset.QuadPart << L" - " << vde->Extents[i].ExtentLength.QuadPart << endl;
    free(vde);
    CloseHandle(volH);
  }
}

bool findVolume(WCHAR *volName, int diskno, long long offs, long long len)
{
  HANDLE vol;
  bool success;

  vol = FindFirstVolume(volName, MAX_PATH); //I'm cheating here! I only know volName is MAX_PATH long because I wrote so in enumPartitions findVolume call
  success = vol != INVALID_HANDLE_VALUE;
  while (success)
  {
    //We are now enumerating volumes. In order for this function to work, we need to get partitions that compose this volume
    HANDLE volH;
    PVOLUME_DISK_EXTENTS vde;
    DWORD bret;

    volName[wcslen(volName) - 1] = '\0'; //For this CreateFile, volume must be without trailing backslash
    volH = CreateFile(volName, 0, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, 0);
    volName[wcslen(volName)] = '\\';
    if (volH != INVALID_HANDLE_VALUE)
    {
      bret = sizeof(VOLUME_DISK_EXTENTS) + 256 * sizeof(DISK_EXTENT);
      vde = (PVOLUME_DISK_EXTENTS)malloc(bret);
      if (DeviceIoControl(volH, IOCTL_VOLUME_GET_VOLUME_DISK_EXTENTS, NULL, 0, (void *)vde, bret, &bret, NULL))
      {
        for (unsigned i = 0; i < vde->NumberOfDiskExtents; i++)
          if (vde->Extents[i].DiskNumber == diskno &&
              vde->Extents[i].StartingOffset.QuadPart == offs &&
              vde->Extents[i].ExtentLength.QuadPart == len)
          {
            free(vde);
            CloseHandle(volH);
            FindVolumeClose(vol);
            return true;
          }
      }
      free(vde);
      CloseHandle(volH);
    }

    success = FindNextVolume(vol, volName, MAX_PATH) != 0;
  }
  FindVolumeClose(vol);
  return false;
}

void enumPartitions()
{
  GUID PARTITION_BASIC_DATA_GUID; //This one is actually defined in windows DDK headers, but the linker kills me when I try to use it in a normal (non driver) program
  PARTITION_BASIC_DATA_GUID.Data1 = 0xEBD0A0A2L;
  PARTITION_BASIC_DATA_GUID.Data2 = 0xB9E5;
  PARTITION_BASIC_DATA_GUID.Data3 = 0x4433;
  PARTITION_BASIC_DATA_GUID.Data4[0] = 0x87;
  PARTITION_BASIC_DATA_GUID.Data4[1] = 0xC0;
  PARTITION_BASIC_DATA_GUID.Data4[2] = 0x68;
  PARTITION_BASIC_DATA_GUID.Data4[3] = 0xB6;
  PARTITION_BASIC_DATA_GUID.Data4[4] = 0xB7;
  PARTITION_BASIC_DATA_GUID.Data4[5] = 0x26;
  PARTITION_BASIC_DATA_GUID.Data4[6] = 0x99;
  PARTITION_BASIC_DATA_GUID.Data4[7] = 0xC7;

  //These structures are needed for IOCTL_DISK_GET_DRIVE_LAYOUT_EX below, but we allocate here so that we don't have to worry about freeing until end of function
  PDRIVE_LAYOUT_INFORMATION_EX partitions;
  DWORD partitionsSize = sizeof(DRIVE_LAYOUT_INFORMATION_EX) + 127 * sizeof(PARTITION_INFORMATION_EX);
  partitions = (PDRIVE_LAYOUT_INFORMATION_EX)malloc(partitionsSize);

  for (int i = 0; ; i++)
  { //The main drive loop. This loop enumerates all physical drives windows currently sees
    //Note that empty removable drives are not enumerated with this method
    //Only volume enumeration will allow you to see those
    WCHAR volume[MAX_PATH];
    wsprintf(volume, L"\\\\.\\PhysicalDrive%d", i);

    //We are enumerating registered physical drives here. So the following CreateFile verifies if this disk even exists. If it does not, we have finished our enumeration
    //We also need the handle to get further info on the storage device
    HANDLE h = CreateFile(volume, 0, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, 0);
    bool success = h != INVALID_HANDLE_VALUE;
    if (!success)
      break;

    wcout << endl << endl << endl << L"Disk #" << i << endl;
    //Get information on storage device itself
    DISK_GEOMETRY_EX driveGeom;
    DWORD ior;
    success = DeviceIoControl(h, IOCTL_DISK_GET_DRIVE_GEOMETRY_EX, NULL, 0, &driveGeom, sizeof(driveGeom), &ior, NULL) != 0;
    if (success)
      wcout << L"Size: " << driveGeom.DiskSize.QuadPart << L"   " << driveGeom.DiskSize.QuadPart / 1024 / 1024 / 1024 << L" GB" << endl; //We could take other info from structure, but let's say disk size is enough
    //Get info on partitions
    success = DeviceIoControl(h, IOCTL_DISK_GET_DRIVE_LAYOUT_EX, NULL, 0, partitions, partitionsSize, &ior, NULL) != 0;
    if (success)
    {
      switch (partitions->PartitionStyle)
      {
      case PARTITION_STYLE_MBR: wcout << L"Partition type " << L"MBR" << endl; break;
      case PARTITION_STYLE_GPT: wcout << L"Partition type " << L"GPT" << endl; break;
      default: wcout << L"Partition type " << L"unknown" << endl; break;
      }

      for (int iPart = 0; iPart < int(partitions->PartitionCount); iPart++)
      { //Loop through partition records that we found
        bool partGood = false;
        if (partitions->PartitionEntry[iPart].PartitionStyle == PARTITION_STYLE_MBR && partitions->PartitionEntry[iPart].Mbr.PartitionType != PARTITION_ENTRY_UNUSED && partitions->PartitionEntry[iPart].Mbr.RecognizedPartition)
        { //For MBR partitions, the partition record must not be unused. That way we know it's a valid partition
          wcout << endl << endl << L"Partition " << iPart + 1 << L" offset: " << partitions->PartitionEntry[iPart].StartingOffset.QuadPart << L" length: " << partitions->PartitionEntry[iPart].PartitionLength.QuadPart << L"  " << partitions->PartitionEntry[iPart].PartitionLength.QuadPart / 1024 / 1024 << " MB" << endl;
          partGood = true;
        }
        else if (partitions->PartitionEntry[iPart].PartitionStyle == PARTITION_STYLE_GPT && partitions->PartitionEntry[iPart].Gpt.PartitionType == PARTITION_BASIC_DATA_GUID)
        { //For GPT partitions, partition type must be PARTITION_BASIC_DATA_GUID for it to be usable
          //Quite frankly, windows is doing some shady stuff here: for each GPT disk, windows takes some 128MB partition for its own use. 
          //I have no idea what that partition is used for, but it seems like an utter waste of space. My first disk was 10MB for crist's sake :(
          wcout << endl << endl << L"Partition " << iPart + 1 << L" offset: " << partitions->PartitionEntry[iPart].StartingOffset.QuadPart << L" length: " << partitions->PartitionEntry[iPart].PartitionLength.QuadPart << L"  " << partitions->PartitionEntry[iPart].PartitionLength.QuadPart / 1024 / 1024 << " MB" << endl;
          partGood = true;
        }
        if (partGood == true)
        {
          WCHAR volume[MAX_PATH];
          if (findVolume(volume, i, partitions->PartitionEntry[iPart].StartingOffset.QuadPart, partitions->PartitionEntry[iPart].PartitionLength.QuadPart))
          {
            wcout << endl << volume <<endl;
            volumeInfo(volume);
          }
        }
      }
    }
    CloseHandle(h);
  }
  free(partitions);
}

int main(int argc, char* argv[])
{
  enumPartitions();
  bool success; //Just so that the app waits before terminating
  cin >> success;
  return 0;
}