December, 2010

  • The Old New Thing

    ZOMG! This program is using 100% CPU!1! Think of the puppies!!11!!1!1!eleven


    For some reason, people treat a program consuming 100% CPU as if it were unrepentantly running around kicking defenseless (and cute) puppies. Calm down already. I get the impression that people view the CPU usage column in Task Manager not as a diagnostic tool but as a way of counting how many puppies a program kicks per second.

    While a program that consumes 100% CPU continuously (even when putatively idle) might legitimately be viewed as an unrepentant puppy-kicker, a program that consumes 100% CPU in pursuit of actually accomplishing something is hardly scorn-worthy; indeed it should be commended for efficiency!

    Think of it this way: Imagine if your CPU usage never exceed 50%. You just overpaid for your computer; you're only using half of it. A task which could have been done in five minutes now takes ten. Your media player drops some frames out of your DVD playback, but that's okay, because your precious CPU meter never went all the way to the top. (Notice that the CPU meter does not turn red when CPU usage exceeds 80%. There is no "danger zone" here.)

    Consider this comment where somebody described that they want their program to use less CPU but get the job done reasonably quickly. Why do you want it to use less CPU? The statement makes the implicit assumption that using less CPU is more important than getting work done as fast as possible.

    You have a crowd of people at the bank and only ten tellers. If you let all the people into the lobby at once, well, then all the tellers will be busy—you will have 100% teller utilization. These people seem to think it would be better to keep all the customers waiting outside the bank and only let them into the lobby five at a time in order to keep teller utilization at 50%.

    If it were done when 'tis done, then 'twere well / It were done quickly.

    Rip off the band-aid.

    Piss or get off the pot.

    Just do it.

    If you're going to go to the trouble of taking the CPU out of a low-power state, you may as well make full use of it. Otherwise, you're the person who buys a bottle of water, drinks half of it, then throws away the other half "because I'm thinking of the environment and reducing my water consumption." You're making the battery drain for double the usual length of time, halving the laptop's run time because you're trying to "conserve CPU."

    If the task you are interested in is a low priority one, then set your thread priority to below-normal so it only consumes CPU time when there are no foreground tasks demanding CPU.

    If you want your task to complete even when there are other foreground tasks active, then leave your task's priority at the normal level. Yes, this means that it will compete with other foreground tasks for CPU, but you just said that's what you want. If you want it to compete "but not too hard", you can sprinkle some Sleep(0) calls into your code to release your time slice before it naturally expires. If there are other foreground tasks, then you will let them run; if there aren't, then the Sleep will return immediately and your task will continue to run at full speed.

    And cheerfully watch that CPU usage go all the way to 100% while your task is running. Just make sure it drops back to zero when your task is complete. You don't want to be a task which consumes 100% CPU even when there's nothing going on. That'd just be kicking puppies.

    [Raymond is currently away; this message was pre-recorded.]

    Clarification: Many people appear to be missing the point. So let's put it more simply: Suppose you have an algorithm that takes 5 CPU-seconds to complete. Should you use 100% CPU for 5 seconds or 50% CPU for 10 seconds? (Obviously, if you can refine your algorithm so it requires only 2 CPU-seconds, that's even better, but that's unrelated to the issue here.)

  • The Old New Thing

    Why are the Compression and Encryption options check boxes instead of radio buttons?


    Tanveer Badar asks why the file properties Advanced dialog shows two checkboxes (compression and encryption) even though NTFS supports only one or the other at a time. "Why not have two radio buttons instead of these silly check boxes?"

    Actually, if you want radio buttons, you'd need three, one to cover the "neither" case. Let's look at what those radio buttons would look like:

    Compress contents to save disk space
    Encrypt contents to secure data
    Neither compress nor encrypt

    This takes an implementation detail (that NTFS currently does not support simultaneous compression and encryption) and elevates it to the user interface, so that it can provide maximum confusion to the user. "What a strange way of exposing compression and encryption. If I want to turn on encryption, I should just check a box that says 'Encryption'."

    The current implementation (two check boxes) matches user expectations, but then says "Sorry, we can't do that" if the user picks a combination that is not currently supported. But who knows, maybe someday, NTFS will support simultaneously encryption and compression, and all that will happen is that the error message goes away. You don't have to redesign the property sheet (and invalidate all the training materials that had been produced in the meantime).

    Either way, it's a leaky abstraction that sucks, but at least the suckiness isn't shoved in the user's face.

  • The Old New Thing

    It rather involved being on the other side of this airtight hatchway: Invalid parameters from one security level crashing code at the same security level


    In the category of dubious security vulnerability, I submit the following (paraphrased) report:

    I have discovered that if you call the XYZ function (whose first parameter is supposed to be a pointer to a IUnknown), and instead of passing a valid COM object pointer, you pass a pointer to a random hunk of data, you can trigger an access violation in the XYZ function which is exploitable by putting specially-crafted data in that memory blob. An attacker can exploit the XYZ function for remote execution and compromise the system, provided an application uses the XYZ function and passes a pointer to untrusted data as the first parameter instead of a valid IUnknown pointer. Although we have not found an application which uses the XYZ in this way, the function neverless contains the potential for exploit, and the bug should be fixed as soon as possible.
    The person included a sample program which went something like this (except more complicated):
    // We can control the behavior by tweaking the value
    // of the Exploit array.
    unsigned char Exploit[] = "\x01\x02\x03...";
    void main()

    Well, yeah, but you're already on the other side of the airtight hatchway. Instead of building up a complicated blob of memory with exactly the right format, just write your bad IUnknown:

    void Pwnz0r()
      ... whatever you want ...
    class Exploit : public IUnknown
      STDMETHODIMP QueryInterface(REFIID riid, void **ppv)
      { Pwnz0r(); return E_NOINTERFACE; }
      STDMETHODIMP_(ULONG) AddRef() { Pwnz0r(); return 2; }
      STDMETHODIMP_(ULONG) Release() { Pwnz0r(); return 1; }
    void main()

    Wow, this new "exploit" is even portable to other architectures!

    Actually, now that you're on the other side of the airtight hatchway, you may as well take XYZ out of the picture since it's just slowing you down:

    void main()

    You're already running code. It's not surprising that you can run code.

    There's nothing subtle going on here. There is no elevation of privilege because the rogue activity happens in user-mode code, based on rogue code provided by an executable with trusted code execution privileges, at the same security level as the original executable.

    The people reporting the alleged vulnerability do say that they haven't yet found any program that calls the XYZ function with untrusted data, but even if they did, that would be a data handling bug in the application itself: Data crossed a trust boundary without proper validation. It's like saying "There is a security vulnerability in the DeleteFile function because it is possible for an application to pass an untrusted file name and thereby result in an attacker deleting any file of his choosing." Even if such a vulnerability existed, the flaw is in the application for not validating its input, not in DeleteFile for, um, deleting the file it was told to delete.

    The sad thing is that it took the security team five days to resolve this issue, because even though it looks like a slam dunk, the issue resolution process must be followed, just to be sure. Who knows, maybe there really is a bug in the XYZ function's use of the first parameter that would result in elevation of privilege. All supported versions of Windows need to be examined for the slim possibility that there's something behind this confused vulnerability report.

    But there isn't. It's just another dubious security vulnerability report.

    Exercise: Apply what you learned to this security vulnerability report. This is also paraphrased from an actual security report:

    There is a serious denial-of-service vulnerability in the function XYZ. This function takes a pointer to a buffer and a length. If the function is passed malformed parameters, it may encounter an access violation when it tries to read from an invalid buffer. Any application which calls this function with bad parameters will crash. Here is a sample program that illustrates the vulnerability:

    int main(int argc, char **argv)
     // crash inside XYZ attempting to read past end of buffer
     XYZ("x", 9999999);
     return 0;

    Credit for discovering this vulnerability goes to ABC Security Research Company. Copyright© 20xx ABC Security Research Company. All Rights Reserved.

  • The Old New Thing

    There is no interface for preventing your notification icon from being hidden


    Yes, it's another installment of I bet somebody got a really nice bonus for that feature. A customer had this question for the Windows 7 team:

    Our program creates a notification icon, and we found that on Windows 7 it is hidden. It appears properly on all previous versions of Windows. What is the API to make our icon visible?

    First of all, I'd like to congratulate you on writing the most awesome program in the history of the universe.

    Unfortunately, Windows 7 was not prepared for your awesomeness, because there is no way to prevent your notification icon from being hidden. That's because if there were, then every other program (the ones that aren't as awesome as you) would use it, thereby causing your awesome icon to be lost among all the non-awesome ones.

    I'm sorry. You will just have to find some other medium for self-actualization.

  • The Old New Thing

    The __fortran calling convention isn't the calling convention used by FORTRAN


    Although the Microsoft C compiler supports a calling convention called __fortran, that's just what the calling convention is called; its relationship with the FORTRAN programming language is only coincidental. The __fortran keyword is now just an old-fashioned synonym for __stdcall.

    Various FORTRAN compilers use different calling conventions; the one I describe here applies to the now-defunct Microsoft Fortran PowerStation.

    Fortran Powerstation pushes parameters on the stack right-to-left, with callee-cleanup. (So far, this matches __fortran aka __stdcall.) Function names are converted to all-uppercase, with an underscore at the beginning and @n appended, where n is the number of bytes of parameters. (This still matches __stdcall aside from the uppercase conversion.)

    As for how the parameters are passed, well, that's where things get weird. FORTRAN natively passes all parameters by reference. This is the source of a famous classic FORTRAN bug known as constants aren't.

          CALL MAGIC(1)
          PRINT *, 'According to the computer, 3 + 1 is ', ADDUP(3, 1)
          FUNCTION ADDUP(I, J)
          ADDUP = I + J
    C     What does this subroutine actually do?
          I = 9

    (It's been a long time since I've written a FORTRAN program, so I may have gotten some of the details wrong, but any errors shouldn't detract from the fundamental issue.)

    When you run this program, it says

    According to the computer, 3 + 1 is 12

    How did that happen? We called a function that adds two numbers together, and instead of getting 4, we get 12?

    The reason is the subroutine MAGIC: We passed it the constant 1, and since all FORTRAN parameters are passed by reference, the assignment I = 9 modifies the constant 1. In C:

    int One = 1;
    int Three = 3;
    int Nine = 9;
    void Magic(int *i) { *i = Nine; }
    int AddUp(int *i, int *j) { return *i + *j; }
    void main()
     printf("According to the computer, 3 + 1 is %d\n",
            AddUp(&Three, &One));

    Since Magic modified the constant One, any further use of the constant 1 ends up using the value 9! (According to the FORTRAN standard, modifying a constant results in undefined behavior.)

    Okay, back to calling conventions. Other significant differences between C and FORTRAN: In FORTRAN, array indices begin at 1, not 0, and arrays are stored in column-major order rather than row-major as in C.

    COMPLEX variables in FORTRAN are stored as two floating point numbers (corresponding to the real and imaginary components).

    Functions which return COMPLEX or CHARACTER*(*) are internally rewritten as subroutines where the location to store the return value is passed as a hidden first parameter. (This is analogous to how C returns large structures.)

    The final commonly-encountered weirdness of FORTRAN is that CHARACTER*n data types (which are used to hold strings) are passed as two parameters: The address of the character buffer, followed by the size of the buffer (n). Note that FORTRAN CHARACTER*n variables are fixed-length; if you assign a string shorter than the buffer, it is padded with spaces. There is no null terminator.

    Anyway, I sort of got carried away with the FORTRAN calling convention. It's definitely more complicated than just sticking __fortran in front of your function. But at least the __fortran keyword takes care of the part that can't be expressed in C. The rest you can manage on your own.

  • The Old New Thing

    What appears superficially to be a line is actually just a one-dimensional mob


    In China, queueing is honored more in the breach than in the observance. If you see a line for something, you must understand that what you are seeing is not really a line. It is a one-dimensional mob. You must be prepared to defend your position in line fiercely, because any sign of weakness will be pounced upon, and the next thing you know, five people just cut in front of you.

    I first became aware of this characteristic of "Chinese queueing theory" while still at the airport. When the gate agents announced that the flight to Beijing had begun boarding, a one-dimensional mob quickly formed, and I naïvely joined the end. It wasn't long before my lack of attentiveness to the minuscule open space in front of me resulted in another person cutting in front. At that point, I realized that the Chinese implementation of queueing theory was already in effect even before we left the United States.

    As another example: After the plane pulls up to the gate after landing, the aisles quickly fill with people anxious to get off the plane. In the United States, you can rely upon the kindness of strangers to let you into the aisle so you can fetch your bags and join the queue. But in China, you must force your way into the aisle. Nobody is going to let you in.

    Colleagues of mine who have spent time in both China and the United States tell me that it's an adjustment they have to make whenever they travel between the two countries. For example, in the United States, it is understood that when you are waiting in line for the ATM, you allow the person at the ATM a few feet of "privacy space." On the other hand, in China, you cannot leave such an allowance, because that is a sign of weakness in the one-dimensional mob. You have to stand right behind the person to protect your place in line.

    Bonus airport observation: How ironic it is that your last meal in your home country often comes from a crappy airport crfeteria.

  • The Old New Thing

    Some notes on my trip to Beijing disguised as travel tips


    Single-use tickets purchased from subway vending machines are valid only on the day of purchase for use in that station. Do not buy your return ticket at the same time as your outbound ticket because it will not work. This detail is clearly explained on the ticket that you receive after you have paid for it. (Also, the vending machine will ask you how many "sheets" you want. It's asking how many tickets you want.)

    Subway station names are printed in both Chinese and pinyin, but the pinyin omits the tone markers, which means that you will have no idea how you're supposed to pronounce the station name, should anybody ask you to say it. (See below.)

    Unlike in some cities, where the subway logo is bright and distinctive (and therefore easy to spot from a long way away), the logo for the Beijing subway is not consistent in color, which makes it hard to pick out from a busy streetscape. (One sign I saw used the high-visibility color scheme of beige-on-brown.) The logo is a monogram of the Latin letters "D" and "G", because the Mandarin word for "subway" is pronounced "dì-tiě". You might think they would use "D" and "T", but you're being too literal.

    Even worse, the Olympic Green station entrance nearest the convention center is so unobtrusively marked that if you aren't standing on the correct side of the building looking directly at it, you won't see the sign at all and you will end up spending an hour walking around Beijing looking for it. (For reference, the station entrance is opposite convention center entrance C-2.)

    When you fail to find the Olympic Green station entrance, you might consider going to a security guard booth (they are all over Beijing, the city being somewhat security-obsessed in a mostly-theater sort of way) holding a subway map with the Olympic Green station circled and asking, "火車?" because you don't know the Mandarin word for "subway" and have to make do by asking for the "train". Do not expect the security guard to have any clue what you're asking for. (Okay, I sort of undermined myself by pronouncing the first word in Mandarin but the second in Cantonese, because the two languages occupy similar portions of my brain and I often get them mixed up. But still, the first two cues...)

    In general, if you ask for directions but don't know more than a few dozen words of Mandarin, you're going to be in a world of hurt. My plan was to hold a map, point at it, and ask, "我在哪裡?" ("Where am I?"), and then calculate what direction to head based on the answer. Do not expect people to answer the question you ask. They will instead ask you other questions like "你去哪兒?" ("Where are you going?") but since answering that question is beyond your vocabulary, all you can do is repeat your original question, and they will give up, frustrated. Telling them that you're from the United States doesn't help, because they don't speak English.

    (The "Where am I?" technique worked great in Germany. The person I asked would locate me on the map, and even orient the map, then follow up with additional questions that I struggled to understand and answer.)

    It has been suggested that my profound difficulty in getting directions was exacerbated by the fact that I look like somebody who should know Mandarin. If I were some European-looking person, I wouldn't have had as much of a problem because, I'm told, Chinese people naturally assume that if they see a Chinese person on the street, that person speaks Mandarin. (The person may speak a regional language as well, but you can count on them speaking at least Mandarin.) If you look Chinese but don't speak Mandarin, then they will just get frustrated at this Chinese person who refuses to speak Chinese.

    This roughly matched my experience. Pretty much everybody assumed that I spoke Mandarin. The exception? The street hustlers and scam artists. They had me pegged for a foreigner.

    By the way, I eventually solved my problem by looking for a bank. The manager on duty spoke some English, and combined with my rudimentary Mandarin, Cantonese, and Hokkien (thank heaven for cognates), I was able to get the information I needed. Of course, by that time, I had wandered so far astray that the nearest subway station was nowhere near the one I was looking for originally!

    Oh, and do not expect the hotel concierge to give you an up-to-date map. The information on the map had not been updated to take into account recent subway expansion, which means that its directions on how to get to points of interest were unnecessarily cumbersome. (What's more, the hotel itself did not appear on the map, because it was covered by an inset of the Olympic stadiums. This makes it hard to orient yourself once you step outside.) In fact, most of the time the street I was standing on didn't appear anywhere on the map (or at least I couldn't find it), so I had no clue where I was.

    The air pollution in Beijing is legendary. The week before I arrived, the United States Embassy declared that the Air Pollution Index in Beijing topped 500, earning the rating "Hazardous for all people." On the other hand, the official Chinese government pollution index was "only" 341. (Mind you, 341 is still off the chart. For grins, compare the scales used by mainland China, Hong Kong, and Malaysia.) You don't really notice the effect of the air pollution until you return to your hotel at the end of a day outdoors and wonder why your throat is sore and you feel like you spent the day in a smoke-filled bar.

    Walking through the Forbidden City makes you feel like Frodo in The Lord of the Rings. You fight your way across a courtyard to reach the building at the other end. Upon reaching the building, you cross the gate and before you lies... another seemingly-identical courtyard. This repeats about twenty-five bazillion times. They should really call it the Forbidden County.

    If you're trying to get to the Summer Palace, do not accidentally leave your map in your hotel room thinking that "This is such a prominent tourist location it must certainly have adequate signage, or at least be present on the 'things nearby' map at the station." The only nearby attraction on the map at the station is the Old Summer Palace. The only directions to the Summer Palace is a single arrow on the plaza level of the station. The arrow tells you to Frogger across a busy four-lane street (and over the fence). If you go to the crosswalk some distance away, you end up wandering in the wrong direction for a while and turning around when you figure "This can't be right." On the way back, you try a slight variation on the path out of the station and notice that there's a directional sign for the Summer Palace facing away from the station. (I.e., only people returning to the station can see it.) You follow that arrow and wonder if you're on the right street since it's pretty much an empty street as far as the eye can see, but you gradually find tour buses so you figure you're getting closer. You then find the Summer Palace parking lot and say, "Cool, there must certainly be a sign to the Summer Palace from the parking lot" but you'd be wrong. You then see a tour group in the distance and take your chances that they are going into the Summer Palace (rather than returning) and follow them down an unmarked side street, then another unmarked side street, before spotting the entrance to the Summer Palace.

    China clearly has yet to figure out this "foreign tourists not visiting as part of a guided tour" thing. My guess is that since it was a closed country for so long, there was no need for directional signage because all foreign tourists were necessarily accompanied by a government-approved tour guide, and the government-approved tour guide knows how to get there.

    The Summer Palace is very scenic. I bet it's even prettier in the summer.

    Orthographic note: Out of habit, I use traditional characters even though China uses simplified characters. With one exception, I'm reasonably comfortable with reading both sets of characters, though when writing I prefer traditional. Traditional characters feel more formal and "standard" to me, whereas simplified characters feel too casual for normal use. (Like writing "u" and "b4" instead of "you" and "before".)

    The one exception? The character for "car": 車. The simplified version is 车 which to me is unrecognizable because it destroys the ideographic representation of the top view of a car. (The central box is the body of the car, and the horizontal bars at the top and bottom are the axles. The simplified version is just a number "4" with some extra bars.)

    Mostly-theater: X-ray machines are omnipresent, but they are largely ignored. People just walk right on past them. The TechEd conference I attended had three security checkpoints: One with an X-ray machine and a metal detector, and two additional checkpoints where security personnel checked that you had a valid badge before letting you pass. What nobody appeared to notice is that if you took the publically-accessible skybridge from the Intercontinental Beijing Beichen hotel next door, you could enter the inner sanctum of the conference without ever passing through a security checkpoint.

  • The Old New Thing

    Psychic debugging: When I copy a file to the clipboard and then paste it, I get an old version of the file


    A customer reported the following strange problem:

    I tried to copy some text files from my computer to another computer on the network. After the copy completes, I looked at the network directory and found that while it did contain files with the same names as the ones I copied, they have completely wrong timestamps. Curious, I opened up the files and noticed that they don't even match the files I copied! Instead, they have yesterday's version of the files, not incorporating the changes that I made today. I still have both the source and destination folders open on my screen and can confirm that the files I copied really are the ones that I modified and not files from some backup directory.

    I tried copying it again but still an outdated version of the file gets copied. Curiously, the problem does not occur if I use drag/drop to copy the files. It happens only if I use Ctrl+C and Ctrl+V. Any ideas?

    This was indeed quite puzzling. One possibility was that the customer was mistakenly copying out of a Previous Versions folder. Before we could develop some additional theories, the customer provided additional information.

    I've narrowed down the problem. I've found that this has something to do with a clipboard tool I've installed. Without the tool running, everything is fine. How is it that with the tool running, Explorer is copying files through some sort of time machine? Those old versions of the files no longer exist on my computer; where is Explorer getting them from?

    Other people started investigation additional avenues, taking I/O trace logs, that sort of thing. Curiously, the I/O trace shows that while Explorer opened both the source and destination files and issued plenty of WriteFile calls to the destination, it never issued a ReadFile request against the source. An investigation of Previous Versions shows that there are no previous versions of the file recorded in the file system. It's as if the contents were being created from nothing.

    While the others were off doing their investigations, my head shuddered and I was sent into a trance state. A hollow voice emanated from my throat as my psychic powers manifested themselves. Shortly thereafter, my eyes closed and I snapped back to reality, at which point I frantically typed up the following note while I still remembered what had happened:

    My psychic powers tell me that this clipboard program is "virtualizing" the clipboard contents (replacing whatever is on the clipboard with its own data) and then trying (and failing) to regurgitate the original contents when the Paste operation asks for the file on the clipboard.

    A closer investigation of the clipboard enhancement utility showed that one of its features was the ability to record old clipboard contents and replay them (similar to the Microsoft Office Clipboard). Hidden inside the I/O operations was a query for the last-access time.

    And that's when things fell into place.

    Starting in Windows Vista, last access time is no longer updated by default. The program apparently saw that the file was never accessed and assumed that that meant that it also had never been modified, so it regenerated the file contents from its internal cache. (The quick fix for the program would be to switch to checking the last modified time instead of the last access time.)

    Upon learning that my psychic powers once again were correct, I realized that my prize for being correct was actually a penalty: Now even more people will ask me to help debug their mysterious problems.

  • The Old New Thing

    2010 year-end link clearance


    Another round of the semi-annual link clearance.

    And, as always, the obligatory plug for my column in TechNet Magazine:

    • Beware the Balloon.
    • Hiding in Plain Sight.
    • History—the Long Way Through. In their zeal to make this article meet length, the editors cut what I consider to be the most important part of the article! Here's the penultimate paragraph in its full unedited version, with the important part underlined.
      But wait, there's still more. What if you want to access the real 64-bit system directory from a 32-bit process? File system redirection will take your attempt to access the C:\Windows\System32 directory and redirect it to the C:\Windows\SysWOW64 directory. Programmatically, you can use functions with unwieldy names like Wow64­Disable­Wow64­Fs­Redirection, but those disable redirection for all operations until re-enabled, which causes trouble if you're doing anything more complicated than opening a single file, because a complex operation may result in multiple files being accessed and possibly even worker threads being created. Instead of using a gross switch like disabling file system redirection, you can use the special C:\Windows\SysNative virtual directory. When a 32-bit process tries to access the C:\Windows\SysNative directory, the operations are redirected to the real C:\Windows\System32 directory. A local solution to a local problem.
    • Leftovers from Windows 3.0.
    • The Story of Restore.
    • The Tumultuous History of 'Up One Level'. The editors messed up the diagram in this article. The "1" is supposed to be an "open folder" icon, but due to the same error that results in that mysterious J, the Wingdings glyph turned into a plain "1". Here's what the diagram was supposed to look like. (Of course, if your browser is one who believes that Wingdings doesn't have a "1" glyph, then you'll just see a "1".)

      So for those of you looking for your Up One Level button, it's right there on the Address Bar. I've drawn a box around it so it's easier to see.

      Computer OS (C:) Windows Web Wallpaper
  • The Old New Thing

    Windows 7 not only can make a wallpaper slide show from images on your computer, it can even pull them from an RSS feed


    Buried in the theme file documentation is a section called [Slideshow] which lets you control the source for images that are used when you put the desktop wallpaper in slideshow mode. And a bonus feature hidden in the [Slideshow] section is the ability to draw the images from an RSS feed. After creating the .theme file, double-click it and it will be added to the list of available themes.

    One thing about the RSS feed is that when you first set it up, it'll probably take a while for the initial images to download. You don't get any feedback that the images are still downloading; they just show up once they're ready. So don't freak out.

    Well, okay, if the images have already downloaded and you still don't see them, then maybe you can freak out. (Did you remember to select the theme after you added it to the list of available themes?)

Page 1 of 4 (31 items) 1234