Microsoft wrote a bunch of code, that in hindsight was a really really bad idea. I'm not really sure that the problems were all Microsoft's fault - they probably didn't invent them. They are Microsoft's fault in that they embedded the concepts into the operating system and then widely popularized them.
The big gotchas all have to do with limitations of C, and the twists used to optimize Microsoft's programming for the 8086 architecture. I'm thinking of:
What can possibly go wrong? Especially before MAX_PATH was invented.
malloc() new() and friends
For any non-trivial program, it's almost impossible to get correct. They only really work for something like a C compiler which will allocate a variable amount of memory, do a task, and then end. If you miss a few free() calls along the way - they will be cleaned up when the compiler terminates.
GlobalAlloc(), LocalAlloc() and friends
Just in case you couldn't make it work with malloc, try the operating system version of the call. Using OS calls really opened the window to some famous bugs - OpenSSH comes to mind.
Multi-threading in C
For any non-trivial problem - it doesn't work. Firstly, for any non-simple piece of code, it is tough to do correctly. Secondly, for a trivial piece of code, like a save operation, you need to somehow make the memory being stored immutable for the duration of the save. For most save operations, this defeats the purpose, as the first thing the user wants to do after save is to change the document. Thirdly, if you really need multi-threading, there is a good chance some of your users need parallel-processing across machines. Multi-threaded code and parallel-processing code are not the same things at all.
Embedding code in data.
Firstly, the x86 architecture encouraged this, by not having proper page protections. Secondly, it completely opens the system up to malicious code. ActiveX, JPEG libraries, font libraries, everywhere Microsoft had an API that embedded code in data, it was all exploited.