cabinet: Add initial tests for FDI.
[wine] / dlls / pdh / pdh_main.c
1 /*
2  * Performance Data Helper (pdh.dll)
3  *
4  * Copyright 2007 Andrey Turkin
5  * Copyright 2007 Hans Leidekker
6  *
7  * This library is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU Lesser General Public
9  * License as published by the Free Software Foundation; either
10  * version 2.1 of the License, or (at your option) any later version.
11  *
12  * This library is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15  * Lesser General Public License for more details.
16  *
17  * You should have received a copy of the GNU Lesser General Public
18  * License along with this library; if not, write to the Free Software
19  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
20  */
21
22 #include <stdarg.h>
23 #include <math.h>
24
25 #define NONAMELESSUNION
26 #define NONAMELESSSTRUCT
27 #include "windef.h"
28 #include "winbase.h"
29
30 #include "pdh.h"
31 #include "pdhmsg.h"
32 #include "winperf.h"
33
34 #include "wine/debug.h"
35 #include "wine/list.h"
36 #include "wine/unicode.h"
37
38 WINE_DEFAULT_DEBUG_CHANNEL(pdh);
39
40 static inline void *pdh_alloc( SIZE_T size )
41 {
42     return HeapAlloc( GetProcessHeap(), 0, size );
43 }
44
45 static inline void *pdh_alloc_zero( SIZE_T size )
46 {
47     return HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY, size );
48 }
49
50 static inline void pdh_free( LPVOID mem )
51 {
52     HeapFree( GetProcessHeap(), 0, mem );
53 }
54
55 static inline WCHAR *pdh_strdup( const WCHAR *src )
56 {
57     WCHAR *dst;
58
59     if (!src) return NULL;
60     if ((dst = pdh_alloc( (strlenW( src ) + 1) * sizeof(WCHAR) ))) strcpyW( dst, src );
61     return dst;
62 }
63
64 static inline WCHAR *pdh_strdup_aw( const char *src )
65 {
66     int len;
67     WCHAR *dst;
68
69     if (!src) return NULL;
70     len = MultiByteToWideChar( CP_ACP, 0, src, -1, NULL, 0 );
71     if ((dst = pdh_alloc( len * sizeof(WCHAR) ))) MultiByteToWideChar( CP_ACP, 0, src, -1, dst, len );
72     return dst;
73 }
74
75 BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved)
76 {
77     TRACE("(0x%p, %d, %p)\n",hinstDLL,fdwReason,lpvReserved);
78
79     if (fdwReason == DLL_WINE_PREATTACH) return FALSE;    /* prefer native version */
80
81     if (fdwReason == DLL_PROCESS_ATTACH)
82     {
83         DisableThreadLibraryCalls( hinstDLL );
84     }
85
86     return TRUE;
87 }
88
89 struct counter
90 {
91     struct list     entry;
92     WCHAR          *path;                           /* identifier */
93     DWORD           type;                           /* counter type */
94     DWORD           status;                         /* update status */
95     LONG            scale;                          /* scale factor */
96     LONG            defaultscale;                   /* default scale factor */
97     DWORD_PTR       user;                           /* user data */
98     DWORD_PTR       queryuser;                      /* query user data */
99     LONGLONG        base;                           /* samples per second */
100     FILETIME        stamp;                          /* time stamp */
101     void (CALLBACK *collect)( struct counter * );   /* collect callback */
102     union
103     {
104         LONG     longvalue;
105         double   doublevalue;
106         LONGLONG largevalue;
107     } one;                                          /* first value */
108     union
109     {
110         LONG     longvalue;
111         double   doublevalue;
112         LONGLONG largevalue;
113     } two;                                          /* second value */
114 };
115
116 static struct counter *create_counter( void )
117 {
118     struct counter *counter;
119
120     if ((counter = pdh_alloc_zero( sizeof(struct counter) ))) return counter;
121     return NULL;
122 }
123
124 #define PDH_MAGIC_QUERY     0x50444830 /* 'PDH0' */
125
126 struct query
127 {
128     DWORD       magic;      /* signature */
129     DWORD_PTR   user;       /* user data */
130     struct list counters;   /* counter list */
131 };
132
133 static struct query *create_query( void )
134 {
135     struct query *query;
136
137     if ((query = pdh_alloc_zero( sizeof(struct query) )))
138     {
139         query->magic = PDH_MAGIC_QUERY;
140         list_init( &query->counters );
141         return query;
142     }
143     return NULL;
144 }
145
146 struct source
147 {
148     DWORD           index;                          /* name index */
149     const WCHAR    *path;                           /* identifier */
150     void (CALLBACK *collect)( struct counter * );   /* collect callback */
151     DWORD           type;                           /* counter type */
152     LONG            scale;                          /* default scale factor */
153     LONGLONG        base;                           /* samples per second */
154 };
155
156 static const WCHAR path_processor_time[] =
157     {'\\','P','r','o','c','e','s','s','o','r','(','_','T','o','t','a','l',')',
158      '\\','%',' ','P','r','o','c','e','s','s','o','r',' ','T','i','m','e',0};
159 static const WCHAR path_uptime[] =
160     {'\\','S','y','s','t','e','m', '\\', 'S','y','s','t','e','m',' ','U','p',' ','T','i','m','e',0};
161
162 static void CALLBACK collect_processor_time( struct counter *counter )
163 {
164     counter->two.largevalue = 500000; /* FIXME */
165     counter->status = PDH_CSTATUS_VALID_DATA;
166 }
167
168 static void CALLBACK collect_uptime( struct counter *counter )
169 {
170     counter->two.largevalue = GetTickCount64();
171     counter->status = PDH_CSTATUS_VALID_DATA;
172 }
173
174 #define TYPE_PROCESSOR_TIME \
175     (PERF_SIZE_LARGE | PERF_TYPE_COUNTER | PERF_COUNTER_RATE | PERF_TIMER_100NS | PERF_DELTA_COUNTER | \
176      PERF_INVERSE_COUNTER | PERF_DISPLAY_PERCENT)
177
178 #define TYPE_UPTIME \
179     (PERF_SIZE_LARGE | PERF_TYPE_COUNTER | PERF_COUNTER_ELAPSED | PERF_OBJECT_TIMER | PERF_DISPLAY_SECONDS)
180
181 /* counter source registry */
182 static const struct source counter_sources[] =
183 {
184     { 6,    path_processor_time,    collect_processor_time,     TYPE_PROCESSOR_TIME,    -5,     10000000 },
185     { 674,  path_uptime,            collect_uptime,             TYPE_UPTIME,            -3,     1000 }
186 };
187
188 static BOOL pdh_match_path( LPCWSTR fullpath, LPCWSTR path )
189 {
190     const WCHAR *p;
191
192     if (strchrW( path, '\\')) p = fullpath;
193     else p = strrchrW( fullpath, '\\' ) + 1;
194     if (strcmpW( p, path )) return FALSE;
195     return TRUE;
196 }
197
198 /***********************************************************************
199  *              PdhAddCounterA   (PDH.@)
200  */
201 PDH_STATUS WINAPI PdhAddCounterA( PDH_HQUERY query, LPCSTR path,
202                                   DWORD_PTR userdata, PDH_HCOUNTER *counter )
203 {
204     PDH_STATUS ret;
205     WCHAR *pathW;
206
207     TRACE("%p %s %lx %p\n", query, debugstr_a(path), userdata, counter);
208
209     if (!path) return PDH_INVALID_ARGUMENT;
210
211     if (!(pathW = pdh_strdup_aw( path )))
212         return PDH_MEMORY_ALLOCATION_FAILURE;
213
214     ret = PdhAddCounterW( query, pathW, userdata, counter );
215
216     pdh_free( pathW );
217     return ret;
218 }
219
220 /***********************************************************************
221  *              PdhAddCounterW   (PDH.@)
222  */
223 PDH_STATUS WINAPI PdhAddCounterW( PDH_HQUERY hquery, LPCWSTR path,
224                                   DWORD_PTR userdata, PDH_HCOUNTER *hcounter )
225 {
226     struct query *query = hquery;
227     struct counter *counter;
228     unsigned int i;
229
230     TRACE("%p %s %lx %p\n", hquery, debugstr_w(path), userdata, hcounter);
231
232     if (!path  || !hcounter) return PDH_INVALID_ARGUMENT;
233     if (!query || (query->magic != PDH_MAGIC_QUERY)) return PDH_INVALID_HANDLE;
234
235     *hcounter = NULL;
236     for (i = 0; i < sizeof(counter_sources) / sizeof(counter_sources[0]); i++)
237     {
238         if (pdh_match_path( counter_sources[i].path, path ))
239         {
240             if ((counter = create_counter()))
241             {
242                 counter->path         = pdh_strdup( counter_sources[i].path );
243                 counter->collect      = counter_sources[i].collect;
244                 counter->type         = counter_sources[i].type;
245                 counter->defaultscale = counter_sources[i].scale;
246                 counter->base         = counter_sources[i].base;
247                 counter->queryuser    = query->user;
248                 counter->user         = userdata;
249
250                 list_add_tail( &query->counters, &counter->entry );
251
252                 *hcounter = counter;
253                 return ERROR_SUCCESS;
254             }
255             return PDH_MEMORY_ALLOCATION_FAILURE;
256         }
257     }
258     return PDH_CSTATUS_NO_COUNTER;
259 }
260
261 /***********************************************************************
262  *              PdhAddEnglishCounterA   (PDH.@)
263  */
264 PDH_STATUS WINAPI PdhAddEnglishCounterA( PDH_HQUERY query, LPCSTR path,
265                                          DWORD_PTR userdata, PDH_HCOUNTER *counter )
266 {
267     return PdhAddCounterA( query, path, userdata, counter );
268 }
269
270 /***********************************************************************
271  *              PdhAddEnglishCounterW   (PDH.@)
272  */
273 PDH_STATUS WINAPI PdhAddEnglishCounterW( PDH_HQUERY query, LPCWSTR path,
274                                          DWORD_PTR userdata, PDH_HCOUNTER *counter )
275 {
276     return PdhAddCounterW( query, path, userdata, counter );
277 }
278
279 /***********************************************************************
280  *              PdhCloseQuery   (PDH.@)
281  */
282 PDH_STATUS WINAPI PdhCloseQuery( PDH_HQUERY handle )
283 {
284     struct query *query = handle;
285     struct list *item, *next;
286
287     TRACE("%p\n", handle);
288
289     if (!query || (query->magic != PDH_MAGIC_QUERY)) return PDH_INVALID_HANDLE;
290
291     LIST_FOR_EACH_SAFE( item, next, &query->counters )
292     {
293         struct counter *counter = LIST_ENTRY( item, struct counter, entry );
294
295         list_remove( &counter->entry );
296
297         pdh_free( counter->path );
298         pdh_free( counter );
299     }
300
301     query->magic = 0;
302     pdh_free( query );
303
304     return ERROR_SUCCESS;
305 }
306
307 /***********************************************************************
308  *              PdhCollectQueryData   (PDH.@)
309  */
310 PDH_STATUS WINAPI PdhCollectQueryData( PDH_HQUERY handle )
311 {
312     struct query *query = handle;
313     struct list *item;
314
315     TRACE("%p\n", handle);
316
317     if (!query || (query->magic != PDH_MAGIC_QUERY)) return PDH_INVALID_HANDLE;
318
319     LIST_FOR_EACH( item, &query->counters )
320     {
321         SYSTEMTIME time;
322         struct counter *counter = LIST_ENTRY( item, struct counter, entry );
323
324         counter->collect( counter );
325
326         GetLocalTime( &time );
327         SystemTimeToFileTime( &time, &counter->stamp );
328     }
329     return ERROR_SUCCESS;
330 }
331
332 /***********************************************************************
333  *              PdhCollectQueryDataWithTime   (PDH.@)
334  */
335 PDH_STATUS WINAPI PdhCollectQueryDataWithTime( PDH_HQUERY handle, LONGLONG *timestamp )
336 {
337     PDH_STATUS ret;
338     struct query *query = handle;
339
340     TRACE("%p %p\n", handle, timestamp);
341
342     if (!query || (query->magic != PDH_MAGIC_QUERY)) return PDH_INVALID_HANDLE;
343
344     if (list_empty( &query->counters )) return PDH_NO_DATA;
345
346     ret = PdhCollectQueryData( query );
347     if (!ret && timestamp)
348     {
349         struct list *item = list_head( &query->counters );
350         struct counter *counter = LIST_ENTRY( item, struct counter, entry );
351
352         *timestamp = ((LONGLONG)counter->stamp.dwHighDateTime << 32) | counter->stamp.dwLowDateTime;
353     }
354     return ret;
355 }
356
357 /***********************************************************************
358  *              PdhGetCounterInfoA   (PDH.@)
359  */
360 PDH_STATUS WINAPI PdhGetCounterInfoA( PDH_HCOUNTER handle, BOOLEAN text, LPDWORD size, PPDH_COUNTER_INFO_A info )
361 {
362     struct counter *counter = handle;
363
364     TRACE("%p %d %p %p\n", handle, text, size, info);
365
366     if (!counter) return PDH_INVALID_HANDLE;
367     if (!size)    return PDH_INVALID_ARGUMENT;
368
369     if (*size < sizeof(PDH_COUNTER_INFO_A))
370     {
371         *size = sizeof(PDH_COUNTER_INFO_A);
372         return PDH_MORE_DATA;
373     }
374
375     memset( info, 0, sizeof(PDH_COUNTER_INFO_A) );
376
377     info->dwType          = counter->type;
378     info->CStatus         = counter->status;
379     info->lScale          = counter->scale;
380     info->lDefaultScale   = counter->defaultscale;
381     info->dwUserData      = counter->user;
382     info->dwQueryUserData = counter->queryuser;
383
384     *size = sizeof(PDH_COUNTER_INFO_A);
385     return ERROR_SUCCESS;
386 }
387
388 /***********************************************************************
389  *              PdhGetCounterInfoW   (PDH.@)
390  */
391 PDH_STATUS WINAPI PdhGetCounterInfoW( PDH_HCOUNTER handle, BOOLEAN text, LPDWORD size, PPDH_COUNTER_INFO_W info )
392 {
393     struct counter *counter = handle;
394
395     TRACE("%p %d %p %p\n", handle, text, size, info);
396
397     if (!counter) return PDH_INVALID_HANDLE;
398     if (!size)    return PDH_INVALID_ARGUMENT;
399
400     if (*size < sizeof(PDH_COUNTER_INFO_W))
401     {
402         *size = sizeof(PDH_COUNTER_INFO_W);
403         return PDH_MORE_DATA;
404     }
405
406     memset( info, 0, sizeof(PDH_COUNTER_INFO_W) );
407
408     info->dwType          = counter->type;
409     info->CStatus         = counter->status;
410     info->lScale          = counter->scale;
411     info->lDefaultScale   = counter->defaultscale;
412     info->dwUserData      = counter->user;
413     info->dwQueryUserData = counter->queryuser;
414
415     *size = sizeof(PDH_COUNTER_INFO_W);
416     return ERROR_SUCCESS;
417 }
418
419 /***********************************************************************
420  *              PdhGetCounterTimeBase   (PDH.@)
421  */
422 PDH_STATUS WINAPI PdhGetCounterTimeBase( PDH_HCOUNTER handle, LONGLONG *base )
423 {
424     struct counter *counter = handle;
425
426     TRACE("%p %p\n", handle, base);
427
428     if (!base)    return PDH_INVALID_ARGUMENT;
429     if (!counter) return PDH_INVALID_HANDLE;
430
431     *base = counter->base;
432     return ERROR_SUCCESS;
433 }
434
435 /***********************************************************************
436  *              PdhGetFormattedCounterValue   (PDH.@)
437  */
438 PDH_STATUS WINAPI PdhGetFormattedCounterValue( PDH_HCOUNTER handle, DWORD format,
439                                                LPDWORD type, PPDH_FMT_COUNTERVALUE value )
440 {
441     LONG factor;
442     struct counter *counter = handle;
443
444     TRACE("%p %x %p %p\n", handle, format, type, value);
445
446     if (!value)   return PDH_INVALID_ARGUMENT;
447     if (!counter) return PDH_INVALID_HANDLE;
448
449     if (counter->status) return PDH_INVALID_DATA;
450
451     factor = counter->scale ? counter->scale : counter->defaultscale;
452     if (format & PDH_FMT_LONG)
453     {
454         if (format & PDH_FMT_1000) value->u.longValue = counter->two.longvalue * 1000;
455         else value->u.longValue = counter->two.longvalue * pow( 10, factor );
456     }
457     else if (format & PDH_FMT_LARGE)
458     {
459         if (format & PDH_FMT_1000) value->u.largeValue = counter->two.largevalue * 1000;
460         else value->u.largeValue = counter->two.largevalue * pow( 10, factor );
461     }
462     else if (format & PDH_FMT_DOUBLE)
463     {
464         if (format & PDH_FMT_1000) value->u.doubleValue = counter->two.doublevalue * 1000;
465         else value->u.doubleValue = counter->two.doublevalue * pow( 10, factor );
466     }
467     else
468     {
469         WARN("unknown format %x\n", format);
470         return PDH_INVALID_ARGUMENT;
471     }
472     value->CStatus = ERROR_SUCCESS;
473
474     if (type) *type = counter->type;
475     return ERROR_SUCCESS;
476 }
477
478 /***********************************************************************
479  *              PdhGetRawCounterValue   (PDH.@)
480  */
481 PDH_STATUS WINAPI PdhGetRawCounterValue( PDH_HCOUNTER handle, LPDWORD type,
482                                          PPDH_RAW_COUNTER value )
483 {
484     struct counter *counter = handle;
485
486     TRACE("%p %p %p\n", handle, type, value);
487
488     if (!value)   return PDH_INVALID_ARGUMENT;
489     if (!counter) return PDH_INVALID_HANDLE;
490
491     value->CStatus                  = counter->status;
492     value->TimeStamp.dwLowDateTime  = counter->stamp.dwLowDateTime;
493     value->TimeStamp.dwHighDateTime = counter->stamp.dwHighDateTime;
494     value->FirstValue               = counter->one.largevalue;
495     value->SecondValue              = counter->two.largevalue;
496     value->MultiCount               = 1; /* FIXME */
497
498     if (type) *type = counter->type;
499     return ERROR_SUCCESS;
500 }
501
502 /***********************************************************************
503  *              PdhLookupPerfIndexByNameA   (PDH.@)
504  */
505 PDH_STATUS WINAPI PdhLookupPerfIndexByNameA( LPCSTR machine, LPCSTR name, LPDWORD index )
506 {
507     PDH_STATUS ret;
508     WCHAR *nameW;
509
510     TRACE("%s %s %p\n", debugstr_a(machine), debugstr_a(name), index);
511
512     if (!name || !index) return PDH_INVALID_ARGUMENT;
513
514     if (machine)
515     {
516         FIXME("remote machine not supported\n");
517         return PDH_CSTATUS_NO_MACHINE;
518     }
519     if (!(nameW = pdh_strdup_aw( name )))
520         return PDH_MEMORY_ALLOCATION_FAILURE;
521
522     ret = PdhLookupPerfIndexByNameW( NULL, nameW, index );
523
524     pdh_free( nameW );
525     return ret;
526 }
527
528 /***********************************************************************
529  *              PdhLookupPerfIndexByNameW   (PDH.@)
530  */
531 PDH_STATUS WINAPI PdhLookupPerfIndexByNameW( LPCWSTR machine, LPCWSTR name, LPDWORD index )
532 {
533     unsigned int i;
534
535     TRACE("%s %s %p\n", debugstr_w(machine), debugstr_w(name), index);
536
537     if (!name || !index) return PDH_INVALID_ARGUMENT;
538
539     if (machine)
540     {
541         FIXME("remote machine not supported\n");
542         return PDH_CSTATUS_NO_MACHINE;
543     }
544     for (i = 0; i < sizeof(counter_sources) / sizeof(counter_sources[0]); i++)
545     {
546         if (pdh_match_path( counter_sources[i].path, name ))
547         {
548             *index = counter_sources[i].index;
549             return ERROR_SUCCESS;
550         }
551     }
552     return PDH_STRING_NOT_FOUND;
553 }
554
555 /***********************************************************************
556  *              PdhLookupPerfNameByIndexA   (PDH.@)
557  */
558 PDH_STATUS WINAPI PdhLookupPerfNameByIndexA( LPCSTR machine, DWORD index, LPSTR buffer, LPDWORD size )
559 {
560     PDH_STATUS ret;
561     WCHAR bufferW[PDH_MAX_COUNTER_NAME];
562     DWORD sizeW = sizeof(bufferW) / sizeof(WCHAR);
563
564     TRACE("%s %d %p %p\n", debugstr_a(machine), index, buffer, size);
565
566     if (machine)
567     {
568         FIXME("remote machine not supported\n");
569         return PDH_CSTATUS_NO_MACHINE;
570     }
571
572     if (!buffer && !size) return PDH_INVALID_ARGUMENT;
573     if (!index) return ERROR_SUCCESS;
574
575     if (!(ret = PdhLookupPerfNameByIndexW( NULL, index, bufferW, &sizeW )))
576     {
577         int required = WideCharToMultiByte( CP_ACP, 0, bufferW, -1, NULL, 0, NULL, NULL );
578
579         if (size && *size < required) ret = PDH_MORE_DATA;
580         else WideCharToMultiByte( CP_ACP, 0, bufferW, -1, buffer, required, NULL, NULL );
581         if (size) *size = required;
582     }
583     return ret;
584 }
585
586 /***********************************************************************
587  *              PdhLookupPerfNameByIndexW   (PDH.@)
588  */
589 PDH_STATUS WINAPI PdhLookupPerfNameByIndexW( LPCWSTR machine, DWORD index, LPWSTR buffer, LPDWORD size )
590 {
591     PDH_STATUS ret;
592     unsigned int i;
593
594     TRACE("%s %d %p %p\n", debugstr_w(machine), index, buffer, size);
595
596     if (machine)
597     {
598         FIXME("remote machine not supported\n");
599         return PDH_CSTATUS_NO_MACHINE;
600     }
601
602     if (!buffer && !size) return PDH_INVALID_ARGUMENT;
603     if (!index) return ERROR_SUCCESS;
604
605     for (i = 0; i < sizeof(counter_sources) / sizeof(counter_sources[0]); i++)
606     {
607         if (counter_sources[i].index == index)
608         {
609             WCHAR *p = strrchrW( counter_sources[i].path, '\\' ) + 1;
610             unsigned int required = strlenW( p ) + 1;
611
612             if (*size < required) ret = PDH_MORE_DATA;
613             else
614             {
615                 strcpyW( buffer, p );
616                 ret = ERROR_SUCCESS;
617             }
618             *size = required;
619             return ret;
620         }
621     }
622     return PDH_INVALID_ARGUMENT;
623 }
624
625 /***********************************************************************
626  *              PdhOpenQueryA   (PDH.@)
627  */
628 PDH_STATUS WINAPI PdhOpenQueryA( LPCSTR source, DWORD_PTR userdata, PDH_HQUERY *query )
629 {
630     PDH_STATUS ret;
631     WCHAR *sourceW = NULL;
632
633     TRACE("%s %lx %p\n", debugstr_a(source), userdata, query);
634
635     if (source && !(sourceW = pdh_strdup_aw( source ))) return PDH_MEMORY_ALLOCATION_FAILURE;
636
637     ret = PdhOpenQueryW( sourceW, userdata, query );
638     pdh_free( sourceW );
639
640     return ret;
641 }
642
643 /***********************************************************************
644  *              PdhOpenQueryW   (PDH.@)
645  */
646 PDH_STATUS WINAPI PdhOpenQueryW( LPCWSTR source, DWORD_PTR userdata, PDH_HQUERY *handle )
647 {
648     struct query *query;
649
650     TRACE("%s %lx %p\n", debugstr_w(source), userdata, handle);
651
652     if (!handle) return PDH_INVALID_ARGUMENT;
653
654     if (source)
655     {
656         FIXME("log file data source not supported\n");
657         return PDH_INVALID_ARGUMENT;
658     }
659     if ((query = create_query()))
660     {
661         query->user = userdata;
662         *handle = query;
663
664         return ERROR_SUCCESS;
665     }
666     return PDH_MEMORY_ALLOCATION_FAILURE;
667 }
668
669 /***********************************************************************
670  *              PdhRemoveCounter   (PDH.@)
671  */
672 PDH_STATUS WINAPI PdhRemoveCounter( PDH_HCOUNTER handle )
673 {
674     struct counter *counter = handle;
675
676     TRACE("%p\n", handle);
677
678     if (!counter) return PDH_INVALID_HANDLE;
679
680     list_remove( &counter->entry );
681
682     pdh_free( counter->path );
683     pdh_free( counter );
684
685     return ERROR_SUCCESS;
686 }
687
688 /***********************************************************************
689  *              PdhSetCounterScaleFactor   (PDH.@)
690  */
691 PDH_STATUS WINAPI PdhSetCounterScaleFactor( PDH_HCOUNTER handle, LONG factor )
692 {
693     struct counter *counter = handle;
694
695     TRACE("%p\n", handle);
696
697     if (!counter) return PDH_INVALID_HANDLE;
698     if (factor < PDH_MIN_SCALE || factor > PDH_MAX_SCALE) return PDH_INVALID_ARGUMENT;
699
700     counter->scale = factor;
701     return ERROR_SUCCESS;
702 }