[Webinar] Streamline your web hosting managementRegister Today

x
?
Solved

Performance monitor question

Posted on 2000-01-24
21
Medium Priority
?
646 Views
Last Modified: 2008-03-17
Guys, I'm after some information on performance monitor dll's.
Basically, I need the simplest demo of a performance dll giving counters back to Perfmon.
I will need to have some guys here convert example to COBOL , in order to demostrate backoffice logo complience, hence I need to example to be very simple. I've got perfmon calling my dll, so the open works, but I'm at a loss as to how to fill in the collect structures. I don't care if the dll just sends back an incrementing number for now, or even a static number, I'll deal with the dll->app comminucation later.

Can anyone fill me in on how to complete the collectdata entry point?
A working example would be great, but a cut-n-paste for that entrypoint would be fine!

if you need any more info, please shout.
I'm kinda in a hurry here, so I've marked the question as 200 points ;-)

thanks again,
Ian
0
Comment
Question by:ianwhite
  • 14
  • 7
21 Comments
 
LVL 8

Expert Comment

by:Brain2000
ID: 2381983
What's the name of the function that it calls in your DLL?  I'm not real familiar with this, but I'm sure I can get something working quickly (while learning something new in the process)
0
 

Author Comment

by:ianwhite
ID: 2381997
Its defined by settings in the registry.
if you see "Adding performance counters" in MSDN it explains it, kind of!
It looks like if the collection entry point doesnt return data when you first look for the dll in Perfmon, perfmon removes it from the list of settings you can poll. That threw me for a long time, as I couldn't even get my dll to show up. Now I just need to get sensible data back to perfmon!
have fun, let me know if you get it to work!
Ian
0
 
LVL 8

Expert Comment

by:Brain2000
ID: 2382008
By the way, is this for Exchange Server?
0
Easily manage email signatures in Office 365

Managing email signatures in Office 365 can be a challenging task if you don't have the right tool. CodeTwo Email Signatures for Office 365 will help you implement a unified email signature look, no matter what email client is used by users. Test it for free!

 

Author Comment

by:ianwhite
ID: 2382014
nope, dll sits between my app and perfmon. Its to demostrate backoffice logo compliance of a development suite (hence the need to convert to COBOL dll eventually)
thanks,
0
 
LVL 8

Expert Comment

by:Brain2000
ID: 2382141
So if I understand correctly, you have already added your registry values for your performance monitor, and you are getting called with Open() function and the Collect() function.  I'm looking at the structures right now to see what Collect() needs to do in order to get meaningful data to your perfmon.
0
 

Author Comment

by:ianwhite
ID: 2382155
yep, well, I've added the registry entries for my performance DLL, that NT perfmon calls.
So just to be sure we're both talking the same language ;-) - I'm using NT perfmon to call MY dll, so its the code in the collect function I need.
I've been looking at the perfgen demo that comes with the SDK, its usefull, but it passes some complex structures around, which I'm hoping are not necc. needed.
Ideally, I just want to return a DWORD to perfmon, a constant is good enough, just to prove we can communicate with it.

thanks mate,
Ian
0
 
LVL 8

Expert Comment

by:Brain2000
ID: 2382954
If you want to just get a return, with no data, try this:

Of course, you won't get any readings because you haven't filled in the lppData buffer.  Unfortunately, you're going to have to fill in that buffer with your PERF_OBJECT_TYPE and PERF_COUNTER_DEFINITION structures as defined in winperf.h.  It looks like there's no way around filling in the structures.  The return value to this function is only a success, failure, or moredata flag.

DWORD APIENTRY
Collect(
    IN      LPWSTR  lpValueName,
    IN OUT  LPVOID  *lppData,
    IN OUT  LPDWORD lpcbTotalBytes,
    IN OUT  LPDWORD lpNumObjectTypes
)
{
  *lpcbTotalBytes = (DWORD) 0;
  *lpNumObjectTypes = (DWORD) 0;
  return ERROR_SUCCESS;
}

0
 

Author Comment

by:ianwhite
ID: 2383254
Thats what I feared. I was looking for the simplest structure to pass, something simpler than perfgen as I have to have someone convert it to COBOL. I'd got it to send back nothing, but that stops perfmon showing it up , and I have to prove it can do *something*!
If you find/write a collect function that just sends back the most basic performance counter, can you send it along? ;-)

thanks mate
0
 
LVL 8

Expert Comment

by:Brain2000
ID: 2385468
Having a slight problem.  PERF_OBJECT_TYPE and PERF_COUNTER_DEFINITION are NOT defined in my MSDN CD's.  I have completely searched them.  I think I'm going to have to upgrade to the next year (I usually don't upgrade but every 2 years or so).  If your MSDN CD's have a list of what these 2 structures look like, please send them to me and I'll get this working for you.
0
 
LVL 8

Expert Comment

by:Brain2000
ID: 2385864
Disregard my last post.  Oddest thing, I updated a component relating to MSDN in my system, and suddenly all these structures have appeared from the MSDN cd's that I'm currently using.  I can't explain it.  It's as if some of the "reference pointers" were corrupt or missing.  Anyways, more to come....
0
 
LVL 8

Expert Comment

by:Brain2000
ID: 2386285
My goodness.  Microsoft has really made this one quite difficult.  I put it in the category of getting a COM object to work for the first time :)

Here it goes:

struct _mydata {
  PERF_OBJECT_TYPE pot;
  PERF_COUNTER_DEFINITION pcd;
  PERF_COUNTER_DATA pcdata;
  DWORD actualdata;
};

DWORD APIENTRY
Collect(
    IN      LPWSTR  lpValueName,
    IN OUT  LPVOID  *lppData,
    IN OUT  LPDWORD lpcbTotalBytes,
    IN OUT  LPDWORD lpNumObjectTypes
)
{
  DWORD value=100; //this is what we want to pass back to perfmon
  struct _mydata *mydata=(_mydata)*lppData;

  if(*lpcbTotalBytes<sizeof(struct _mydata))
  {
    *lpcbTotalBytes = (DWORD) 0;
    *lpNumObjectTypes = (DWORD) 0;
    return ERROR_MORE_DATA; //send more paramedics!
  }


  mydata->pot.TotalByteLength=sizeof(struct _mydata)+sizeof(value);
  mydata->pot.DefinitionLength=sizeof(struct _mydata);
  mydata->pot.HeaderLength=sizeof(pot);
  mydata->pot.ObjectNameTitleIndex=0;  //this is a bit confusing
  mydata->pot.ObjectNameTitle=0;
  mydata->pot.ObjectHelpTitleIndex=0;
  mydata->pot.ObjectHelpTitle=0;
  mydata->pot.DetailLevel=PERF_DETAIL_NOVICE;
  mydata->pot.NumCounters=1; //yay!  1 counter
  mydata->pot.DefaultCounter=0;
  mydata->pot.NumInstances=0;
  mydata->pot.CodePage=0;

  mydata->pcd.ByteLength=sizeof(pcd);
  mydata->pcd.CounterNameTitleIndex=2; //you have to go by 2's.
  mydata->pcd.CounterNameTitle=0;
  mydata->pcd.CounterHelpTitleIndex=2;
  mydata->pcd.CounterHelpTitle=0;
  mydata->pcd.DefaultScale=2; //scale of 0 to 100 (10^2)
  mydata->pcd.DetailLevel=PERF_DETAIL_NOVICE;
  mydata->pcd.CounterType=PERF_SIZE_DWORD|PERF_TYPE_NUMBER|PERF_NUMBER_DECIMAL;
  mydata->pcd.CounterSize=sizeof(value);
  mydata->pcd.CounterOffset=sizeof(DWORD); //offset past end of pcd structure to where actual data is

  mydata->pcdata.ByteLength=sizeof(DWORD)+sizeof(value); //size of this structure plus actualdata size

  mydata->actualdata=value; //YES!  This sets the value you're passing back!

  *lppData+=sizeof(struct _mydata); //set *lppData to point at end of structure
  *lpcbTotalBytes = sizeof(struct _mydata);
  *lpNumObjectTypes = (DWORD) 1;
  return ERROR_SUCCESS;
}

WHEW!!!
0
 
LVL 8

Expert Comment

by:Brain2000
ID: 2386292
By the way, I haven't compiled that...
0
 
LVL 8

Expert Comment

by:Brain2000
ID: 2386294
In fact, I don't think I want to compile that...
0
 

Author Comment

by:ianwhite
ID: 2388357
Ok! I think we must be getting close.
I made a couple of changes to get it to compile, and also from looking at an MSDN sample the lppData pointer movement seemed wrong.
BUT, it still doesnt show up in perfmon. I think its missing an instance definition, but I'm not sure where it goes!
want to have another crack?

heres the modified code:
struct _mydata {
  PERF_OBJECT_TYPE pot;
  PERF_COUNTER_DEFINITION pcd;
  PERF_DATA_BLOCK pcdata;
  DWORD actualdata;
};

DWORD APIENTRY
CollectPerfData(
    IN      LPWSTR  lpValueName,
    IN OUT  LPVOID  *lppData,
    IN OUT  LPDWORD lpcbTotalBytes,
    IN OUT  LPDWORD lpNumObjectTypes
)
{
  DWORD value=100; //this is what we want to pass back to perfmon
  struct _mydata *mydata=(_mydata*)*lppData;

  if(*lpcbTotalBytes<sizeof(struct _mydata))
  {
    *lpcbTotalBytes = (DWORD) 0;
    *lpNumObjectTypes = (DWORD) 0;
      return ERROR_MORE_DATA; //send more paramedics!
  }

  mydata->pot.HeaderLength=sizeof(mydata->pot);
  mydata->pot.TotalByteLength=sizeof(struct _mydata)+sizeof(value);
  mydata->pot.DefinitionLength=sizeof(struct _mydata);
  mydata->pot.ObjectNameTitleIndex=0;  //this is a bit confusing
  mydata->pot.ObjectNameTitle=0;
  mydata->pot.ObjectHelpTitleIndex=0;
  mydata->pot.ObjectHelpTitle=0;
  mydata->pot.DetailLevel=PERF_DETAIL_NOVICE;
  mydata->pot.NumCounters=1; //yay!  1 counter
  mydata->pot.DefaultCounter=0;
  mydata->pot.NumInstances=0;
  mydata->pot.CodePage=0;
 
  mydata->pcd.ByteLength=sizeof(mydata->pcd);
  mydata->pcd.CounterNameTitleIndex=2; //you have to go by 2's.
  mydata->pcd.CounterNameTitle=0;
  mydata->pcd.CounterHelpTitleIndex=2;
  mydata->pcd.CounterHelpTitle=0;
  mydata->pcd.DefaultScale=2; //scale of 0 to 100 (10^2)
  mydata->pcd.DetailLevel=PERF_DETAIL_NOVICE;
  mydata->pcd.CounterType=PERF_SIZE_DWORD|PERF_TYPE_NUMBER|PERF_NUMBER_DECIMAL;
  mydata->pcd.CounterSize=sizeof(value);
  mydata->pcd.CounterOffset=sizeof(DWORD); //offset past end of pcd structure to where actual data is
 

  mydata->pcdata.TotalByteLength=sizeof(DWORD)+sizeof(value); //size of this structure plus actualdata size

  mydata->actualdata=value; //YES!  This sets the value you're passing back!

  // shift the lppData pointer
  //robbed from MSDN (mk:@MSITStore:F:\MSDN\dnwinnt.chm::/HTML/D21/S8023.HTM)
  DWORD *dwAddress=(PDWORD)*((PDWORD)mydata);
  *++dwAddress = ((PDWORD) mydata)[1];
  // this seems to allow perfmon to find other dll's it wasnt showing before, but still not this one..
  *lppData=(PVOID)++dwAddress;; //set *lppData to point at end of structure
  *lpcbTotalBytes = sizeof(struct _mydata);
  *lpNumObjectTypes = (DWORD) 1;
  return ERROR_SUCCESS;
}


Actually, I can send you the dll project if you'd like, may make things easier?
Ian
0
 
LVL 8

Expert Comment

by:Brain2000
ID: 2389205
I revisited their example.  I believe my original way of setting lppData is correct.  Here is why:

*lppData must point to the byte following all the data you filled in at *lppData.  So, for example, if *lppData points to 0x100000 and we fill in 20 bytes (0x100000 -> 0x100013), you would want to set *lppData to the next byte, which is 0x100014.  Thus, 0x100000+20(decimal)=0x100014.

Their use of dwAddress points it to the end of their block, where the perf values are stored, then they fill in their perf values while incrementing that pointer.  I actually include the perf values inside my structure, so mine is much more straightforward than the example.

*lppData+=sizeof(struct _mydata); //set *lppData to point at end of structure


Go ahead and send me the project and I'll see what I can do.  I have a feeling that the perfmon isn't finding the registry entries for your .DLL, so it's not showing up.  However, I'll revisit that portion of the MSDN cd's.  I briefly skimmed over it before.

My e-mail is brain@pcioh.com
0
 
LVL 8

Expert Comment

by:Brain2000
ID: 2389224
OOPS!  Found some errors in my size calculations.

Change these lines:

  mydata->pot.TotalByteLength=sizeof(struct _mydata);
  mydata->pot.DefinitionLength=sizeof(mydata->pot)+sizeof(mydata->pcd);
  mydata->pot.HeaderLength=sizeof(mydata->pot);
0
 
LVL 8

Expert Comment

by:Brain2000
ID: 2390614
Ok, I got the name appearing in the registry now!  Of course, when I select it, the entire perfmon is crashing (Details... details...) but I have it working nonetheless!

New code listing:  (by the way, I found a few doozies above)

//
// Ext Counters
//

#include <windows.h>
#include <winperf.h>
#include <winreg.h>

#define QUERY_GLOBAL    1
#define QUERY_ITEMS     2
#define QUERY_FOREIGN   3
#define QUERY_COSTLY    4

WCHAR GLOBAL_STRING[] = L"Global";
WCHAR FOREIGN_STRING[] = L"Foreign";
WCHAR COSTLY_STRING[] = L"Costly";

WCHAR NULL_STRING[] = L"\0";    // pointer to null string

// test for delimiter, end of line and non-digit characters
// used by IsNumberInUnicodeList routine
//
#define DIGIT       1
#define DELIMITER   2
#define INVALID     3

#define EvalThisChar(c,d) ( \
     (c == d) ? DELIMITER : \
     (c == 0) ? DELIMITER : \
     (c < (WCHAR)'0') ? INVALID : \
     (c > (WCHAR)'9') ? INVALID : \
     DIGIT)

DWORD GetQueryType (IN LPWSTR lpValue);
BOOL IsNumberInUnicodeList(IN DWORD   dwNumber,IN LPWSTR  lpwszUnicodeList);
DWORD APIENTRY CollectPerfData(IN      LPWSTR  lpValueName,IN OUT  LPVOID  *lppData,IN OUT  LPDWORD lpcbTotalBytes,IN OUT  LPDWORD lpNumObjectTypes);

//pack the structure on a 1 byte boundary
#pragma pack(1)
struct _mydata {
  PERF_OBJECT_TYPE pot;
  PERF_COUNTER_DEFINITION pcd;
  PERF_COUNTER_BLOCK pcdata;
  DWORD actualdata;
};
#pragma pack()

DWORD APIENTRY OpenPerfData(LPWSTR lpDeviceNames)
{
      return ERROR_SUCCESS;
}

DWORD APIENTRY
CollectPerfData(
    IN      LPWSTR  lpValueName,
    IN OUT  LPVOID  *lppData,
    IN OUT  LPDWORD lpcbTotalBytes,
    IN OUT  LPDWORD lpNumObjectTypes
)
{
  DWORD value=100; //this is what we want to pass back to perfmon
  DWORD dwQueryType;
  struct _mydata *mydata=(_mydata*)*lppData;
  HKEY myHKEY;
  DWORD reg_index;
  DWORD key_type,temp_size;

  if(*lpcbTotalBytes<sizeof(struct _mydata))
  {
    *lpcbTotalBytes=(DWORD)0;
    *lpNumObjectTypes=(DWORD)0;
    return ERROR_MORE_DATA; //send more paramedics!
  }

  dwQueryType=GetQueryType(lpValueName);
  if(dwQueryType==QUERY_ITEMS)
  {
    if(!(IsNumberInUnicodeList(0,lpValueName)))
    {
      // request received for data object not provided by this routine
      *lpcbTotalBytes=(DWORD)0;
      *lpNumObjectTypes=(DWORD)0;
      return ERROR_SUCCESS;
    }
  }


  reg_index=0;
  if(RegOpenKeyEx(HKEY_LOCAL_MACHINE,"SYSTEM\\CurrentControlSet\\Services\\SERVICENAME\\Performance",0,KEY_ALL_ACCESS,&myHKEY)==ERROR_SUCCESS)
  {
    key_type=REG_DWORD;
    temp_size=sizeof(reg_index);
    RegQueryValueEx(myHKEY,"First Counter",NULL,&key_type,(unsigned char *)&reg_index,&temp_size);
    RegCloseKey(myHKEY);
  }
  if(!reg_index)
  {
    *lpcbTotalBytes=(DWORD)0;
    *lpNumObjectTypes=(DWORD)0;
    return ERROR_SUCCESS;
  }

  mydata->pot.TotalByteLength=sizeof(struct _mydata);
  mydata->pot.DefinitionLength=sizeof(mydata->pot)+sizeof(mydata->pcd);
  mydata->pot.HeaderLength=sizeof(mydata->pot);
  mydata->pot.ObjectNameTitleIndex=reg_index;
  mydata->pot.ObjectNameTitle=0;
  mydata->pot.ObjectHelpTitleIndex=reg_index+1;
  mydata->pot.ObjectHelpTitle=0;
  mydata->pot.DetailLevel=PERF_DETAIL_NOVICE;
  mydata->pot.NumCounters=1; //yay!  1 counter
  mydata->pot.DefaultCounter=-1;
  mydata->pot.NumInstances=1;
  mydata->pot.CodePage=0;

  mydata->pcd.ByteLength=sizeof(mydata->pcd);
  mydata->pcd.CounterNameTitleIndex=reg_index+2; //you have to go by 2's.
  mydata->pcd.CounterNameTitle=0;
  mydata->pcd.CounterHelpTitleIndex=reg_index+3;
  mydata->pcd.CounterHelpTitle=0;
  mydata->pcd.DefaultScale=2; //scale of 0 to 100 (10^2)
  mydata->pcd.DetailLevel=PERF_DETAIL_NOVICE;
  mydata->pcd.CounterType=PERF_SIZE_DWORD|PERF_TYPE_NUMBER|PERF_NUMBER_DECIMAL;
  mydata->pcd.CounterSize=sizeof(value);
  mydata->pcd.CounterOffset=sizeof(PERF_COUNTER_BLOCK); //offset past end of pcd structure to where actual data is

  mydata->pcdata.ByteLength=sizeof(DWORD)+sizeof(value); //size of this structure plus actualdata size

  mydata->actualdata=value; //YES!  This sets the value you're passing back!

  // slight change *lppData is a void, so we can't add to it. can we do it this way, I always got lost moving *'s and &'s !
  *lppData=(void *)((char *)*lppData+sizeof(struct _mydata));
  *lpcbTotalBytes = sizeof(struct _mydata);
  *lpNumObjectTypes = (DWORD) 1;
  return ERROR_SUCCESS;
}

DWORD APIENTRY ClosePerfData()
{
      return ERROR_SUCCESS;
}




DWORD
GetQueryType (
    IN LPWSTR lpValue
)
/*++

GetQueryType

    returns the type of query described in the lpValue string so that
    the appropriate processing method may be used

Arguments

    IN lpValue
        string passed to PerfRegQuery Value for processing

Return Value

    QUERY_GLOBAL
        if lpValue == 0 (null pointer)
           lpValue == pointer to Null string
           lpValue == pointer to "Global" string

    QUERY_FOREIGN
        if lpValue == pointer to "Foreign" string

    QUERY_COSTLY
        if lpValue == pointer to "Costly" string

    otherwise:

    QUERY_ITEMS

--*/
{
    WCHAR   *pwcArgChar, *pwcTypeChar;
    BOOL    bFound;

    if (lpValue == 0) {
        return QUERY_GLOBAL;
    } else if (*lpValue == 0) {
        return QUERY_GLOBAL;
    }

    // check for "Global" request

    pwcArgChar = lpValue;
    pwcTypeChar = GLOBAL_STRING;
    bFound = TRUE;  // assume found until contradicted

    // check to the length of the shortest string
   
    while ((*pwcArgChar != 0) && (*pwcTypeChar != 0)) {
        if (*pwcArgChar++ != *pwcTypeChar++) {
            bFound = FALSE; // no match
            break;          // bail out now
        }
    }

    if (bFound) return QUERY_GLOBAL;

    // check for "Foreign" request
   
    pwcArgChar = lpValue;
    pwcTypeChar = FOREIGN_STRING;
    bFound = TRUE;  // assume found until contradicted

    // check to the length of the shortest string
   
    while ((*pwcArgChar != 0) && (*pwcTypeChar != 0)) {
        if (*pwcArgChar++ != *pwcTypeChar++) {
            bFound = FALSE; // no match
            break;          // bail out now
        }
    }

    if (bFound) return QUERY_FOREIGN;

    // check for "Costly" request
   
    pwcArgChar = lpValue;
    pwcTypeChar = COSTLY_STRING;
    bFound = TRUE;  // assume found until contradicted

    // check to the length of the shortest string
   
    while ((*pwcArgChar != 0) && (*pwcTypeChar != 0)) {
        if (*pwcArgChar++ != *pwcTypeChar++) {
            bFound = FALSE; // no match
            break;          // bail out now
        }
    }

    if (bFound) return QUERY_COSTLY;

    // if not Global and not Foreign and not Costly,
    // then it must be an item list
   
    return QUERY_ITEMS;

}

BOOL
IsNumberInUnicodeList (
    IN DWORD   dwNumber,
    IN LPWSTR  lpwszUnicodeList
)
/*++

IsNumberInUnicodeList

Arguments:
       
    IN dwNumber
        DWORD number to find in list

    IN lpwszUnicodeList
        Null terminated, Space delimited list of decimal numbers

Return Value:

    TRUE:
            dwNumber was found in the list of unicode number strings

    FALSE:
            dwNumber was not found in the list.

--*/
{
    DWORD   dwThisNumber;
    WCHAR   *pwcThisChar;
    BOOL    bValidNumber;
    BOOL    bNewItem;
    BOOL    bReturnValue;
    WCHAR   wcDelimiter;    // could be an argument to be more flexible

    if (lpwszUnicodeList == 0) return FALSE;    // null pointer, # not found

    pwcThisChar = lpwszUnicodeList;
    dwThisNumber = 0;
    wcDelimiter = (WCHAR)' ';
    bValidNumber = FALSE;
    bNewItem = TRUE;
   
    while (TRUE) {
        switch (EvalThisChar (*pwcThisChar, wcDelimiter)) {
            case DIGIT:
                // if this is the first digit after a delimiter, then
                // set flags to start computing the new number
                if (bNewItem) {
                    bNewItem = FALSE;
                    bValidNumber = TRUE;
                }
                if (bValidNumber) {
                    dwThisNumber *= 10;
                    dwThisNumber += (*pwcThisChar - (WCHAR)'0');
                }
                break;
           
            case DELIMITER:
                // a delimiter is either the delimiter character or the
                // end of the string ('\0') if when the delimiter has been
                // reached a valid number was found, then compare it to the
                // number from the argument list. if this is the end of the
                // string and no match was found, then return.
                //
                if (bValidNumber) {
                    if (dwThisNumber == dwNumber) return TRUE;
                    bValidNumber = FALSE;
                }
                if (*pwcThisChar == 0) {
                    return FALSE;
                } else {
                    bNewItem = TRUE;
                    dwThisNumber = 0;
                }
                break;

            case INVALID:
                // if an invalid character was encountered, ignore all
                // characters up to the next delimiter and then start fresh.
                // the invalid number is not compared.
                bValidNumber = FALSE;
                break;

            default:
                break;

        }
        pwcThisChar++;
    }

}   // IsNumberInUnicodeList
0
 
LVL 8

Expert Comment

by:Brain2000
ID: 2390629
That was quick.  Now I can select my performance counter and it doesn't crash.  Change this line:

  mydata->pot.NumInstances=0;

However, the data is ZERO, and not 100 like it should be... more to come...
0
 
LVL 8

Accepted Solution

by:
Brain2000 earned 800 total points
ID: 2390738
AHA!  I got EVERYTHING working now.  I put a sample counter that will go from 0 to 100 to 0 to 100, etc...

Here's the final code:

//
// Ext Counters
//

#include <windows.h>
#include <winperf.h>
#include <winreg.h>

#define QUERY_GLOBAL    1
#define QUERY_ITEMS     2
#define QUERY_FOREIGN   3
#define QUERY_COSTLY    4

WCHAR GLOBAL_STRING[] = L"Global";
WCHAR FOREIGN_STRING[] = L"Foreign";
WCHAR COSTLY_STRING[] = L"Costly";

WCHAR NULL_STRING[] = L"\0";    // pointer to null string

// test for delimiter, end of line and non-digit characters
// used by IsNumberInUnicodeList routine
//
#define DIGIT       1
#define DELIMITER   2
#define INVALID     3

#define EvalThisChar(c,d) ( \
     (c == d) ? DELIMITER : \
     (c == 0) ? DELIMITER : \
     (c < (WCHAR)'0') ? INVALID : \
     (c > (WCHAR)'9') ? INVALID : \
     DIGIT)

DWORD GetQueryType (IN LPWSTR lpValue);
BOOL IsNumberInUnicodeList(IN DWORD   dwNumber,IN LPWSTR  lpwszUnicodeList);
DWORD APIENTRY CollectPerfData(IN      LPWSTR  lpValueName,IN OUT  LPVOID  *lppData,IN OUT  LPDWORD lpcbTotalBytes,IN OUT  LPDWORD lpNumObjectTypes);

//pack the structure on a 1 byte boundary
#pragma pack(1)
struct _mydata {
  PERF_OBJECT_TYPE pot;
  PERF_COUNTER_DEFINITION pcd;
  PERF_COUNTER_BLOCK pcdata;
  DWORD actualdata;
};
#pragma pack()

DWORD APIENTRY OpenPerfData(LPWSTR lpDeviceNames)
{
      return ERROR_SUCCESS;
}

DWORD APIENTRY
CollectPerfData(
    IN      LPWSTR  lpValueName,
    IN OUT  LPVOID  *lppData,
    IN OUT  LPDWORD lpcbTotalBytes,
    IN OUT  LPDWORD lpNumObjectTypes
)
{
  static DWORD value=0; //this is what we want to pass back to perfmon
  static DWORD updown=1; //flag to count up and down
  DWORD dwQueryType;
  struct _mydata *mydata=(_mydata*)*lppData;
  HKEY myHKEY;
  static DWORD reg_index=0;
  DWORD key_type,temp_size;

  if(*lpcbTotalBytes<sizeof(struct _mydata))
  {
    *lpcbTotalBytes=(DWORD)0;
    *lpNumObjectTypes=(DWORD)0;
    return ERROR_MORE_DATA; //send more paramedics!
  }

  if(!reg_index)
  {
    if(RegOpenKeyEx(HKEY_LOCAL_MACHINE,"SYSTEM\\CurrentControlSet\\Services\\SERVICENAME\\Performance",0,KEY_ALL_ACCESS,&myHKEY)==ERROR_SUCCESS)
    {
      key_type=REG_DWORD;
      temp_size=sizeof(reg_index);
      RegQueryValueEx(myHKEY,"First Counter",NULL,&key_type,(unsigned char *)&reg_index,&temp_size);
      RegCloseKey(myHKEY);
    }
  }
  if(!reg_index)
  {
    *lpcbTotalBytes=(DWORD)0;
    *lpNumObjectTypes=(DWORD)0;
    return ERROR_SUCCESS;
  }

  dwQueryType=GetQueryType(lpValueName);
  if(dwQueryType==QUERY_ITEMS)
  {
    if(!(IsNumberInUnicodeList(reg_index,lpValueName)))
    {
      // request received for data object not provided by this routine
      *lpcbTotalBytes=(DWORD)0;
      *lpNumObjectTypes=(DWORD)0;
      return ERROR_SUCCESS;
    }
  }

  mydata->pot.TotalByteLength=sizeof(struct _mydata);
  mydata->pot.DefinitionLength=sizeof(mydata->pot)+sizeof(mydata->pcd);
  mydata->pot.HeaderLength=sizeof(mydata->pot);
  mydata->pot.ObjectNameTitleIndex=reg_index;
  mydata->pot.ObjectNameTitle=0;
  mydata->pot.ObjectHelpTitleIndex=reg_index;
  mydata->pot.ObjectHelpTitle=0;
  mydata->pot.DetailLevel=PERF_DETAIL_NOVICE;
  mydata->pot.NumCounters=1; //yay!  1 counter
  mydata->pot.DefaultCounter=-1;
  mydata->pot.NumInstances=0;
  mydata->pot.CodePage=0;

  mydata->pcd.ByteLength=sizeof(mydata->pcd);
  mydata->pcd.CounterNameTitleIndex=reg_index+2; //you have to go by 2's.
  mydata->pcd.CounterNameTitle=0;
  mydata->pcd.CounterHelpTitleIndex=reg_index+2;
  mydata->pcd.CounterHelpTitle=0;
  mydata->pcd.DefaultScale=0; //scale of 1.0 (10^0)
  mydata->pcd.DetailLevel=PERF_DETAIL_NOVICE;
  mydata->pcd.CounterType=PERF_SIZE_DWORD|PERF_TYPE_NUMBER|PERF_NUMBER_DECIMAL|PERF_DISPLAY_NO_SUFFIX;
  mydata->pcd.CounterSize=sizeof(value);
  mydata->pcd.CounterOffset=sizeof(PERF_COUNTER_BLOCK); //offset past end of pcd structure to where actual data is

  mydata->pcdata.ByteLength=sizeof(DWORD)+sizeof(value); //size of this structure plus actualdata size

  mydata->actualdata=value; //YES!  This sets the value you're passing back!

  //simulate counting from 0 to 100 and back to 0
  if(updown)
  {
    if(++value>100)
    {
      value=100;
      updown=0;
    }
  }
  else
  {
    if(!--value)
    {
      value=0;
      updown=1;
    }
  }

  // slight change *lppData is a void, so we can't add to it. can we do it this way, I always got lost moving *'s and &'s !
  *lppData=(void *)((char *)*lppData+sizeof(struct _mydata));
  *lpcbTotalBytes = sizeof(struct _mydata);
  *lpNumObjectTypes = (DWORD)1;
  return ERROR_SUCCESS;
}

DWORD APIENTRY ClosePerfData()
{
      return ERROR_SUCCESS;
}




DWORD
GetQueryType (
    IN LPWSTR lpValue
)
/*++

GetQueryType

    returns the type of query described in the lpValue string so that
    the appropriate processing method may be used

Arguments

    IN lpValue
        string passed to PerfRegQuery Value for processing

Return Value

    QUERY_GLOBAL
        if lpValue == 0 (null pointer)
           lpValue == pointer to Null string
           lpValue == pointer to "Global" string

    QUERY_FOREIGN
        if lpValue == pointer to "Foreign" string

    QUERY_COSTLY
        if lpValue == pointer to "Costly" string

    otherwise:

    QUERY_ITEMS

--*/
{
    WCHAR   *pwcArgChar, *pwcTypeChar;
    BOOL    bFound;

    if (lpValue == 0) {
        return QUERY_GLOBAL;
    } else if (*lpValue == 0) {
        return QUERY_GLOBAL;
    }

    // check for "Global" request

    pwcArgChar = lpValue;
    pwcTypeChar = GLOBAL_STRING;
    bFound = TRUE;  // assume found until contradicted

    // check to the length of the shortest string
   
    while ((*pwcArgChar != 0) && (*pwcTypeChar != 0)) {
        if (*pwcArgChar++ != *pwcTypeChar++) {
            bFound = FALSE; // no match
            break;          // bail out now
        }
    }

    if (bFound) return QUERY_GLOBAL;

    // check for "Foreign" request
   
    pwcArgChar = lpValue;
    pwcTypeChar = FOREIGN_STRING;
    bFound = TRUE;  // assume found until contradicted

    // check to the length of the shortest string
   
    while ((*pwcArgChar != 0) && (*pwcTypeChar != 0)) {
        if (*pwcArgChar++ != *pwcTypeChar++) {
            bFound = FALSE; // no match
            break;          // bail out now
        }
    }

    if (bFound) return QUERY_FOREIGN;

    // check for "Costly" request
   
    pwcArgChar = lpValue;
    pwcTypeChar = COSTLY_STRING;
    bFound = TRUE;  // assume found until contradicted

    // check to the length of the shortest string
   
    while ((*pwcArgChar != 0) && (*pwcTypeChar != 0)) {
        if (*pwcArgChar++ != *pwcTypeChar++) {
            bFound = FALSE; // no match
            break;          // bail out now
        }
    }

    if (bFound) return QUERY_COSTLY;

    // if not Global and not Foreign and not Costly,
    // then it must be an item list
   
    return QUERY_ITEMS;

}

BOOL
IsNumberInUnicodeList (
    IN DWORD   dwNumber,
    IN LPWSTR  lpwszUnicodeList
)
/*++

IsNumberInUnicodeList

Arguments:
       
    IN dwNumber
        DWORD number to find in list

    IN lpwszUnicodeList
        Null terminated, Space delimited list of decimal numbers

Return Value:

    TRUE:
            dwNumber was found in the list of unicode number strings

    FALSE:
            dwNumber was not found in the list.

--*/
{
    DWORD   dwThisNumber;
    WCHAR   *pwcThisChar;
    BOOL    bValidNumber;
    BOOL    bNewItem;
    BOOL    bReturnValue;
    WCHAR   wcDelimiter;    // could be an argument to be more flexible

    if (lpwszUnicodeList == 0) return FALSE;    // null pointer, # not found

    pwcThisChar = lpwszUnicodeList;
    dwThisNumber = 0;
    wcDelimiter = (WCHAR)' ';
    bValidNumber = FALSE;
    bNewItem = TRUE;
   
    while (TRUE) {
        switch (EvalThisChar (*pwcThisChar, wcDelimiter)) {
            case DIGIT:
                // if this is the first digit after a delimiter, then
                // set flags to start computing the new number
                if (bNewItem) {
                    bNewItem = FALSE;
                    bValidNumber = TRUE;
                }
                if (bValidNumber) {
                    dwThisNumber *= 10;
                    dwThisNumber += (*pwcThisChar - (WCHAR)'0');
                }
                break;
           
            case DELIMITER:
                // a delimiter is either the delimiter character or the
                // end of the string ('\0') if when the delimiter has been
                // reached a valid number was found, then compare it to the
                // number from the argument list. if this is the end of the
                // string and no match was found, then return.
                //
                if (bValidNumber) {
                    if (dwThisNumber == dwNumber) return TRUE;
                    bValidNumber = FALSE;
                }
                if (*pwcThisChar == 0) {
                    return FALSE;
                } else {
                    bNewItem = TRUE;
                    dwThisNumber = 0;
                }
                break;

            case INVALID:
                // if an invalid character was encountered, ignore all
                // characters up to the next delimiter and then start fresh.
                // the invalid number is not compared.
                bValidNumber = FALSE;
                break;

            default:
                break;

        }
        pwcThisChar++;
    }

}   // IsNumberInUnicodeList
0
 

Author Comment

by:ianwhite
ID: 2459622
You are a star - that works a treat!
I'm going to add a little interface between a sample app and this dll, I think I'll count button clicks - they just want to see something!
Then I have to have our guys convert it to COBOL !!
Thanks again Brian, much appreciated.

Ooh, hope the MCP and the other 'window' thing went well ;-)
Ian
0
 

Author Comment

by:ianwhite
ID: 2459624
Thanks mate.
0

Featured Post

Will You Be GDPR Compliant by 5/28/2018?

GDPR? That's a regulation for the European Union. But, if you collect data from customers or employees within the EU, then you need to know about GDPR and make sure your organization is compliant by May 2018. Check out our preparation checklist to make sure you're on track today!

Question has a verified solution.

If you are experiencing a similar issue, please ask a related question

An Outlet in Cocoa is a persistent reference to a GUI control; it connects a property (a variable) to a control.  For example, it is common to create an Outlet for the text field GUI control and change the text that appears in this field via that Ou…
This tutorial is posted by Aaron Wojnowski, administrator at SDKExpert.net.  To view more iPhone tutorials, visit www.sdkexpert.net. This is a very simple tutorial on finding the user's current location easily. In this tutorial, you will learn ho…
The goal of this video is to provide viewers with basic examples to understand recursion in the C programming language.
The goal of this video is to provide viewers with basic examples to understand opening and reading files in the C programming language.

591 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question