msvcp90: Flip num_get<char/wchar_t> do_get_long and do_get_ulong in vtable.
[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 CRITICAL_SECTION pdh_handle_cs;
41 static CRITICAL_SECTION_DEBUG pdh_handle_cs_debug =
42 {
43     0, 0, &pdh_handle_cs,
44     { &pdh_handle_cs_debug.ProcessLocksList,
45       &pdh_handle_cs_debug.ProcessLocksList },
46       0, 0, { (DWORD_PTR)(__FILE__ ": pdh_handle_cs") }
47 };
48 static CRITICAL_SECTION pdh_handle_cs = { &pdh_handle_cs_debug, -1, 0, 0, 0, 0 };
49
50 static inline void *heap_alloc( SIZE_T size )
51 {
52     return HeapAlloc( GetProcessHeap(), 0, size );
53 }
54
55 static inline void *heap_alloc_zero( SIZE_T size )
56 {
57     return HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY, size );
58 }
59
60 static inline void heap_free( LPVOID mem )
61 {
62     HeapFree( GetProcessHeap(), 0, mem );
63 }
64
65 static inline WCHAR *pdh_strdup( const WCHAR *src )
66 {
67     WCHAR *dst;
68
69     if (!src) return NULL;
70     if ((dst = heap_alloc( (strlenW( src ) + 1) * sizeof(WCHAR) ))) strcpyW( dst, src );
71     return dst;
72 }
73
74 static inline WCHAR *pdh_strdup_aw( const char *src )
75 {
76     int len;
77     WCHAR *dst;
78
79     if (!src) return NULL;
80     len = MultiByteToWideChar( CP_ACP, 0, src, -1, NULL, 0 );
81     if ((dst = heap_alloc( len * sizeof(WCHAR) ))) MultiByteToWideChar( CP_ACP, 0, src, -1, dst, len );
82     return dst;
83 }
84
85 BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved)
86 {
87     TRACE("(0x%p, %d, %p)\n",hinstDLL,fdwReason,lpvReserved);
88     switch (fdwReason)
89     {
90     case DLL_WINE_PREATTACH:
91         return FALSE;    /* prefer native version */
92     case DLL_PROCESS_ATTACH:
93         DisableThreadLibraryCalls(hinstDLL);
94         break;
95     case DLL_PROCESS_DETACH:
96         DeleteCriticalSection(&pdh_handle_cs);
97         break;
98     }
99
100     return TRUE;
101 }
102
103 union value
104 {
105     LONG     longvalue;
106     double   doublevalue;
107     LONGLONG largevalue;
108 };
109
110 struct counter
111 {
112     DWORD           magic;                          /* signature */
113     struct list     entry;                          /* list entry */
114     WCHAR          *path;                           /* identifier */
115     DWORD           type;                           /* counter type */
116     DWORD           status;                         /* update status */
117     LONG            scale;                          /* scale factor */
118     LONG            defaultscale;                   /* default scale factor */
119     DWORD_PTR       user;                           /* user data */
120     DWORD_PTR       queryuser;                      /* query user data */
121     LONGLONG        base;                           /* samples per second */
122     FILETIME        stamp;                          /* time stamp */
123     void (CALLBACK *collect)( struct counter * );   /* collect callback */
124     union value     one;                            /* first value */
125     union value     two;                            /* second value */
126 };
127
128 #define PDH_MAGIC_COUNTER   0x50444831 /* 'PDH1' */
129
130 static struct counter *create_counter( void )
131 {
132     struct counter *counter;
133
134     if ((counter = heap_alloc_zero( sizeof(struct counter) )))
135     {
136         counter->magic = PDH_MAGIC_COUNTER;
137         return counter;
138     }
139     return NULL;
140 }
141
142 static void destroy_counter( struct counter *counter )
143 {
144     counter->magic = 0;
145     heap_free( counter->path );
146     heap_free( counter );
147 }
148
149 #define PDH_MAGIC_QUERY     0x50444830 /* 'PDH0' */
150
151 struct query
152 {
153     DWORD       magic;      /* signature */
154     DWORD_PTR   user;       /* user data */
155     HANDLE      thread;     /* collect thread */
156     DWORD       interval;   /* collect interval */
157     HANDLE      wait;       /* wait event */
158     HANDLE      stop;       /* stop event */
159     struct list counters;   /* counter list */
160 };
161
162 static struct query *create_query( void )
163 {
164     struct query *query;
165
166     if ((query = heap_alloc_zero( sizeof(struct query) )))
167     {
168         query->magic = PDH_MAGIC_QUERY;
169         list_init( &query->counters );
170         return query;
171     }
172     return NULL;
173 }
174
175 static void destroy_query( struct query *query )
176 {
177     query->magic = 0;
178     heap_free( query );
179 }
180
181 struct source
182 {
183     DWORD           index;                          /* name index */
184     const WCHAR    *path;                           /* identifier */
185     void (CALLBACK *collect)( struct counter * );   /* collect callback */
186     DWORD           type;                           /* counter type */
187     LONG            scale;                          /* default scale factor */
188     LONGLONG        base;                           /* samples per second */
189 };
190
191 static const WCHAR path_processor_time[] =
192     {'\\','P','r','o','c','e','s','s','o','r','(','_','T','o','t','a','l',')',
193      '\\','%',' ','P','r','o','c','e','s','s','o','r',' ','T','i','m','e',0};
194 static const WCHAR path_uptime[] =
195     {'\\','S','y','s','t','e','m', '\\', 'S','y','s','t','e','m',' ','U','p',' ','T','i','m','e',0};
196
197 static void CALLBACK collect_processor_time( struct counter *counter )
198 {
199     counter->two.largevalue = 500000; /* FIXME */
200     counter->status = PDH_CSTATUS_VALID_DATA;
201 }
202
203 static void CALLBACK collect_uptime( struct counter *counter )
204 {
205     counter->two.largevalue = GetTickCount64();
206     counter->status = PDH_CSTATUS_VALID_DATA;
207 }
208
209 #define TYPE_PROCESSOR_TIME \
210     (PERF_SIZE_LARGE | PERF_TYPE_COUNTER | PERF_COUNTER_RATE | PERF_TIMER_100NS | PERF_DELTA_COUNTER | \
211      PERF_INVERSE_COUNTER | PERF_DISPLAY_PERCENT)
212
213 #define TYPE_UPTIME \
214     (PERF_SIZE_LARGE | PERF_TYPE_COUNTER | PERF_COUNTER_ELAPSED | PERF_OBJECT_TIMER | PERF_DISPLAY_SECONDS)
215
216 /* counter source registry */
217 static const struct source counter_sources[] =
218 {
219     { 6,    path_processor_time,    collect_processor_time,     TYPE_PROCESSOR_TIME,    -5,     10000000 },
220     { 674,  path_uptime,            collect_uptime,             TYPE_UPTIME,            -3,     1000 }
221 };
222
223 static BOOL pdh_match_path( LPCWSTR fullpath, LPCWSTR path )
224 {
225     const WCHAR *p;
226
227     if (strchrW( path, '\\')) p = fullpath;
228     else p = strrchrW( fullpath, '\\' ) + 1;
229     if (strcmpW( p, path )) return FALSE;
230     return TRUE;
231 }
232
233 /***********************************************************************
234  *              PdhAddCounterA   (PDH.@)
235  */
236 PDH_STATUS WINAPI PdhAddCounterA( PDH_HQUERY query, LPCSTR path,
237                                   DWORD_PTR userdata, PDH_HCOUNTER *counter )
238 {
239     PDH_STATUS ret;
240     WCHAR *pathW;
241
242     TRACE("%p %s %lx %p\n", query, debugstr_a(path), userdata, counter);
243
244     if (!path) return PDH_INVALID_ARGUMENT;
245
246     if (!(pathW = pdh_strdup_aw( path )))
247         return PDH_MEMORY_ALLOCATION_FAILURE;
248
249     ret = PdhAddCounterW( query, pathW, userdata, counter );
250
251     heap_free( pathW );
252     return ret;
253 }
254
255 /***********************************************************************
256  *              PdhAddCounterW   (PDH.@)
257  */
258 PDH_STATUS WINAPI PdhAddCounterW( PDH_HQUERY hquery, LPCWSTR path,
259                                   DWORD_PTR userdata, PDH_HCOUNTER *hcounter )
260 {
261     struct query *query = hquery;
262     struct counter *counter;
263     unsigned int i;
264
265     TRACE("%p %s %lx %p\n", hquery, debugstr_w(path), userdata, hcounter);
266
267     if (!path  || !hcounter) return PDH_INVALID_ARGUMENT;
268
269     EnterCriticalSection( &pdh_handle_cs );
270     if (!query || query->magic != PDH_MAGIC_QUERY)
271     {
272         LeaveCriticalSection( &pdh_handle_cs );
273         return PDH_INVALID_HANDLE;
274     }
275
276     *hcounter = NULL;
277     for (i = 0; i < sizeof(counter_sources) / sizeof(counter_sources[0]); i++)
278     {
279         if (pdh_match_path( counter_sources[i].path, path ))
280         {
281             if ((counter = create_counter()))
282             {
283                 counter->path         = pdh_strdup( counter_sources[i].path );
284                 counter->collect      = counter_sources[i].collect;
285                 counter->type         = counter_sources[i].type;
286                 counter->defaultscale = counter_sources[i].scale;
287                 counter->base         = counter_sources[i].base;
288                 counter->queryuser    = query->user;
289                 counter->user         = userdata;
290
291                 list_add_tail( &query->counters, &counter->entry );
292                 *hcounter = counter;
293
294                 LeaveCriticalSection( &pdh_handle_cs );
295                 return ERROR_SUCCESS;
296             }
297             LeaveCriticalSection( &pdh_handle_cs );
298             return PDH_MEMORY_ALLOCATION_FAILURE;
299         }
300     }
301     LeaveCriticalSection( &pdh_handle_cs );
302     return PDH_CSTATUS_NO_COUNTER;
303 }
304
305 /***********************************************************************
306  *              PdhAddEnglishCounterA   (PDH.@)
307  */
308 PDH_STATUS WINAPI PdhAddEnglishCounterA( PDH_HQUERY query, LPCSTR path,
309                                          DWORD_PTR userdata, PDH_HCOUNTER *counter )
310 {
311     TRACE("%p %s %lx %p\n", query, debugstr_a(path), userdata, counter);
312
313     if (!query) return PDH_INVALID_ARGUMENT;
314     return PdhAddCounterA( query, path, userdata, counter );
315 }
316
317 /***********************************************************************
318  *              PdhAddEnglishCounterW   (PDH.@)
319  */
320 PDH_STATUS WINAPI PdhAddEnglishCounterW( PDH_HQUERY query, LPCWSTR path,
321                                          DWORD_PTR userdata, PDH_HCOUNTER *counter )
322 {
323     TRACE("%p %s %lx %p\n", query, debugstr_w(path), userdata, counter);
324
325     if (!query) return PDH_INVALID_ARGUMENT;
326     return PdhAddCounterW( query, path, userdata, counter );
327 }
328
329 /* caller must hold counter lock */
330 static PDH_STATUS format_value( struct counter *counter, DWORD format, union value *raw1,
331                                 union value *raw2, PDH_FMT_COUNTERVALUE *value )
332 {
333     LONG factor;
334
335     factor = counter->scale ? counter->scale : counter->defaultscale;
336     if (format & PDH_FMT_LONG)
337     {
338         if (format & PDH_FMT_1000) value->u.longValue = raw2->longvalue * 1000;
339         else value->u.longValue = raw2->longvalue * pow( 10, factor );
340     }
341     else if (format & PDH_FMT_LARGE)
342     {
343         if (format & PDH_FMT_1000) value->u.largeValue = raw2->largevalue * 1000;
344         else value->u.largeValue = raw2->largevalue * pow( 10, factor );
345     }
346     else if (format & PDH_FMT_DOUBLE)
347     {
348         if (format & PDH_FMT_1000) value->u.doubleValue = raw2->doublevalue * 1000;
349         else value->u.doubleValue = raw2->doublevalue * pow( 10, factor );
350     }
351     else
352     {
353         WARN("unknown format %x\n", format);
354         return PDH_INVALID_ARGUMENT;
355     }
356     return ERROR_SUCCESS;
357 }
358
359 /***********************************************************************
360  *              PdhCalculateCounterFromRawValue   (PDH.@)
361  */
362 PDH_STATUS WINAPI PdhCalculateCounterFromRawValue( PDH_HCOUNTER handle, DWORD format,
363                                                    PPDH_RAW_COUNTER raw1, PPDH_RAW_COUNTER raw2,
364                                                    PPDH_FMT_COUNTERVALUE value )
365 {
366     PDH_STATUS ret;
367     struct counter *counter = handle;
368
369     TRACE("%p 0x%08x %p %p %p\n", handle, format, raw1, raw2, value);
370
371     if (!value) return PDH_INVALID_ARGUMENT;
372
373     EnterCriticalSection( &pdh_handle_cs );
374     if (!counter || counter->magic != PDH_MAGIC_COUNTER)
375     {
376         LeaveCriticalSection( &pdh_handle_cs );
377         return PDH_INVALID_HANDLE;
378     }
379
380     ret = format_value( counter, format, (union value *)&raw1->SecondValue,
381                                          (union value *)&raw2->SecondValue, value );
382
383     LeaveCriticalSection( &pdh_handle_cs );
384     return ret;
385 }
386
387
388 /***********************************************************************
389  *              PdhCloseQuery   (PDH.@)
390  */
391 PDH_STATUS WINAPI PdhCloseQuery( PDH_HQUERY handle )
392 {
393     struct query *query = handle;
394     struct list *item, *next;
395
396     TRACE("%p\n", handle);
397
398     EnterCriticalSection( &pdh_handle_cs );
399     if (!query || query->magic != PDH_MAGIC_QUERY)
400     {
401         LeaveCriticalSection( &pdh_handle_cs );
402         return PDH_INVALID_HANDLE;
403     }
404
405     if (query->thread)
406     {
407         HANDLE thread = query->thread;
408         SetEvent( query->stop );
409         LeaveCriticalSection( &pdh_handle_cs );
410
411         WaitForSingleObject( thread, INFINITE );
412
413         EnterCriticalSection( &pdh_handle_cs );
414         if (query->magic != PDH_MAGIC_QUERY)
415         {
416             LeaveCriticalSection( &pdh_handle_cs );
417             return ERROR_SUCCESS;
418         }
419         CloseHandle( query->stop );
420         CloseHandle( query->thread );
421         query->thread = NULL;
422     }
423
424     LIST_FOR_EACH_SAFE( item, next, &query->counters )
425     {
426         struct counter *counter = LIST_ENTRY( item, struct counter, entry );
427
428         list_remove( &counter->entry );
429         destroy_counter( counter );
430     }
431
432     destroy_query( query );
433
434     LeaveCriticalSection( &pdh_handle_cs );
435     return ERROR_SUCCESS;
436 }
437
438 /* caller must hold query lock */
439 static void collect_query_data( struct query *query )
440 {
441     struct list *item;
442
443     LIST_FOR_EACH( item, &query->counters )
444     {
445         SYSTEMTIME time;
446         struct counter *counter = LIST_ENTRY( item, struct counter, entry );
447
448         counter->collect( counter );
449
450         GetLocalTime( &time );
451         SystemTimeToFileTime( &time, &counter->stamp );
452     }
453 }
454
455 /***********************************************************************
456  *              PdhCollectQueryData   (PDH.@)
457  */
458 PDH_STATUS WINAPI PdhCollectQueryData( PDH_HQUERY handle )
459 {
460     struct query *query = handle;
461
462     TRACE("%p\n", handle);
463
464     EnterCriticalSection( &pdh_handle_cs );
465     if (!query || query->magic != PDH_MAGIC_QUERY)
466     {
467         LeaveCriticalSection( &pdh_handle_cs );
468         return PDH_INVALID_HANDLE;
469     }
470
471     if (list_empty( &query->counters ))
472     {
473         LeaveCriticalSection( &pdh_handle_cs );
474         return PDH_NO_DATA;
475     }
476
477     collect_query_data( query );
478
479     LeaveCriticalSection( &pdh_handle_cs );
480     return ERROR_SUCCESS;
481 }
482
483 static DWORD CALLBACK collect_query_thread( void *arg )
484 {
485     struct query *query = arg;
486     DWORD interval = query->interval;
487     HANDLE stop = query->stop;
488
489     for (;;)
490     {
491         if (WaitForSingleObject( stop, interval ) != WAIT_TIMEOUT) ExitThread( 0 );
492
493         EnterCriticalSection( &pdh_handle_cs );
494         if (query->magic != PDH_MAGIC_QUERY)
495         {
496             LeaveCriticalSection( &pdh_handle_cs );
497             ExitThread( PDH_INVALID_HANDLE );
498         }
499
500         collect_query_data( query );
501
502         if (!SetEvent( query->wait ))
503         {
504             LeaveCriticalSection( &pdh_handle_cs );
505             ExitThread( 0 );
506         }
507         LeaveCriticalSection( &pdh_handle_cs );
508     }
509 }
510
511 /***********************************************************************
512  *              PdhCollectQueryDataEx   (PDH.@)
513  */
514 PDH_STATUS WINAPI PdhCollectQueryDataEx( PDH_HQUERY handle, DWORD interval, HANDLE event )
515 {
516     PDH_STATUS ret;
517     struct query *query = handle;
518
519     TRACE("%p %d %p\n", handle, interval, event);
520
521     EnterCriticalSection( &pdh_handle_cs );
522     if (!query || query->magic != PDH_MAGIC_QUERY)
523     {
524         LeaveCriticalSection( &pdh_handle_cs );
525         return PDH_INVALID_HANDLE;
526     }
527     if (list_empty( &query->counters ))
528     {
529         LeaveCriticalSection( &pdh_handle_cs );
530         return PDH_NO_DATA;
531     }
532     if (query->thread)
533     {
534         HANDLE thread = query->thread;
535         SetEvent( query->stop );
536         LeaveCriticalSection( &pdh_handle_cs );
537
538         WaitForSingleObject( thread, INFINITE );
539
540         EnterCriticalSection( &pdh_handle_cs );
541         if (query->magic != PDH_MAGIC_QUERY)
542         {
543             LeaveCriticalSection( &pdh_handle_cs );
544             return PDH_INVALID_HANDLE;
545         }
546         CloseHandle( query->thread );
547         query->thread = NULL;
548     }
549     else if (!(query->stop = CreateEventW( NULL, FALSE, FALSE, NULL )))
550     {
551         ret = GetLastError();
552         LeaveCriticalSection( &pdh_handle_cs );
553         return ret;
554     }
555     query->wait = event;
556     query->interval = interval * 1000;
557     if (!(query->thread = CreateThread( NULL, 0, collect_query_thread, query, 0, NULL )))
558     {
559         ret = GetLastError();
560         CloseHandle( query->stop );
561
562         LeaveCriticalSection( &pdh_handle_cs );
563         return ret;
564     }
565
566     LeaveCriticalSection( &pdh_handle_cs );
567     return ERROR_SUCCESS;
568 }
569
570 /***********************************************************************
571  *              PdhCollectQueryDataWithTime   (PDH.@)
572  */
573 PDH_STATUS WINAPI PdhCollectQueryDataWithTime( PDH_HQUERY handle, LONGLONG *timestamp )
574 {
575     struct query *query = handle;
576     struct counter *counter;
577     struct list *item;
578
579     TRACE("%p %p\n", handle, timestamp);
580
581     if (!timestamp) return PDH_INVALID_ARGUMENT;
582
583     EnterCriticalSection( &pdh_handle_cs );
584     if (!query || query->magic != PDH_MAGIC_QUERY)
585     {
586         LeaveCriticalSection( &pdh_handle_cs );
587         return PDH_INVALID_HANDLE;
588     }
589     if (list_empty( &query->counters ))
590     {
591         LeaveCriticalSection( &pdh_handle_cs );
592         return PDH_NO_DATA;
593     }
594
595     collect_query_data( query );
596
597     item = list_head( &query->counters );
598     counter = LIST_ENTRY( item, struct counter, entry );
599
600     *timestamp = ((LONGLONG)counter->stamp.dwHighDateTime << 32) | counter->stamp.dwLowDateTime;
601
602     LeaveCriticalSection( &pdh_handle_cs );
603     return ERROR_SUCCESS;
604 }
605
606 /***********************************************************************
607  *              PdhExpandWildCardPathA   (PDH.@)
608  */
609 PDH_STATUS WINAPI PdhExpandWildCardPathA( LPCSTR szDataSource, LPCSTR szWildCardPath, LPSTR mszExpandedPathList, LPDWORD pcchPathListLength, DWORD dwFlags )
610 {
611     FIXME("%s, %s, %p, %p, 0x%x: stub\n", debugstr_a(szDataSource), debugstr_a(szWildCardPath), mszExpandedPathList, pcchPathListLength, dwFlags);
612     return PDH_NOT_IMPLEMENTED;
613 }
614
615 /***********************************************************************
616  *              PdhExpandWildCardPathW   (PDH.@)
617  */
618 PDH_STATUS WINAPI PdhExpandWildCardPathW( LPCWSTR szDataSource, LPCWSTR szWildCardPath, LPWSTR mszExpandedPathList, LPDWORD pcchPathListLength, DWORD dwFlags )
619 {
620     FIXME("%s, %s, %p, %p, 0x%x: stub\n", debugstr_w(szDataSource), debugstr_w(szWildCardPath), mszExpandedPathList, pcchPathListLength, dwFlags);
621     return PDH_NOT_IMPLEMENTED;
622 }
623
624 /***********************************************************************
625  *              PdhGetCounterInfoA   (PDH.@)
626  */
627 PDH_STATUS WINAPI PdhGetCounterInfoA( PDH_HCOUNTER handle, BOOLEAN text, LPDWORD size, PPDH_COUNTER_INFO_A info )
628 {
629     struct counter *counter = handle;
630
631     TRACE("%p %d %p %p\n", handle, text, size, info);
632
633     EnterCriticalSection( &pdh_handle_cs );
634     if (!counter || counter->magic != PDH_MAGIC_COUNTER)
635     {
636         LeaveCriticalSection( &pdh_handle_cs );
637         return PDH_INVALID_HANDLE;
638     }
639     if (!size)
640     {
641         LeaveCriticalSection( &pdh_handle_cs );
642         return PDH_INVALID_ARGUMENT;
643     }
644     if (*size < sizeof(PDH_COUNTER_INFO_A))
645     {
646         *size = sizeof(PDH_COUNTER_INFO_A);
647         LeaveCriticalSection( &pdh_handle_cs );
648         return PDH_MORE_DATA;
649     }
650
651     memset( info, 0, sizeof(PDH_COUNTER_INFO_A) );
652
653     info->dwType          = counter->type;
654     info->CStatus         = counter->status;
655     info->lScale          = counter->scale;
656     info->lDefaultScale   = counter->defaultscale;
657     info->dwUserData      = counter->user;
658     info->dwQueryUserData = counter->queryuser;
659
660     *size = sizeof(PDH_COUNTER_INFO_A);
661
662     LeaveCriticalSection( &pdh_handle_cs );
663     return ERROR_SUCCESS;
664 }
665
666 /***********************************************************************
667  *              PdhGetCounterInfoW   (PDH.@)
668  */
669 PDH_STATUS WINAPI PdhGetCounterInfoW( PDH_HCOUNTER handle, BOOLEAN text, LPDWORD size, PPDH_COUNTER_INFO_W info )
670 {
671     struct counter *counter = handle;
672
673     TRACE("%p %d %p %p\n", handle, text, size, info);
674
675     EnterCriticalSection( &pdh_handle_cs );
676     if (!counter || counter->magic != PDH_MAGIC_COUNTER)
677     {
678         LeaveCriticalSection( &pdh_handle_cs );
679         return PDH_INVALID_HANDLE;
680     }
681     if (!size)
682     {
683         LeaveCriticalSection( &pdh_handle_cs );
684         return PDH_INVALID_ARGUMENT;
685     }
686     if (*size < sizeof(PDH_COUNTER_INFO_W))
687     {
688         *size = sizeof(PDH_COUNTER_INFO_W);
689         LeaveCriticalSection( &pdh_handle_cs );
690         return PDH_MORE_DATA;
691     }
692
693     memset( info, 0, sizeof(PDH_COUNTER_INFO_W) );
694
695     info->dwType          = counter->type;
696     info->CStatus         = counter->status;
697     info->lScale          = counter->scale;
698     info->lDefaultScale   = counter->defaultscale;
699     info->dwUserData      = counter->user;
700     info->dwQueryUserData = counter->queryuser;
701
702     *size = sizeof(PDH_COUNTER_INFO_W);
703
704     LeaveCriticalSection( &pdh_handle_cs );
705     return ERROR_SUCCESS;
706 }
707
708 /***********************************************************************
709  *              PdhGetCounterTimeBase   (PDH.@)
710  */
711 PDH_STATUS WINAPI PdhGetCounterTimeBase( PDH_HCOUNTER handle, LONGLONG *base )
712 {
713     struct counter *counter = handle;
714
715     TRACE("%p %p\n", handle, base);
716
717     if (!base) return PDH_INVALID_ARGUMENT;
718
719     EnterCriticalSection( &pdh_handle_cs );
720     if (!counter || counter->magic != PDH_MAGIC_COUNTER)
721     {
722         LeaveCriticalSection( &pdh_handle_cs );
723         return PDH_INVALID_HANDLE;
724     }
725
726     *base = counter->base;
727
728     LeaveCriticalSection( &pdh_handle_cs );
729     return ERROR_SUCCESS;
730 }
731
732 /***********************************************************************
733  *              PdhGetDllVersion   (PDH.@)
734  */
735 PDH_STATUS WINAPI PdhGetDllVersion( LPDWORD version )
736 {
737     if (!version)
738         return PDH_INVALID_ARGUMENT;
739
740     *version = PDH_VERSION;
741
742     return ERROR_SUCCESS;
743 }
744
745 /***********************************************************************
746  *              PdhGetFormattedCounterValue   (PDH.@)
747  */
748 PDH_STATUS WINAPI PdhGetFormattedCounterValue( PDH_HCOUNTER handle, DWORD format,
749                                                LPDWORD type, PPDH_FMT_COUNTERVALUE value )
750 {
751     PDH_STATUS ret;
752     struct counter *counter = handle;
753
754     TRACE("%p %x %p %p\n", handle, format, type, value);
755
756     if (!value) return PDH_INVALID_ARGUMENT;
757
758     EnterCriticalSection( &pdh_handle_cs );
759     if (!counter || counter->magic != PDH_MAGIC_COUNTER)
760     {
761         LeaveCriticalSection( &pdh_handle_cs );
762         return PDH_INVALID_HANDLE;
763     }
764     if (counter->status)
765     {
766         LeaveCriticalSection( &pdh_handle_cs );
767         return PDH_INVALID_DATA;
768     }
769     if (!(ret = format_value( counter, format, &counter->one, &counter->two, value )))
770     {
771         value->CStatus = ERROR_SUCCESS;
772         if (type) *type = counter->type;
773     }
774
775     LeaveCriticalSection( &pdh_handle_cs );
776     return ret;
777 }
778
779 /***********************************************************************
780  *              PdhGetRawCounterValue   (PDH.@)
781  */
782 PDH_STATUS WINAPI PdhGetRawCounterValue( PDH_HCOUNTER handle, LPDWORD type,
783                                          PPDH_RAW_COUNTER value )
784 {
785     struct counter *counter = handle;
786
787     TRACE("%p %p %p\n", handle, type, value);
788
789     if (!value) return PDH_INVALID_ARGUMENT;
790
791     EnterCriticalSection( &pdh_handle_cs );
792     if (!counter || counter->magic != PDH_MAGIC_COUNTER)
793     {
794         LeaveCriticalSection( &pdh_handle_cs );
795         return PDH_INVALID_HANDLE;
796     }
797
798     value->CStatus                  = counter->status;
799     value->TimeStamp.dwLowDateTime  = counter->stamp.dwLowDateTime;
800     value->TimeStamp.dwHighDateTime = counter->stamp.dwHighDateTime;
801     value->FirstValue               = counter->one.largevalue;
802     value->SecondValue              = counter->two.largevalue;
803     value->MultiCount               = 1; /* FIXME */
804
805     if (type) *type = counter->type;
806
807     LeaveCriticalSection( &pdh_handle_cs );
808     return ERROR_SUCCESS;
809 }
810
811 /***********************************************************************
812  *              PdhLookupPerfIndexByNameA   (PDH.@)
813  */
814 PDH_STATUS WINAPI PdhLookupPerfIndexByNameA( LPCSTR machine, LPCSTR name, LPDWORD index )
815 {
816     PDH_STATUS ret;
817     WCHAR *machineW = NULL;
818     WCHAR *nameW;
819
820     TRACE("%s %s %p\n", debugstr_a(machine), debugstr_a(name), index);
821
822     if (!name) return PDH_INVALID_ARGUMENT;
823
824     if (machine && !(machineW = pdh_strdup_aw( machine ))) return PDH_MEMORY_ALLOCATION_FAILURE;
825
826     if (!(nameW = pdh_strdup_aw( name )))
827         return PDH_MEMORY_ALLOCATION_FAILURE;
828
829     ret = PdhLookupPerfIndexByNameW( machineW, nameW, index );
830
831     heap_free( nameW );
832     heap_free( machineW );
833     return ret;
834 }
835
836 /***********************************************************************
837  *              PdhLookupPerfIndexByNameW   (PDH.@)
838  */
839 PDH_STATUS WINAPI PdhLookupPerfIndexByNameW( LPCWSTR machine, LPCWSTR name, LPDWORD index )
840 {
841     unsigned int i;
842
843     TRACE("%s %s %p\n", debugstr_w(machine), debugstr_w(name), index);
844
845     if (!name || !index) return PDH_INVALID_ARGUMENT;
846
847     if (machine)
848     {
849         FIXME("remote machine not supported\n");
850         return PDH_CSTATUS_NO_MACHINE;
851     }
852     for (i = 0; i < sizeof(counter_sources) / sizeof(counter_sources[0]); i++)
853     {
854         if (pdh_match_path( counter_sources[i].path, name ))
855         {
856             *index = counter_sources[i].index;
857             return ERROR_SUCCESS;
858         }
859     }
860     return PDH_STRING_NOT_FOUND;
861 }
862
863 /***********************************************************************
864  *              PdhLookupPerfNameByIndexA   (PDH.@)
865  */
866 PDH_STATUS WINAPI PdhLookupPerfNameByIndexA( LPCSTR machine, DWORD index, LPSTR buffer, LPDWORD size )
867 {
868     PDH_STATUS ret;
869     WCHAR *machineW = NULL;
870     WCHAR bufferW[PDH_MAX_COUNTER_NAME];
871     DWORD sizeW = sizeof(bufferW) / sizeof(WCHAR);
872
873     TRACE("%s %d %p %p\n", debugstr_a(machine), index, buffer, size);
874
875     if (!buffer || !size) return PDH_INVALID_ARGUMENT;
876
877     if (machine && !(machineW = pdh_strdup_aw( machine ))) return PDH_MEMORY_ALLOCATION_FAILURE;
878
879     if (!(ret = PdhLookupPerfNameByIndexW( machineW, index, bufferW, &sizeW )))
880     {
881         int required = WideCharToMultiByte( CP_ACP, 0, bufferW, -1, NULL, 0, NULL, NULL );
882
883         if (size && *size < required) ret = PDH_MORE_DATA;
884         else WideCharToMultiByte( CP_ACP, 0, bufferW, -1, buffer, required, NULL, NULL );
885         if (size) *size = required;
886     }
887     heap_free( machineW );
888     return ret;
889 }
890
891 /***********************************************************************
892  *              PdhLookupPerfNameByIndexW   (PDH.@)
893  */
894 PDH_STATUS WINAPI PdhLookupPerfNameByIndexW( LPCWSTR machine, DWORD index, LPWSTR buffer, LPDWORD size )
895 {
896     PDH_STATUS ret;
897     unsigned int i;
898
899     TRACE("%s %d %p %p\n", debugstr_w(machine), index, buffer, size);
900
901     if (machine)
902     {
903         FIXME("remote machine not supported\n");
904         return PDH_CSTATUS_NO_MACHINE;
905     }
906
907     if (!buffer || !size) return PDH_INVALID_ARGUMENT;
908     if (!index) return ERROR_SUCCESS;
909
910     for (i = 0; i < sizeof(counter_sources) / sizeof(counter_sources[0]); i++)
911     {
912         if (counter_sources[i].index == index)
913         {
914             WCHAR *p = strrchrW( counter_sources[i].path, '\\' ) + 1;
915             unsigned int required = strlenW( p ) + 1;
916
917             if (*size < required) ret = PDH_MORE_DATA;
918             else
919             {
920                 strcpyW( buffer, p );
921                 ret = ERROR_SUCCESS;
922             }
923             *size = required;
924             return ret;
925         }
926     }
927     return PDH_INVALID_ARGUMENT;
928 }
929
930 /***********************************************************************
931  *              PdhOpenQueryA   (PDH.@)
932  */
933 PDH_STATUS WINAPI PdhOpenQueryA( LPCSTR source, DWORD_PTR userdata, PDH_HQUERY *query )
934 {
935     PDH_STATUS ret;
936     WCHAR *sourceW = NULL;
937
938     TRACE("%s %lx %p\n", debugstr_a(source), userdata, query);
939
940     if (source && !(sourceW = pdh_strdup_aw( source ))) return PDH_MEMORY_ALLOCATION_FAILURE;
941
942     ret = PdhOpenQueryW( sourceW, userdata, query );
943     heap_free( sourceW );
944
945     return ret;
946 }
947
948 /***********************************************************************
949  *              PdhOpenQueryW   (PDH.@)
950  */
951 PDH_STATUS WINAPI PdhOpenQueryW( LPCWSTR source, DWORD_PTR userdata, PDH_HQUERY *handle )
952 {
953     struct query *query;
954
955     TRACE("%s %lx %p\n", debugstr_w(source), userdata, handle);
956
957     if (!handle) return PDH_INVALID_ARGUMENT;
958
959     if (source)
960     {
961         FIXME("log file data source not supported\n");
962         return PDH_INVALID_ARGUMENT;
963     }
964     if ((query = create_query()))
965     {
966         query->user = userdata;
967         *handle = query;
968
969         return ERROR_SUCCESS;
970     }
971     return PDH_MEMORY_ALLOCATION_FAILURE;
972 }
973
974 /***********************************************************************
975  *              PdhRemoveCounter   (PDH.@)
976  */
977 PDH_STATUS WINAPI PdhRemoveCounter( PDH_HCOUNTER handle )
978 {
979     struct counter *counter = handle;
980
981     TRACE("%p\n", handle);
982
983     EnterCriticalSection( &pdh_handle_cs );
984     if (!counter || counter->magic != PDH_MAGIC_COUNTER)
985     {
986         LeaveCriticalSection( &pdh_handle_cs );
987         return PDH_INVALID_HANDLE;
988     }
989
990     list_remove( &counter->entry );
991     destroy_counter( counter );
992
993     LeaveCriticalSection( &pdh_handle_cs );
994     return ERROR_SUCCESS;
995 }
996
997 /***********************************************************************
998  *              PdhSetCounterScaleFactor   (PDH.@)
999  */
1000 PDH_STATUS WINAPI PdhSetCounterScaleFactor( PDH_HCOUNTER handle, LONG factor )
1001 {
1002     struct counter *counter = handle;
1003
1004     TRACE("%p\n", handle);
1005
1006     EnterCriticalSection( &pdh_handle_cs );
1007     if (!counter || counter->magic != PDH_MAGIC_COUNTER)
1008     {
1009         LeaveCriticalSection( &pdh_handle_cs );
1010         return PDH_INVALID_HANDLE;
1011     }
1012     if (factor < PDH_MIN_SCALE || factor > PDH_MAX_SCALE)
1013     {
1014         LeaveCriticalSection( &pdh_handle_cs );
1015         return PDH_INVALID_ARGUMENT;
1016     }
1017
1018     counter->scale = factor;
1019
1020     LeaveCriticalSection( &pdh_handle_cs );
1021     return ERROR_SUCCESS;
1022 }
1023
1024 /***********************************************************************
1025  *              PdhValidatePathA   (PDH.@)
1026  */
1027 PDH_STATUS WINAPI PdhValidatePathA( LPCSTR path )
1028 {
1029     PDH_STATUS ret;
1030     WCHAR *pathW;
1031
1032     TRACE("%s\n", debugstr_a(path));
1033
1034     if (!path) return PDH_INVALID_ARGUMENT;
1035     if (!(pathW = pdh_strdup_aw( path ))) return PDH_MEMORY_ALLOCATION_FAILURE;
1036
1037     ret = PdhValidatePathW( pathW );
1038
1039     heap_free( pathW );
1040     return ret;
1041 }
1042
1043 static PDH_STATUS validate_path( LPCWSTR path )
1044 {
1045     if (!path || !*path) return PDH_INVALID_ARGUMENT;
1046     if (*path++ != '\\' || !strchrW( path, '\\' )) return PDH_CSTATUS_BAD_COUNTERNAME;
1047     return ERROR_SUCCESS;
1048  }
1049
1050 /***********************************************************************
1051  *              PdhValidatePathW   (PDH.@)
1052  */
1053 PDH_STATUS WINAPI PdhValidatePathW( LPCWSTR path )
1054 {
1055     PDH_STATUS ret;
1056     unsigned int i;
1057
1058     TRACE("%s\n", debugstr_w(path));
1059
1060     if ((ret = validate_path( path ))) return ret;
1061
1062     for (i = 0; i < sizeof(counter_sources) / sizeof(counter_sources[0]); i++)
1063         if (pdh_match_path( counter_sources[i].path, path )) return ERROR_SUCCESS;
1064
1065     return PDH_CSTATUS_NO_COUNTER;
1066 }
1067
1068 /***********************************************************************
1069  *              PdhValidatePathExA   (PDH.@)
1070  */
1071 PDH_STATUS WINAPI PdhValidatePathExA( PDH_HLOG source, LPCSTR path )
1072 {
1073     TRACE("%p %s\n", source, debugstr_a(path));
1074
1075     if (source)
1076     {
1077         FIXME("log file data source not supported\n");
1078         return ERROR_SUCCESS;
1079     }
1080     return PdhValidatePathA( path );
1081 }
1082
1083 /***********************************************************************
1084  *              PdhValidatePathExW   (PDH.@)
1085  */
1086 PDH_STATUS WINAPI PdhValidatePathExW( PDH_HLOG source, LPCWSTR path )
1087 {
1088     TRACE("%p %s\n", source, debugstr_w(path));
1089
1090     if (source)
1091     {
1092         FIXME("log file data source not supported\n");
1093         return ERROR_SUCCESS;
1094     }
1095     return PdhValidatePathW( path );
1096 }
1097
1098 /***********************************************************************
1099  *              PdhMakeCounterPathA   (PDH.@)
1100  */
1101 PDH_STATUS WINAPI PdhMakeCounterPathA( PDH_COUNTER_PATH_ELEMENTS_A *e, LPSTR buffer,
1102                                        LPDWORD buflen, DWORD flags )
1103 {
1104     PDH_STATUS ret = PDH_MEMORY_ALLOCATION_FAILURE;
1105     PDH_COUNTER_PATH_ELEMENTS_W eW;
1106     WCHAR *bufferW;
1107     DWORD buflenW;
1108
1109     TRACE("%p %p %p 0x%08x\n", e, buffer, buflen, flags);
1110
1111     if (!e || !buflen) return PDH_INVALID_ARGUMENT;
1112
1113     memset( &eW, 0, sizeof(eW) );
1114     if (e->szMachineName    && !(eW.szMachineName    = pdh_strdup_aw( e->szMachineName ))) goto done;
1115     if (e->szObjectName     && !(eW.szObjectName     = pdh_strdup_aw( e->szObjectName ))) goto done;
1116     if (e->szInstanceName   && !(eW.szInstanceName   = pdh_strdup_aw( e->szInstanceName ))) goto done;
1117     if (e->szParentInstance && !(eW.szParentInstance = pdh_strdup_aw( e->szParentInstance ))) goto done;
1118     if (e->szCounterName    && !(eW.szCounterName    = pdh_strdup_aw( e->szCounterName ))) goto done;
1119     eW.dwInstanceIndex = e->dwInstanceIndex;
1120
1121     buflenW = 0;
1122     ret = PdhMakeCounterPathW( &eW, NULL, &buflenW, flags );
1123     if (ret == PDH_MORE_DATA)
1124     {
1125         if ((bufferW = heap_alloc( buflenW * sizeof(WCHAR) )))
1126         {
1127             if (!(ret = PdhMakeCounterPathW( &eW, bufferW, &buflenW, flags )))
1128             {
1129                 int len = WideCharToMultiByte(CP_ACP, 0, bufferW, -1, NULL, 0, NULL, NULL);
1130                 if (*buflen >= len) WideCharToMultiByte(CP_ACP, 0, bufferW, -1, buffer, *buflen, NULL, NULL);
1131                 else ret = PDH_MORE_DATA;
1132                 *buflen = len;
1133             }
1134             heap_free( bufferW );
1135         }
1136         else
1137             ret = PDH_MEMORY_ALLOCATION_FAILURE;
1138     }
1139
1140 done:
1141     heap_free( eW.szMachineName );
1142     heap_free( eW.szObjectName );
1143     heap_free( eW.szInstanceName );
1144     heap_free( eW.szParentInstance );
1145     heap_free( eW.szCounterName );
1146     return ret;
1147 }
1148
1149 /***********************************************************************
1150  *              PdhMakeCounterPathW   (PDH.@)
1151  */
1152 PDH_STATUS WINAPI PdhMakeCounterPathW( PDH_COUNTER_PATH_ELEMENTS_W *e, LPWSTR buffer,
1153                                        LPDWORD buflen, DWORD flags )
1154 {
1155     static const WCHAR bslash[] = {'\\',0};
1156     static const WCHAR fslash[] = {'/',0};
1157     static const WCHAR lparen[] = {'(',0};
1158     static const WCHAR rparen[] = {')',0};
1159     static const WCHAR fmt[]    = {'#','%','u',0};
1160
1161     WCHAR path[PDH_MAX_COUNTER_NAME], instance[12];
1162     PDH_STATUS ret = ERROR_SUCCESS;
1163     DWORD len;
1164
1165     TRACE("%p %p %p 0x%08x\n", e, buffer, buflen, flags);
1166
1167     if (flags) FIXME("unimplemented flags 0x%08x\n", flags);
1168
1169     if (!e || !e->szCounterName || !e->szObjectName || !buflen)
1170         return PDH_INVALID_ARGUMENT;
1171
1172     path[0] = 0;
1173     if (e->szMachineName)
1174     {
1175         strcatW(path, bslash);
1176         strcatW(path, bslash);
1177         strcatW(path, e->szMachineName);
1178     }
1179     strcatW(path, bslash);
1180     strcatW(path, e->szObjectName);
1181     if (e->szInstanceName)
1182     {
1183         strcatW(path, lparen);
1184         if (e->szParentInstance)
1185         {
1186             strcatW(path, e->szParentInstance);
1187             strcatW(path, fslash);
1188         }
1189         strcatW(path, e->szInstanceName);
1190         sprintfW(instance, fmt, e->dwInstanceIndex);
1191         strcatW(path, instance);
1192         strcatW(path, rparen);
1193     }
1194     strcatW(path, bslash);
1195     strcatW(path, e->szCounterName);
1196
1197     len = strlenW(path) + 1;
1198     if (*buflen >= len) strcpyW(buffer, path);
1199     else ret = PDH_MORE_DATA;
1200     *buflen = len;
1201     return ret;
1202 }
1203
1204 /***********************************************************************
1205  *              PdhEnumObjectItemsA   (PDH.@)
1206  */
1207 PDH_STATUS WINAPI PdhEnumObjectItemsA(LPCSTR szDataSource, LPCSTR szMachineName, LPCSTR szObjectName,
1208                                       LPSTR mszCounterList, LPDWORD pcchCounterListLength, LPSTR mszInstanceList,
1209                                       LPDWORD pcchInstanceListLength, DWORD dwDetailLevel, DWORD dwFlags)
1210 {
1211     FIXME("%s, %s, %s, %p, %p, %p, %p, %d, 0x%x: stub\n", debugstr_a(szDataSource), debugstr_a(szMachineName),
1212          debugstr_a(szObjectName), mszCounterList, pcchCounterListLength, mszInstanceList,
1213          pcchInstanceListLength, dwDetailLevel, dwFlags);
1214
1215     return PDH_NOT_IMPLEMENTED;
1216 }
1217
1218 /***********************************************************************
1219  *              PdhEnumObjectItemsW   (PDH.@)
1220  */
1221 PDH_STATUS WINAPI PdhEnumObjectItemsW(LPCWSTR szDataSource, LPCWSTR szMachineName, LPCWSTR szObjectName,
1222                                       LPWSTR mszCounterList, LPDWORD pcchCounterListLength, LPWSTR mszInstanceList,
1223                                       LPDWORD pcchInstanceListLength, DWORD dwDetailLevel, DWORD dwFlags)
1224 {
1225     FIXME("%s, %s, %s, %p, %p, %p, %p, %d, 0x%x: stub\n", debugstr_w(szDataSource), debugstr_w(szMachineName),
1226          debugstr_w(szObjectName), mszCounterList, pcchCounterListLength, mszInstanceList,
1227          pcchInstanceListLength, dwDetailLevel, dwFlags);
1228
1229     return PDH_NOT_IMPLEMENTED;
1230 }
1231
1232 /***********************************************************************
1233  *              PdhSetDefaultRealTimeDataSource   (PDH.@)
1234  */
1235 PDH_STATUS WINAPI PdhSetDefaultRealTimeDataSource( DWORD source )
1236 {
1237     FIXME("%u\n", source);
1238     return ERROR_SUCCESS;
1239 }