Whether or not a higher level programming language is the right tool is as much a function of the programmer as it is the application being built. I've seen too many coders who believe that higher level languages such as Java, C#, Smalltalk, Perl, etc. are substitutes to knowing the details of what is happening at lower levels. In my experience, this attitude leads to heartache.
Using a language and a platform at the right level of abstraction for the work to be done is key to maximizing productivity. It is often technically possible to write code at the wrong abstraction layer, but the mismatch between the tool and the job ends up causing extra work and decreased productivity. Imagine writing a device driver in Smalltalk, is that the right tool for the job?
On the other hand, there are plenty of projects for which assembly or C are too low level to maximize productivity. You end up rewriting (and re debugging) boiler plate code and having to re implement implementation patterns... or reinvent them if it your first time solving a particular problem.
Not having to solve a problem for the umpteenth time either by having the programming language abstract the solution for you or by building on a platform or API that does it does not mean that you don't need to understand it, only that you don't need to reinvent it.
Building on something but not understanding what it is doing and how is a form of "Ignorance Is Bliss." You may be able to get away with it, even for a while, but there's a great chance the lack of understanding will cause very difficult issues.
Let's take a vague example: inter process communication.
Starting at the lowest level, you could write assembly to tell the CPU exactly what you want to happen, map memory around, and get it to work. Clearly you need to know the ins and outs of the CPU and exactly what is going on at the lowest level to get it to work correctly and reliably.
At the next layer up, you can build on low level facilities provided by the operating system, let's use the example of memory mapped files in Windows. These are built based on the same things you'd have to do at the previous layer, but it is already done and debugged. This doesn't excuse you from knowing how it works, though, because proper usage of any technology requires understanding of its design, strengths, and limitations. When APIs and languages are used improperly it can cause major problems in software that fundamentally can't be worked around without re architecture.
COM is a great example for the next layer of abstraction (and within itself has many layers). It builds on memory mapped files and other facilities in the OS, adding its own semantics and twists. If you code using COM and don't understand how it works, why it does what it does, and where the pitfalls lay you will cause bugs. Trust me.
Next level up let's consider the ATL C++ wrappers around COM, smart pointers, etc. There are a few new things to worry about here, but the important thing is to realize that the conveniences are not a substitute from an understanding of COM (like using COM is not a substitute from understanding IPC issues). Raymond Chen gives a great ATL COM smart pointer example where not understanding COM requirements (when CoUninitialize can be called) while using the simplifying templates will cause you problems.
I'm a big believer in using ATL and smart pointers, but their usefulness is in allowing a programmer who does understand the issues to code more effectively and efficiently by removing the need to write (and potentially make a mistake with) a lot of boiler plate source. The usage must still be correct, and the only way to ensure that is to know how all the underlying systems work and relate to each and to your usage of the smart pointers.
Pop up one more level to managed (.NET) code like C#, and interop with COM objects. You can easily make calls to COM objects with C#, but once again it does not remove the need to understand what is going on. Now you need to understand what the .NET Framework is doing and how it interacts with COM in addition to everything already discussed (and this is only for the IPC portion of the code!).
This is not to say that there are not substantial gains from using a higher level of abstraction, I firmly believe there are. But too often people assume that everything underneath their layer is being handled in such a way that they don't need to understand the details (or even the generalities), and go about writing code that makes no sense to anyone who really understands the full system.
In the end it all comes down to 1s and 0s, knowing the details about how the CPU performs its calculations, what a cache line is, and for pity sake what a pointer is and how it works is essential to being a computer programmer. Knowing how to manipulate pointers and writing code on a daily basis that does so are two different things, and I think that gets confused by some who are good C programmers.
A really good C/C++ programmer can do great work in Java or C# once they learn how things work for that language, platform, and abstraction level. Someone who only understands high abstraction layers, however, has a major task ahead of them to understand how things work at the fundamental level needed to write good C. Whether or not they work in C, understanding the system they work at in that depth will make them more productive, more correct, and better programmers.