No you don't have to send a WM_PAINT message. You have 2 basic options when using text in DDraw. You can use GDI functions or create your own text sprite class.
I've used both in the past but now I stick with the text sprite class. It's faster and customizable. You can see an example of it here.
Basically you have a bitmap that has the alphabet, numbers and whatever special symbols you want in a row all confined within a set rectangle (whatever size you prefer, I use 8x8). Then you parse in the string you want to display and use char arithmetic to get the position.
IE: I use all lower case letters and I start with a and end with the number 9, so the letter be would be at position 1, c at position 2, etc.
If you're interested email or PM me and I'll send you the code for the class.
//str is a member var that hold the text you want to display
//iLen is a member var that represents str's length
//sfText is LPDIRECTDRAWSURFACE7 member var
//rcText is a RECT member var
void CText::Blt(lpdds7 sf)
**EDIT** sorry I just realized that I forgot to address your GDI questions. There is absolutely nothing wrong with mixing GDI and DirectDraw in an app as long as you do it sensibly. In other words, using BitBlt to draw all your sprites to a DirectDraw backbuffer each frame would be ridiculous. But on the other hand, during initialization I can't think of a quicker, easier way to load the bitmap's image onto the Sprite's surface than BitBlt. I use a 2 or 3 GDI functions during my set-up, but for all of my in game blitting I use DDraw.
As for how to use GDI to do text with DDraw simply use TextOut. Here's an example:
//where sfBackbuffer is your backbuffer surface and sfPrime is
//your primary primary surface
//in your main loop after clearing the backbuffer