xref: /reactos/dll/win32/pdh/pdh_main.c (revision 5bfe6a53)
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 
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/heap.h"
36 #include "wine/list.h"
37 #include "wine/unicode.h"
38 
39 WINE_DEFAULT_DEBUG_CHANNEL(pdh);
40 
41 static CRITICAL_SECTION pdh_handle_cs;
42 static CRITICAL_SECTION_DEBUG pdh_handle_cs_debug =
43 {
44     0, 0, &pdh_handle_cs,
45     { &pdh_handle_cs_debug.ProcessLocksList,
46       &pdh_handle_cs_debug.ProcessLocksList },
47       0, 0, { (DWORD_PTR)(__FILE__ ": pdh_handle_cs") }
48 };
49 static CRITICAL_SECTION pdh_handle_cs = { &pdh_handle_cs_debug, -1, 0, 0, 0, 0 };
50 
51 static inline WCHAR *pdh_strdup( const WCHAR *src )
52 {
53     WCHAR *dst;
54 
55     if (!src) return NULL;
56     if ((dst = heap_alloc( (strlenW( src ) + 1) * sizeof(WCHAR) ))) strcpyW( dst, src );
57     return dst;
58 }
59 
60 static inline WCHAR *pdh_strdup_aw( const char *src )
61 {
62     int len;
63     WCHAR *dst;
64 
65     if (!src) return NULL;
66     len = MultiByteToWideChar( CP_ACP, 0, src, -1, NULL, 0 );
67     if ((dst = heap_alloc( len * sizeof(WCHAR) ))) MultiByteToWideChar( CP_ACP, 0, src, -1, dst, len );
68     return dst;
69 }
70 
71 BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved)
72 {
73     TRACE("(0x%p, %d, %p)\n",hinstDLL,fdwReason,lpvReserved);
74     switch (fdwReason)
75     {
76     case DLL_WINE_PREATTACH:
77         return FALSE;    /* prefer native version */
78     case DLL_PROCESS_ATTACH:
79         DisableThreadLibraryCalls(hinstDLL);
80         break;
81     case DLL_PROCESS_DETACH:
82         if (lpvReserved) break;
83         DeleteCriticalSection(&pdh_handle_cs);
84         break;
85     }
86 
87     return TRUE;
88 }
89 
90 union value
91 {
92     LONG     longvalue;
93     double   doublevalue;
94     LONGLONG largevalue;
95 };
96 
97 struct counter
98 {
99     DWORD           magic;                          /* signature */
100     struct list     entry;                          /* list entry */
101     WCHAR          *path;                           /* identifier */
102     DWORD           type;                           /* counter type */
103     DWORD           status;                         /* update status */
104     LONG            scale;                          /* scale factor */
105     LONG            defaultscale;                   /* default scale factor */
106     DWORD_PTR       user;                           /* user data */
107     DWORD_PTR       queryuser;                      /* query user data */
108     LONGLONG        base;                           /* samples per second */
109     FILETIME        stamp;                          /* time stamp */
110     void (CALLBACK *collect)( struct counter * );   /* collect callback */
111     union value     one;                            /* first value */
112     union value     two;                            /* second value */
113 };
114 
115 #define PDH_MAGIC_COUNTER   0x50444831 /* 'PDH1' */
116 
117 static struct counter *create_counter( void )
118 {
119     struct counter *counter;
120 
121     if ((counter = heap_alloc_zero( sizeof(struct counter) )))
122     {
123         counter->magic = PDH_MAGIC_COUNTER;
124         return counter;
125     }
126     return NULL;
127 }
128 
129 static void destroy_counter( struct counter *counter )
130 {
131     counter->magic = 0;
132     heap_free( counter->path );
133     heap_free( counter );
134 }
135 
136 #define PDH_MAGIC_QUERY     0x50444830 /* 'PDH0' */
137 
138 struct query
139 {
140     DWORD       magic;      /* signature */
141     DWORD_PTR   user;       /* user data */
142     HANDLE      thread;     /* collect thread */
143     DWORD       interval;   /* collect interval */
144     HANDLE      wait;       /* wait event */
145     HANDLE      stop;       /* stop event */
146     struct list counters;   /* counter list */
147 };
148 
149 static struct query *create_query( void )
150 {
151     struct query *query;
152 
153     if ((query = heap_alloc_zero( sizeof(struct query) )))
154     {
155         query->magic = PDH_MAGIC_QUERY;
156         list_init( &query->counters );
157         return query;
158     }
159     return NULL;
160 }
161 
162 static void destroy_query( struct query *query )
163 {
164     query->magic = 0;
165     heap_free( query );
166 }
167 
168 struct source
169 {
170     DWORD           index;                          /* name index */
171     const WCHAR    *path;                           /* identifier */
172     void (CALLBACK *collect)( struct counter * );   /* collect callback */
173     DWORD           type;                           /* counter type */
174     LONG            scale;                          /* default scale factor */
175     LONGLONG        base;                           /* samples per second */
176 };
177 
178 static const WCHAR path_processor_time[] =
179     {'\\','P','r','o','c','e','s','s','o','r','(','_','T','o','t','a','l',')',
180      '\\','%',' ','P','r','o','c','e','s','s','o','r',' ','T','i','m','e',0};
181 static const WCHAR path_uptime[] =
182     {'\\','S','y','s','t','e','m', '\\', 'S','y','s','t','e','m',' ','U','p',' ','T','i','m','e',0};
183 
184 static void CALLBACK collect_processor_time( struct counter *counter )
185 {
186     counter->two.largevalue = 500000; /* FIXME */
187     counter->status = PDH_CSTATUS_VALID_DATA;
188 }
189 
190 static void CALLBACK collect_uptime( struct counter *counter )
191 {
192     counter->two.largevalue = GetTickCount64();
193     counter->status = PDH_CSTATUS_VALID_DATA;
194 }
195 
196 #define TYPE_PROCESSOR_TIME \
197     (PERF_SIZE_LARGE | PERF_TYPE_COUNTER | PERF_COUNTER_RATE | PERF_TIMER_100NS | PERF_DELTA_COUNTER | \
198      PERF_INVERSE_COUNTER | PERF_DISPLAY_PERCENT)
199 
200 #define TYPE_UPTIME \
201     (PERF_SIZE_LARGE | PERF_TYPE_COUNTER | PERF_COUNTER_ELAPSED | PERF_OBJECT_TIMER | PERF_DISPLAY_SECONDS)
202 
203 /* counter source registry */
204 static const struct source counter_sources[] =
205 {
206     { 6,    path_processor_time,    collect_processor_time,     TYPE_PROCESSOR_TIME,    -5,     10000000 },
207     { 674,  path_uptime,            collect_uptime,             TYPE_UPTIME,            -3,     1000 }
208 };
209 
210 static BOOL is_local_machine( const WCHAR *name, DWORD len )
211 {
212     WCHAR buf[MAX_COMPUTERNAME_LENGTH + 1];
213     DWORD buflen = ARRAY_SIZE(buf);
214 
215     if (!GetComputerNameW( buf, &buflen )) return FALSE;
216     return len == buflen && !memicmpW( name, buf, buflen );
217 }
218 
219 static BOOL pdh_match_path( LPCWSTR fullpath, LPCWSTR path )
220 {
221     const WCHAR *p;
222 
223     if (path[0] == '\\' && path[1] == '\\' && (p = strchrW( path + 2, '\\' )) &&
224         is_local_machine( path + 2, p - path - 2 ))
225     {
226         path += p - path;
227     }
228     if (strchrW( path, '\\' )) p = fullpath;
229     else p = strrchrW( fullpath, '\\' ) + 1;
230     return !strcmpW( p, path );
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 < ARRAY_SIZE(counter_sources); 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  *              PdhExpandCounterPathA   (PDH.@)
626  */
627 PDH_STATUS WINAPI PdhExpandCounterPathA( LPCSTR szWildCardPath, LPSTR mszExpandedPathList, LPDWORD pcchPathListLength )
628 {
629     FIXME("%s, %p, %p: stub\n", debugstr_a(szWildCardPath), mszExpandedPathList, pcchPathListLength);
630     return PdhExpandWildCardPathA(NULL, szWildCardPath, mszExpandedPathList, pcchPathListLength, 0);
631 }
632 
633 /***********************************************************************
634  *              PdhExpandCounterPathW   (PDH.@)
635  */
636 PDH_STATUS WINAPI PdhExpandCounterPathW( LPCWSTR szWildCardPath, LPWSTR mszExpandedPathList, LPDWORD pcchPathListLength )
637 {
638     FIXME("%s, %p, %p: stub\n", debugstr_w(szWildCardPath), mszExpandedPathList, pcchPathListLength);
639     return PdhExpandWildCardPathW(NULL, szWildCardPath, mszExpandedPathList, pcchPathListLength, 0);
640 }
641 
642 /***********************************************************************
643  *              PdhGetCounterInfoA   (PDH.@)
644  */
645 PDH_STATUS WINAPI PdhGetCounterInfoA( PDH_HCOUNTER handle, BOOLEAN text, LPDWORD size, PPDH_COUNTER_INFO_A info )
646 {
647     struct counter *counter = handle;
648 
649     TRACE("%p %d %p %p\n", handle, text, size, info);
650 
651     EnterCriticalSection( &pdh_handle_cs );
652     if (!counter || counter->magic != PDH_MAGIC_COUNTER)
653     {
654         LeaveCriticalSection( &pdh_handle_cs );
655         return PDH_INVALID_HANDLE;
656     }
657     if (!size)
658     {
659         LeaveCriticalSection( &pdh_handle_cs );
660         return PDH_INVALID_ARGUMENT;
661     }
662     if (*size < sizeof(PDH_COUNTER_INFO_A))
663     {
664         *size = sizeof(PDH_COUNTER_INFO_A);
665         LeaveCriticalSection( &pdh_handle_cs );
666         return PDH_MORE_DATA;
667     }
668 
669     memset( info, 0, sizeof(PDH_COUNTER_INFO_A) );
670 
671     info->dwType          = counter->type;
672     info->CStatus         = counter->status;
673     info->lScale          = counter->scale;
674     info->lDefaultScale   = counter->defaultscale;
675     info->dwUserData      = counter->user;
676     info->dwQueryUserData = counter->queryuser;
677 
678     *size = sizeof(PDH_COUNTER_INFO_A);
679 
680     LeaveCriticalSection( &pdh_handle_cs );
681     return ERROR_SUCCESS;
682 }
683 
684 /***********************************************************************
685  *              PdhGetCounterInfoW   (PDH.@)
686  */
687 PDH_STATUS WINAPI PdhGetCounterInfoW( PDH_HCOUNTER handle, BOOLEAN text, LPDWORD size, PPDH_COUNTER_INFO_W info )
688 {
689     struct counter *counter = handle;
690 
691     TRACE("%p %d %p %p\n", handle, text, size, info);
692 
693     EnterCriticalSection( &pdh_handle_cs );
694     if (!counter || counter->magic != PDH_MAGIC_COUNTER)
695     {
696         LeaveCriticalSection( &pdh_handle_cs );
697         return PDH_INVALID_HANDLE;
698     }
699     if (!size)
700     {
701         LeaveCriticalSection( &pdh_handle_cs );
702         return PDH_INVALID_ARGUMENT;
703     }
704     if (*size < sizeof(PDH_COUNTER_INFO_W))
705     {
706         *size = sizeof(PDH_COUNTER_INFO_W);
707         LeaveCriticalSection( &pdh_handle_cs );
708         return PDH_MORE_DATA;
709     }
710 
711     memset( info, 0, sizeof(PDH_COUNTER_INFO_W) );
712 
713     info->dwType          = counter->type;
714     info->CStatus         = counter->status;
715     info->lScale          = counter->scale;
716     info->lDefaultScale   = counter->defaultscale;
717     info->dwUserData      = counter->user;
718     info->dwQueryUserData = counter->queryuser;
719 
720     *size = sizeof(PDH_COUNTER_INFO_W);
721 
722     LeaveCriticalSection( &pdh_handle_cs );
723     return ERROR_SUCCESS;
724 }
725 
726 /***********************************************************************
727  *              PdhGetCounterTimeBase   (PDH.@)
728  */
729 PDH_STATUS WINAPI PdhGetCounterTimeBase( PDH_HCOUNTER handle, LONGLONG *base )
730 {
731     struct counter *counter = handle;
732 
733     TRACE("%p %p\n", handle, base);
734 
735     if (!base) return PDH_INVALID_ARGUMENT;
736 
737     EnterCriticalSection( &pdh_handle_cs );
738     if (!counter || counter->magic != PDH_MAGIC_COUNTER)
739     {
740         LeaveCriticalSection( &pdh_handle_cs );
741         return PDH_INVALID_HANDLE;
742     }
743 
744     *base = counter->base;
745 
746     LeaveCriticalSection( &pdh_handle_cs );
747     return ERROR_SUCCESS;
748 }
749 
750 /***********************************************************************
751  *              PdhGetDllVersion   (PDH.@)
752  */
753 PDH_STATUS WINAPI PdhGetDllVersion( LPDWORD version )
754 {
755     if (!version)
756         return PDH_INVALID_ARGUMENT;
757 
758     *version = PDH_VERSION;
759 
760     return ERROR_SUCCESS;
761 }
762 
763 /***********************************************************************
764  *              PdhGetFormattedCounterValue   (PDH.@)
765  */
766 PDH_STATUS WINAPI PdhGetFormattedCounterValue( PDH_HCOUNTER handle, DWORD format,
767                                                LPDWORD type, PPDH_FMT_COUNTERVALUE value )
768 {
769     PDH_STATUS ret;
770     struct counter *counter = handle;
771 
772     TRACE("%p %x %p %p\n", handle, format, type, value);
773 
774     if (!value) return PDH_INVALID_ARGUMENT;
775 
776     EnterCriticalSection( &pdh_handle_cs );
777     if (!counter || counter->magic != PDH_MAGIC_COUNTER)
778     {
779         LeaveCriticalSection( &pdh_handle_cs );
780         return PDH_INVALID_HANDLE;
781     }
782     if (counter->status)
783     {
784         LeaveCriticalSection( &pdh_handle_cs );
785         return PDH_INVALID_DATA;
786     }
787     if (!(ret = format_value( counter, format, &counter->one, &counter->two, value )))
788     {
789         value->CStatus = ERROR_SUCCESS;
790         if (type) *type = counter->type;
791     }
792 
793     LeaveCriticalSection( &pdh_handle_cs );
794     return ret;
795 }
796 
797 /***********************************************************************
798  *              PdhGetRawCounterValue   (PDH.@)
799  */
800 PDH_STATUS WINAPI PdhGetRawCounterValue( PDH_HCOUNTER handle, LPDWORD type,
801                                          PPDH_RAW_COUNTER value )
802 {
803     struct counter *counter = handle;
804 
805     TRACE("%p %p %p\n", handle, type, value);
806 
807     if (!value) return PDH_INVALID_ARGUMENT;
808 
809     EnterCriticalSection( &pdh_handle_cs );
810     if (!counter || counter->magic != PDH_MAGIC_COUNTER)
811     {
812         LeaveCriticalSection( &pdh_handle_cs );
813         return PDH_INVALID_HANDLE;
814     }
815 
816     value->CStatus                  = counter->status;
817     value->TimeStamp.dwLowDateTime  = counter->stamp.dwLowDateTime;
818     value->TimeStamp.dwHighDateTime = counter->stamp.dwHighDateTime;
819     value->FirstValue               = counter->one.largevalue;
820     value->SecondValue              = counter->two.largevalue;
821     value->MultiCount               = 1; /* FIXME */
822 
823     if (type) *type = counter->type;
824 
825     LeaveCriticalSection( &pdh_handle_cs );
826     return ERROR_SUCCESS;
827 }
828 
829 /***********************************************************************
830  *              PdhLookupPerfIndexByNameA   (PDH.@)
831  */
832 PDH_STATUS WINAPI PdhLookupPerfIndexByNameA( LPCSTR machine, LPCSTR name, LPDWORD index )
833 {
834     PDH_STATUS ret;
835     WCHAR *machineW = NULL;
836     WCHAR *nameW;
837 
838     TRACE("%s %s %p\n", debugstr_a(machine), debugstr_a(name), index);
839 
840     if (!name) return PDH_INVALID_ARGUMENT;
841 
842     if (machine && !(machineW = pdh_strdup_aw( machine ))) return PDH_MEMORY_ALLOCATION_FAILURE;
843 
844     if (!(nameW = pdh_strdup_aw( name )))
845         return PDH_MEMORY_ALLOCATION_FAILURE;
846 
847     ret = PdhLookupPerfIndexByNameW( machineW, nameW, index );
848 
849     heap_free( nameW );
850     heap_free( machineW );
851     return ret;
852 }
853 
854 /***********************************************************************
855  *              PdhLookupPerfIndexByNameW   (PDH.@)
856  */
857 PDH_STATUS WINAPI PdhLookupPerfIndexByNameW( LPCWSTR machine, LPCWSTR name, LPDWORD index )
858 {
859     unsigned int i;
860 
861     TRACE("%s %s %p\n", debugstr_w(machine), debugstr_w(name), index);
862 
863     if (!name || !index) return PDH_INVALID_ARGUMENT;
864 
865     if (machine)
866     {
867         FIXME("remote machine not supported\n");
868         return PDH_CSTATUS_NO_MACHINE;
869     }
870     for (i = 0; i < ARRAY_SIZE(counter_sources); i++)
871     {
872         if (pdh_match_path( counter_sources[i].path, name ))
873         {
874             *index = counter_sources[i].index;
875             return ERROR_SUCCESS;
876         }
877     }
878     return PDH_STRING_NOT_FOUND;
879 }
880 
881 /***********************************************************************
882  *              PdhLookupPerfNameByIndexA   (PDH.@)
883  */
884 PDH_STATUS WINAPI PdhLookupPerfNameByIndexA( LPCSTR machine, DWORD index, LPSTR buffer, LPDWORD size )
885 {
886     PDH_STATUS ret;
887     WCHAR *machineW = NULL;
888     WCHAR bufferW[PDH_MAX_COUNTER_NAME];
889     DWORD sizeW = ARRAY_SIZE(bufferW);
890 
891     TRACE("%s %d %p %p\n", debugstr_a(machine), index, buffer, size);
892 
893     if (!buffer || !size) return PDH_INVALID_ARGUMENT;
894 
895     if (machine && !(machineW = pdh_strdup_aw( machine ))) return PDH_MEMORY_ALLOCATION_FAILURE;
896 
897     if (!(ret = PdhLookupPerfNameByIndexW( machineW, index, bufferW, &sizeW )))
898     {
899         int required = WideCharToMultiByte( CP_ACP, 0, bufferW, -1, NULL, 0, NULL, NULL );
900 
901         if (size && *size < required) ret = PDH_MORE_DATA;
902         else WideCharToMultiByte( CP_ACP, 0, bufferW, -1, buffer, required, NULL, NULL );
903         if (size) *size = required;
904     }
905     heap_free( machineW );
906     return ret;
907 }
908 
909 /***********************************************************************
910  *              PdhLookupPerfNameByIndexW   (PDH.@)
911  */
912 PDH_STATUS WINAPI PdhLookupPerfNameByIndexW( LPCWSTR machine, DWORD index, LPWSTR buffer, LPDWORD size )
913 {
914     PDH_STATUS ret;
915     unsigned int i;
916 
917     TRACE("%s %d %p %p\n", debugstr_w(machine), index, buffer, size);
918 
919     if (machine)
920     {
921         FIXME("remote machine not supported\n");
922         return PDH_CSTATUS_NO_MACHINE;
923     }
924 
925     if (!buffer || !size) return PDH_INVALID_ARGUMENT;
926     if (!index) return ERROR_SUCCESS;
927 
928     for (i = 0; i < ARRAY_SIZE(counter_sources); i++)
929     {
930         if (counter_sources[i].index == index)
931         {
932             WCHAR *p = strrchrW( counter_sources[i].path, '\\' ) + 1;
933             unsigned int required = strlenW( p ) + 1;
934 
935             if (*size < required) ret = PDH_MORE_DATA;
936             else
937             {
938                 strcpyW( buffer, p );
939                 ret = ERROR_SUCCESS;
940             }
941             *size = required;
942             return ret;
943         }
944     }
945     return PDH_INVALID_ARGUMENT;
946 }
947 
948 /***********************************************************************
949  *              PdhOpenQueryA   (PDH.@)
950  */
951 PDH_STATUS WINAPI PdhOpenQueryA( LPCSTR source, DWORD_PTR userdata, PDH_HQUERY *query )
952 {
953     PDH_STATUS ret;
954     WCHAR *sourceW = NULL;
955 
956     TRACE("%s %lx %p\n", debugstr_a(source), userdata, query);
957 
958     if (source && !(sourceW = pdh_strdup_aw( source ))) return PDH_MEMORY_ALLOCATION_FAILURE;
959 
960     ret = PdhOpenQueryW( sourceW, userdata, query );
961     heap_free( sourceW );
962 
963     return ret;
964 }
965 
966 /***********************************************************************
967  *              PdhOpenQueryW   (PDH.@)
968  */
969 PDH_STATUS WINAPI PdhOpenQueryW( LPCWSTR source, DWORD_PTR userdata, PDH_HQUERY *handle )
970 {
971     struct query *query;
972 
973     TRACE("%s %lx %p\n", debugstr_w(source), userdata, handle);
974 
975     if (!handle) return PDH_INVALID_ARGUMENT;
976 
977     if (source)
978     {
979         FIXME("log file data source not supported\n");
980         return PDH_INVALID_ARGUMENT;
981     }
982     if ((query = create_query()))
983     {
984         query->user = userdata;
985         *handle = query;
986 
987         return ERROR_SUCCESS;
988     }
989     return PDH_MEMORY_ALLOCATION_FAILURE;
990 }
991 
992 /***********************************************************************
993  *              PdhRemoveCounter   (PDH.@)
994  */
995 PDH_STATUS WINAPI PdhRemoveCounter( PDH_HCOUNTER handle )
996 {
997     struct counter *counter = handle;
998 
999     TRACE("%p\n", handle);
1000 
1001     EnterCriticalSection( &pdh_handle_cs );
1002     if (!counter || counter->magic != PDH_MAGIC_COUNTER)
1003     {
1004         LeaveCriticalSection( &pdh_handle_cs );
1005         return PDH_INVALID_HANDLE;
1006     }
1007 
1008     list_remove( &counter->entry );
1009     destroy_counter( counter );
1010 
1011     LeaveCriticalSection( &pdh_handle_cs );
1012     return ERROR_SUCCESS;
1013 }
1014 
1015 /***********************************************************************
1016  *              PdhSetCounterScaleFactor   (PDH.@)
1017  */
1018 PDH_STATUS WINAPI PdhSetCounterScaleFactor( PDH_HCOUNTER handle, LONG factor )
1019 {
1020     struct counter *counter = handle;
1021 
1022     TRACE("%p\n", handle);
1023 
1024     EnterCriticalSection( &pdh_handle_cs );
1025     if (!counter || counter->magic != PDH_MAGIC_COUNTER)
1026     {
1027         LeaveCriticalSection( &pdh_handle_cs );
1028         return PDH_INVALID_HANDLE;
1029     }
1030     if (factor < PDH_MIN_SCALE || factor > PDH_MAX_SCALE)
1031     {
1032         LeaveCriticalSection( &pdh_handle_cs );
1033         return PDH_INVALID_ARGUMENT;
1034     }
1035 
1036     counter->scale = factor;
1037 
1038     LeaveCriticalSection( &pdh_handle_cs );
1039     return ERROR_SUCCESS;
1040 }
1041 
1042 /***********************************************************************
1043  *              PdhValidatePathA   (PDH.@)
1044  */
1045 PDH_STATUS WINAPI PdhValidatePathA( LPCSTR path )
1046 {
1047     PDH_STATUS ret;
1048     WCHAR *pathW;
1049 
1050     TRACE("%s\n", debugstr_a(path));
1051 
1052     if (!path) return PDH_INVALID_ARGUMENT;
1053     if (!(pathW = pdh_strdup_aw( path ))) return PDH_MEMORY_ALLOCATION_FAILURE;
1054 
1055     ret = PdhValidatePathW( pathW );
1056 
1057     heap_free( pathW );
1058     return ret;
1059 }
1060 
1061 static PDH_STATUS validate_path( LPCWSTR path )
1062 {
1063     if (!path || !*path) return PDH_INVALID_ARGUMENT;
1064     if (*path++ != '\\' || !strchrW( path, '\\' )) return PDH_CSTATUS_BAD_COUNTERNAME;
1065     return ERROR_SUCCESS;
1066  }
1067 
1068 /***********************************************************************
1069  *              PdhValidatePathW   (PDH.@)
1070  */
1071 PDH_STATUS WINAPI PdhValidatePathW( LPCWSTR path )
1072 {
1073     PDH_STATUS ret;
1074     unsigned int i;
1075 
1076     TRACE("%s\n", debugstr_w(path));
1077 
1078     if ((ret = validate_path( path ))) return ret;
1079 
1080     for (i = 0; i < ARRAY_SIZE(counter_sources); i++)
1081         if (pdh_match_path( counter_sources[i].path, path )) return ERROR_SUCCESS;
1082 
1083     return PDH_CSTATUS_NO_COUNTER;
1084 }
1085 
1086 /***********************************************************************
1087  *              PdhVbAddCounter   (PDH.@)
1088  */
1089 PDH_STATUS WINAPI PdhVbAddCounter( PDH_HQUERY query, LPCSTR path, PDH_HCOUNTER *counter )
1090 {
1091     FIXME("%p, %s, %p: stub!\n", query, debugstr_a(path), counter);
1092 
1093     if (!path) return PDH_INVALID_ARGUMENT;
1094 
1095     return PDH_NOT_IMPLEMENTED;
1096 }
1097 
1098 /***********************************************************************
1099  *              PdhValidatePathExA   (PDH.@)
1100  */
1101 PDH_STATUS WINAPI PdhValidatePathExA( PDH_HLOG source, LPCSTR path )
1102 {
1103     TRACE("%p %s\n", source, debugstr_a(path));
1104 
1105     if (source)
1106     {
1107         FIXME("log file data source not supported\n");
1108         return ERROR_SUCCESS;
1109     }
1110     return PdhValidatePathA( path );
1111 }
1112 
1113 /***********************************************************************
1114  *              PdhValidatePathExW   (PDH.@)
1115  */
1116 PDH_STATUS WINAPI PdhValidatePathExW( PDH_HLOG source, LPCWSTR path )
1117 {
1118     TRACE("%p %s\n", source, debugstr_w(path));
1119 
1120     if (source)
1121     {
1122         FIXME("log file data source not supported\n");
1123         return ERROR_SUCCESS;
1124     }
1125     return PdhValidatePathW( path );
1126 }
1127 
1128 /***********************************************************************
1129  *              PdhMakeCounterPathA   (PDH.@)
1130  */
1131 PDH_STATUS WINAPI PdhMakeCounterPathA( PDH_COUNTER_PATH_ELEMENTS_A *e, LPSTR buffer,
1132                                        LPDWORD buflen, DWORD flags )
1133 {
1134     PDH_STATUS ret = PDH_MEMORY_ALLOCATION_FAILURE;
1135     PDH_COUNTER_PATH_ELEMENTS_W eW;
1136     WCHAR *bufferW;
1137     DWORD buflenW;
1138 
1139     TRACE("%p %p %p 0x%08x\n", e, buffer, buflen, flags);
1140 
1141     if (!e || !buflen) return PDH_INVALID_ARGUMENT;
1142 
1143     memset( &eW, 0, sizeof(eW) );
1144     if (e->szMachineName    && !(eW.szMachineName    = pdh_strdup_aw( e->szMachineName ))) goto done;
1145     if (e->szObjectName     && !(eW.szObjectName     = pdh_strdup_aw( e->szObjectName ))) goto done;
1146     if (e->szInstanceName   && !(eW.szInstanceName   = pdh_strdup_aw( e->szInstanceName ))) goto done;
1147     if (e->szParentInstance && !(eW.szParentInstance = pdh_strdup_aw( e->szParentInstance ))) goto done;
1148     if (e->szCounterName    && !(eW.szCounterName    = pdh_strdup_aw( e->szCounterName ))) goto done;
1149     eW.dwInstanceIndex = e->dwInstanceIndex;
1150 
1151     buflenW = 0;
1152     ret = PdhMakeCounterPathW( &eW, NULL, &buflenW, flags );
1153     if (ret == PDH_MORE_DATA)
1154     {
1155         if ((bufferW = heap_alloc( buflenW * sizeof(WCHAR) )))
1156         {
1157             if (!(ret = PdhMakeCounterPathW( &eW, bufferW, &buflenW, flags )))
1158             {
1159                 int len = WideCharToMultiByte(CP_ACP, 0, bufferW, -1, NULL, 0, NULL, NULL);
1160                 if (*buflen >= len) WideCharToMultiByte(CP_ACP, 0, bufferW, -1, buffer, *buflen, NULL, NULL);
1161                 else ret = PDH_MORE_DATA;
1162                 *buflen = len;
1163             }
1164             heap_free( bufferW );
1165         }
1166         else
1167             ret = PDH_MEMORY_ALLOCATION_FAILURE;
1168     }
1169 
1170 done:
1171     heap_free( eW.szMachineName );
1172     heap_free( eW.szObjectName );
1173     heap_free( eW.szInstanceName );
1174     heap_free( eW.szParentInstance );
1175     heap_free( eW.szCounterName );
1176     return ret;
1177 }
1178 
1179 /***********************************************************************
1180  *              PdhMakeCounterPathW   (PDH.@)
1181  */
1182 PDH_STATUS WINAPI PdhMakeCounterPathW( PDH_COUNTER_PATH_ELEMENTS_W *e, LPWSTR buffer,
1183                                        LPDWORD buflen, DWORD flags )
1184 {
1185     static const WCHAR bslash[] = {'\\',0};
1186     static const WCHAR fslash[] = {'/',0};
1187     static const WCHAR lparen[] = {'(',0};
1188     static const WCHAR rparen[] = {')',0};
1189     static const WCHAR fmt[]    = {'#','%','u',0};
1190 
1191     WCHAR path[PDH_MAX_COUNTER_NAME], instance[12];
1192     PDH_STATUS ret = ERROR_SUCCESS;
1193     DWORD len;
1194 
1195     TRACE("%p %p %p 0x%08x\n", e, buffer, buflen, flags);
1196 
1197     if (flags) FIXME("unimplemented flags 0x%08x\n", flags);
1198 
1199     if (!e || !e->szCounterName || !e->szObjectName || !buflen)
1200         return PDH_INVALID_ARGUMENT;
1201 
1202     path[0] = 0;
1203     if (e->szMachineName)
1204     {
1205         strcatW(path, bslash);
1206         strcatW(path, bslash);
1207         strcatW(path, e->szMachineName);
1208     }
1209     strcatW(path, bslash);
1210     strcatW(path, e->szObjectName);
1211     if (e->szInstanceName)
1212     {
1213         strcatW(path, lparen);
1214         if (e->szParentInstance)
1215         {
1216             strcatW(path, e->szParentInstance);
1217             strcatW(path, fslash);
1218         }
1219         strcatW(path, e->szInstanceName);
1220         sprintfW(instance, fmt, e->dwInstanceIndex);
1221         strcatW(path, instance);
1222         strcatW(path, rparen);
1223     }
1224     strcatW(path, bslash);
1225     strcatW(path, e->szCounterName);
1226 
1227     len = strlenW(path) + 1;
1228     if (*buflen >= len) strcpyW(buffer, path);
1229     else ret = PDH_MORE_DATA;
1230     *buflen = len;
1231     return ret;
1232 }
1233 
1234 /***********************************************************************
1235  *              PdhEnumObjectItemsA   (PDH.@)
1236  */
1237 PDH_STATUS WINAPI PdhEnumObjectItemsA(LPCSTR szDataSource, LPCSTR szMachineName, LPCSTR szObjectName,
1238                                       LPSTR mszCounterList, LPDWORD pcchCounterListLength, LPSTR mszInstanceList,
1239                                       LPDWORD pcchInstanceListLength, DWORD dwDetailLevel, DWORD dwFlags)
1240 {
1241     FIXME("%s, %s, %s, %p, %p, %p, %p, %d, 0x%x: stub\n", debugstr_a(szDataSource), debugstr_a(szMachineName),
1242          debugstr_a(szObjectName), mszCounterList, pcchCounterListLength, mszInstanceList,
1243          pcchInstanceListLength, dwDetailLevel, dwFlags);
1244 
1245     return PDH_NOT_IMPLEMENTED;
1246 }
1247 
1248 /***********************************************************************
1249  *              PdhEnumObjectItemsW   (PDH.@)
1250  */
1251 PDH_STATUS WINAPI PdhEnumObjectItemsW(LPCWSTR szDataSource, LPCWSTR szMachineName, LPCWSTR szObjectName,
1252                                       LPWSTR mszCounterList, LPDWORD pcchCounterListLength, LPWSTR mszInstanceList,
1253                                       LPDWORD pcchInstanceListLength, DWORD dwDetailLevel, DWORD dwFlags)
1254 {
1255     FIXME("%s, %s, %s, %p, %p, %p, %p, %d, 0x%x: stub\n", debugstr_w(szDataSource), debugstr_w(szMachineName),
1256          debugstr_w(szObjectName), mszCounterList, pcchCounterListLength, mszInstanceList,
1257          pcchInstanceListLength, dwDetailLevel, dwFlags);
1258 
1259     return PDH_NOT_IMPLEMENTED;
1260 }
1261 
1262 /***********************************************************************
1263  *              PdhSetDefaultRealTimeDataSource   (PDH.@)
1264  */
1265 PDH_STATUS WINAPI PdhSetDefaultRealTimeDataSource( DWORD source )
1266 {
1267     FIXME("%u\n", source);
1268     return ERROR_SUCCESS;
1269 }
1270 
1271 /***********************************************************************
1272  *              PdhGetLogFileTypeA   (PDH.@)
1273  */
1274 PDH_STATUS WINAPI PdhGetLogFileTypeA(const char *log, DWORD *type)
1275 {
1276     FIXME("%s, %p: stub\n", debugstr_a(log), type);
1277     return PDH_NOT_IMPLEMENTED;
1278 }
1279 
1280 /***********************************************************************
1281  *              PdhGetLogFileTypeW   (PDH.@)
1282  */
1283 PDH_STATUS WINAPI PdhGetLogFileTypeW(const WCHAR *log, DWORD *type)
1284 {
1285     FIXME("%s, %p: stub\n", debugstr_w(log), type);
1286     return PDH_NOT_IMPLEMENTED;
1287 }
1288 
1289 /***********************************************************************
1290  *              PdhBindInputDataSourceA   (PDH.@)
1291  */
1292 PDH_STATUS WINAPI PdhBindInputDataSourceA(PDH_HLOG *source, const char *filenamelist)
1293 {
1294     FIXME("%p %s: stub\n", source, debugstr_a(filenamelist));
1295     return PDH_NOT_IMPLEMENTED;
1296 }
1297 
1298 /***********************************************************************
1299  *              PdhBindInputDataSourceW   (PDH.@)
1300  */
1301 PDH_STATUS WINAPI PdhBindInputDataSourceW(PDH_HLOG *source, const WCHAR *filenamelist)
1302 {
1303     FIXME("%p %s: stub\n", source, debugstr_w(filenamelist));
1304     return PDH_NOT_IMPLEMENTED;
1305 }
1306