/***********************************************************************/
/*  TABWMI: Author Olivier Bertrand -- PlugDB -- 2012 - 2013           */
/*  TABWMI: Virtual table to get WMI information.                      */
/***********************************************************************/
#if !defined(WIN32)
#error This is a WIN32 only table type
#endif   // !WIN32
#include "my_global.h"
#include <stdio.h>

#include "global.h"
#include "plgdbsem.h"
#include "mycat.h"
#include "reldef.h"
#include "xtable.h"
#include "colblk.h"
#include "filter.h"
//#include "xindex.h"
#include "tabwmi.h"
#include "valblk.h"
#include "plgcnx.h"                       // For DB types
#include "resource.h"

extern "C" int trace;

/* ------------------- Functions WMI Column info --------------------- */

/***********************************************************************/
/*  Initialize WMI operations.                                         */
/***********************************************************************/
PWMIUT InitWMI(PGLOBAL g, char *nsp, char *classname)
{
  IWbemLocator *loc;
  char         *p;
  HRESULT       res;
  PWMIUT        wp = (PWMIUT)PlugSubAlloc(g, NULL, sizeof(WMIUTIL));

  if (trace)
    htrc("WMIColumns class %s space %s\n", SVP(classname), SVP(nsp));

  /*********************************************************************/
  /*  Set default values for the namespace and class name.             */
  /*********************************************************************/
  if (!nsp)
    nsp = "root\\cimv2";

  if (!classname) {
    if (!stricmp(nsp, "root\\cimv2"))
      classname = "ComputerSystemProduct";
    else if (!stricmp(nsp, "root\\cli"))
      classname = "Msft_CliAlias";
    else {
      strcpy(g->Message, "Missing class name");
      return NULL;
      } // endif classname

    } // endif classname

  /*********************************************************************/
  /*  Initialize WMI.                                                  */
  /*********************************************************************/
//res = CoInitializeEx(NULL, COINIT_MULTITHREADED);
  res = CoInitializeEx(NULL, COINIT_APARTMENTTHREADED);

  if (FAILED(res)) {
    sprintf(g->Message, "Failed to initialize COM library. " 
            "Error code = %p", res);
    return NULL;
    } // endif res

#if 0 // irrelevant for a DLL
  res = CoInitializeSecurity(NULL, -1, NULL, NULL,
                       RPC_C_AUTHN_LEVEL_CONNECT,
                       RPC_C_IMP_LEVEL_IMPERSONATE,
                       NULL, EOAC_NONE, NULL);
  
  if (res != RPC_E_TOO_LATE && FAILED(res)) {
    sprintf(g->Message, "Failed to initialize security. " 
            "Error code = %p", res);
    CoUninitialize();
    return NULL;
    }  // endif Res
#endif // 0

  res = CoCreateInstance(CLSID_WbemLocator, NULL, 
                         CLSCTX_INPROC_SERVER, IID_IWbemLocator, 
                         (void**) &loc);
  if (FAILED(res)) {
    sprintf(g->Message, "Failed to create Locator. " 
            "Error code = %p", res);
    CoUninitialize();
    return NULL;
    }  // endif res
    
  res = loc->ConnectServer(_bstr_t(nsp), 
                    NULL, NULL, NULL, 0, NULL, NULL, &wp->Svc);

  if (FAILED(res)) {
    sprintf(g->Message, "Could not connect. Error code = %p", res); 
    loc->Release();     
    CoUninitialize();
    return NULL;
    }  // endif res

  loc->Release();

  if (trace)
    htrc("Successfully connected to namespace.\n");

  /*********************************************************************/
  /*  Perform a full class object retrieval.                           */
  /*********************************************************************/
  p = (char*)PlugSubAlloc(g, NULL, strlen(classname) + 7);

  if (strchr(classname, '_'))
    strcpy(p, classname);
  else
    strcat(strcpy(p, "Win32_"), classname);

  res = wp->Svc->GetObject(bstr_t(p), 0, 0, &wp->Cobj, 0);

  if (FAILED(res)) {
    sprintf(g->Message, "failed GetObject %s in %s\n", classname, nsp);
    wp->Svc->Release();
    wp->Svc = NULL;    // MUST be set to NULL  (why?)
    return NULL;
    }  // endif res

  return wp;
} // end of InitWMI

/***********************************************************************/
/* WMIColumns: constructs the result blocks containing the description */
/* of all the columns of a WMI table of a specified class.             */
/***********************************************************************/
PQRYRES WMIColumns(PGLOBAL g, char *nsp, char *cls, bool info)
  {
  static int  buftyp[] = {TYPE_STRING, TYPE_SHORT, TYPE_STRING,
                          TYPE_INT,   TYPE_INT, TYPE_SHORT};
  static XFLD fldtyp[] = {FLD_NAME, FLD_TYPE,   FLD_TYPENAME,
                          FLD_PREC, FLD_LENGTH, FLD_SCALE};
  static unsigned int len, length[] = {0, 6, 8, 10, 10, 6};
  int     i = 0, n = 0, ncol = sizeof(buftyp) / sizeof(int);
  int     lng, typ, prec;
  LONG    low, upp;
  BSTR    propname;
  VARIANT val;
  CIMTYPE type;
  HRESULT res;
  PWMIUT  wp; 
  SAFEARRAY *prnlist = NULL;
  PQRYRES qrp = NULL;
  PCOLRES crp;

  if (!info) {
    /*******************************************************************/
    /*  Initialize WMI if not done yet.                                */
    /*******************************************************************/
    if (!(wp = InitWMI(g, nsp, cls)))
      return NULL;

    /*******************************************************************/
    /*  Get the number of properties to return.                        */
    /*******************************************************************/
    res = wp->Cobj->Get(bstr_t("__Property_Count"), 0, &val, NULL, NULL);

    if (FAILED(res)) {
      sprintf(g->Message, "failed Get(__Property_Count) res=%d\n", res);
      goto err;
      }  // endif res

    if (!(n = val.lVal)) {
      sprintf(g->Message, "Class %s in %s has no properties\n",
                          cls, nsp);
      goto err;
      }  // endif res

    /*******************************************************************/
    /*  Get max property name length.                                  */
    /*******************************************************************/
    res = wp->Cobj->GetNames(NULL, 
          WBEM_FLAG_ALWAYS | WBEM_FLAG_NONSYSTEM_ONLY, 
          NULL, &prnlist);

    if (FAILED(res)) {
      sprintf(g->Message, "failed GetNames res=%d\n", res);
      goto err;
      }  // endif res

    res = SafeArrayGetLBound(prnlist, 1, &low);
    res = SafeArrayGetUBound(prnlist, 1, &upp);

    for (long i = low; i <= upp; i++) {
      // Get this property name.
      res = SafeArrayGetElement(prnlist, &i, &propname);

      if (FAILED(res)) {
        sprintf(g->Message, "failed GetArrayElement res=%d\n", res);
        goto err;
        }  // endif res

      len = (unsigned)SysStringLen(propname);
      length[0] = max(length[0], len);
      } // enfor i

    res = SafeArrayDestroy(prnlist);
  } else
    length[0] = 128;

  /*********************************************************************/
  /*  Allocate the structures used to refer to the result set.         */
  /*********************************************************************/
  qrp = PlgAllocResult(g, ncol, n, IDS_COLUMNS + 3,
                          buftyp, fldtyp, length, false, true);

  if (info || !qrp)
    return qrp;

  /*********************************************************************/
  /*  Now get the results into blocks.                                 */
  /*********************************************************************/
  res = wp->Cobj->BeginEnumeration(WBEM_FLAG_NONSYSTEM_ONLY);

  if (FAILED(res)) {
    sprintf(g->Message, "failed BeginEnumeration hr=%d\n", res);
    qrp = NULL;
    goto err;
    }  // endif hr

  while (TRUE) {
    res = wp->Cobj->Next(0, &propname, &val, &type, NULL);

    if (FAILED(res)) {
      sprintf(g->Message, "failed getting Next hr=%d\n", res);
      qrp = NULL;
      goto err;
    }  else if (res == WBEM_S_NO_MORE_DATA) {
      VariantClear(&val);
      break;
    } // endif res

    if (i >= n)
      break;                 // Should never happen
    else
      prec = 0;

    switch (type) {
      case CIM_STRING:
        typ = TYPE_STRING;
        lng = 255;
        prec = 1;   // Case insensitive
        break;
      case CIM_SINT32:                          
      case CIM_UINT32:
      case CIM_BOOLEAN:
        typ = TYPE_INT;
        lng = 11;
        break;
      case CIM_SINT8:
      case CIM_UINT8:
        typ = TYPE_TINY;
        lng = 4;
        break;
      case CIM_SINT16:
      case CIM_UINT16:
        typ = TYPE_SHORT;
        lng = 6;
        break;
      case CIM_REAL64:
      case CIM_REAL32:
        prec = 2;
        typ = TYPE_DOUBLE;
        lng = 15;
        break;
      case CIM_SINT64:
      case CIM_UINT64:
        typ = TYPE_BIGINT;
        lng = 20;
        break;
      case CIM_DATETIME:
        typ = TYPE_DATE;
        lng = 19;
        break;
      case CIM_CHAR16:
        typ = TYPE_STRING;
        lng = 16;
        break;
      case CIM_EMPTY:
        typ = TYPE_STRING;
        lng = 24;             // ???
        break;
      default:
        qrp->BadLines++;
        goto suite;
      } // endswitch type

    crp = qrp->Colresp;                    // Column Name
    crp->Kdata->SetValue(_com_util::ConvertBSTRToString(propname), i);
    crp = crp->Next;                       // Data Type
    crp->Kdata->SetValue(typ, i);
    crp = crp->Next;                       // Type Name
    crp->Kdata->SetValue(GetTypeName(typ), i);
    crp = crp->Next;                       // Precision
    crp->Kdata->SetValue(lng, i);
    crp = crp->Next;                       // Length
    crp->Kdata->SetValue(lng, i);
    crp = crp->Next;                       // Scale (precision)
    crp->Kdata->SetValue(prec, i);
    i++;

 suite:
    SysFreeString(propname);
    VariantClear(&val);
    } // endfor i

  qrp->Nblin = i;

 err:
  // Cleanup
  wp->Cobj->Release();
  wp->Svc->Release();
  wp->Svc = NULL;    // MUST be set to NULL  (why?)
  CoUninitialize();

  /*********************************************************************/
  /*  Return the result pointer for use by GetData routines.           */
  /*********************************************************************/
  return qrp;
  } // end of WMIColumns

/* -------------- Implementation of the WMI classes  ------------------ */

/***********************************************************************/
/*  DefineAM: define specific AM values for WMI table.                 */
/***********************************************************************/
bool WMIDEF::DefineAM(PGLOBAL g, LPCSTR am, int poff)
  {
  Nspace = Cat->GetStringCatInfo(g, "Namespace", "Root\\CimV2");
  Wclass = Cat->GetStringCatInfo(g, "Class",
          (!stricmp(Nspace, "root\\cimv2") ? "ComputerSystemProduct" : 
           !stricmp(Nspace, "root\\cli")   ? "Msft_CliAlias" : ""));

  if (!*Wclass) {
    sprintf(g->Message, "Missing class name for %s", Nspace);
    return true;
  } else if (!strchr(Wclass, '_')) {
    char *p = (char*)PlugSubAlloc(g, NULL, strlen(Wclass) + 7);
    Wclass = strcat(strcpy(p, "Win32_"), Wclass);
  } // endif Wclass

  if (Catfunc == FNC_NO)
    Ems = Cat->GetIntCatInfo("Estimate", 100);

  return false;
  } // end of DefineAM

/***********************************************************************/
/*  GetTable: makes a new TDB of the proper type.                      */
/***********************************************************************/
PTDB WMIDEF::GetTable(PGLOBAL g, MODE m)
  {
  if (Catfunc == FNC_NO)
    return new(g) TDBWMI(this);
  else if (Catfunc == FNC_COL)
    return new(g) TDBWCL(this);

  sprintf(g->Message, "Bad catfunc %ud for WMI", Catfunc);
  return NULL;
  } // end of GetTable

/* ------------------------------------------------------------------- */

/***********************************************************************/
/*  Implementation of the TDBWMI class.                                */
/***********************************************************************/
TDBWMI::TDBWMI(PWMIDEF tdp) : TDBASE(tdp)
  {
  Svc = NULL;
  Enumerator = NULL;
  ClsObj = NULL;
  Nspace = tdp->Nspace;
  Wclass = tdp->Wclass;
  ObjPath = NULL;
  Kvp = NULL;
  Ems = tdp->Ems;
  Kcol = NULL;
  Vbp = NULL;
  Init = false;
  Done = false;
  Res = 0;
  Rc = 0;
  N = -1;
  } // end of TDBWMI constructor

/***********************************************************************/
/*  Allocate WMI column description block.                             */
/***********************************************************************/
PCOL TDBWMI::MakeCol(PGLOBAL g, PCOLDEF cdp, PCOL cprec, int n)
  {
  PCOL colp;

  colp = new(g) WMICOL(cdp, this, n);

  if (cprec) {
    colp->SetNext(cprec->GetNext());
    cprec->SetNext(colp);
  } else {
    colp->SetNext(Columns);
    Columns = colp;
  } // endif cprec

  return colp;
  } // end of MakeCol

/***********************************************************************/
/*  Initialize: Initialize WMI operations.                             */
/***********************************************************************/
bool TDBWMI::Initialize(PGLOBAL g)
  {
  if (Init)
    return false;

  // Initialize COM.
  Res = CoInitializeEx(NULL, COINIT_APARTMENTTHREADED);

  if (FAILED(Res)) {
    sprintf(g->Message, "Failed to initialize COM library. " 
            "Error code = %p", Res);
    return true;              // Program has failed.
    } // endif Res

  // Obtain the initial locator to Windows Management
  // on a particular host computer.
  IWbemLocator *loc;        // Initial Windows Management locator

  Res = CoCreateInstance(CLSID_WbemLocator, 0,  CLSCTX_INPROC_SERVER, 
                         IID_IWbemLocator, (LPVOID*) &loc);
 
  if (FAILED(Res)) {
    sprintf(g->Message, "Failed to create Locator. " 
                        "Error code = %p", Res);
    CoUninitialize();
    return true;       // Program has failed.
    }  // endif Res

  // Connect to the specified namespace with the
  // current user and obtain pointer to Svc
  // to make IWbemServices calls.
  Res = loc->ConnectServer(_bstr_t(Nspace),
                             NULL, NULL,0, NULL, 0, 0, &Svc);
  
  if (FAILED(Res)) {
    sprintf(g->Message, "Could not connect. Error code = %p", Res); 
    loc->Release();     
    CoUninitialize();
    return true;                // Program has failed.
    }  // endif hres

  loc->Release();                // Not used anymore

  // Set the IWbemServices proxy so that impersonation
  // of the user (client) occurs.
  Res = CoSetProxyBlanket(Svc, RPC_C_AUTHN_WINNT, RPC_C_AUTHZ_NONE, 
                          NULL, RPC_C_AUTHN_LEVEL_CALL,
                          RPC_C_IMP_LEVEL_IMPERSONATE, NULL, EOAC_NONE);

  if (FAILED(Res)) {
    sprintf(g->Message, "Could not set proxy. Error code = 0x", Res);
    Svc->Release();
    CoUninitialize();
    return true;               // Program has failed.
    }  // endif Res

  Init = true;
  return false;
  } // end of Initialize

/***********************************************************************/
/*  Changes '\' into '\\' in the filter.                               */
/***********************************************************************/
void TDBWMI::DoubleSlash(PGLOBAL g)
  {
  if (To_Filter && strchr(To_Filter->Body, '\\')) {
    char *body = To_Filter->Body;
    char *buf = (char*)PlugSubAlloc(g, NULL, strlen(body) * 2);
    int   i = 0, k = 0;

    do {
      if (body[i] == '\\')
        buf[k++] = '\\';

      buf[k++] = body[i];
      } while (body[i++]);

    To_Filter->Body = buf;
    } // endif To_Filter

  } // end of DoubleSlash

/***********************************************************************/
/*  MakeWQL: make the WQL statement use with WMI ExecQuery.            */
/***********************************************************************/
char *TDBWMI::MakeWQL(PGLOBAL g)
  {
  char  *colist, *wql/*, *pw = NULL*/;
  int    len, ncol = 0;
  bool   first = true, noloc = false;
  PCOL   colp;

  // Normal WQL statement to retrieve results
  for (colp = Columns; colp; colp = colp->GetNext())
    if (!colp->IsSpecial() && (colp->GetColUse(U_P | U_J_EXT) || noloc))
      ncol++;

  if (ncol) {
    colist = (char*)PlugSubAlloc(g, NULL, (NAM_LEN + 4) * ncol);

    for (colp = Columns; colp; colp = colp->GetNext())
      if (!colp->IsSpecial()) {
        if (colp->GetResultType() == TYPE_DATE)
          ((DTVAL*)colp->GetValue())->SetFormat(g, "YYYYMMDDhhmmss", 19);

        if (colp->GetColUse(U_P | U_J_EXT) || noloc) {
          if (first) {
            strcpy(colist, colp->GetName());
            first = false;
          } else
            strcat(strcat(colist, ", "), colp->GetName());
      
          } // endif ColUse

        } // endif Special

  } else {
    // ncol == 0 can occur for queries such that sql count(*) from...
    // for which we will count the rows from sql * from...
    colist = (char*)PlugSubAlloc(g, NULL, 2);
    strcpy(colist, "*");
  } // endif ncol

  // Below 14 is length of 'select ' + length of ' from ' + 1
  len = (strlen(colist) + strlen(Wclass) + 14);
  len += (To_Filter ? strlen(To_Filter->Body) + 7 : 0);
  wql = (char*)PlugSubAlloc(g, NULL, len);
  strcat(strcat(strcpy(wql, "SELECT "), colist), " FROM ");
  strcat(wql, Wclass);

  if (To_Filter)
    strcat(strcat(wql, " WHERE "), To_Filter->Body);

  return wql;
  } // end of MakeWQL

/***********************************************************************/
/*  GetWMIInfo: Get info for the WMI class.                            */
/***********************************************************************/
bool TDBWMI::GetWMIInfo(PGLOBAL g)
  {
  if (Done)
    return false;

  char *cmd = MakeWQL(g);

  if (cmd == NULL) {
    sprintf(g->Message, "Error making WQL statement"); 
    Svc->Release();
    CoUninitialize();
    return true;               // Program has failed.
    }  // endif cmd

  // Query for Wclass in Nspace
  Rc = Svc->ExecQuery(bstr_t("WQL"), bstr_t(cmd),
//    WBEM_FLAG_BIDIRECTIONAL | WBEM_FLAG_RETURN_IMMEDIATELY, 
      WBEM_FLAG_FORWARD_ONLY | WBEM_FLAG_RETURN_IMMEDIATELY, 
      NULL, &Enumerator);
  
  if (FAILED(Rc)) {
    sprintf(g->Message, "Query %s failed. Error code = %p", cmd, Rc); 
    Svc->Release();
    CoUninitialize();
    return true;               // Program has failed.
    }  // endif Rc

  Done = true;
  return false;
  } // end of GetWMIInfo

/***********************************************************************/
/*  WMI: Get the number returned instances.                            */
/***********************************************************************/
int TDBWMI::GetMaxSize(PGLOBAL g)
  {
  if (MaxSize < 0) {
    /*******************************************************************/
    /*  Loop enumerating to get the count. This is prone to last a     */
    /*  very long time for some classes such as DataFile, this is why  */
    /*  we just return an estimated value that will be ajusted later.  */
    /*******************************************************************/
    MaxSize = Ems;
#if 0
    if (Initialize(g))
      return -1;
    else if (GetWMIInfo(g))
      return -1;
    else
      MaxSize = 0;

    PDBUSER dup = PlgGetUser(g);

    while (Enumerator) {
      Res = Enumerator->Next(WBEM_INFINITE, 1, &ClsObj, &Rc);

      if (Rc == 0)
        break;

      MaxSize++;
      } // endwile Enumerator

    Res = Enumerator->Reset();
#endif // 0
    } // endif MaxSize

  return MaxSize;
  } // end of GetMaxSize

/***********************************************************************/
/*  When making a Kindex, must provide the Key column info.            */
/***********************************************************************/
int TDBWMI::GetRecpos(void)
  {
  if (!Kcol || !Vbp) 
    return N;

  Kcol->Reset();
  Kcol->Eval(NULL);
  Vbp->SetValue(Kcol->GetValue(), N);
  return N;
  }  // end of GetRecpos

/***********************************************************************/
/*  WMI Access Method opening routine.                                 */
/***********************************************************************/
bool TDBWMI::OpenDB(PGLOBAL g)
  {
  if (Use == USE_OPEN) {
    /*******************************************************************/
    /*  Table already open.                                            */
    /*******************************************************************/
    Res = Enumerator->Reset();
    N = 0;
    return false;
    } // endif use

  if (Mode != MODE_READ) {
    /*******************************************************************/
    /* WMI tables cannot be modified.                                  */
    /*******************************************************************/
    strcpy(g->Message, "WMI tables are read only");
    return true;
    } // endif Mode

  if (!To_Filter && !stricmp(Wclass, "CIM_Datafile")
                 && !stricmp(Nspace, "root\\cimv2")) {
    strcpy(g->Message, 
      "Would last forever when not filtered, use DIR table instead");
    return true;
  } else
    DoubleSlash(g);

  Use = USE_OPEN;       // Do it now in case we are recursively called

  /*********************************************************************/
  /*  Initialize the WMI processing.                                   */
  /*********************************************************************/
  if (Initialize(g))
    return true;
  else
    return GetWMIInfo(g);

  } // end of OpenDB

/***********************************************************************/
/*  Data Base read routine for WMI access method.                      */
/***********************************************************************/
int TDBWMI::ReadDB(PGLOBAL g)
  {
  Res = Enumerator->Next(WBEM_INFINITE, 1, &ClsObj, &Rc);

  if (Rc == 0)
    return RC_EF;

  N++;
  return RC_OK;
  } // end of ReadDB

/***********************************************************************/
/*  WriteDB: Data Base write routine for WMI access methods.           */
/***********************************************************************/
int TDBWMI::WriteDB(PGLOBAL g)
  {
  strcpy(g->Message, "WMI tables are read only");
  return RC_FX;
  } // end of WriteDB

/***********************************************************************/
/*  Data Base delete line routine for WMI access methods.              */
/***********************************************************************/
int TDBWMI::DeleteDB(PGLOBAL g, int irc)
  {
  strcpy(g->Message, "Delete not enabled for WMI tables");
  return RC_FX;
  } // end of DeleteDB

/***********************************************************************/
/*  Data Base close routine for WMI access method.                     */
/***********************************************************************/
void TDBWMI::CloseDB(PGLOBAL g)
  {
  // Cleanup
  if (ClsObj)
    ClsObj->Release();

  if (Enumerator)
    Enumerator->Release();

  if (Svc)
    Svc->Release();

  CoUninitialize();
  } // end of CloseDB

// ------------------------ WMICOL functions ----------------------------

/***********************************************************************/
/*  WMICOL public constructor.                                         */
/***********************************************************************/
WMICOL::WMICOL(PCOLDEF cdp, PTDB tdbp, int n)
      : COLBLK(cdp, tdbp, n)
  {
  Tdbp = (PTDBWMI)tdbp;
  VariantInit(&Prop);
  Ctype = CIM_ILLEGAL;
  Res = 0;
  } // end of WMICOL constructor

#if 0
/***********************************************************************/
/*  WMICOL constructor used for copying columns.                       */
/*  tdbp is the pointer to the new table descriptor.                   */
/***********************************************************************/
WMICOL::WMICOL(WMICOL *col1, PTDB tdbp) : COLBLK(col1, tdbp)
  {
  } // end of WMICOL copy constructor
#endif // 0

/***********************************************************************/
/*  Read the next WMI address elements.                                */
/***********************************************************************/
void WMICOL::ReadColumn(PGLOBAL g)
  {
  // Get the value of the Name property
  Res = Tdbp->ClsObj->Get(_bstr_t(Name), 0, &Prop, &Ctype, 0);

  switch (Prop.vt) {
    case VT_EMPTY:
    case VT_NULL:
    case VT_VOID:
      Value->Reset();
      break;
    case VT_BSTR:
      Value->SetValue_psz(_com_util::ConvertBSTRToString(Prop.bstrVal));
      break;
    case VT_I4:
    case VT_UI4:
      Value->SetValue(Prop.lVal);
      break;
    case VT_I2:
    case VT_UI2:
      Value->SetValue(Prop.iVal);
      break;
    case VT_INT:
    case VT_UINT:
      Value->SetValue((int)Prop.intVal);
      break;
    case VT_BOOL:
      Value->SetValue(((int)Prop.boolVal) ? 1 : 0);
      break;
    case VT_R8:
      Value->SetValue(Prop.dblVal);
      break;
    case VT_R4:
      Value->SetValue((double)Prop.fltVal);
      break;
    case VT_DATE:
      switch (Value->GetType()) {
        case TYPE_DATE:
         {SYSTEMTIME stm;
          struct tm  ptm;
          int        rc = VariantTimeToSystemTime(Prop.date, &stm);

          ptm.tm_year = stm.wYear;
          ptm.tm_mon  = stm.wMonth;
          ptm.tm_mday  = stm.wDay;
          ptm.tm_hour = stm.wHour;
          ptm.tm_min  = stm.wMinute;
          ptm.tm_sec  = stm.wSecond;
          ((DTVAL*)Value)->MakeTime(&ptm);
         }break;
        case TYPE_STRING:
         {SYSTEMTIME stm;
          char       buf[24];
          int        rc = VariantTimeToSystemTime(Prop.date, &stm);

          sprintf(buf, "%02d/%02d/%d %02d:%02d:%02d", 
                       stm.wDay, stm.wMonth, stm.wYear,
                       stm.wHour, stm.wMinute, stm.wSecond);
          Value->SetValue_psz(buf);
         }break;
        default:
          Value->SetValue((double)Prop.fltVal);
        } // endswitch Type

      break;
    default:
      // This will reset numeric column value
      Value->SetValue_psz("Type not supported");
      break;
    } // endswitch vt

  VariantClear(&Prop);
  } // end of ReadColumn

/* ---------------------------TDBWCL class --------------------------- */

/***********************************************************************/
/*  TDBWCL class constructor.                                          */
/***********************************************************************/
TDBWCL::TDBWCL(PWMIDEF tdp) : TDBCAT(tdp)
  {
  Nsp = tdp->Nspace; 
  Cls = tdp->Wclass;
  } // end of TDBWCL constructor

/***********************************************************************/
/*  GetResult: Get the list of the WMI class properties.               */
/***********************************************************************/
PQRYRES TDBWCL::GetResult(PGLOBAL g)
  {
  return WMIColumns(g, Nsp, Cls, false);
	} // end of GetResult