<?xml version="1.0" encoding="utf-8"?>
<rss xmlns:atom="https://www.w3.org/2005/Atom" xmlns:sy="https://purl.org/rss/1.0/modules/syndication/" xmlns:dc="https://purl.org/dc/elements/1.1/" xmlns:content="https://purl.org/rss/1.0/modules/content/" xmlns:wfw="https://wellformedweb.org/CommentAPI/" xmlns:slash="https://purl.org/rss/1.0/modules/slash/"
  version="2.0">
  <channel>
    <title>Where are we going, and what's with the handbasket?</title>
    <atom:link
      href="https://docs.microsoft.com/archive/blogs/dcook/feed.xml"
      rel="self"
      type="application/rss+xml" />
    <link>https://docs.microsoft.com/archive/blogs/dcook/feed.xml</link>
    <description />
    <lastBuildDate>Tue, 16 Apr 2019 05:00:26 GMT</lastBuildDate>
    <language>en-US</language>
    <sy:updatePeriod>hourly</sy:updatePeriod>
    <sy:updateFrequency>1</sy:updateFrequency>
    <item>
      <title>ETW - Overview</title>
      <link>https://docs.microsoft.com/archive/blogs/dcook/etw-overview</link>
      <pubDate>Wed, 30 Sep 2015 20:23:00 GMT</pubDate>
      <dc:creator><![CDATA[Doug E. Cook]]></dc:creator>
      <guid
        isPermaLink="false">https://blogs.msdn.microsoft.com/dcook/2015/09/30/etw-overview/</guid>
      <description><![CDATA[Event Tracing for Windows (ETW) is a system for routing events. ETW is primarily intended for...]]></description>
      <content:encoded><![CDATA[<p>Event Tracing for Windows (ETW) is a system for routing events. ETW is primarily intended for diagnostic purposes and is optimized to minimize impact on the overall system performance. ETW should not be used for control purposes as&nbsp;it does not offer guaranteed delivery -- events might be lost in certain circumstances (e.g. if events occur too quickly or if the system shuts down before the events are saved to disk).</p>
<h2>ETW Components</h2>
<p>ETW&nbsp;works as follows: a controller tool is used to configure sessions, telling ETW what events should be routed to a session and how to store the events that have been routed to a session; an event provider generates events; the ETW runtime&nbsp;routes events to the appropriate sessions; the sessions record the event; a decoder tool extracts information from the events; an analysis tool makes use of the information from the event.</p>
<p>Microsoft provides several controller tools such as XPerf, LogMan, and TraceLog. You can use these tools to start a session, to configure a session&nbsp;(controlling which events should be saved and the location to which they should be saved), and to stop a session.&nbsp;3rd parties can make use of the ETW APIs (StartTrace, ControlTrace,&nbsp;TraceSetInformation, EnableTraceEx2), &nbsp;to create their own controller tools.&nbsp;In addition, the Windows OS itself controls several sessions that it uses to monitor its own performance and reliability.&nbsp;ETW supports&nbsp;global sessions (receive events&nbsp;from any provider&nbsp;on&nbsp;the system) and private sessions (receive events only from providers in the same process). The system is limited to 64 global sessions, and each process is limited to&nbsp;4 private sessions.&nbsp;Special privileges (e.g. membership in Administrator or Performance Log Users) are required&nbsp;to control a global session.&nbsp;You can use&nbsp;"TraceLog -l" to get a list of active global sessions.</p>
<p>ETW sessions can be configured to save events in various ways:</p>
<ul>
<li>The session can be configured to write events to a file. In addition, ETW can treat the file as unlimited, as a circular buffer (overwriting old events once the file has reached a certain size), starting a new file when the file has reached a certain size, or stopping the trace when the file has reached the size limit.</li>
<li>The session can be configured to write events to a reserved block of&nbsp;memory. These events will be lost if the system is turned off. However, the data in memory can be flushed to disk on-demand. The session&nbsp;will use the memory as a circular buffer (overwriting old events once&nbsp;the block&nbsp;is full).</li>
<li>The session can be configured to send events to a consumer in real-time (via a callback). The&nbsp;consumer can then decide what to do with each event.</li>
</ul>
<p>3rd parties can make use of the ETW APIs (e.g. EventWrite)&nbsp;to create providers. Many Microsoft-provided components also act as providers. The Windows OS kernel publishes many events regarding the operation of the OS, and many Microsoft-provided drivers and applications publish events. Many ETW sessions are always running on the system, started and controlled by the OS itself. For example, the Windows Event Log uses ETW sessions to receive events, so the Event Log sessions are always running to enable this functionality. Other ETW sessions are started as needed. For example, Visual Studio will start a special ETW session when you ask it to do performance profiling of your application. Visual Studio will configure the session to receive events from the OS about the performance of your application, which it will then analyze to determine where your application is spending its time and memory.</p>
<p>ETW itself treats the data as binary data, and any kind of data can be encoded using any system. If the data is encoded using a custom scheme, you must use a custom tool to decode the data. However, there are several Microsoft-supported systems for encoding ETW data, and corresponding tools that can decode the ETW events generated by these systems. For example:</p>
<ul>
<li>WPP is a system for encoding diagnostic trace information and writing it to ETW. If you use WPP to generate ETW events, you can decode them using tools such as TraceFmt or TraceRpt. Note that in order to decode these traces, you must have the TMF or PDB files with information about your events.</li>
<li>MOF events are rarely used in new code, but many MOF-decodable events&nbsp;are generated by the Windows kernel. TraceRpt and other TDH-based decoders can decode MOF-decodable events as long as the appropriate event schema has been registered with WMI.&nbsp;The XPerf and WPA tools&nbsp;cannot access the WMI-registered data, so they cannot decode 3rd-party MOF-decodable events, but&nbsp;they contain hard-coded decoders for many common events generated by the Windows kernel. In particular, WPA makes heavy use of the kernel-generated events to analyze system behavior.</li>
<li>Manifest-based ETW is a general-purpose system for encoding and decoding ETW data using XML manifests. If you use manifest-based ETW, you can decode the events using TraceRpt or XPerf. Note that in order to decode these traces, the data from the manifest must be available to the decoding tool. This can be provided directly (e.g. by providing the path to the XML manifest on the TraceRpt command line) or indirectly by compiling the manifest into a binary format, including the binary data in the resources of your component, and registering the component on the system so that the decoding tool can find it. (You can use "TraceLog -enumguid" to get a list of components that have registered their decoding data on&nbsp;a system.)</li>
<li>TraceLogging is a general-purpose system for encoding and decoding ETW data without using separate decoding information. TraceLogging events can be decoded using the Windows 10 versions of TraceRpt or XPerf.&nbsp;Each event contains its own decoding information. This means each event will be larger, but it also means that the decoding information is always available and no separate XML, MOF, PDB, or TMF files need to be tracked.</li>
</ul>
<p>3rd parties can make use of the ETW APIs&nbsp;(ProcessTrace, EVENT_RECORD) to get the data from an ETL file, and can use the&nbsp;TDH APIs (TdhGetEventInformation)&nbsp;to decode events that use the Microsoft-supported encoding systems.</p>
<p>ETW was introduced as part of WMI in Windows 2000. Events written using the Windows 2000 APIs (e.g. TraceEvent) are sometimes called "Classic" events. The WPP and MOF frameworks for writing events use the Windows 2000 APIs, so they&nbsp;generate classic events. Providers that use the Windows 2000 APIs have a significant limitation --&nbsp;they can only be associated with one session at a time. If multiple sessions try to&nbsp;subscribe to&nbsp;events from the same classic provider, only the&nbsp;last session will actually receive events.</p>
<p>ETW was significantly updated for Windows Vista. Events written using the Windows Vista APIs (e.g. EventWrite) are sometimes called "Crimson" events. The XML manifest and TraceLogging&nbsp;frameworks use the Windows Vista&nbsp;APIs, so they generate crimson events. Providers that use the Windows Vista&nbsp;APIs can be associated with up to 8 sessions at a time.</p>
<h2>Event Content</h2>
<p>Each ETW event contains the following information:</p>
<ul>
<li>Required user-supplied information, such as the event provider GUID, the event ID, the event's "severity level", and the event's keywords.</li>
<li>Optional user-supplied information, such as event payload (binary data), activity GUID, and related activity GUID.</li>
<li>Always-present ETW-supplied information, such as the event's timestamp and&nbsp;the ID of the thread logging the event.</li>
<li>Optional ETW-supplied information, such as call stacks. The session controller configures ETW to include or exclude this data.</li>
</ul>
<p>The Event Provider GUID is supplied in the call to EventRegister. This GUID is used when routing and decoding events. For example, an event controller might configure a session&nbsp;to include all events with a particular provider&nbsp;GUID. When decoding MOF-based and manifest-based events, the provider GUID is used to&nbsp;look up the&nbsp;data that explains how to decode events. Note that multiple components can use the same provider GUID (even if they run at the same time) as long as you intend to enable/disable/route the events from multiple components as a unit, and in the case of manifest-based and MOF-based events as long as they share the same decoding information. Note also that there should be a strong 1-to-1 relationship between a provider name and the provider GUID. In order to maintain this relationship, you might want to base the provider GUID directly on the provider name using a hash. (A tool that performs this hash is provided in <a title="Provider names and GUIDs" href="https://blogs.msdn.com/b/dcook/archive/2015/09/08/etw-provider-names-and-guids.aspx">an earlier blog post</a>.)</p>
<p>Each event has an event ID. The event ID is used when routing and decoding events. For example, an event controller might configure a session to include only events 1, 2, and 5 from a particular provider. When decoding MOF and manifest-based events, the event ID + event Version should uniquely identify the event within the provider&nbsp;(e.g. given a provider GUID, an event ID, and an event Version, you should be able to look up the event's binary layout, and&nbsp;the&nbsp;binary layout should never change for the GUID + Id + Version combination;&nbsp;every event with the given GUID + Id + Version should have the same fields with the same types in the same order).</p>
<p>Each event has an event version. This is used when you need to change the fields in an event. When the binary layout of an event is changed, you'll typically duplicate the event in the manifest or MOF file, increment the event version in one copy, and make the necessary changes in the copy. That way, you can still decode events in the old format, and you can move forward using the new format.</p>
<p>Each event has an event level. This is an indicator of the event's severity or importance, and is used in event filtering. For example, an event controller might configure a session to include only events with severity "error" from a particular provider, or an event analysis tool might filter out events with severity below "warning". The level can be any value from 0 to 255. Levels 1-5 have been given names by Microsoft: 1 is critical, 2 is error, 3 is warning, 4 is info, and 5 is verbose. Typically, events should default to level 5 (verbose). The level 0 is special -- it means that the event does not specify a particular level, and will be enabled regardless of any level-based filtering done for the session. A lower numeric&nbsp;value means a more-severe event.</p>
<p>Each event has a keyword field. (In classic ETW, the keyword field was&nbsp;called "flags".)&nbsp;Each bit in the keyword corresponds to a category, and if the bit is set in the keyword for an event, it indicates that the event&nbsp;belongs to the specified category. The low 48 bits of the keyword are user-defined, while the upper bits must only be set for specific scenarios as defined by Microsoft. For example, the provider might define bit&nbsp;0x1 as "Networking event",&nbsp;bit 0x2 as "I/O event", and bit&nbsp;0x4 as "UI event", and a particular event might set its keyword to 0x5 indicating that the event is related to both networking and UI.&nbsp;An event controller might configure a session to&nbsp;only include events with&nbsp;certain keywords.&nbsp;If the keyword value is 0 (no bits set), that means the event does not specify any keyword and will be enabled regardless of any keyword-based filtering. (Note that starting with Windows 10, a controller can configure a session to ignore events with keyword set to 0.)</p>
<p>Each event has an opcode field. (In classic ETW, the opcode field was called "class".) The opcode is used to mark events, but does not affect ETW routing. A few opcodes have well-defined semantics recognized by analysis tools. Other opcodes can be defined by the user for any purpose. General-purpose analysis tools will only use the opcode to label or group the events. The default opcode is 0 = Info. The most commonly-used opcodes are Start (indicating the beginning of an activity) and Stop (indicating the end of an activity). Other commonly-used opcodes include DC_START (typically indicating that the&nbsp;associated event contains a dump&nbsp;of the provider's&nbsp;state, triggered by a notification that a session has started collecting data from the provider) and DC_END (similarly indicating that the associated event contains a state dump triggered by the end of data collection). The remaining opcodes can be used or defined at the discretion of the user.</p>
<p>Each crimson event has a task field. The task is a user-defined category that can be used to mark&nbsp;events, but does not affect ETW routing. Tasks can be used to group events into categories. It is also common to use tasks to&nbsp;give&nbsp;a name to an event, since manifest-based ETW does not have a first-class concept of an&nbsp;"event name" but it does have a concept of "task name".&nbsp;This is done by assigning the same&nbsp;numeric value for Event ID and Task ID, then&nbsp;setting the Task Name to the desired Event Name. (TraceLogging-based ETW is the reverse. TraceLogging supports Task ID, but does not support Task Name. On the other hand, TraceLogging does support Event Name, and uses the Task Name field for the Event Name.)</p>
<p>Each crimson event has a channel field. The channel can cause an event to be given special treatment by the ETW runtime or by an event consumer. The default channel 0 means no special treatment is requested. Channels 1-15 are reserved for definition by Microsoft and may lead to special treatment for the event by the ETW runtime or other built-in Windows OS components. Other channel values are user-defined and can be used to request special treatment from a session. The kind of special treatment depends on the kind of session that receives the event.&nbsp;For example, a real-time session might interpret channel 25 as indicating a high-priority event. Channels are most commonly used when interacting with the Windows Event Log. The Event Log's session listens to certain event providers that have registered themselves with the service. The Windows Event Log service uses the event's channel to route the event to the correct&nbsp;log. For example,&nbsp;a particular provider might&nbsp;register itself with Event Log and&nbsp;configure channel 16 to mean&nbsp;the Operational log.</p>
<h2>Activities</h2>
<p>The Windows Vista (crimson) ETW APIs support the concept of an activity.&nbsp;An ETW activity is a set of related events. For example, there might be an activity for reading the data from a file. The activity&nbsp;might start with an event from opening the file, continue with an event each time a block of data is read, and end with an event from closing the file.</p>
<p>At an ETW level, an activity is simply a set of events that all have their ActivityId field set to the same nonzero value. The first event in the activity is the Start event, and it should have its OpCode set to START. The last event in the activity is the Stop event, and it should have its Opcode set to STOP. Activities can be logically nested by associating one activity with a "related" activity. This association is recorded by setting the RelatedActivityId field of the Start event to the ActivityId of the parent event.</p>
<p>The ActivityId is a 128-bit value that is intended to be unique within the scope of a given ETW trace. The ActivityId can be a GUID. However, GUID generation is relatively slow, and ActivityIds do not need to be globally unique, so ETW provides a more efficient&nbsp;API that generates locally-unique ActivityId values (EventActivityIdControl).</p>
<p>Every crimson event has an ActivityId field. The&nbsp;value for the field can be set using the EventWriteTransfer or EventWriteEx APIs.&nbsp;If the user does not explicitly specify the value of the ActivityId field (e.g. if the EventWrite API is used, or if the user passes an activity ID of NULL to the EventWriteTransfer or EventWriteEx&nbsp;API), ETW will use the value of a thread-local ActivityID variable.</p>
<p>Every crimson event can optionally have a RelatedActivityId field. The value for the field can be provided using the EventWriteTransfer or EventWriteEx APIs. If the user does not explicitly specify the value of the RelatedActivityId field (e.g. if the EventWrite API is used, or if the user passes a related activity ID of NULL to the EventWriteTransfer or EventWriteEx API), ETW not include a RelatedActivityId in the event.</p>
<p>The value of the thread-local ActivityID variable can be read and written using the EventActivityIdControl API. Setting this variable will make all subsequent events from the same thread default to the specified activity ID value. This allows for an&nbsp;the following&nbsp;pattern for creating activities:</p>
<ol>
<li>Save the original value of the thread-local ActivityID variable.</li>
<li>Enter the scope of the activity.</li>
<li>Generate a new activity ID value, and set the thread-local ActivityID variable to this value.</li>
<li>Write a start event.&nbsp;Set the event's OpCode to START. Optionally set the event's RelatedActivityId to the saved original value of the thread-local ActivityID variable. Note that the start event will implicitly use the thread-local ActivityID value, so an explicit Activity ID does not need to be provided.</li>
<li>Write any number of events. As long as no code overwrites the thread-local ActivityID, all of the events on this thread will default to using the thread-local ActivityID value, implicitly making them part of the activity. (Events can exclude themselves from the activity by explicitly providing a different activity ID using the EventWriteTransfer or EventWriteEx APIs.)</li>
<li>Write a stop event. Set the event's OpCode to STOP. Note that the start event will implicitly use the thread-local ActivityID value, so an explicit Activity ID does not need to be provided.</li>
<li>On scope exit, restore the original value of the thread-local ActivityID variable.</li>
</ol>
<div>The advantage of this pattern is that the events in the activity can be written without explicitly providing an activity ID -- they will default to the thread-local value. The disadvantages of this pattern&nbsp;include the following:</div>
<ul>
<li>This pattern only works for single-thread synchronous activities. It does not work for asynchronous or multi-threaded activities.</li>
<li>This pattern depends on the good behavior of all other code that runs on the thread. If you call into a function that sets the thread-local ActivityID and does not restore the value on exit, the events after that function returns will not be associated with the correct activity. Note that while it is good practice to restore the activity ID on scope exit (even on exception or other error), there is no enforcement of this rule. In addition, it is possible for a call to a Windows OS API to overwrite the current thread's Activity ID, e.g. if the OS API calls into a driver that sets the thread-local activity ID and does not restore the original value.</li>
</ul>
<h2>Event Filtering and Routing</h2>
<p>ETW is responsible for routing each event to the correct set of sessions. Classic event providers (providers using the Windows 2000 APIs such as TraceEvent) can be routed to at most one session. Crimson event providers (providers using the Windows Vista APIs such as EventWrite) can be routed to up to 8 sessions.</p>
<p>Most events are not routed at all. If no session has asked for any events from a particular provider, the provider is considered disabled, and any events from that provider are ignored. When&nbsp;a disabled&nbsp;provider calls EventWrite, ETW will determine that the provider is disabled and will immediately return success, indicating that the event was delivered to all interested providers (all 0 of them). However, this is relatively inefficient -- it takes ~100 CPU cycles for EventWrite to validate its parameters and&nbsp;look up the provider's state&nbsp;to determine that the provider is disabled. (Note that using APIs such as EventEnabled or EventProviderEnabled will not help, since these APIs also require about the same amount of time&nbsp;to look up the provider's state. The only difference is that EventEnabled can be called before the event payload is set up.) The implementation of EventWrite looks something like this:</p>
<p>ULONG EventWrite(<br />&nbsp;&nbsp;&nbsp; REGHANDLE handle,<br />&nbsp;&nbsp;&nbsp; PCEVENT_DESCRIPTOR descriptor,<br />&nbsp;&nbsp;&nbsp; ULONG count,<br />&nbsp;&nbsp;&nbsp; PEVENT_DATA_DESCRIPTOR data)<br />{<br />&nbsp;&nbsp;&nbsp;&nbsp;if (!ValidateParameters(handle, descriptor, count, data))<br />&nbsp;&nbsp;&nbsp; {<br />&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; return ERROR_INVALID_PARAMETER;<br />&nbsp;&nbsp;&nbsp; }<br /><br />&nbsp;&nbsp;&nbsp; ULONG errorCode = ERROR_SUCCESS;<br />&nbsp;&nbsp;&nbsp; PROVIDER_INFO info = LookupProviderInfo(handle);<br />&nbsp;&nbsp;&nbsp; if (info.IsEnabled &amp;&amp;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; info.IsLevelOk(descriptor.Level) &amp;&amp;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; info.IsKeywordOk(descriptor.Keyword))<br />&nbsp;&nbsp;&nbsp; {<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; errorCode = info.SendEventToSessions(descriptor, count, data);<br />&nbsp;&nbsp;&nbsp; }<br /><br />&nbsp;&nbsp;&nbsp; return errorCode;<br />}</p>
<p>Providers can improve their performance by providing a callback function when they register with ETW (see the EventRegister API). ETW will invoke the callback whenever a session enables or disables the provider. This allows the provider to keep track of its own state, including whether it is enabled or disabled, what levels are being filtered out, and what keywords are being filtered out. The provider can check its own state much more efficiently than EventWrite, allowing it to run much more efficiently when the provider is disabled. A provider might store its state in global variables and might then write events using code like this:</p>
<p>if (g_providerIsEnabled &amp;&amp;<br />&nbsp;&nbsp;&nbsp; g_providerLevel &gt; ThisEventDescriptor.Level &amp;&amp;<br />&nbsp;&nbsp;&nbsp; IsKeywordEnabled(ThisEventDescriptor.Keyword))<br />{<br />&nbsp;&nbsp;&nbsp; EVENT_DATA_DESCRIPTOR data[4];<br />&nbsp;&nbsp;&nbsp; // Prepare data for event ...<br />&nbsp;&nbsp;&nbsp; EventWrite(g_providerHandle, &amp;ThisEventDescriptor, 4, data);<br />}</p>
<p>In the case where the provider is disabled, this will skip the event-write code using only 1 or 2 CPU cycles. In the case where the provider is enabled, but the event is filtered out by level or by keyword, this will skip the event-write code using only 5 or 10 CPU cycles. The code to prepare the data and call into ETW will only run if the provider is enabled for events with the given level or keyword.</p>
<p>The code for maintaining the callback and for checking the state of the provider for each event can be cumbersome. Various frameworks exist to hide this complexity from the user. WPP and manifest-based ETW generate header files with code that looks similar to that shown above, implementing very efficient filtering with little effort on the part of the developer.&nbsp;The TraceLoggingProvider.h header&nbsp;implements similar filtering in the TraceLoggingWrite macro. For example:</p>
<p>TraceLoggingWrite(<br />&nbsp;&nbsp;&nbsp; g_hProvider,<br />&nbsp;&nbsp;&nbsp; "EventName",<br />&nbsp;&nbsp;&nbsp; TraceLoggingValue(GetMyValue(), "MyValue"));</p>
<p>In this code, the GetMyValue() function will only be invoked if the provider is enabled.</p>
<p>The .NET EventSource class implements efficient filtering within the&nbsp;WriteEvent, WriteEventCore, and Write methods, so these methods return very quickly if the provider is disabled or if the event is filtered by level or keyword. Similarly, the Windows Runtime LoggingChannel class&nbsp;implements&nbsp;efficient filtering so that the LogEvent, LogMessage, and LogValuePair methods return&nbsp;very quickly if the event does not need to be written. However, in these cases, the automatic filtering does not&nbsp;eliminate the time spent preparing the data for an event. For example, with .NET EventSource,</p>
<p>myEventSource.WriteMyValue(GetMyValue());</p>
<p>In this code, the GetMyValue() function will be invoked even if the provider is disabled. If GetMyValue()&nbsp;returns&nbsp;quickly, this might&nbsp;be ok.&nbsp;On the other hand, if the GetMyValue() function might be expensive, it would be more efficient to use code like this:</p>
<p>if (myEventSource.IsEnabled())<br />{<br />&nbsp;&nbsp;&nbsp; myEventSource.WriteMyValue(GetMyValue());<br />}</p>
<p>Note that the above check only tests the overall provider state. This might be sufficient, or it might be better to use another overload of IsEnabled to also check for level-based or keyword-based filters.</p>
<p>The same applies&nbsp;to TraceLoggingProvider.h -- the automatic filtering does not skip code outside the TraceLoggingWrite macro.&nbsp;For these cases, a manual check might be needed for optimal code. Each of these frameworks provides methods for determining whether the provider is enabled without sending an event. For example, if preparing the data is more complex than a single GetMyValue() call, the above code might be written as follows:</p>
<p>if (TraceLoggingProviderEnabled(g_hProvider, 0, 0))<br />{<br />&nbsp;&nbsp;&nbsp; int myValue;<br />&nbsp;&nbsp;&nbsp; GetMyValue(&amp;myValue);<br />&nbsp;&nbsp;&nbsp; TraceLoggingWrite(...);<br />}</p>
<p>ETW supports many other kinds of filters that are difficult or impossible to replicate within the provider code. The provider-side filtering will not exactly match the complete filtering and routing done by ETW, and some events will pass the provider-side filtering only to be dropped by the more complete filtering done by the ETW runtime. This is generally ok -- the provider-side filtering is&nbsp;usually successful at eliminating the vast majority of unnecessary events in only one or two CPU cycles. However, for this simple provider-side filtering to be most effective, events and providers should be organized in a way that allows event scenarios to be defined by a combination of provider GUID, level, and keyword.</p>
<p>If scenarios are well-defined using filters understood by the provider (e.g. filters based on level and keyword), the scenario will have minimal impact on the system when the corresponding session is created. For example, if&nbsp;a "networking" keyword is defined,&nbsp;and the "error" level is used consistently, it is very easy to diagnose a networking error by&nbsp;setting up a session to collect events where level &lt;= error and keyword mask = networking. On the other hand, if&nbsp;no keywords are defined&nbsp;for your events, and levels are not used consistently,&nbsp;the same scenario&nbsp;might not allow for any meaningful filtering at the level or keyword layer. All events in the provider might need to be enabled and collected, leading to a performance impact while the session is enabled.</p>
<p>Each session can be configured to receive events from any number of provider GUIDs. For each provider GUID, various filters can be established, such as filtering by level, keyword, or event ID. The session can also request additional information&nbsp;(e.g. callstacks)&nbsp;for certain events.</p>
<p>When ETW receives an event from a provider, it determines&nbsp;the set of&nbsp;sessions that are receiving events from that provider. It then checks the filters for each session, removing the session from the set if the event does not meet the session filters. If the set of sessions is non-empty, it reserves space for the event in each of the session's buffers. After it has reserved space, it copies the event into the reserved space for each session.</p>
<p>If ETW fails to reserve space in all sessions, its default behavior is to drop the event (i.e. if ETW cannot deliver the event to all interested sessions, it does not deliver the event at all). This policy was intended for consistency, i.e. so that the different sessions will all record a consistent story. However, this behavior is not always desirable. A session can opt out of this policy by setting the EVENT_TRACE_INDEPENDENT_SESSION_MODE flag. (Newer versions of the XPerf tool will set this flag for the sessions it creates.)</p>
<p>Each ETW session has a worker thread that handles tasks for the session. For file-based sessions, the worker thread is responsible for noticing when a buffer is full, writing the full buffer to disk, marking the buffer as empty, and allowing it to be reused for future events. For real-time sessions, the worker thread is responsible for sending full buffers to the real-time consumer process.</p>]]></content:encoded>
    </item>
    <item>
      <title>TraceLogging - Background</title>
      <link>https://docs.microsoft.com/archive/blogs/dcook/tracelogging-background</link>
      <pubDate>Thu, 24 Sep 2015 20:06:25 GMT</pubDate>
      <dc:creator><![CDATA[Doug E. Cook]]></dc:creator>
      <guid
        isPermaLink="false">https://blogs.msdn.microsoft.com/dcook/2015/09/24/tracelogging-background/</guid>
      <description><![CDATA[ETW is a system for getting data from providers&nbsp;to consumers. The core ETW runtime does not...]]></description>
      <content:encoded><![CDATA[<p>ETW is a system for getting data from providers&nbsp;to consumers. The core ETW runtime does not know anything about the payload of the ETW event -- it just routes the event based on event attributes such as provider ID, event ID, level, and keywords. The user of ETW can put any data in any format into the event (unless the payload causes the event to exceed the ETW event size limit of 64KB).</p>
<p>While it is possible to write custom code to pack and unpack each event, this tends to be hard to manage. Developers don't want to spend time writing custom packing and unpacking code.&nbsp;Worse, if each event has a custom packing/unpacking format, there can be no general-purpose decoding tools. To avoid this situation, several event encoding/decoding protocols have been built over the general ETW infrastructure.</p>
<ul>
<li>MOF - description of the event encoding provided in MOF&nbsp;(WMI) files. (Old technology, generally not used for new ETW providers. Covers mostly the same scenarios as manifest-based ETW, but with less flexibility, so it won't be discussed further in this post.)</li>
<li>WPP - description of the event encoding provided in C/C++ code. A special preprocessor&nbsp;(tracewpp)&nbsp;generates code for packing the event data and generates TMF files that can be used by event decoding tools to decode events. The preprocessor also adds attributes to the code that allows the TMF files to be reconstructed from the PDB. (Intended for logging diagnostic information for use by developers.)</li>
<li>Manifest - description of the event encoding provided in MAN (XML) files. A special preprocessor (mc)&nbsp;generates a binary representation of the manifest file&nbsp;and optionally generates C/C++ code for packing the event. The binary version of the manifest file is added to the resources of a DLL and registered on a system so that decoding tools can find it when they need to decode events. (Intended for general purpose logging, e.g. for the Event Log or for diagnostic purposes.)</li>
</ul>
<p>One of the major challenges of ETW is managing the decoding information (the "metadata")&nbsp;for events. Somebody has to author the event description in a way that allows both machines (encoder and decoder) and humans (developer writing the event and whoever wants to read the decoded version of the event) to understand the event's content. This event description has to somehow be made available to the tools that are used to process and decode the event.</p>
<p>WPP is popular because in straightforward cases, it is very easy to use. A developer adds a few macros to the code, sets up the build system to enable the WPP preprocessor, and writes familiar printf-style code. Decoding typically requires TMF or PDB files and an ETL file turns into a list of timestamped messages that is easy to look through. However, it becomes hard to manage in more complex scenarios - the macros are very tricky to customize, the preprocessor can interfere with the build process, the flexibility of the printf-style formatting means the data is not in a standard format that is easy to analyze, and the decoding information (PDB or TMF) is specific to a particular build of the DLL, so it can be hard to decode the WPP data from a particular ETL file (you have to figure out what version of the DLL was used, then locate the TMF or PDB file for that specific version). In addition, WPP is very tightly-coupled to C/C++ and won't work for other languages.</p>
<p>Manifests work well in other scenarios where the centralized manifest provides benefit. Developers maintain a manifest file, and the binary representation of the manifest is included with the product (embedded in the DLL resources). Maintaining the manifest is more work, but it provides benefits like stable event definitions (events from the old product can generally be decoded using the new manifest) and&nbsp;localizable event messages. The data decodes into name-value pairs (where the value has a strong type like "uint32")&nbsp;that allow for structured analysis if necessary. On the other hand, maintaining the manifest is more work, and can be completely infeasible in some scenarios (e.g. scripted environments). Proper decoding requires global registration of the binary during component installation, which can also be problematic (e.g. for Windows store apps, or xcopy-deployed apps).</p>
<p>Neither of these technologies is ideal for all situations. During development of Windows 10, the ETW team looked for ways to improve the metadata management story. We came to the following conclusions</p>
<ol>
<li>There is no way to make a single metadata handling system that meets all needs. A centralized design (a&nbsp;single point where all events in a component are defined, e.g. a manifest) is a requirement for some scenarios but adds unnecessary complexity for other scenarios.</li>
<li>Manifest-based ETW is a reasonable solution for&nbsp;centralized scenarios.</li>
<li>WPP-based ETW is a reasonable solution for some distributed scenarios.</li>
<li>There are many scenarios for which neither manifest-based nor WPP-based ETW is a good fit.</li>
</ol>
<p>We designed a new ETW metadata system to address scenarios that weren't met with either manifest-based or WPP-based ETW. The new system is called "TraceLogging" or "manifest-free ETW". The new system was designed with the following goals:</p>
<ol>
<li>Usable on any development platform, including C/C++, .NET, and scripting languages.</li>
<li>Automatic management of metadata. The developer shouldn't have to worry about how the decoder gets the decoding information. ETW should Just Work, and ETL files should Just Decode.</li>
<li>Permit distributed management of decoding metadata. The developer should be able to write the event description in code.</li>
<li>Provide strongly-typed and structured data. Data is organized as name-value pairs. Values are strongly-typed as uint32, string, datetime, etc. Values can have complex types such as structure, array, array of structure. Structures can nest.</li>
<li>Providers should be able to use this technology on Vista or later.</li>
</ol>
<p>At its core, TraceLogging is simply a convention on how to encode data and how to describe the encoded data. A TraceLogging ETW event is simply a normal ETW event with a payload packed using the TraceLogging encoding rules and a flag set that tells the decoder to unpack using TraceLogging decoding rules. The implementation of this technology includes helpers for packing the TraceLogging event and libraries for decoding TraceLogging events.</p>
<p>The primary difference between TraceLogging and&nbsp;the&nbsp;other systems&nbsp;is that each TraceLogging event includes its own decoding information. In WPP,&nbsp;MOF, or manifest-based ETW, the decoder must locate a file with the appropriate decoding information, but&nbsp;each TraceLogging event is self-contained, allowing the decoder to&nbsp;interpret the event content without access to any external information.&nbsp;In some ways this is similar to the way a JSON object or XML document can be parsed (and potentially even analyzed)&nbsp;without access to any external schema reference.</p>
<p>The benefits to this approach include the following:</p>
<ul>
<li>Decoding information is always&nbsp;available. There is no need to track down a manifest, MOF, PDB, or TMF file. There is no need to register your component's metadata. The decoding information is never out of date.</li>
<li>Events can be generated dynamically. This makes ETW more easily usable from scripting languages like JavaScript or Python.</li>
<li>Event definitions can be distributed and can be written directly in code with no need for an external tool or preprocessor.</li>
</ul>
<p>As mentioned above, there is no way to make a single ETW solution that meets all needs. The following issues must be considered when discussing the new TraceLogging technology:</p>
<ul>
<li>Each TraceLogging event contains&nbsp;its own metadata, so TraceLogging events are always larger than the equivalent manifest-based or WPP-based event. While the metadata encoding is relatively compact, it can add several hundred bytes per event.&nbsp;In cases where reducing the event size is a high priority (e.g. when generating millions of events), TraceLogging is not ideal.</li>
<li>TraceLogging supports distributed event authoring, so there is no single place to go to find a list of all of a component's events.</li>
<li>To write a TraceLogging event, the metadata must be available (and in memory) at runtime. The design of WPP and manifest-based ETW support installing a provider without also installing the decoding information, potentially saving disk space. In addition, neither WPP nor manifest-based ETW need to load the decoding information into memory until the event needs to be decoded.</li>
<li>TraceLogging events do not support localization. The provider name, event name, and field names are hard-coded into the event, and there is no built-in support for localized stringtables to map these identifiers to an&nbsp;alternate language.</li>
<li>TraceLogging does not have built-in support for enumerated types. The existing TraceLogging APIs simply convert the enumerated value to an integer. There is no built-in support for associating friendly names with each enumeration value.</li>
</ul>
<p>We've implemented support for TraceLogging in C/C++, .NET, and Windows Runtime.</p>
<ul>
<li>For C/C++ code (kernel-mode or user-mode), use the Windows 10 SDK and include the &lt;TraceLoggingProvider.h&gt; header. Note that while this header is new to the Windows 10 SDK, you can use this header in projects that target Vista or later.</li>
<li>For .NET code, we've updated System.Diagnostics.Tracing.EventSource in .NET 4.6 to add extensive new capabilities based on the flexibility provided by TraceLogging.&nbsp;If you want to use this functionality with an earlier .NET runtime, you can use the EventSource NuGet redistributable from nuget.org.</li>
<li>For Windows Runtime code, we've updated the Windows 10 version of LoggingChannel to support writing structured events based on the new TraceLogging encoding.</li>
</ul>
<p>The provider side of the TraceLogging technology stack does not require any new OS features, and works correctly on Vista or later (with a few limitations). You can write a program that generates TraceLogging events, run the program on Vista, and capture the events into an ETL file. The TraceLogging provider&nbsp;library will pack the payload using TraceLogging rules, and will set the event's channel = 11 to flag the event as being TraceLogging-encoded. It will then use the Vista-compatible EventWriteTransfer API to send the encoded event to ETW, which will treat it like any other event. However, you will need an OS update for the following functionality:</p>
<ul>
<li>Using the TDH APIs to decode TraceLogging events requires new TDH functionality. This functionality is built-into Windows 10, and is available as a Windows Update for Windows 7 SP1 and for Windows 8.1.</li>
<li>Writing an event with a channel other than 11 requires new functionality in the core ETW runtime. Again, this support is built-into Windows 10 and is available as a Windows Update for Windows 7 SP1 and for Windows 8.1.</li>
</ul>]]></content:encoded>
    </item>
    <item>
      <title>ETW Provider Names and GUIDs</title>
      <link>https://docs.microsoft.com/archive/blogs/dcook/etw-provider-names-and-guids</link>
      <pubDate>Tue, 08 Sep 2015 16:56:00 GMT</pubDate>
      <dc:creator><![CDATA[Doug E. Cook]]></dc:creator>
      <guid
        isPermaLink="false">https://blogs.msdn.microsoft.com/dcook/2015/09/08/etw-provider-names-and-guids/</guid>
      <description><![CDATA[An ETW provider is a component that creates ETW events. Internally, ETW identifies each provider...]]></description>
      <content:encoded><![CDATA[<p>An ETW provider is a component that creates ETW events. Internally, ETW identifies each provider with a GUID, and all event filtering/routing in ETW starts with the provider GUID. For example, if I want to capture an ETL file containing events from a particular provider, I would find the provider's GUID, and then execute an xperf or tracelog command similar to the following:</p>
<p>xperf -start MySessionName -f MySession.etl -on 2e0582f3-d1b6-516a-9de3-9fd79ef952f8</p>
<p>GUIDs are ugly, so the systems layered above core ETW have established conventions for friendly provider names. For example, ETW manifests include the provider GUID and the provider name. When you compile the manifest and register the resulting decoding information, both the GUID and the name are added to the registry, allowing the decoding information to be found either by GUID or by name.</p>
<p>To avoid problems with this system:</p>
<ul>
<li>The association between GUID and Name is one-to-one. Once you've published the Name and GUID for a provider, the association between Name and GUID is permanent. If you change the GUID, you must change the Name, and if you change the Name, you must change the GUID.</li>
<li>Searching for providers by Name is generally not case-sensitive. However, some reporting tools may be confused if they see two names that differ in case. Because of this, do not change the uppercase/lowercase of any letters in the name once you've published the Name of a provider.</li>
<li>The provider Name is not localized. It is a symbol, like a variable name or file name.</li>
<li>The provider Name is global, so it should be properly namespaced. Manifest providers often use the convention&nbsp;CompanyName-ProductName-ComponentName (with additional parts as needed for SubCompontName), e.g.&nbsp;"Microsoft-Windows-Globalization". It is also common to use periods instead of hyphens, e.g. "Microsoft.Windows.DHCP".</li>
</ul>
<p>The .NET EventSource class developed a convention for generating the provider GUID by hashing the provider Name (algorithm based on RFC 4122). This has worked well in practice -- the developer provides the human-friendly provider name, and EventSource internally manages turning that into a machine-friendly GUID. If the provider Name is changed, the provider GUID will also change (as long as the change involves more than just uppercase/lowercase).</p>
<p>Support for this convention has been added to WPR. Normally, you provide WPR with the provider's GUID, but if you generated the provider GUID using the EventSource algorithm, you can instead provide the friendly name to WPR prefixed with an asterisk, e.g. "*MyProviderName".</p>
<p>This convention has also been adopted by the Windows Framework LoggingChannel class. The provider GUID is an optional parameter, and if omitted, the provider GUID will be generated using the EventSource algorithm.</p>
<p>This convention can also be used with manifest-based ETW or with TraceLoggingProvider.h, but the tools will not automatically generate the GUID for you. Instead, you can use the attached EtwGuid tool (public domain C# sample code). The tool treats each command-line argument as a provider Name. For each argument, it outputs a code snippet with the corresponding provider GUID. The resulting code snippet is formatted for use with TraceLoggingProvider.h, but the GUID can also be used in a manifest.</p>
<p>Example usage:</p>
<pre>D:\Stuff\Projects\EtwGuid\bin\Debug&gt;EtwGuid.exe SampleProvider1 SampleProvider2

TRACELOGGING_DEFINE_PROVIDER(
    g_hMyProvider,
    "SampleProvider1",
    // {69aed5be-95eb-5b26-ed54-cf841b064fed}
    (0x69aed5be,0x95eb,0x5b26,0xed,0x54,0xcf,0x84,0x1b,0x06,0x4f,0xed));

TRACELOGGING_DEFINE_PROVIDER(
    g_hMyProvider,
    "SampleProvider2",
    // {7880d1eb-f225-56df-f429-3e3a24c6f149}
    (0x7880d1eb,0xf225,0x56df,0xf4,0x29,0x3e,0x3a,0x24,0xc6,0xf1,0x49));

D:\Stuff\Projects\EtwGuid\bin\Debug&gt;
</pre>
<p><a href="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Components.PostAttachments/00/10/64/04/35/EtwGuid.zip" original-url="https://blogs.msdn.com/cfs-file.ashx/__key/communityserver-components-postattachments/00-10-64-04-35/EtwGuid.zip">EtwGuid.zip</a></p>]]></content:encoded>
    </item>
    <item>
      <title>We apologize for the inconvenience</title>
      <link>https://docs.microsoft.com/archive/blogs/dcook/we-apologize-for-the-inconvenience</link>
      <pubDate>Tue, 08 Sep 2015 14:57:21 GMT</pubDate>
      <dc:creator><![CDATA[Doug E. Cook]]></dc:creator>
      <guid
        isPermaLink="false">https://blogs.msdn.microsoft.com/dcook/2015/09/08/we-apologize-for-the-inconvenience/</guid>
      <description><![CDATA[Sorry I haven't blogged for a while.
I'm on the Core ETW team now, and there are some things I want...]]></description>
      <content:encoded><![CDATA[<p>Sorry I haven't blogged for a while.</p>
<p>I'm on the Core ETW team now, and there are some things I want to share about ETW, so expect a few postings in the near future.</p>
<p>Let me know if there are things you'd like to have explained about ETW. I'm hoping to do some posts that go over some of the details and explain how ETW works.</p>]]></content:encoded>
    </item>
    <item>
      <title>SD Cards in Windows Phone 7</title>
      <link>https://docs.microsoft.com/archive/blogs/dcook/sd-cards-in-windows-phone-7</link>
      <pubDate>Mon, 08 Nov 2010 16:34:00 GMT</pubDate>
      <dc:creator><![CDATA[Doug E. Cook]]></dc:creator>
      <guid
        isPermaLink="false">https://blogs.msdn.microsoft.com/dcook/2010/11/08/sd-cards-in-windows-phone-7/</guid>
      <description><![CDATA[There have been a lot of questions on forums about the SD card support in Windows Phone 7. The SD...]]></description>
      <content:encoded><![CDATA[<p>There have been a lot of questions on forums about the SD card support in Windows Phone 7. The SD card is not used the same way in a Windows Phone 7 device as it is in many&nbsp;other smartphones, and this can cause some confusion. In this post, I don&rsquo;t want to discuss whether this change is a good idea or a bad one. I just want to dispel some confusion by stating the facts as I understand them. I&rsquo;m not an official spokesperson for the Windows Phone team, but I have some information that might be helpful to the average consumer.</p>
<p>The first point is that the SD card should be thought of as if it were your phone&rsquo;s hard drive, not as removable storage. Not all Windows Phone 7 devices support replacing the SD card, and if the device does support replacing the SD card, you&rsquo;ll have to perform a factory reset (losing all of your settings and saved files) as part of the replacement process. The phone won&rsquo;t work correctly if you remove the SD card. Replacing the SD card on a Windows Phone 7 device is like replacing the hard drive on a desktop PC.</p>
<p>It follows that a slow SD card will have a big impact on the overall performance of Windows Phone 7. Adding a slow SD card to the device will slow down the whole phone. Unfortunately,&nbsp;determining whether an&nbsp;SD card is &ldquo;fast&rdquo;&nbsp;or &ldquo;slow&rdquo;&nbsp;requires a lot of testing, and the advertised &ldquo;class&rdquo; of an SD card has almost nothing to do with how well it will perform in a phone. SD card &ldquo;class&rdquo; measures how quickly an SD card can save a single large file such as a photo (think of this like you would think about a car&rsquo;s top speed). Windows Phone&rsquo;s performance is more affected by how quickly the SD card can load and&nbsp;save a whole bunch of tiny files (think of this like you would think about a car&rsquo;s performance in an obstacle course). Don&rsquo;t fool yourself into thinking that any old SD card will work or that a higher "class" of&nbsp;SD card will make your phone work better. Microsoft tested a whole bunch of different SD cards. Very few performed well enough to be approved for use in the phone.&nbsp;SD cards&nbsp;that are not "approved" may work correctly, and may even work well,&nbsp;but they may also work poorly or not at all. Unfortunately, the SD card&rsquo;s &ldquo;class&rdquo; won&rsquo;t be very helpful in predicting how the card will perform in the phone.</p>
<p>Finally, the SD card is locked to your phone, and data on the SD card is encrypted. The data on the SD card can only be read by the phone, and cannot be read by other computers or phones. The encryption key is set when the phone first boots after a factory reset. Once the SD card has been locked, there are two ways to unlock it. The first is to provide the encryption key, which only your phone can do. The second is to&nbsp;perform a low-level reset&nbsp;on SD card, which erases all of the SD card's data.&nbsp;The phone will&nbsp;perform a low-level reset on the SD card during its first boot after a factory reset, and will then lock the card to itself. Unfortunately, I don&rsquo;t know how to do a low-level reset&nbsp;on an&nbsp;SD&nbsp;from a desktop computer. This means that once your SD card has been locked to a Windows Phone, it won't work anywhere else. (In the future, tools might become available to reset the SD card, but for now, you should assume that the SD card is no longer usable.)</p>
<p>Official Microsoft KB article: <span style="font-family: 'Calibri','sans-serif'; color: #1f497d; font-size: 11pt; mso-fareast-font-family: Calibri; mso-fareast-theme-font: minor-latin; mso-ansi-language: EN-US; mso-fareast-language: EN-US; mso-bidi-language: AR-SA;"><a target="_blank" href="https://support.microsoft.com/kb/2450831"><span style="color: #0000ff;">https://support.microsoft.com/kb/2450831</span></a></span></p>
<p>Hope this helps!</p>
<p><strong>2010/11/10: </strong>Minor editing, added link to KB article.</p>]]></content:encoded>
    </item>
    <item>
      <title>The quest for the three screens</title>
      <link>https://docs.microsoft.com/archive/blogs/dcook/the-quest-for-the-three-screens</link>
      <pubDate>Sat, 30 Jan 2010 23:39:00 GMT</pubDate>
      <dc:creator><![CDATA[Doug E. Cook]]></dc:creator>
      <guid
        isPermaLink="false">https://blogs.msdn.microsoft.com/dcook/2010/01/30/the-quest-for-the-three-screens/</guid>
      <description><![CDATA[Like most developers, I have overwhelming neurotic tendencies to incessantly become...]]></description>
      <content:encoded><![CDATA[<P>Like most developers, I have overwhelming neurotic tendencies to incessantly become insanely&nbsp;focused on trivial issues. One particular thorn in my side is my failure to get a good triple-monitor system set up at work. (Three monitors is the only proper way to work. That simply goes without question. If you don't believe me, then this article probably isn't for you.)</P>
<P>Back in the days of XP, this wasn't actually a big problem.&nbsp;All of the decent AGP video cards supported two monitors, so&nbsp;I just had to dig up any old&nbsp;PCI video card to complete the trinity. The third monitor would be a bit laggy, but it wasn't a big problem.</P>
<P>Unfortunately, the Vista display driver model made this solution more difficult. Vista's display architecture took several steps forward and enabled a lot of new capabilities, but to make it possible, a few rarely-used capabilities of the older architecture were left behind. One of these capabilities was the ability for Windows to work with more than one display driver at once.</P>
<P>Being a creative genius, I was able to resolve this issue by getting two new nVidia&nbsp;graphics cards. One was AGP, the other was PCI, but otherwise they were identical (same chipset and GPU). This worked, but everything on the PCI display was slow. This was especially noticable when dragging windows from one screen to the other, and also when switching from one window station to another (as required for Ctrl+Alt+Del and UAC prompts).</P>
<P>I later upgraded to a PCI-express system. Unfortunately, it only had one x16 PCI Express slot, but I still managed to get the triple-monitor configuration working with two ATI video cards (one x16, the other x1). Again, the slower card was definitely noticable, but I couldn't figure out a good solution that didn't involve a new motherboard, so I just tried to live with it.</P>
<P>At one point, I got a video card with three outputs (VGA, DVI, and HDMI), only to discover that only two of the outputs could be active at once. Apparently the video card cabal has a rule: thou shalt not create a video card that can support more than two monitors.</P>
<P>&nbsp;Recently, I found out that ATI&nbsp;had released a new line&nbsp;of video cards with "Eyefinity" technology. In theory, it supports three monitors, but only if one of them uses DisplayPort. None of my existing monitors supports DisplayPort, but minor technical issues&nbsp;like that have never stopped me before, so I&nbsp;immediately ordered a 5770&nbsp;(I would have gotten a 5750, but my case didn't have room for&nbsp;a two slot card, and the only single-slot card in stock at NewEgg was a 5770)&nbsp;and a DisplayPort-to-DVI adapter.</P>
<P>To make a long story short,&nbsp;after some work with a pair of&nbsp;tin snips&nbsp;(the single slot card fits perfectly, but only for certain definitions of "perfectly") and a&nbsp;bigger power supply (they're not kidding when they say you need a 450 watt power supply - the card was&nbsp;not reliable&nbsp;with&nbsp;just&nbsp;350 watts), I did get the card installed and running on my desktop. But it still only worked with two monitors at once. Any two, but only two.</P>
<P>Apparently, all DisplayPort adapters are created equal, but some are more equal than others. ATI isn't particularly forthcoming about this, but the "passive" DisplayPort adapters (read: anything less than $50) apparently don't count as a DisplayPort monitor. The video card only has enough circuitry to handle two VGA/DVI/HDMI ports at once, and a passive DisplayPort-to-DVI adapter counts as a DVI port.</P>
<P>So I've ordered a second DisplayPort adapter. This time, it's an "active" DisplayPort-to-VGA adapter (one of my monitors is VGA only, and I suspect that by the time it is replaced, the replacement will have a DisplayPort input), so hopefully it will work.&nbsp;If it doesn't, I'll probably rip my hair out.&nbsp;Tune in next week for the exciting conclusion!</P>]]></content:encoded>
    </item>
    <item>
      <title>Creating a self-signed certificate in C#</title>
      <link>https://docs.microsoft.com/archive/blogs/dcook/creating-a-self-signed-certificate-in-c</link>
      <pubDate>Tue, 25 Nov 2008 19:19:00 GMT</pubDate>
      <dc:creator><![CDATA[Doug E. Cook]]></dc:creator>
      <guid
        isPermaLink="false">https://blogs.msdn.microsoft.com/dcook/2008/11/25/creating-a-self-signed-certificate-in-c/</guid>
      <description><![CDATA[For a personal project involving SSL, I wanted to create some certificates that could be used to...]]></description>
      <content:encoded><![CDATA[For a personal project involving SSL, I wanted to create some certificates that could be used to authenticate the client and server to each other. Nothing fancy - self-signed is perfectly fine in this case since the client would have an actual copy of the server cert to use when validating the server, and having the cert on the filesystem is secure enough for the task. In any case, I was disappointed to find out that even with all of the other crypto and certificate support, .NET lacks support for this. I was also disappointed by how difficult it was to figure out how to do this. 
<P>CertCreateSelfSignCertificate sounds promising, but it ends up not being quite enough. It turns out that you have to do the following (as simple as I know how to make it, anyway):</P>
<OL>
<LI>CryptAcquireContext(out providerContext, randomContainerName, null, PROV_RSA_FULL, CRYPT_NEWKEYSET)</LI>
<LI>CryptGenKey(providerContext, AT_KEYEXCHANGE, CRYPT_EXPORTABLE, out cryptKey)</LI>
<LI>CertStrToName(X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, name, CERT_X500_NAME_STR, 0, dataBuffer, ref dataLength, 0)</LI>
<LI>cert = CreateSelfSignCertificate(providerContext, blob(dataBuffer, dataLength), 0, KeyProviderInfo(randomContainerName, PROV_RSA_FULL, AT_KEYEXCHANGE), 0, startTime, endTime, 0)</LI>
<LI>certificateStore = CertOpenStore("Memory", 0, 0, CERT_STORE_CREATE_NEW_FLAG, 0)</LI>
<LI>CertAddCertificateContextToStore(certificateStore, cert, CERT_STORE_ADD_NEW, out storeCert)</LI>
<LI>CertSetCertificateContextProperty(storeCert, CERT_KEY_PROV_INFO_PROP_ID, 0, KeyProviderInfo(randomContainerName, PROV_RSA_FULL, AT_KEYEXCHANGE))</LI>
<LI>PFXExportCertStoreEx(certificateStore, pfxBlob, password, 0, EXPORT_PRIVATE_KEYS)</LI>
<LI>Free everything.</LI></OL>
<P>In case anybody is interested, source code is attached and is free for use by anybody as long as you don't hold me or Microsoft liable for it -- I have no idea whether this is actually the right or best way to do this. Give it the X500&nbsp;distinguished name,&nbsp;validity start and end dates, and an optional&nbsp;password for encrypting the key data, and&nbsp;it will give you the&nbsp;PFX file data.&nbsp;Let me know if you find any bugs or have any suggestions.</P>
<p><a href="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Components.PostAttachments/00/09/14/30/36/Certificate.cs" original-url="https://blogs.msdn.com/cfs-file.ashx/__key/communityserver-components-postattachments/00-09-14-30-36/Certificate.cs">Certificate.cs</a></p>]]></content:encoded>
    </item>
    <item>
      <title>Introduction to the Catalog Object Model</title>
      <link>https://docs.microsoft.com/archive/blogs/dcook/introduction-to-the-catalog-object-model</link>
      <pubDate>Tue, 18 Dec 2007 16:03:00 GMT</pubDate>
      <dc:creator><![CDATA[Doug E. Cook]]></dc:creator>
      <guid
        isPermaLink="false">https://blogs.msdn.microsoft.com/dcook/2007/12/18/introduction-to-the-catalog-object-model/</guid>
      <description><![CDATA[Today's post will&nbsp;provide a&nbsp;basic introduction to the&nbsp;Windows Embedded CE Platform...]]></description>
      <content:encoded><![CDATA[<P>Today's post will&nbsp;provide a&nbsp;basic introduction to the&nbsp;Windows Embedded CE Platform Builder Catalog. If you don't know what that is, consider yourself lucky. If you do have to deal with the catalog data, today's post might be helpful.&nbsp;We'll write a quick C# utility that demonstrates the catalog object model and also provides some useful diagnostic information that might be helpful to catalog file authors.&nbsp;The tool will display any errors or warnings that PB encounters while loading the catalog. It will also list all of the catalog files that it was able to load.</P>
<P>I don't really know how it happened, but somehow about 5 years ago, through no fault of my own, I ended up owning the PB "catalog". For those who don't know, the catalog is a repository for information about the CE operating system, and PB uses this information to help developers customize their CE builds. For example, if you have the environment variable "SYSGEN_AUDIO" set when you build a CE operating system, your operating system will include sound system support. The catalog has a list of these "features", along with a friendly title, description, and some metadata that helps PB ensure that the environment variable is properly exposed to the developer.</P>
<P>There were some significant issues with the catalog's design, and maintaining the catalog data was not a fun experience. CEC files were not &nbsp;Maintaining the code that worked with the catalog was also problematic. For PB 6.0, I finally had the opportunity to start from scratch with a new catalog format. While a certain amount of backwards compatibility was required (the new catalog needed to essentially provide the same data as the old one, and there would need to be an upgrade path). So I designed a new catalog system, written in C# and based on XML files. While it isn't perfect, I think it is an improvement.</P>
<P>From a development perspective, one thing that is nice about the new catalog is that it has a pretty clean object model (an interface for developers who want to use the catalog). While we've never published the interface for the object model, we don't support it for external use, and everything I say here comes without warrantee or guarrantee of future compatibility, it isn't too hard to get started with basic tasks using the catalog object model. The reflection and metadata included in every managed assembly make this possible.</P>
<P>Before I go too far, I want to be clear that the design for the catalog object model&nbsp;emphasized the most common use of the catalog as a read-only repository. Loading and reading data from the catalog is (hopefully) reasonably simple and efficient. Using the object model to create or edit catalog files is a bit more of a challenge and not quite as efficient.</P>
<P>Loading a catalog means parsing about 2 megabytes of XML and loading the data into memory. While this happens in less than 1/10 of a second, we don't want to do this too often. In addition, we don't usually need (or want)&nbsp;multiple copies of the catalog in memory. To help with this, the catalog object model provides a cache of read-only loaded catalogs. When loading a catalog via the object model, you can choose to get your own copy of the catalog or you can ask for a read-only copy, which may come from the cache. (The cache is based on the .NET garbage collector via weak references. As long as anybody is using a catalog, it won't be evicted from the cache. As soon as nobody is using it, it becomes eligible for garbage collection. If it re-enters use before it is collected, it is no longer eligible for collection.)</P>
<P>The first step in using the catalog from your own code is to reference the catalog library. This code is in the Microsoft.PlatformBuilder.Catalog assembly, which (assuming you've installed Platform Builder 6 into C:\Program Files)&nbsp;can be found in C:\Program Files\Microsoft Platform Builder\6.00\cepb\idevs\Microsoft.PlatformBuilder.Catalog.dll. In Visual Studio, add a reference to this file. For convenience, you can add a "using" declaration to the top of your code to import the Catalog's namespace:</P>
<DIV style="FONT-SIZE: 10pt; FONT-FAMILY: Monospace; BACKGROUND-COLOR: white"><SPAN style="COLOR: blue">using</SPAN><SPAN style="COLOR: black"> Microsoft.PlatformBuilder.Catalog;</SPAN> </DIV>
<P>Next, to load a catalog, you use the CatalogManager class. The CatalogManager is a collection of catalog files. It handles searching through a winceroot for catalog files to load, loading all of the catalog files into memory, and maintaining the organization of the data in the catalog. It "flattens" the contents of the files, merging the data from all of the catalog files into a single collection of catalog elements. It also groups the data by type (so I can get a list of BSPs without going through all elements in the whole catalog), and generates indexes (so I can quickly look up a BSP by name).</P>
<P>When creating a CatalogManager, you can either create your own (writable) copy or you can load a read-only cached copy that might be shared by other components in the same process. To create a writable copy, just use the CatalogManager's constructor:</P><FONT size=3>
<P><FONT face=Monospace size=-1><FONT color=#2b91af>CatalogManager</FONT><FONT color=black> catalog = </FONT><FONT color=blue>new</FONT><FONT color=black> </FONT><FONT color=#2b91af>CatalogManager</FONT><FONT color=black>();</FONT></FONT></P>
<P></FONT>The default constructor for the CatalogManager will load only the "Global" catalog files. These are the catalog files in C:\Program Files\Microsoft Platform Builder\6.00\Catalog, and need to be available to PB whether or not it is working with a CE OS tree. To load the catalog files from an OS tree as well as the global catalog files, just add the winceroot path to the constructor call:</P><SPAN style="COLOR: #2b91af">
<DIV style="FONT-SIZE: 10pt; FONT-FAMILY: Monospace; BACKGROUND-COLOR: white"><SPAN style="COLOR: #2b91af">CatalogManager</SPAN><SPAN style="COLOR: black"> catalog = </SPAN><SPAN style="COLOR: blue">new</SPAN><SPAN style="COLOR: black"> </SPAN><SPAN style="COLOR: #2b91af">CatalogManager</SPAN><SPAN style="COLOR: black">(</SPAN><SPAN style="COLOR: #a31515">@"C:\wince600"</SPAN><SPAN style="COLOR: black">);</SPAN></DIV>
<P></SPAN>If you don't need to make changes to the catalog manager's data and don't need precise control over the load sequence, you can use the CatalogManager factory method. This will return a cached catalog if one is available, so it is&nbsp;usually more efficient for repeated use&nbsp;than creating and loading a new catalog each time.</P><FONT color=#2b91af size=3><FONT color=#000000 size=2>
<DIV style="FONT-SIZE: 10pt; FONT-FAMILY: Monospace; BACKGROUND-COLOR: white"><SPAN style="COLOR: #2b91af">CatalogManager</SPAN><SPAN style="COLOR: black"> catalog = </SPAN><SPAN style="COLOR: #2b91af">CatalogManager</SPAN><SPAN style="COLOR: black">.GetCatalogManager(</SPAN><SPAN style="COLOR: #a31515">@"C:\wince600"</SPAN><SPAN style="COLOR: black">);</SPAN></DIV></FONT></FONT>
<P>Next, we want to know if the catalog manager had any trouble while loading the catalog. For normal operation,&nbsp;developers generally&nbsp;don't want a defective catalog file to prevent PB from working correctly, so the catalog manager will silently catch catalog load errors. However, when working on catalog files, it is helpful to know what PB really thinks about your catalog. The catalog manager keeps track of warnings and errors generated during catalog load. You can access the errors via the catalog manager's Messages property, which is a read-only collection of CatalogMessage objects.</P>
<P>The catalog manager also provides a utility method to format the error or warning messages. It will then write the messages to any TextWriter (i.e. a StreamWriter or&nbsp;StringWriter). For example, to print all error or warning messages to stdout, you could do this:</P>
<DIV style="FONT-SIZE: 10pt; FONT-FAMILY: Monospace; BACKGROUND-COLOR: white"><SPAN style="COLOR: #2b91af">CatalogManager</SPAN><SPAN style="COLOR: black">.WriteCatalogMessages(catalog.Messages, </SPAN><SPAN style="COLOR: #2b91af">Console</SPAN><SPAN style="COLOR: black">.Out);</SPAN> </DIV>
<P>Finally, we want to access some of the data in the catalog. Let's print out a list of all of the catalog files that were successfully loaded:</P>
<DIV style="FONT-SIZE: 10pt; FONT-FAMILY: Monospace; BACKGROUND-COLOR: white"><SPAN style="COLOR: blue">foreach</SPAN><SPAN style="COLOR: black"> (</SPAN><SPAN style="COLOR: #2b91af">CatalogFile</SPAN><SPAN style="COLOR: black"> file </SPAN><SPAN style="COLOR: blue">in</SPAN><SPAN style="COLOR: black"> catalog.Files)<BR>{<BR>&nbsp;&nbsp;&nbsp;&nbsp;</SPAN><SPAN style="COLOR: #2b91af">Console</SPAN><SPAN style="COLOR: black">.WriteLine(</SPAN><SPAN style="COLOR: #a31515">"CatalogFile: {0} - \"{0}\""</SPAN><SPAN style="COLOR: black">,<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;file.FileInformation.Title, file.FullName);<BR>}<BR></SPAN></DIV>
<P><SPAN style="COLOR: blue"><SPAN style="COLOR: black">Similar collections exist for the BSPs, CPUs, and "Items" in the catalog.</SPAN></SPAN></P>
<P><SPAN style="COLOR: blue"><SPAN style="COLOR: black">Here is a simple command-line program that pulls this all together. You can build this via a Visual Studio C# command-line app project, or you can compile this at the command line with the csc.exe C# compiler. The only trick is to add a reference to the Microsoft.PlatformBuilder.Catalog.dll assembly (from C:\Program Files\Microsoft Platform Builder\6.00\cepb\idevs).</SPAN></SPAN></P>
<P><SPAN style="COLOR: blue"><SPAN style="COLOR: black">Have fun!</SPAN></SPAN></P><SPAN style="COLOR: blue"><SPAN style="COLOR: black">
<DIV style="FONT-SIZE: 10pt; FONT-FAMILY: Monospace; BACKGROUND-COLOR: white"><SPAN style="COLOR: blue">using</SPAN><SPAN style="COLOR: black"> System;<BR></SPAN><SPAN style="COLOR: blue">using</SPAN><SPAN style="COLOR: black"> System.IO;<BR></SPAN><SPAN style="COLOR: blue">using</SPAN><SPAN style="COLOR: black"> Microsoft.Win32;<BR></SPAN><SPAN style="COLOR: blue">using</SPAN><SPAN style="COLOR: black"> Microsoft.PlatformBuilder.Catalog;<BR></SPAN><SPAN style="COLOR: blue">namespace</SPAN><SPAN style="COLOR: black"> CatalogErrors<BR>{<BR>&nbsp;&nbsp;&nbsp;&nbsp;</SPAN><SPAN style="COLOR: blue">public</SPAN><SPAN style="COLOR: black"> </SPAN><SPAN style="COLOR: blue">class</SPAN><SPAN style="COLOR: black"> </SPAN><SPAN style="COLOR: #2b91af">Program<BR></SPAN><SPAN style="COLOR: black">&nbsp;&nbsp;&nbsp;&nbsp;{<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</SPAN><SPAN style="COLOR: blue">public</SPAN><SPAN style="COLOR: black"> </SPAN><SPAN style="COLOR: blue">static</SPAN><SPAN style="COLOR: black"> </SPAN><SPAN style="COLOR: blue">void</SPAN><SPAN style="COLOR: black"> Main(</SPAN><SPAN style="COLOR: blue">string</SPAN><SPAN style="COLOR: black">[] args)<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</SPAN><SPAN style="COLOR: blue">try<BR></SPAN><SPAN style="COLOR: black">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</SPAN><SPAN style="COLOR: blue">string</SPAN><SPAN style="COLOR: black"> winceroot = </SPAN><SPAN style="COLOR: blue">null</SPAN><SPAN style="COLOR: black">;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</SPAN><SPAN style="COLOR: blue">if</SPAN><SPAN style="COLOR: black"> (args.Length &gt; 0)<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</SPAN><SPAN style="COLOR: blue">string</SPAN><SPAN style="COLOR: black"> arg0 = args[0].ToLowerInvariant();<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</SPAN><SPAN style="COLOR: blue">if</SPAN><SPAN style="COLOR: black"> (arg0 != </SPAN><SPAN style="COLOR: #a31515">"/?"</SPAN><SPAN style="COLOR: black"> &amp;&amp;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;arg0 != </SPAN><SPAN style="COLOR: #a31515">"-?"</SPAN><SPAN style="COLOR: black"> &amp;&amp;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;arg0 != </SPAN><SPAN style="COLOR: #a31515">"/h"</SPAN><SPAN style="COLOR: black"> &amp;&amp;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;arg0 != </SPAN><SPAN style="COLOR: #a31515">"-h"</SPAN><SPAN style="COLOR: black"> &amp;&amp;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;arg0 != </SPAN><SPAN style="COLOR: #a31515">"--help"</SPAN><SPAN style="COLOR: black">)<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;winceroot = args[0];<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</SPAN><SPAN style="COLOR: blue">else<BR></SPAN><SPAN style="COLOR: black">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;winceroot = GetDefaultWinceroot();<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<BR><BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</SPAN><SPAN style="COLOR: blue">if</SPAN><SPAN style="COLOR: black"> (winceroot != </SPAN><SPAN style="COLOR: blue">null</SPAN><SPAN style="COLOR: black"> &amp;&amp;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;!</SPAN><SPAN style="COLOR: #2b91af">Directory</SPAN><SPAN style="COLOR: black">.Exists(winceroot))<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</SPAN><SPAN style="COLOR: #2b91af">Console</SPAN><SPAN style="COLOR: black">.Error.WriteLine(<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</SPAN><SPAN style="COLOR: #a31515">"ERROR: Winceroot \"{0}\" not found."</SPAN><SPAN style="COLOR: black">,<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;winceroot);<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;winceroot = </SPAN><SPAN style="COLOR: blue">null</SPAN><SPAN style="COLOR: black">;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<BR><BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</SPAN><SPAN style="COLOR: blue">if</SPAN><SPAN style="COLOR: black"> (winceroot == </SPAN><SPAN style="COLOR: blue">null</SPAN><SPAN style="COLOR: black">)<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</SPAN><SPAN style="COLOR: #2b91af">Console</SPAN><SPAN style="COLOR: black">.Error.WriteLine(<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</SPAN><SPAN style="COLOR: #a31515">"Usage: CatalogErrors [winceroot]"</SPAN><SPAN style="COLOR: black">);<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</SPAN><SPAN style="COLOR: #2b91af">Console</SPAN><SPAN style="COLOR: black">.Error.WriteLine(<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</SPAN><SPAN style="COLOR: #a31515">"Displays any CE catalog load warnings and errors."</SPAN><SPAN style="COLOR: black">);<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</SPAN><SPAN style="COLOR: #2b91af">Console</SPAN><SPAN style="COLOR: black">.Error.WriteLine(<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</SPAN><SPAN style="COLOR: #a31515">"If winceroot is not specified, uses default from registry."</SPAN><SPAN style="COLOR: black">);<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</SPAN><SPAN style="COLOR: blue">else<BR></SPAN><SPAN style="COLOR: black">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Run(winceroot);<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</SPAN><SPAN style="COLOR: blue">catch</SPAN><SPAN style="COLOR: black"> (</SPAN><SPAN style="COLOR: #2b91af">Exception</SPAN><SPAN style="COLOR: black"> ex)<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</SPAN><SPAN style="COLOR: #2b91af">Console</SPAN><SPAN style="COLOR: black">.Error.WriteLine(<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</SPAN><SPAN style="COLOR: #a31515">"ERROR: {0}: {1}{2}"</SPAN><SPAN style="COLOR: black">,<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;ex.GetType().FullName,<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;ex.Message,<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;ex.StackTrace);<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;ex = ex.InnerException;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</SPAN><SPAN style="COLOR: blue">while</SPAN><SPAN style="COLOR: black"> (ex != </SPAN><SPAN style="COLOR: blue">null</SPAN><SPAN style="COLOR: black">)<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</SPAN><SPAN style="COLOR: #2b91af">Console</SPAN><SPAN style="COLOR: black">.Error.WriteLine(<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</SPAN><SPAN style="COLOR: #a31515">"&nbsp;&nbsp;&nbsp;&nbsp; : {0}: {1}{2}"</SPAN><SPAN style="COLOR: black">,<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;ex.GetType().FullName,<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;ex.Message,<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;ex.StackTrace);<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<BR><BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</SPAN><SPAN style="COLOR: blue">private</SPAN><SPAN style="COLOR: black"> </SPAN><SPAN style="COLOR: blue">static</SPAN><SPAN style="COLOR: black"> </SPAN><SPAN style="COLOR: blue">string</SPAN><SPAN style="COLOR: black"> GetDefaultWinceroot()<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</SPAN><SPAN style="COLOR: blue">string</SPAN><SPAN style="COLOR: black"> winceroot = </SPAN><SPAN style="COLOR: blue">null</SPAN><SPAN style="COLOR: black">;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</SPAN><SPAN style="COLOR: blue">using</SPAN><SPAN style="COLOR: black"> (</SPAN><SPAN style="COLOR: #2b91af">RegistryKey</SPAN><SPAN style="COLOR: black"> localDir = </SPAN><SPAN style="COLOR: #2b91af">Registry</SPAN><SPAN style="COLOR: black">.CurrentUser.OpenSubKey(<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</SPAN><SPAN style="COLOR: #a31515">@"Software\Microsoft\Platform Builder\6.00\Directories"</SPAN><SPAN style="COLOR: black">))<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</SPAN><SPAN style="COLOR: blue">if</SPAN><SPAN style="COLOR: black"> (localDir != </SPAN><SPAN style="COLOR: blue">null</SPAN><SPAN style="COLOR: black">)<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;winceroot = localDir.GetValue(</SPAN><SPAN style="COLOR: #a31515">"OS Install Dir"</SPAN><SPAN style="COLOR: black">) </SPAN><SPAN style="COLOR: blue">as</SPAN><SPAN style="COLOR: black"> </SPAN><SPAN style="COLOR: blue">string</SPAN><SPAN style="COLOR: black">;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<BR><BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</SPAN><SPAN style="COLOR: blue">if</SPAN><SPAN style="COLOR: black"> (winceroot == </SPAN><SPAN style="COLOR: blue">null</SPAN><SPAN style="COLOR: black">)<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</SPAN><SPAN style="COLOR: blue">using</SPAN><SPAN style="COLOR: black"> (</SPAN><SPAN style="COLOR: #2b91af">RegistryKey</SPAN><SPAN style="COLOR: black"> globalDir = </SPAN><SPAN style="COLOR: #2b91af">Registry</SPAN><SPAN style="COLOR: black">.CurrentUser.OpenSubKey(<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</SPAN><SPAN style="COLOR: #a31515">@"Software\Microsoft\Platform Builder\6.00\Directories"</SPAN><SPAN style="COLOR: black">))<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</SPAN><SPAN style="COLOR: blue">if</SPAN><SPAN style="COLOR: black"> (globalDir != </SPAN><SPAN style="COLOR: blue">null</SPAN><SPAN style="COLOR: black">)<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;winceroot = globalDir.GetValue(</SPAN><SPAN style="COLOR: #a31515">"OS Install Dir"</SPAN><SPAN style="COLOR: black">) </SPAN><SPAN style="COLOR: blue">as</SPAN><SPAN style="COLOR: black"> </SPAN><SPAN style="COLOR: blue">string</SPAN><SPAN style="COLOR: black">;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<BR><BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</SPAN><SPAN style="COLOR: blue">return</SPAN><SPAN style="COLOR: black"> winceroot;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<BR><BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</SPAN><SPAN style="COLOR: blue">private</SPAN><SPAN style="COLOR: black"> </SPAN><SPAN style="COLOR: blue">static</SPAN><SPAN style="COLOR: black"> </SPAN><SPAN style="COLOR: blue">void</SPAN><SPAN style="COLOR: black"> Run(</SPAN><SPAN style="COLOR: blue">string</SPAN><SPAN style="COLOR: black"> winceroot)<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</SPAN><SPAN style="COLOR: #2b91af">CatalogManager</SPAN><SPAN style="COLOR: black"> catalog =<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</SPAN><SPAN style="COLOR: #2b91af">CatalogManager</SPAN><SPAN style="COLOR: black">.GetCatalogManager(</SPAN><SPAN style="COLOR: #a31515">@"C:\wince600"</SPAN><SPAN style="COLOR: black">);<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</SPAN><SPAN style="COLOR: #2b91af">CatalogManager</SPAN><SPAN style="COLOR: black">.WriteCatalogMessages(catalog.Messages, </SPAN><SPAN style="COLOR: #2b91af">Console</SPAN><SPAN style="COLOR: black">.Out);<BR><BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</SPAN><SPAN style="COLOR: blue">foreach</SPAN><SPAN style="COLOR: black"> (</SPAN><SPAN style="COLOR: #2b91af">CatalogFile</SPAN><SPAN style="COLOR: black"> file </SPAN><SPAN style="COLOR: blue">in</SPAN><SPAN style="COLOR: black"> catalog.Files)<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</SPAN><SPAN style="COLOR: #2b91af">Console</SPAN><SPAN style="COLOR: black">.WriteLine(</SPAN><SPAN style="COLOR: #a31515">"CatalogFile: {0} - \"{0}\""</SPAN><SPAN style="COLOR: black">,<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;file.FileInformation.Title, file.FullName);<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<BR>&nbsp;&nbsp;&nbsp;&nbsp;}<BR>}<BR></SPAN></DIV></SPAN></SPAN>]]></content:encoded>
    </item>
    <item>
      <title>Scritch, scratch</title>
      <link>https://docs.microsoft.com/archive/blogs/dcook/scritch-scratch</link>
      <pubDate>Wed, 31 Oct 2007 01:38:00 GMT</pubDate>
      <dc:creator><![CDATA[Doug E. Cook]]></dc:creator>
      <guid
        isPermaLink="false">https://blogs.msdn.microsoft.com/dcook/2007/10/31/scritch-scratch/</guid>
      <description><![CDATA[Steve Ball posted an article about&nbsp;some "glitching" issues in Vista. I can't resist adding my...]]></description>
      <content:encoded><![CDATA[<P>Steve Ball posted <A class="" title="Steve Ball on glitching issues in Vista" href="https://windowsvistablog.com/blogs/windowsvista/archive/2007/10/29/an-overview-of-windows-sound-and-music-glitching-issues.aspx" mce_href="https://windowsvistablog.com/blogs/windowsvista/archive/2007/10/29/an-overview-of-windows-sound-and-music-glitching-issues.aspx">an article about&nbsp;some "glitching" issues in Vista</A>. I can't resist adding my two cents.</P>
<P>For me, Vista definitely glitches a LOT more than previous versions of Windows. As a fairly experienced developer, I think I understand the reasons pretty well, so I can explain it away. But as a user, when my laptop audio is glitchy, I want to find the developer responsible and <EM>(censored for&nbsp;mild descriptions of hypothetical violence)</EM>.</P>
<P>I've read a lot of comments raising various theories, some&nbsp;that call into question the&nbsp;sanity of the Windows developers. I can't say I blame them. There is definitely some room for improvement in the way things work. However, in the interest of fairness and progress,&nbsp;the&nbsp;attention should be focused where it will do the most good. That means we shouldn't simply blame the Windows developers&nbsp;unless there is really something they can do about the problem. And that means that before we start placing blame, we probably ought to figure out where the&nbsp;problem really lies.</P>
<P>The first complaints always mention something about the "lame" and "brain-dead"&nbsp;Windows NT scheduler.&nbsp;However, I'm pretty convinced that this is not the problem. In fact, my audio sounds BETTER when my system is under load (more on this later). I agree with the statement that audio glitches under CPU load are usually the fault of the OS and the scheduler, but I've seen very little correlation between CPU load and audio glitching. I haven't seen any evidence that the CPU scheduler at fault.</P>
<P>Hard disk load is occasionally an issue, but that is generally fairly obvious and easy to fix at the application level. The application simply didn't buffer enough sound samples and ran out of music to play while waiting for the hard drive to load the next bit of music. Either tweak the buffering algorithm&nbsp;of the application or get a faster hard drive (or network).</P>
<P>Memory can also be an issue.&nbsp;Some buffer or code needed to play the music might be paged out because you're running low on free memory, and it didn't get paged in quickly enough once the application tried to access it. This could possibly be&nbsp;blamed on the OS&nbsp;if the OS is too aggressive in trimming the working set. If an app pre-loads 60 seconds of audio, that means it doesn't won't touch the last page of the buffer for 60 seconds, which might be long enough for the OS to page out the buffer. Here, you would probably get better results by buffering only 5 seconds worth of music. In any case, I haven't had any&nbsp;significant trouble with this on Vista (except once in a while when I let Firefox run too long, it eats up 1.5 GB of RAM,&nbsp;and&nbsp;my&nbsp;system goes into memory panic mode).</P>
<P>Drivers are a much more&nbsp;significant issue. Traditional OSes (XP, Vista,&nbsp;Linux)&nbsp;can't really&nbsp;schedule a driver's activity. Once the driver starts doing something it thinks is important, it can only be interrupted by a&nbsp;higher-priority driver. On a single-CPU system, if a driver takes over, no new audio can be buffered until the driver returns. Many drivers written for XP or earlier systems (where the audio buffer was somewhat more forgiving -- more on this later) cause trouble by doing too much processing at once.&nbsp;In testing (under XP), the driver's latency wasn't a problem, but on Vista, the latency requirements are much less lenient.&nbsp;I've seen significant Vista audio glitch issues go away after upgrading from an XP-era driver to a newly released Vista-compiant driver.</P>
<P>Even if the driver only does 1 millisecond of work at a time (or whatever the Vista latency recommendations are), if it has to do this 1000 times a second, it will still&nbsp;use up all available CPU time. Drivers have priority over all&nbsp;applications, so on a single-CPU system, this leaves no CPU for the audio application and mixer. On a multi-CPU system, this can still be a problem if the driver holds certain locks that are needed for audio processing. This is why Vista throttles network activity when the audio channel is open -- network&nbsp;packet&nbsp;bursts&nbsp;can easily use&nbsp;enough CPU to cause audio glitches.&nbsp;Probably a good idea overall, though it seems that the throttling algorithm is a bit too aggressive and has some room for improvement.</P>
<P>Another issue is power management. This turned out to be the major problem on my laptop. My laptop's motherboard (CPU and chipset) goes into sleep mode whenever it detects that it is "idle".&nbsp;That's actually a pretty good thing because it means I can get 2 or 3 hours of use out of the battery instead of 20 minutes. This happens hundreds of times per second -- it sleeps for&nbsp;2 milliseconds,&nbsp;wakes up to handle a keystroke, sleeps for another 2 milliseconds, wakes up to handle&nbsp;the calculations for an animation,&nbsp;sleeps a bit more,&nbsp;wakes to fill an&nbsp;audio buffer,&nbsp;etc.&nbsp;But it sometimes doesn't wake up quickly enough to buffer the next bit of audio. If it ever takes longer than 9 ms to wake up,&nbsp;there will&nbsp;be a glitch. This was a real problem when my laptop was new. Recent drivers have improved this a lot, but there's still a bit of scritch-scratch during some games or media.</P>
<P>As an experiment, I wrote a very simple application to prevent the motherboard from going to sleep. It starts a low-priority thread that does a simple busy wait in a low priority loop. A Sleep(1) loop&nbsp;didn't help -- it gave the motherboard a chance to go to sleep. While&nbsp;the busy&nbsp;wait&nbsp;makes my laptop get very hot, it also completely stops the glitching.</P>
<BLOCKQUOTE style="MARGIN: 0in; FONT-FAMILY: 'Courier New', 'Courier', 'Monospace'">
<P style="MARGIN: 0in"><SPAN style="COLOR: blue">#include</SPAN> &lt;windows.h&gt;</P>
<P style="MARGIN: 0in"><SPAN style="COLOR: blue">#include</SPAN> &lt;stdio.h&gt;</P>
<P style="MARGIN: 0in" mce_keep="true">&nbsp;</P>
<P style="MARGIN: 0in"><SPAN style="COLOR: green">// This probably doesn't really do anything. At such a low priority, the</SPAN></P>
<P style="MARGIN: 0in"><SPAN style="COLOR: green">// process usually terminates before the thread exits. But this is an easy</SPAN></P>
<P style="MARGIN: 0in"><SPAN style="COLOR: green">// way to avoid&nbsp;certain compiler warnings. Without it, some compilers warn</SPAN></P>
<P style="MARGIN: 0in"><SPAN style="COLOR: green">//&nbsp;that the "return 0" below is unreachable. If I remove the "return 0",</SPAN></P>
<P style="MARGIN: 0in"><SPAN style="COLOR: green">// other compilers warn that I don't return a value from DoNothingQuickly.</SPAN></P>
<P style="MARGIN: 0in"><SPAN style="COLOR: blue">volatile</SPAN> BOOL g_stopNow = FALSE;</P>
<P style="MARGIN: 0in" mce_keep="true">&nbsp;</P>
<P style="MARGIN: 0in">DWORD WINAPI DoNothingQuickly(LPVOID <SPAN style="COLOR: green">/* unused */</SPAN>)</P>
<P style="MARGIN: 0in">{</P>
<P style="MARGIN: 0in">&nbsp; &nbsp; &nbsp; <SPAN style="COLOR: blue">while</SPAN> (!g_stopNow)</P>
<P style="MARGIN: 0in">&nbsp; &nbsp; &nbsp; {</P>
<P style="MARGIN: 0in">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <SPAN style="COLOR: green">// Sleep(1) didn't fix the glitching. Sleep(0) just spends all</SPAN></P>
<P style="MARGIN: 0in">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <SPAN style="COLOR: green">// the time context switching, which is probably as bad or</SPAN></P>
<P style="MARGIN: 0in">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <SPAN style="COLOR: green">// worse than a busy wait in terms of impact on the rest of</SPAN></P>
<P style="MARGIN: 0in">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <SPAN style="COLOR: green">//&nbsp;the system. So I'll just do a busy wait.</SPAN></P>
<P style="MARGIN: 0in">&nbsp; &nbsp; &nbsp; }</P>
<P style="MARGIN: 0in">&nbsp; &nbsp; &nbsp; <SPAN style="COLOR: blue">return</SPAN> 0; <SPAN style="COLOR: green">// Usually never reached.</SPAN></P>
<P style="MARGIN: 0in">}</P>
<P style="MARGIN: 0in" mce_keep="true">&nbsp;</P>
<P style="MARGIN: 0in"><SPAN style="COLOR: blue">int</SPAN> main()</P>
<P style="MARGIN: 0in">{</P>
<P style="MARGIN: 0in">&nbsp; &nbsp; &nbsp; <SPAN style="COLOR: blue">int</SPAN> returnCode;</P>
<P style="MARGIN: 0in">&nbsp; &nbsp; &nbsp; DWORD dwThreadId;</P>
<P style="MARGIN: 0in">&nbsp; &nbsp; &nbsp; HANDLE hThread;</P>
<P style="MARGIN: 0in" mce_keep="true">&nbsp;</P>
<P style="MARGIN: 0in">&nbsp; &nbsp; &nbsp; hThread = CreateThread(</P>
<P style="MARGIN: 0in">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; NULL,</P>
<P style="MARGIN: 0in">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; 0,</P>
<P style="MARGIN: 0in">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; DoNothingQuickly,</P>
<P style="MARGIN: 0in">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; NULL,</P>
<P style="MARGIN: 0in">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; CREATE_SUSPENDED,</P>
<P style="MARGIN: 0in">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &amp;dwThreadId);</P>
<P style="MARGIN: 0in" mce_keep="true">&nbsp;</P>
<P style="MARGIN: 0in">&nbsp; &nbsp; &nbsp; <SPAN style="COLOR: blue">if</SPAN> (hThread != NULL)</P>
<P style="MARGIN: 0in">&nbsp; &nbsp; &nbsp; {</P>
<P style="MARGIN: 0in">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <SPAN style="COLOR: green">// We want to keep one CPU wide-awake and leave any other</SPAN></P>
<P style="MARGIN: 0in"><SPAN style="COLOR: green">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // CPU(s)&nbsp;idle.</SPAN></P>
<P style="MARGIN: 0in">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; SetThreadAffinityMask(hThread, 1);</P>
<P style="MARGIN: 0in" mce_keep="true">&nbsp;</P>
<P style="MARGIN: 0in">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <SPAN style="COLOR: green">// We don't want to get in the way of any useful work.</SPAN></P>
<P style="MARGIN: 0in">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; SetThreadPriority(hThread, THREAD_BASE_PRIORITY_IDLE);</P>
<P style="MARGIN: 0in" mce_keep="true">&nbsp;</P>
<P style="MARGIN: 0in">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; ResumeThread(hThread);</P>
<P style="MARGIN: 0in">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; CloseHandle(hThread);</P>
<P style="MARGIN: 0in">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; printf("Caffeine: Now running. Press &lt;Enter&gt; to quit...");</P>
<P style="MARGIN: 0in" mce_keep="true">&nbsp;</P>
<P style="MARGIN: 0in">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; getchar();</P>
<P style="MARGIN: 0in">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; g_stopNow = TRUE; <SPAN style="COLOR: green">// Probably useless, but might as well...</SPAN></P>
<P style="MARGIN: 0in">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; returnCode = 0;</P>
<P style="MARGIN: 0in">&nbsp; &nbsp; &nbsp; }</P>
<P style="MARGIN: 0in">&nbsp; &nbsp; &nbsp; <SPAN style="COLOR: blue">else</SPAN></P>
<P style="MARGIN: 0in">&nbsp; &nbsp; &nbsp; {</P>
<P style="MARGIN: 0in">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; printf("Caffeine: Unable to create thread. Exiting.\n");</P>
<P style="MARGIN: 0in">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; returnCode = GetLastError();</P>
<P style="MARGIN: 0in">&nbsp; &nbsp; &nbsp; }</P>
<P style="MARGIN: 0in" mce_keep="true">&nbsp;</P>
<P style="MARGIN: 0in">&nbsp; &nbsp; &nbsp; <SPAN style="COLOR: blue">return</SPAN> returnCode;</P>
<P style="MARGIN: 0in">}</P></BLOCKQUOTE>
<P mce_keep="true">Drivers seem to be a big part of the problem here -- they either spend too much time working, or they take too long to wake up after going into a sleep state. Hopefully this means that audio problems will go away as the drivers improve. Computer retailers like Dell and HP will probably ensure that their new hardware meets the Vista latency requirements before putting it on the market. Unfortunately, owners of older hardware might be out of luck.</P>
<P mce_keep="true">Hindsight is 20/20.&nbsp;I've seen how these kinds of issues come up, and I've been involved in some mistakes&nbsp;myself,&nbsp;so I don't want to sound like I'm smarter than anybody on the Windows audio team. However, there is certainly some room for improvement in they way this issue has played out. While the&nbsp;changes in the audio stack are technically admirable and the problems can generally be blamed on drivers,&nbsp;that's little comfort to those enduring static on their speakers. Things work in XP and don't work in Vista. That sounds like a regression, not an improvement. It's getting better, but there really shouldn't have been a problem in the first place.</P>
<P mce_keep="true">What's wrong? Well, Vista aimed for a technically superior audio experience. Latency has been significantly reduced in Vista -- when you fire your machine gun in your favorite game, you'll hear the sound effect a bit more quickly. For gamers and audio professionals, this reduction in latency&nbsp;can make a big difference. For people trying to listen to their MP3s, this probably doesn't matter much.&nbsp;The downside to reducing latency is that it reduces the margin for error. Vista cannot tolerate any delays longer than 5 or 10 milliseconds without glitching, while XP could usually tolerate a much longer delay with no problem. Assuming all of the drivers do their part, modern hardware actually has no problem meeting the deadlines. But if anything goes wrong on Vista, you can hear it.</P>
<P mce_keep="true">What was the mistake? The core of the issue is that a&nbsp;change was made that was detrimental to some customers. In the long term,&nbsp;the change is probably a step (or two) in the right direction, but&nbsp;for many people, the change causes trouble and offers no immediate benefit. Obviously the severity of the problem was underestimated (contrary to popular belief, Windows developers <EM>do </EM>care&nbsp;about their customers and would never have&nbsp;done this had they known the&nbsp;outcome). I've never been a big fan of taking away without giving something in return.</P>
<P mce_keep="true">What would have prevented the problem? I don't know how hard this would have been to implement, but I would love to&nbsp;have some kind of adjustment knob to control the amount of latency I want on my system. That would have sidestepped the whole issue by allowing&nbsp;the customer to&nbsp;pick their priorities, i.e.&nbsp;lower latency on my desktop where there aren't power issues and where I want my games to sound great, higher latency on my laptop where I want additional power savings, the drivers aren't as good (and are also optimized for power savings), and where I'm never using the audio stack for anything other than music or videos anyway. This would also have been a great mitigation for driver issues during&nbsp;the transition period.</P>
<P mce_keep="true">&nbsp;This is probably a good lesson for developers in general -- be sure to consider the transition period between the old system and the new system when designing the new system.</P>
<P mce_keep="true">In the meantime, I've finally gotten my audio problems worked out. After the latest motherboard chipset upgrade,&nbsp;I no longer have to run caffeine.exe anymore, and I only run into minor static when running a few specific programs. Hopefully things are improving for everybody else, too.</P>]]></content:encoded>
    </item>
    <item>
      <title>Missing OS Design View tab in PB 5.0</title>
      <link>https://docs.microsoft.com/archive/blogs/dcook/missing-os-design-view-tab-in-pb-5-0</link>
      <pubDate>Tue, 11 Sep 2007 18:41:00 GMT</pubDate>
      <dc:creator><![CDATA[Doug E. Cook]]></dc:creator>
      <guid
        isPermaLink="false">https://blogs.msdn.microsoft.com/dcook/2007/09/11/missing-os-design-view-tab-in-pb-5-0/</guid>
      <description><![CDATA[A common complaint about PB 5.0 is that&nbsp;the OS Design View tab will sometimes...]]></description>
      <content:encoded><![CDATA[<P>A common complaint about PB 5.0 is that&nbsp;the OS Design View tab will sometimes mysteriously&nbsp;disappear. The best answer I have is that&nbsp;people should be using PB 6.0. Unfortunately, that answer tends to make people want to punch me. In the interest of my own safety, here is what I know about alternative methods of resolving the problem.</P>
<P>The OS Design View tab is one of the places where the old PB shell code (VS 6.0 vintage 1998) meets up with the new PB object model (totally re-written and designed for integration with the VS 2005 shell).&nbsp;So this&nbsp;is more evidence that you can't put new wine in old bottles -- you'll almost always end up with leaks.</P>
<P>In the case of the OS Design View, most of the issues I've resolved were due to an exception leaking. Exception handling is a great thing, but it has the good/bad quality that it is often easy to forget that you're calling a function that can throw an exception (good when you don't need to handle it, bad when you do...).</P>
<P>Ideally, the exception will be caught by PB and PB will properly recover and/or display a useful error message, but this doesn't always happen, in which case PB just silently fails or crashes. Luckily, the exception type we use does a little bit of logging. The exception constructor will send a quick message to the system debug stream. Sometimes this message is useless, but every once in a while, the exception has enough information to help you track down the problem.</P>
<P>There are several utilities that can monitor the debug stream. DBMon.exe is a simple console tool that is part of the Microsoft Platform SDK. <A class="" href="https://www.microsoft.com/technet/sysinternals/Miscellaneous/DebugView.mspx" mce_href="https://www.microsoft.com/technet/sysinternals/Miscellaneous/DebugView.mspx">DebugView</A> can be downloaded from the <A class="" href="https://www.microsoft.com/technet/sysinternals/default.mspx" mce_href="https://www.microsoft.com/technet/sysinternals/default.mspx">SysInternals</A> website. If you run cepb.exe under a debugger such as <A class="" href="https://www.microsoft.com/whdc/devtools/debugging/installx86.mspx" mce_href="https://www.microsoft.com/whdc/devtools/debugging/installx86.mspx">WinDbg</A> or Visual Studio, you'll also see the messages from the debug stream.</P>
<P>In the case of Missing OS Design View tabs, I have occasionally been able to track down the problem by monitoring the system debug stream while opening an OS Design (pbxml). For example, I saw an "Access Denied" exception that occurred while trying to open a file in&nbsp;an OS Design. The exception message included the file name. Removing the "Read Only" attribute from the file allowed me to successfully open the OS Design.</P>
<P>Obviously, this is a problem with PB. PB should probably be able to do its job with read-only access to the file. And if it fails, it should at least pop up a useful error message indicating what the problem was. Hopefully, we'll do better in the future. In the meantime, you can try to identify the file that is causing trouble for PB and remove the read-only attribute. While this might cause trouble for your source control system, you'll hopefully at least be able to get back to work.</P>
<OL>
<LI>Start&nbsp;a debug stream monitor utility such as DbMon.exe or DebugView, then start cepb.exe. Alternatively, launch cepb.exe under a debugger such as Visual Studio or WinDbg.</LI>
<LI>Recreate the scenario that is causing trouble. For example, if the OS Design View tab is missing when you&nbsp;open&nbsp;a particular OS Design, open that OS Design.</LI>
<LI>Examine the messages logged to the debug output stream.</LI>
<LI>If you find any messages that look interesting, try to resolve the problem they indicate. For example, if you see that one of the messages refers to an access denied failure when opening a particular file, verify that the file exists and that you can access the file. If the file is read-only, try removing the read-only attribute from the file using Explorer (right-click on the file and select "Properties") or the DOS Attrib command.</LI>
<LI>Leave a comment below or <A class="" title="Contact Doug" href="https://blogs.msdn.com/dcook/contact.aspx" mce_href="https://blogs.msdn.com/dcook/contact.aspx">contact me</A> to let me know if this does or does not work for you!</LI></OL>]]></content:encoded>
    </item>
  </channel>
</rss>