/* * HrSWInstalledEntry.c v0.10 * Generated in conjunction with Management Factory scripts: * script version: SNMPv1, 0.16, Apr 25, 1996 * project: D:\TEMP\EXAMPLE\HOSTMIB **************************************************************************** * * * (C) Copyright 1995 DIGITAL EQUIPMENT CORPORATION * * * * This software is an unpublished work protected under the * * the copyright laws of the United States of America, all * * rights reserved. * * * * In the event this software is licensed for use by the United * * States Government, all use, duplication or disclosure by the * * United States Government is subject to restrictions as set * * forth in either subparagraph (c)(1)(ii) of the Rights in * * Technical Data And Computer Software Clause at DFARS * * 252.227-7013, or the Commercial Computer Software Restricted * * Rights Clause at FAR 52.221-19, whichever is applicable. * * * **************************************************************************** * * Facility: * * Windows NT SNMP Extension Agent * * Abstract: * * This module contains the code for dealing with the get, set, and * instance name routines for the HrSWInstalledEntry. Actual instrumentation code is * supplied by the developer. * * Functions: * * A get and set routine for each attribute in the class. * * The routines for instances within the class. * * Author: * * D. D. Burns @ Webenable Inc * * Revision History: * * V1.00 - 04/27/97 D. D. Burns Genned: Thu Nov 07 16:49:12 1996 * */ #include #include #include #include /* For htons() */ #include "mib.h" #include "smint.h" #include "hostmsmi.h" #include "user.h" /* Developer supplied include file */ #include "HMCACHE.H" /* Cache-related definitions */ #include /* For Registry-lookup on software */ #include /* For Registry-lookup on software */ #include /* For CoFileTimeToDosDateTime() */ /* |============================================================================== | Function prototypes for this module. | */ /* Gen_SingleDevices - Generate Single Device row entries in HrDevice */ BOOL Gen_SingleDevices( void ); /* AddSWInstalled - Add a row to HrSWInstalled Table */ BOOL AddSWInstalled( HKEY sw_key, FILETIME *ft); #if defined(CACHE_DUMP) /* debug_print_hrswinstalled - Prints a Row from HrSWInstalled table */ static void debug_print_hrswinstalled( CACHEROW *row /* Row in hrSWInstalled table */ ); #endif /* |============================================================================== | Create the list-head for the HrSWInstalled table cache. | | (This macro is defined in "HMCACHE.H"). */ static CACHEHEAD_INSTANCE(hrSWInstalled_cache, debug_print_hrswinstalled); /* |============================================================================== | hrSWInstalledTable Attribute Defines | | Each attribute defined for this table is associated with one of the | #defines below. These symbols are used as C indices into the array of | attributes within a cached-row. | */ #define HRIN_INDEX 0 // hrSWInstalledIndex #define HRIN_NAME 1 // hrSWInstalledName #define HRIN_DATE 2 // hrSWInstalledDate //-->Add more here, change count below! #define HRIN_ATTRIB_COUNT 3 /* * GetHrSWInstalledIndex * A unique value for each piece of software installed on the host. This * value shall be in the range from 1 to the number of piece * * Gets the value for HrSWInstalledIndex. * * Arguments: * * outvalue address to return variable value * accesss Reserved for future security use * instance address of instance name as ordered native * data type(s) * * Return Codes: * * Standard PDU error codes. * * SNMP_ERRORSTATUS_NOERROR Successful get * SNMP_ERRORSTATUS_GENERR Catch-all failure code * mibtget.c v0.10 * | =============== From WebEnable Design Spec Rev 3 04/11/97================== | hrSWInstalledIndex | | ACCESS SYNTAX | read-only INTEGER (1..2147483647) | | "A unique value for each piece of software installed on the host. This value | shall be in the range from 1 to the number of pieces of software installed on | the host." | |============================================================================ | 1.3.6.1.2.1.25.6.3.1.1. | | | | | | | | | *-hrSwInstalledIndex | | | *-hrSWInstalledEntry | | *-hrSWInstalledTable | *-hrSWInstalled */ UINT GetHrSWInstalledIndex( OUT Integer *outvalue , IN Access_Credential *access , IN InstanceName *instance ) { ULONG index; /* As fetched from instance structure */ CACHEROW *row; /* Row entry fetched from cache */ /* | Grab the instance information */ index = GET_INSTANCE(0); /* | Use it to find the right entry in the cache */ if ((row = FindTableRow(index, &hrSWInstalled_cache)) == NULL) { return SNMP_ERRORSTATUS_GENERR; } /* | Return the "hrSWInstalledIndex" value from this entry */ *outvalue = row->attrib_list[HRIN_INDEX].u.unumber_value; return SNMP_ERRORSTATUS_NOERROR ; } /* end of GetHrSWInstalledIndex() */ /* * GetHrSWInstalledName * A textual description of this installed piece of software, including the * manufacturer, revision, the name by which it is commonl * * Gets the value for HrSWInstalledName. * * Arguments: * * outvalue address to return variable value * accesss Reserved for future security use * instance address of instance name as ordered native * data type(s) * * Return Codes: * * Standard PDU error codes. * * SNMP_ERRORSTATUS_NOERROR Successful get * SNMP_ERRORSTATUS_GENERR Catch-all failure code * mibtget.c v0.10 * | =============== From WebEnable Design Spec Rev 3 04/11/97================== | hrSWInstalledName | | ACCESS SYNTAX | read-only InternationalDisplayString (SIZE (0..64)) | | "A textual description of this installed piece of software, including the | manufacturer, revision, the name by which it is commonly known, and | optionally, its serial number." | |============================================================================ | DISCUSSION: | For logo 95 programs, we use the Registry sub-key name. | | 1.3.6.1.2.1.25.6.3.1.2. | | | | | | | | | *-hrSwInstalledName | | | *-hrSWInstalledEntry | | *-hrSWInstalledTable | *-hrSWInstalled */ UINT GetHrSWInstalledName( OUT InternationalDisplayString *outvalue , IN Access_Credential *access , IN InstanceName *instance ) { ULONG index; /* As fetched from instance structure */ CACHEROW *row; /* Row entry fetched from cache */ /* | Grab the instance information */ index = GET_INSTANCE(0); /* | Use it to find the right entry in the cache */ if ((row = FindTableRow(index, &hrSWInstalled_cache)) == NULL) { return SNMP_ERRORSTATUS_GENERR; } /* | Return the "hrSWInstalledName" value from this entry */ outvalue->string = row->attrib_list[HRIN_NAME].u.string_value; /* "Truncate" here to meet RFC as needed*/ if ((outvalue->length = strlen(outvalue->string)) > 64) { outvalue->length = 64; } return SNMP_ERRORSTATUS_NOERROR ; } /* end of GetHrSWInstalledName() */ /* * GetHrSWInstalledID * The product ID of this installed piece of software. * * Gets the value for HrSWInstalledID. * * Arguments: * * outvalue address to return variable value * accesss Reserved for future security use * instance address of instance name as ordered native * data type(s) * * Return Codes: * * Standard PDU error codes. * * SNMP_ERRORSTATUS_NOERROR Successful get * SNMP_ERRORSTATUS_GENERR Catch-all failure code * mibtget.c v0.10 * | =============== From WebEnable Design Spec Rev 3 04/11/97================== | hrSWInstalledID | | ACCESS SYNTAX | read-only ProductID | | "The product ID of this installed piece of software." | |============================================================================ | DISCUSSION: | For logo 95 programs we don't know the ProductID as an OID, so we | return the ProductID for "unknown": { 0.0 } | | 1.3.6.1.2.1.25.6.3.1.3. | | | | | | | | | *-hrSwInstalledID | | | *-hrSWInstalledEntry | | *-hrSWInstalledTable | *-hrSWInstalled */ UINT GetHrSWInstalledID( OUT ProductID *outvalue , IN Access_Credential *access , IN InstanceName *instance ) { /* | The deal on this attribute is that we'll never have a valid OID value | for this attribute. Consequently, we always return the standard | "unknown" OID value ("0.0") regardless of the instance value (which | by now in the calling sequence of things has been validated anyway). */ if ( (outvalue->ids = SNMP_malloc(2 * sizeof( UINT ))) == NULL) { return SNMP_ERRORSTATUS_GENERR; } outvalue->idLength = 2; /* | Load in the OID value for "unknown" for ProductID: "0.0" */ outvalue->ids[0] = 0; outvalue->ids[1] = 0; return SNMP_ERRORSTATUS_NOERROR ; } /* end of GetHrSWInstalledID() */ /* * GetHrSWInstalledType * The type of this software. * * Gets the value for HrSWInstalledType. * * Arguments: * * outvalue address to return variable value * accesss Reserved for future security use * instance address of instance name as ordered native * data type(s) * * Return Codes: * * Standard PDU error codes. * * SNMP_ERRORSTATUS_NOERROR Successful get * SNMP_ERRORSTATUS_GENERR Catch-all failure code * mibtget.c v0.10 * | =============== From WebEnable Design Spec Rev 3 04/11/97================== | hrSWInstalledType | | ACCESS SYNTAX | read-only INTEGER {unknown(1),operatingSystem(2),deviceDriver(3), | application(4)} | | "The type of this software." |============================================================================ | DISCUSSION: | For logo 95 programs we presume that all uninstallable software is | an application. That is the only type we return. | | 1.3.6.1.2.1.25.6.3.1.4. | | | | | | | | | *-hrSwInstalledType | | | *-hrSWInstalledEntry | | *-hrSWInstalledTable | *-hrSWInstalled */ UINT GetHrSWInstalledType( OUT INTSWType *outvalue , IN Access_Credential *access , IN InstanceName *instance ) { *outvalue = 4; // 4 = "application" return SNMP_ERRORSTATUS_NOERROR ; } /* end of GetHrSWInstalledType() */ /* * GetHrSWInstalledDate * The last-modification date of this application as it would appear in a * directory listing. * * Gets the value for HrSWInstalledDate. * * Arguments: * * outvalue address to return variable value * accesss Reserved for future security use * instance address of instance name as ordered native * data type(s) * * Return Codes: * * Standard PDU error codes. * * SNMP_ERRORSTATUS_NOERROR Successful get * SNMP_ERRORSTATUS_GENERR Catch-all failure code * mibtget.c v0.10 * | =============== From WebEnable Design Spec Rev 3 04/11/97================== | hrSWInstalledDate | | ACCESS SYNTAX | read-only DateAndTime | | "The last-modification date of this application as it would appear in a | directory listing." |============================================================================ | DISCUSSION: | For logo 95 programs we use the date of the last write into the | Registry key associated with the application. | | 1.3.6.1.2.1.25.6.3.1.5. | | | | | | | | | *-hrSwInstalledDate | | | *-hrSWInstalledEntry | | *-hrSWInstalledTable | *-hrSWInstalled */ UINT GetHrSWInstalledDate( OUT DateAndTime *outvalue , IN Access_Credential *access , IN InstanceName *instance ) { ULONG index; /* As fetched from instance structure */ CACHEROW *row; /* Row entry fetched from cache */ /* | Grab the instance information */ index = GET_INSTANCE(0); /* | Use it to find the right entry in the cache */ if ((row = FindTableRow(index, &hrSWInstalled_cache)) == NULL) { return SNMP_ERRORSTATUS_GENERR; } /* | Return the "hrSWInstalledDate" value from this entry */ outvalue->string = row->attrib_list[HRIN_DATE].u.string_value; outvalue->length = 8; return SNMP_ERRORSTATUS_NOERROR ; } /* end of GetHrSWInstalledDate() */ /* * HrSWInstalledEntryFindInstance * * This routine is used to verify that the specified instance is * valid. * * Arguments: * * FullOid Address for the full oid - group, variable, * and instance information * instance Address for instance specification as an oid * * Return Codes: * * SNMP_ERRORSTATUS_NOERROR Instance found and valid * SNMP_ERRORSTATUS_NOSUCHNAME Invalid instance * */ UINT HrSWInstalledEntryFindInstance( IN ObjectIdentifier *FullOid , IN OUT ObjectIdentifier *instance ) { UINT tmp_instance ; // // Developer instrumentation code to find appropriate instance goes here. // For non-tables, it is not necessary to modify this routine. However, if // there is any context that needs to be set, it can be done here. // if ( FullOid->idLength <= HRSWINSTALLEDENTRY_VAR_INDEX ) // No instance was specified return SNMP_ERRORSTATUS_NOSUCHNAME ; else if ( FullOid->idLength != HRSWINSTALLEDENTRY_VAR_INDEX + 1 ) // Instance length is more than 1 return SNMP_ERRORSTATUS_NOSUCHNAME ; else // The only valid instance for a non-table are instance 0. If this // is a non-table, the following code validates the instances. If this // is a table, developer modification is necessary below. tmp_instance = FullOid->ids[ HRSWINSTALLEDENTRY_VAR_INDEX ] ; /* | For hrSWInstalledTable, the instance arc(s) is a single arc, and | it must correctly select an entry in the hrSWInstalled Table cache. | Check that here. */ if ( FindTableRow(tmp_instance, &hrSWInstalled_cache) == NULL ) { return SNMP_ERRORSTATUS_NOSUCHNAME ; } else { // the instance is valid. Create the instance portion of the OID // to be returned from this call. instance->ids[ 0 ] = tmp_instance ; instance->idLength = 1 ; } return SNMP_ERRORSTATUS_NOERROR ; } /* end of HrSWInstalledEntryFindInstance() */ /* * HrSWInstalledEntryFindNextInstance * * This routine is called to get the next instance. If no instance * was passed than return the first instance (1). * * Arguments: * * FullOid Address for the full oid - group, variable, * and instance information * instance Address for instance specification as an oid * * Return Codes: * * SNMP_ERRORSTATUS_NOERROR Instance found and valid * SNMP_ERRORSTATUS_NOSUCHNAME Invalid instance * */ UINT HrSWInstalledEntryFindNextInstance( IN ObjectIdentifier *FullOid , IN OUT ObjectIdentifier *instance ) { // // Developer supplied code to find the next instance of class goes here. // If this is a class with cardinality 1, no modification of this routine // is necessary unless additional context needs to be set. // If the FullOid does not specify an instance, then the only instance // of the class is returned. If this is a table, the first row of the // table is returned. // // If an instance is specified and this is a non-table class, then NOSUCHNAME // is returned so that correct MIB rollover processing occurs. If this is // a table, then the next instance is the one following the current instance. // If there are no more instances in the table, return NOSUCHNAME. // CACHEROW *row; ULONG tmp_instance; if ( FullOid->idLength <= HRSWINSTALLEDENTRY_VAR_INDEX ) { /* | Too short: must return the instance arc that selects the first | entry in the table if there is one. */ tmp_instance = 0; } else { /* | There is at least one instance arc. Even if it is the only arc | we use it as the "index" in a request for the "NEXT" one. */ tmp_instance = FullOid->ids[ HRSWINSTALLEDENTRY_VAR_INDEX ] ; } /* Now go off and try to find the next instance in the table */ if ((row = FindNextTableRow(tmp_instance, &hrSWInstalled_cache)) == NULL) { return SNMP_ERRORSTATUS_NOSUCHNAME ; } instance->ids[ 0 ] = row->index ; instance->idLength = 1 ; return SNMP_ERRORSTATUS_NOERROR ; } /* end of HrSWInstalledEntryFindNextInstance() */ /* * HrSWInstalledEntryConvertInstance * * This routine is used to convert the object id specification of an * instance into an ordered native representation. The object id format * is that object identifier that is returned from the Find Instance * or Find Next Instance routines. It is NOT the full object identifier * that contains the group and variable object ids as well. The native * representation is an argc/argv-like structure that contains the * ordered variables that define the instance. This is specified by * the MIB's INDEX clause. See RFC 1212 for information about the INDEX * clause. * * * Arguments: * * oid_spec Address of the object id instance specification * native_spec Address to return the ordered native instance * specification * * Return Codes: * * SUCCESS Conversion complete successfully * FAILURE Unable to convert object id into native format * */ UINT HrSWInstalledEntryConvertInstance( IN ObjectIdentifier *oid_spec , IN OUT InstanceName *native_spec ) { static char *array; /* The address of this (char *) is passed back */ /* as though it were an array of length 1 of these */ /* types. */ static ULONG inst; /* The address of this ULONG is passed back */ /* (Obviously, no "free()" action is needed) */ /* We only expect the one arc in "oid_spec" */ inst = oid_spec->ids[0]; array = (char *) &inst; native_spec->count = 1; native_spec->array = &array; return SUCCESS ; } /* end of HrSWInstalledEntryConvertInstance() */ /* * HrSWInstalledEntryFreeInstance * * This routine is used to free an ordered native representation of an * instance name. * * Arguments: * * instance Address to return the ordered native instance * specification * * Return Codes: * * */ void HrSWInstalledEntryFreeInstance( IN OUT InstanceName *instance ) { /* No action needed for hrSWInstalledTable */ } /* end of HrSWInstalledEntryFreeInstance() */ /* | End of Generated Code */ /* Gen_HrSWInstalled_Cache - Generate a cache for HrSWInstalled Table */ /* Gen_HrSWInstalled_Cache - Generate a cache for HrSWInstalled Table */ /* Gen_HrSWInstalled_Cache - Generate a cache for HrSWInstalled Table */ BOOL Gen_HrSWInstalled_Cache( void ) /* | EXPLICIT INPUTS: | | None. | | IMPLICIT INPUTS: | | The module-local head of the cache for the HrSWInstalled table, | "hrSWInstalled_cache". | | OUTPUTS: | | On Success: | Function returns TRUE indicating that the cache has been fully | populated with all "static" cache-able values. | | On any Failure: | Function returns FALSE (indicating "not enough storage" or other | internal logic error). | | THE BIG PICTURE: | | At subagent startup time, the cache for each table in the MIB is | populated with rows for each row in the table. This function is | invoked by the start-up code in "UserMibInit()" ("MIB.C") to | populate the cache for the HrSWInstalled table. | | OTHER THINGS TO KNOW: | | There is one of these function for every table that has a cache. | Each is found in the respective source file. | |=============== From WebEnable Design Spec Rev 3 04/11/97================== | DISCUSSION: | | This implementation of this entire group is rather problematical without the | creation of a standard. It certainly appears that many software manufacturers | dutifully register SOMETHING in the Registry when software is installed, | however there appears to be no rhyme nor reason to the information put into | the Registry in leaves below the manufacturers name. Consequently, for | installed application software, there is no easy, reliable way of mapping | the Registry information into entries in this table. | | It is clear that some consistent scheme seems to be currently implemented for | Microsoft software, however the details of extracting it from the Registry | (and whether or not all information needed for full population of entries in | this table is available) is not documented. | | Proper implementation of "hrSWInstalled" requires the creation and | promulgation of a standard for registering ISD software (presumably in the | Registry). Information in "hrSWInstalled" includes attributes with | values of Object Identifiers. Webenable Inc. is prepared to work with | Microsoft in establishing a standard for registering software in a fashion | that allows proper implementation of the "hrSWInstalled" table. | | Resolution: | Report only logo 95 compliant software initially. | |============================================================================ | | 1.3.6.1.2.1.25.6.1.0.... | | | | | *hrSWInstalledLastChange | *-hrSWInstalled | | 1.3.6.1.2.1.25.6.2.0.... | | | | | *hrSWInstalledLastUpdateTime | *-hrSWInstalled | | 1.3.6.1.2.1.25.6.3.1.... | | | | | | | *-hrSWInstalledEntry | | *-hrSWInstalledTable | *-hrSWInstalled | */ #define SUBKEY_LEN 64 // Long enough for short key-name of software { HKEY subkey; /* Handle of subkey for uninstall software */ DWORD index; /* Index counter for enumerating subkeys */ LONG enum_status=ERROR_SUCCESS; /* Status from subkey enumeration */ CHAR subkey_name[SUBKEY_LEN]; /* Subkey name returned here */ DWORD subkey_len=SUBKEY_LEN; /* Subkey name buffer size */ FILETIME keytime; /* Time subkey was last written to */ BOOL add_status; /* Status from add-row operation */ HKEY sw_key; /* Handle of software key for value enum */ if (RegOpenKeyEx(HKEY_LOCAL_MACHINE, // hkey - currently open REGSTR_PATH_UNINSTALL, // subkey to open 0, // options KEY_READ, // Security access mask &subkey ) == ERROR_SUCCESS) { /* Enumerate the keys using the Uninstall subkey */ for (index = 0; enum_status != ERROR_NO_MORE_ITEMS; index += 1) { subkey_len=SUBKEY_LEN; enum_status = RegEnumKeyEx(subkey, // Enumerating this key index, // next subkey index subkey_name, // Buffer to rcv subkey name &subkey_len, // Buffer size NULL, // Reserved NULL, // Class name buffer NULL, // Class buffer size &keytime // Time of last write to subkey ); /* Skip if we didn't open OK */ if (enum_status != ERROR_SUCCESS) { continue; } /* Now try for the software itself */ if (RegOpenKeyEx(subkey, // hkey - currently open subkey_name, // subkey to open 0, // options KEY_READ, // Security access mask &sw_key ) == ERROR_SUCCESS) { /* Now Enumerate the Values of this key */ add_status = AddSWInstalled(sw_key, // Key to obtain DisplayName &keytime // Date and Time installed ); RegCloseKey(sw_key); /* If something blew down below, bail out */ if (add_status == FALSE) { RegCloseKey(subkey); return ( FALSE ); } } } RegCloseKey(subkey); } #if defined(CACHE_DUMP) PrintCache(&hrSWInstalled_cache); #endif /* hrSWInstalled cache initialized */ return ( TRUE ); } /* UTCDosDateTimeToLocalSysTime - converts UTC msdos date and time to local SYSTEMTIME structure */ /* UTCDosDateTimeToLocalSysTime - converts UTC msdos date and time to local SYSTEMTIME structure */ /* UTCDosDateTimeToLocalSysTime - converts UTC msdos date and time to local SYSTEMTIME structure */ void UTCDosDateTimeToLocalSysTime(WORD msdos_date, WORD msdos_time, LPSYSTEMTIME pSysTime) { SYSTEMTIME utcSysTime; utcSysTime.wYear = (msdos_date >> 9) + 1980; utcSysTime.wMonth = ((msdos_date >> 5) & 0x0F); utcSysTime.wDay = (msdos_date & 0x1F); utcSysTime.wDayOfWeek = 0; utcSysTime.wHour = (msdos_time >> 11); utcSysTime.wMinute = ((msdos_time >> 5) & 0x3F); utcSysTime.wSecond = ((msdos_time & 0x1F) * 2); utcSysTime.wMilliseconds = 0; if (!SystemTimeToTzSpecificLocalTime( NULL, // [in] use active time zone &utcSysTime, // [in] utc system time pSysTime)) // [out] local time { // if the utc time could not be converted to local time, // just return the utc time memcpy(&utcSysTime, pSysTime, sizeof(SYSTEMTIME)); } } /* AddSWInstalled - Add a row to HrSWInstalled Table */ /* AddSWInstalled - Add a row to HrSWInstalled Table */ /* AddSWInstalled - Add a row to HrSWInstalled Table */ BOOL AddSWInstalled( HKEY sw_key, FILETIME *ft ) /* | EXPLICIT INPUTS: | | "sw_key" - an opened key for a piece of software whose Values we | must enumerate looking for "DisplayName" | | "ft" - This is the time that the key was last written, and we | take it as the time the software was installed. | | IMPLICIT INPUTS: | | The module-local head of the cache for the HrSWInstalled table, | "hrSWInstalled_cache". | | OUTPUTS: | | On Success: | Function returns TRUE indicating that the cache has been fully | populated with all "static" cache-able values. | | On any Failure: | Function returns FALSE (indicating "not enough storage" or other | internal logic error). | | THE BIG PICTURE: | | The Gen_SWInstalled_cache() function has been invoked to populate | the cache, and this function is called when it has found another | piece of software for which a row-entry needs to be placed into | the cache. | | OTHER THINGS TO KNOW: | */ #define VALUE_LEN 32 // Long enough for "UninstallString" #define VALUE_DATA_LEN 128 // Long enough for a long "casual" name { DWORD index; /* Index counter for enumerating subkeys */ LONG enum_status=ERROR_SUCCESS; /* Status from subkey enumeration */ CHAR value_name[VALUE_LEN]; /* Value name returned here */ DWORD value_len; /* Value name buffer size */ DWORD value_type; /* Type code for value string */ CHAR value_data[VALUE_DATA_LEN]; /* Value's value returned here */ DWORD value_data_len; /* Value's value buffer length resides here*/ static /* NOTE: "static" is a 'must' */ ULONG table_index=0; /* HrDeviceTable index counter */ CACHEROW *row; /* --> Cache structure for row-being built */ WORD msdos_date; /* Conversion area for software date/time */ WORD msdos_time; /* Conversion area for software date/time */ char *octet_string; /* Alias for building DateAndTime */ UINT i; /* Loop index */ /* | Now go a-lookin' for "DisplayName", the value whose data will give us | the name of the installed software. | | For each Value associated with this software's key . . . */ for (index = 0; enum_status != ERROR_NO_MORE_ITEMS; index += 1) { /* Make sure these cells continues to reflect the buffer size */ value_len = VALUE_LEN; value_data_len = VALUE_DATA_LEN; enum_status = RegEnumValue(sw_key, // Key whose values we're enuming index, // index of next value value_name, // Value name buffer &value_len, // length of Value name buffer NULL, // reserved &value_type, // type of value data value_data, // Buffer for value's data &value_data_len// Length of data buffer ); /* Only if we managed to fetch this key do we try to recognize it */ if (enum_status == ERROR_SUCCESS) { /* If the value we just read is for "DisplayName" */ if ( strcmp(value_name, REGSTR_VAL_UNINSTALLER_DISPLAYNAME) == 0) { /* | Get a row-entry created. */ if ((row = CreateTableRow( HRIN_ATTRIB_COUNT ) ) == NULL) { return ( FALSE ); // Out of memory } /* | Set up the cached hrSWInstalled attributes in the new row */ /* =========== hrSWInstalledIndex ==========*/ row->attrib_list[HRIN_INDEX].attrib_type = CA_NUMBER; row->attrib_list[HRIN_INDEX].u.unumber_value = (table_index += 1) ; /* =========== hrSWInstalledName ==========*/ row->attrib_list[HRIN_NAME].attrib_type = CA_STRING; if ( (row->attrib_list[HRIN_NAME].u.string_value = ( LPSTR ) malloc(value_data_len + 1)) == NULL) { return ( FALSE ); /* out of memory */ } strcpy(row->attrib_list[HRIN_NAME].u.string_value, value_data); /* =========== hrSWInstalledDate ========== | | Here's the deal on this one. We've got a 64-bit FILETIME | representation of when the Registry entry was made for the | software. We're taking this as the install time of the software. | | So we convert to MS-DOS time, then to DateAndTime (in the | 8-octet form) below: |========================== From RFC1514 ======================== | | DateAndTime ::= OCTET STRING (SIZE (8 | 11)) | -- A date-time specification for the local time of day. | -- This data type is intended to provide a consistent | -- method of reporting date information. | -- | -- field octets contents range | -- _____ ______ ________ _____ | -- 1 1-2 year 0..65536 | -- (in network byte order) | -- 2 3 month 1..12 | -- 3 4 day 1..31 | -- 4 5 hour 0..23 | -- 5 6 minutes 0..59 | -- 6 7 seconds 0..60 | -- (use 60 for leap-second) | -- 7 8 deci-seconds 0..9 | -- 8 9 direction from UTC "+" / "-" | -- (in ascii notation) | -- 9 10 hours from UTC 0..11 | -- 10 11 minutes from UTC 0..59 | -- | -- Note that if only local time is known, then | -- timezone information (fields 8-10) is not present. | | MS-DOS records file dates and times as packed 16-bit values. | An MS-DOS date has the following format: | Bits Contents | ---- -------- | 0-4 Days of the month (1-31). | 5-8 Months (1 = January, 2 = February, and so forth). | 9-15 Year offset from 1980 (add 1980 to get actual year). | | An MS-DOS time has the following format: | Bits Contents | ---- -------- | 0-4 Seconds divided by 2. | 5-10 Minutes (0-59). | 11-15 Hours (0-23 on a 24-hour clock). | */ row->attrib_list[HRIN_DATE].attrib_type = CA_STRING; if ( (row->attrib_list[HRIN_DATE].u.string_value = octet_string = ( LPSTR ) malloc(8)) == NULL) { return ( FALSE ); /* out of memory */ } for (i=0; i < 8; i += 1) octet_string[i] = '\0'; if (CoFileTimeToDosDateTime(ft, &msdos_date, &msdos_time) == TRUE) { SYSTEMTIME localInstTime; USHORT year; UTCDosDateTimeToLocalSysTime(msdos_date, msdos_time, &localInstTime); year = htons(localInstTime.wYear); octet_string[0] = (year & 0xFF); octet_string[1] = (year >> 8); octet_string[2] = (char)localInstTime.wMonth; octet_string[3] = (char)localInstTime.wDay; octet_string[4] = (char)localInstTime.wHour; octet_string[5] = (char)localInstTime.wMinute; octet_string[6] = (char)localInstTime.wSecond; octet_string[7] = (char)localInstTime.wMilliseconds / 10; } /* | The other standard hrSWInstalled attributes are currently | "hardwired" in the Get functions. */ /* | Now insert the filled-in CACHEROW structure into the | cache-list for the hrDeviceTable. */ if (AddTableRow(row->attrib_list[HRIN_INDEX].u.unumber_value, /* Index */ row, /* Row */ &hrSWInstalled_cache /* Cache */ ) == FALSE) { return ( FALSE ); /* Internal Logic Error! */ } /* | Break from the Enumeration loop on the values, we've found | the one we want. */ break; } } } /* for */ /* Add succeeded */ return ( TRUE ); } #if defined(CACHE_DUMP) /* debug_print_hrswinstalled - Prints a Row from HrSWInstalled table */ /* debug_print_hrswinstalled - Prints a Row from HrSWInstalled table */ /* debug_print_hrswinstalled - Prints a Row from HrSWInstalled table */ static void debug_print_hrswinstalled( CACHEROW *row /* Row in hrSWInstalled table */ ) /* | EXPLICIT INPUTS: | | "row" - points to the row to be dumped, if NULL, the function | merely prints a suitable title. | | IMPLICIT INPUTS: | | - Symbols used to reference the attributes in the row entry. | - File handle defined by OFILE, presumed to be open. | | OUTPUTS: | | On Success: | Function prints a dump of the row in ASCII for debugging purposes | on file handle OFILE. | | THE BIG PICTURE: | | Debugging only. | | OTHER THINGS TO KNOW: */ { UINT i; if (row == NULL) { fprintf(OFILE, "=========================\n"); fprintf(OFILE, "hrSWInstalled Table Cache\n"); fprintf(OFILE, "=========================\n"); return; } fprintf(OFILE, "hrSWInstalledIndex . . . . %d\n", row->attrib_list[HRIN_INDEX].u.unumber_value); fprintf(OFILE, "hrSWInstalledName . . . . %s\n", row->attrib_list[HRIN_NAME].u.string_value); fprintf(OFILE, "hrSWInstalledDate . . . . "); for (i = 0; i < 8; i += 1) { fprintf(OFILE, "%2.2x ",row->attrib_list[HRIN_DATE].u.string_value[i]); } fprintf(OFILE, "\n"); } #endif