Mike29936

Members
  • Content count

    44
  • Joined

  • Last visited

Community Reputation

132 Neutral

About Mike29936

  • Rank
    Member
  1. Vertex/Index buffers are essentially a specialized array. You create it with a fixed size, fill it with data, send it off to be rendered, and possibly change it's data later. Because it's a vertex array, indexing starts at 0 and goings up from there. However, when you render with one, you can specify an index, and a range ( vertices 50 through 100 ).
  2. I'm implementing a remake of the first game I had ever made, which was a Win32 remake of Boulderdash. My remake now written in C# with Managed DirectX. It's built in layers, with low-level DirectX wrappers on the bottom, with more advanced things built on top of it. There's an iffy problem I'm having, where if I draw my TileSet class ( which processes, renders, and stores tiles ) in the main loop, it only gets drawn when my window recieves a message. I've come to that conclusion because moving my mouse around on the window will cause it to flicker the image, while moving it outside of the window doesn't. Even more odd, if I use my low-level DirectX wrapper to draw an image on the window directly, it fixes the entire flicker problem. Even if I only draw a small image in the corner, the entire TileSet will then render and process perfectly fine. I've lost probably 5 weeks of development on this one. Note that I've used a Reference device and it exhibits the same problem. A general description of my code before I post it all ( skip to source if you don't need it ): My DirectXRenderer is derived from the System.Windows.Form class. That allows a DirectXRenderer to be created and rendering just by constructing one. The Renderer has a LoadTexture(name,location) function which loads a texture and gives it a "name". This allows a texture to be referred to by say, "grass" when drawing it and have it automatically work. The Animation class builds on that by allowing you to load images in a sequence. Even better, you can specify an Area rectangle, and that part of the sequence will be just that area on the image. The Animation class uses an ImageMapper class, which does the actual loading and drawing of the sequence images. And the TileSet class ( currently the "heaviest" class of them all ) manages all of the tiles. It stores a 3D array of Tile objects, which contain an Animation and a "name", which is used to lookup the "master" tile of that type, so it can copy it's data. And then my main program does a couple of tests with it all. Currently, it registers a Grass tile with the TileSet using a "grass.bmp" image. And now, the code, from low-level component to high-level component: #region Using directives using System; using System.Collections.Generic; using System.Text; using System.Windows.Forms; using System.Drawing; using Microsoft.DirectX; using Microsoft.DirectX.Direct3D; using System.Collections; #endregion namespace Boulderworld_2 { public class DirectXRenderer : Form { //Stores all fonts ArrayList FontList = new ArrayList(); Device device = null; //Stores all textures ArrayList TextureList = new ArrayList(); //The sprite object Sprite m_Sprite = null; struct DXTexture { public Texture Texture; public string Filename; } struct DXFont { public Microsoft.DirectX.Direct3D.Font Font; public string name; public void Dispose() { Font.Dispose(); } } /// <summary> /// Clears the screen to a color /// </summary> /// <param name="color">Specifies the color to set the screen to.</param> public void ClearColor(Color color) { device.Clear(ClearFlags.Target, color, 0, 0); } /// <summary> /// Clears the stencil buffer to a value. /// </summary> /// <param name="Stencil"></param> public void ClearStencil(int Stencil) { device.Clear(ClearFlags.Stencil, 0, 0, Stencil); } /// <summary> /// Clears the Z-Buffer /// </summary> /// <param name="NewValue">The new value in the Z-Buffer</param> public void ClearZBuffer(float NewValue) { device.Clear(ClearFlags.ZBuffer, 0, NewValue, 0); } /// <summary> /// Readies the device for drawing. /// </summary> public void StartDraw(SpriteFlags Flags) { device.BeginScene(); m_Sprite.Begin(Flags); } /// <summary> /// Stops drawing the device. /// </summary> public void EndDraw() { m_Sprite.End(); device.EndScene(); } /// <summary> /// Tells the renderer you are done drawing everything /// </summary> public void DoneDrawingAll() { device.Present(); } /// <summary> /// Disposes of the renderer /// </summary> public new void Dispose() { if (device != null) { device.Dispose(); device = null; } foreach (DXTexture i in TextureList) i.Texture.Dispose(); foreach (DXFont i in FontList) i.Dispose(); m_Sprite.Dispose(); } //Property for accessing the device. public Device Device { get { return device; } } /// <summary> /// Constructor for the DirectX rendering class. /// </summary> /// <param name="Title"></param> /// <param name="Windowed"></param> /// <param name="Resolution"></param> /// <param name="BitDepth"></param> public DirectXRenderer(string Title, bool Windowed, Size Resolution, Int16 BitDepth) { try { //Set size of window this.ClientSize = Resolution; //Set window caption this.Text = Title; PresentParameters presentParams = new PresentParameters(); //If we want it windowed... if (Windowed == true) { //Setup our D3D stuff presentParams.Windowed = true; presentParams.SwapEffect = SwapEffect.Discard; presentParams.BackBufferCount = 1; presentParams.BackBufferFormat = Format.Unknown; presentParams.BackBufferHeight = 0; presentParams.BackBufferWidth = 0; presentParams.DeviceWindow = this; presentParams.PresentationInterval = PresentInterval.Default; device = new Device(0, DeviceType.Hardware, this, CreateFlags.SoftwareVertexProcessing, presentParams); device.RenderState.Ambient = System.Drawing.Color.White; device.RenderState.CullMode = Cull.None; device.RenderState.Lighting = false; m_Sprite = new Sprite(device); } //If Fullscreen... else { //Todo: Implement full-screen functionality. } this.Show(); } catch (DirectXException e) { MessageBox.Show("Error creating device object: " + e.Message); } } //An event handler for when the window is closed. protected override void OnClosed(EventArgs e) { Dispose(); } public Texture GetTexture(string Filename) { Texture ret = null; foreach (DXTexture i in TextureList) { if (i.Filename == Filename) { ret = i.Texture; break; } } return ret; } /// <summary> /// Loads a texture if it hasn't been loaded before. /// </summary> /// <param name="Filename">The texture to load</param> public void LoadTexture(string Filename) { if (GetTexture(Filename) == null) { DXTexture Temp = new DXTexture(); Temp.Filename = Filename; Temp.Texture = TextureLoader.FromFile(device, Filename); TextureList.Add(Temp); } } /// <summary> /// Creates a font for use. /// </summary> /// <param name="height"></param> /// <param name="width"></param> /// <param name="Italic"></param> /// <param name="Name"></param> public void MakeFont(int height, int width, bool Italic, string Name) { Microsoft.DirectX.Direct3D.Font Temp = new Microsoft.DirectX.Direct3D.Font( device, height, width, FontWeight.Normal, 0, Italic, CharacterSet.Ansi, Precision.Default, FontQuality.AntiAliased, PitchAndFamily.DefaultPitch, Name); DXFont TempStruct; TempStruct.Font = Temp; TempStruct.name = Name; FontList.Add(TempStruct); } public void DrawText(string Text, string Name, Point Position, Color color) { DXFont Result = new DXFont(); bool found = false; foreach (DXFont i in FontList) { if (i.name == Name) { Result = i; found = true; break; } } if (found) { Result.Font.DrawText(null, Text, Position, color); } } public void DrawTexture(string Filename, Rectangle Source, Rectangle Dest, Color color) { //LoadTexture has built-in error checking, so it's only loaded if needed. LoadTexture(Filename); Texture Temp = GetTexture(Filename); m_Sprite.Draw2D(Temp, Source, Dest, new Point(Dest.X, Dest.Y), color); } /// <summary> /// Draws the specified texture onto the device. /// </summary> /// <param name="Tex"></param> /// <param name="Source"></param> /// <param name="Dest"></param> /// <param name="color"></param> public void DrawTexture(Texture Tex, Rectangle Source, Rectangle Dest, Color color) { m_Sprite.Draw2D(Tex, Source, Dest, new Point(Dest.X, Dest.Y), color); } } } ---- ---- ---- ---- ImageMapper #region Using directives using System; using System.Collections.Generic; using System.Text; using System.Drawing; using Microsoft.DirectX.Direct3D; using Microsoft.DirectX; #endregion namespace Boulderworld_2 { /// <summary> /// This class is responsible for mapping areas onto an image, and drawing those sub-images. /// </summary> public class ImageMapper { /// <summary> /// Stores Rectangles representing the areas of the image. The Rectangles are mapped to a string. /// </summary> SortedDictionary<string, Rectangle> m_ImageAreas = null; /// <summary> /// The image to map out sections on. /// </summary> Texture m_Image = null; /// <summary> /// The renderer used to draw the image. /// </summary> DirectXRenderer m_Render = null; /// <summary> /// Constructor for the ImageMapper class /// </summary> /// <param name="Dev"></param> /// <param name="Image"></param> ImageMapper(DirectXRenderer Dev, Texture Image) { //Store the renderer. m_Render = Dev; //Store the image. m_Image = Image; //Create the dictionary to map areas of the image to strings. m_ImageAreas = new SortedDictionary<string, Rectangle>(); } /// <summary> /// Adds a new area of the map to the image, which is referenced by it's "name". /// </summary> /// <param name="Name"></param> /// <param name="Area"></param> void Add(string Name, Rectangle Area) { m_ImageAreas.Add(Name, Area); } /// <summary> /// This function draws the texture with the specified name at a particular area, with it's color modulated by a certain color. /// </summary> /// <param name="Name"></param> /// <param name="Destination"></param> /// <param name="color"></param> void Draw(string Name, Rectangle Destination, Color color) { m_Render.DrawTexture(m_Image, m_ImageAreas[Name], Destination, color); } /// <summary> /// Gets the area of the part of the image specified by the Name parameter. /// </summary> /// <param name="Name"></param> /// <returns></returns> Rectangle GetArea(string Name) { return m_ImageAreas[Name]; } /// <summary> /// Removes the entry of the specified name. /// </summary> /// <param name="Name"></param> void Remove(string Name) { m_ImageAreas.Remove(Name); } } } Animation: #region Using directives using System; using System.Collections.Generic; using System.Windows.Forms; using System.Drawing; using Microsoft.DirectX; using Microsoft.DirectX.Direct3D; using System.Collections; #endregion namespace Boulderworld_2 { /// <summary> /// This class handles animation of images. /// </summary> public class Animation { DirectXRenderer Render = null; /// <summary> /// Keeps track of the current frame. /// </summary> UInt16 Counter = 0; /// <summary> /// This structure stores information about a image frame. /// </summary> struct ImageSequenceSection { public Texture tex; public Rectangle rect; /// <summary> /// Constructor for the object. /// </summary> /// <param name="Tex"></param> /// <param name="Area"></param> public ImageSequenceSection(Texture Tex, Rectangle Area) { tex = Tex; rect = Area; } } /// <summary> /// A list of image frames. /// </summary> List<ImageSequenceSection> ImageList = new List<ImageSequenceSection>(); public Animation(DirectXRenderer Arg) { Render = Arg; } /// <summary> /// Clears the Animation of all it's images. /// </summary> public void Clear() { ImageList.Clear(); } /// <summary> /// Adds an image from a texture with the given area to the list of animations. /// </summary> /// <param name="Tex"></param> /// <param name="Area"></param> public void Add(Texture Tex, Rectangle Area) { ImageList.Add(new ImageSequenceSection(Tex, Area)); } /// <summary> /// Draws the current frame in the animation, and advances to the next. /// </summary> /// <param name="Area"></param> /// <param name="color"></param> public void Draw(Rectangle Area, Color color) { Render.DrawTexture(ImageList[Counter].tex, ImageList[Counter].rect, Area, color); if (Counter < ImageList.Count-1) { Counter++; } else { Counter = 0; } } public Animation Clone() { Animation ret = new Animation(this.Render); foreach (ImageSequenceSection Section in this.ImageList) { ret.Add(Section.tex,Section.rect); } ret.Counter = this.Counter; return ret; } } } TileSet: #region Using directives using System; using System.Collections.Generic; using System.Text; using Microsoft.DirectX.Direct3D; using Microsoft.DirectX; using System.Drawing; #endregion namespace Boulderworld_2 { /// <summary> /// The TileSet class manages tiles, making it easy to create sets of tiles and manipulate them. /// </summary> sealed public class TileSet : IDisposable { Tile[, ,] m_Tiles = null; Color[, ,] m_ModColor = null; SortedDictionary<string, TileRegistrationData> m_TileData = null; /// <summary> /// This function gives every tile it's own proper animation object. /// </summary> public void SetTileAnimations() { //Get dimensions. int Width = m_Tiles.GetLength(0); int Height = m_Tiles.GetLength(1); int Layers = m_Tiles.GetLength(2); //Loop through the tiles. for (int temp = 0; temp < Layers; temp++) for (int temp2 = 0; temp2 < Height; temp2++) for (int temp3 = 0; temp3 < Width; temp3++) { //Set tile's animation to a clone of the proper one. m_Tiles[temp3, temp2, temp].Animation = m_TileData[m_Tiles[temp3, temp2, temp].Name].Anim.Clone(); } } public abstract class Tile { Animation m_Animation = null; public Animation Animation { get { return m_Animation; } set { m_Animation = value; } } protected string m_Name = null; /// <summary> /// Property for accessing the name. /// </summary> /// <value></value> public string Name { get { return m_Name; } } /// <summary> /// Processes the tile in the given grid. Returns the modified grid. /// </summary> /// <param name="Grid"></param> /// <returns></returns> public abstract Tile[, ,] Process(Tile[, ,] Grid, Vector3 Position); } public struct TileRegistrationData { public Animation Anim; public string Name; } public struct TileInfo { public Point Location; public UInt16 Layer; public Tile Tile; public TileInfo(Point Pos, UInt16 Lay, Tile Tiled) { Location = Pos; Layer = Lay; this.Tile = Tiled; } } public void ClearColorMod(Color c) { //Get dimensions. int Width = m_Tiles.GetLength(0); int Height = m_Tiles.GetLength(1); int Layers = m_Tiles.GetLength(2); for (UInt32 i = 0; i < Width; i++) for (UInt32 j = 0; j < Height; j++) for (UInt32 k = 0; k < Layers; k++) { m_ModColor[i, j, k] = c; } } /// <summary> /// Constructor for the TileSet object. /// </summary> /// <param name="size"></param> /// <param name="Layers"></param> public TileSet(Size size, UInt16 Layers) { m_Tiles = new Tile[size.Width, size.Height, Layers]; m_ModColor = new Color[size.Width, size.Height, Layers]; ClearColorMod(Color.White); m_TileData = new SortedDictionary<string, TileRegistrationData>(); } /// <summary> /// Disposes of the object. /// </summary> public void Dispose() { } public void Resize(Size Size, UInt16 Layers) { m_Tiles = new Tile[Size.Width, Size.Height, Layers]; GC.Collect(); } public void Set(TileInfo Info) { //Get dimensions from 3D array of Tiles Int32 Width = m_Tiles.GetLength(0); Int32 Height = m_Tiles.GetLength(1); Int32 Layer = m_Tiles.GetLength(2); //Verify input. if (Info.Location.X >= 0 && Info.Location.X < Width && Info.Location.Y >= 0 && Info.Location.Y < Height && Info.Layer < Layer) { //Set the tile. m_Tiles[Info.Location.X, Info.Location.Y, Info.Layer] = Info.Tile; } //Set tile's animation. m_Tiles[Info.Layer, Info.Location.Y, Info.Location.X].Animation = m_TileData[m_Tiles[Info.Layer, Info.Location.Y, Info.Location.X].Name].Anim.Clone(); } TileInfo Get(Point Location, UInt16 Layer) { TileInfo ret = new TileInfo(); //Fill in position ret.Layer = Layer; ret.Location = Location; //Set the tile in the TileInfo object to the appropriate tile. ret.Tile = m_Tiles[Location.X, Location.Y, Layer]; return ret; } public void Process() { //Get dimensions from 3D array of Tiles Int32 Width = m_Tiles.GetLength(0); Int32 Height = m_Tiles.GetLength(1); Int32 Layer = m_Tiles.GetLength(2); for (Int32 i = 0; i < Width; i++) for (Int32 j = 0; j < Height; j++) for (Int32 k = 0; k < Layer; k++) { m_Tiles = m_Tiles[i, j, k].Process(m_Tiles, new Vector3(i, j, k)); } } public void SetColor(Point point, UInt32 layer, Color color) { m_ModColor[point.X, point.Y, layer] = color; } public Color GetColor(Point point, UInt32 layer) { return m_ModColor[point.X, point.Y, layer]; } public void Render(Rectangle RenderArea, Size TileSize) { //Get Dimensions Int32 Width = m_Tiles.GetLength(0); Int32 Height = m_Tiles.GetLength(1); Int32 Layer = m_Tiles.GetLength(2); //SetTileAnimations(); //Loop through all dimensions for (Int32 i = 0; i < Layer; i++) { for (Int32 j = 0; j < Height; j++) { for (Int32 k = 0; k < Width; k++) { //Get the tile Tile t = m_Tiles[k, j, i]; Int32 TileX = RenderArea.X + k * TileSize.Width; Int32 TileY = RenderArea.Y + j * TileSize.Height; //Check width if (TileX >= RenderArea.X + RenderArea.Width) { //If bad, go to next row. break; } //Check height if (TileY >= RenderArea.Y + RenderArea.Height) { //If bad, exit the entire loop. goto exit; } //Create rectangle area. Rectangle Area = new Rectangle(TileX, TileY, 32, 32); t.Animation.Draw(Area, m_ModColor[k, j, i]); //t.Animation.Draw(Area, m_ModColor[k, j, i]); } } } exit: ; } public void RegisterTile(TileRegistrationData Arg) { m_TileData.Add(Arg.Name, Arg); } public void Clear(Tile t) { //Get dimensions. int Width = m_Tiles.GetLength(0); int Height = m_Tiles.GetLength(1); int Layers = m_Tiles.GetLength(2); //Loop through dimensions for (UInt32 i = 0; i < Width; i++) for (UInt32 j = 0; j < Height; j++) for (UInt32 k = 0; k < Layers; k++) { m_Tiles[i, j, k] = t; t.Animation = m_TileData[t.Name].Anim; } } /// <summary> /// Destructor for the TileSet object /// </summary> ~TileSet() { Dispose(); } } } And the main program: #region Using directives using System; using System.Collections.Generic; using System.Windows.Forms; using System.Drawing; using Microsoft.DirectX; using Microsoft.DirectX.Direct3D; using System.Collections; using TileRegistrationData = Boulderworld_2.TileSet.TileRegistrationData; #endregion namespace Boulderworld_2 { public class Grass : TileSet.Tile { public Grass() { m_Name = "Grass"; } public override TileSet.Tile[, ,] Process(TileSet.Tile[, ,] Arg,Vector3 Position) { return Arg; } } static class Program { /// <summary> /// The main entry point for the application. /// </summary> [STAThread] static void Main() { DirectXRenderer a = new DirectXRenderer("Boulderworld 2", true, new Size(400,400),4); bool running = true; TileSet Tiles = new TileSet(new Size(5, 5), 1); Grass Temp = new Grass(); TileRegistrationData T = new TileRegistrationData(); a.LoadTexture("grass.bmp"); T.Anim = new Animation(a); T.Anim.Add(a.GetTexture("grass.bmp"), new Rectangle(0, 0, 32, 32)); T.Anim.Add(a.GetTexture("grass.bmp"), new Rectangle(0, 0, 32, 32)); T.Name = "Grass"; Tiles.RegisterTile(T); Tiles.Clear(Temp); Tiles.ClearColorMod(Color.White); while (running) { if (a.Device == null) { running = false; break; } a.ClearColor(Color.Blue); a.StartDraw(SpriteFlags.None); T.Anim.Draw(new Rectangle(0, 0, 32, 32), Color.White); Tiles.Render(new Rectangle(0, 0, 128, 128), new Size(32, 32)); a.EndDraw(); a.DoneDrawingAll(); Application.DoEvents(); } a.Dispose(); } } } Other notes: -dbmon.exe reports no error messages. -No exceptions are ever thrown. The program thinks it's working perfectly fine. -If you compile & run it, you'll need to provide your own 'grass.bmp' file in the same directory. I thank anybody for helping, in advance.
  3. ^That was me. Wasn't logged in for some reason.
  4. You could store a map of strings to function pointers, then index into the map and call the pointer. Note that all your functions would have to have the same number/type of arguments. Though, you could just pass in a vector, so you can have a dynamic number of arguments. To make this clearer: #include <map> using namespace std; typedef CommandFunction void vector<unsigned int> (*)(vector<unsigned int>);//Or however you declare those function pointers. vector<unsigned int> Palindrome(vector<unsigned int> Arg) { return Arg;//Implement your palindrome here. } int main(void) { map<string,CommandFunction> MyMap = new map<string,CommandFunction>(); map["palindrome"] = Palindrome; string input; cin >> input; vector<unsigned int> blah;//Argument for Palindrome() function. blah.push_back(5666); map[input](blah);//Retrieve the function pointer from the input, and call it with the vector as input. } ^In this example ( if it compiles ), if you type "palindrome", it will dynamically search through the map for a key,value pair with the key "palindrome", and return the value, which is a function pointer to the Palindrome() function. It will call it with a preset vector argument, but your code can, of course, add anything it wants in there. Note that if you type anything besides "palindrome", the map won't find a function pointer and will throw an exception. You should catch this exception and inform the user of an invalid command.
  5. what compiler / IDE do you use?

    The free beta 2005 C# compiler from Microsoft, with GDI+ or DirectX.
  6. Sorting Collisions

    Well, you shouldn't worry about the speed of a few clock cycles before implementing something. But if it's very important that you avoid sqrt() at all costs, note that you don't need the exact distance between 2 things. Assuming x and y are positive, and x > y: sqrt(x) > sqrt(y) So when you're sorting by distance, you don't need the exact distance, so don't take a sqrt() operation. It'll still sort okay.
  7. Quote:For a minute I thought you meant use the ZBuffer as a texture. I think he is: Quote:it is slow to use it as an actual texture
  8. Look up "Audio and Video playback". Audio objects are DirectMusic, and Video is DirectShow.
  9. void BlitRect(LPDIRECT3DDEVICE8 lpDevice, LPDIRECT3DTEXTURE8 lpSrc, float left, float top, float right, float bottom, D3DCOLOR col,float z) { // calculate rhw float rhw=1.0f/(z*990.0f+10.0f); // set up rectangle D3DTLVERTEX verts[4]; verts[0]=D3DTLVERTEX(D3DXVECTOR3(left-0.5f, top-0.5f, z),rhw,col,0.0f,0.0f); verts[1]=D3DTLVERTEX(D3DXVECTOR3(right-0.5f, top-0.5f, z),rhw,col,1.0f,0.0f); verts[2]=D3DTLVERTEX(D3DXVECTOR3(right-0.5f, bottom-0.5f, z),rhw,col,1.0f,1.0f); verts[3]=D3DTLVERTEX(D3DXVECTOR3(left-0.5f, bottom-0.5f, z),rhw,col,0.0f,1.0f); // set the texture lpDevice->SetTexture(0,lpSrc); // draw the rectangle lpDevice->DrawPrimitiveUP(D3DPT_TRIANGLEFAN,2,verts,sizeof(D3DTLVERTEX)); } Seems to be that you should look into a better solution for your texture drawing needs. You set the texture every single time you want to draw a picture, and you also send a seperate DrawPrimitiveUP call for every picture. You want to minimize the number of texture changes, because the video card doesn't like that. So this is usually accomplished with 1 picture with alot of tiles in it. DrawPrimitiveUP for every square is bad because the video card likes to process a whole ton of triangles at once, rather than a little bit spread over 10,000 different calls. Of those 2, I believe calling DrawPrimitiveUP alot would be limiting your FPS. I hadn't looked at your code too closely; I just noticed that one function. I think you're using DirectX 8, though. If you were using DX 9.0, I would suggest using ID3DXSprite, because it's easy to use and it's also efficient, because it batches up your calls, and then sends them all at once. You thus have 2 choices: -Write an efficient texture rendering solution yourself. -Switch to DX 9.0 and use ID3DXSprite.
  10. Where do you get the idea it uses OpenGL? I saw VGA on their site, and no mention of OpenGL anywhere.
  11. [java] A* algorithmn in Java

    Quote:the entire point of an *algorithm* is to be expressed independently of any particular code, so that it can be adaptable to any particular code. Why do you need one that is in Java specific? For easy copy-pasting. http://www.google.com/search?hl=en&ie=UTF-8&q=java+pathfinding&spell=1
  12. an example of a simple game

    After I beat the third level, it pops up, "Cannot load level 4", "cannot load level 5", etc. until I terminate the program. Other than that, it's good. And the problem is solved easily.
  13. VC++ 2005 express code execution

    Quote:it may sound like a dumb question, but how do i execute/run my code? F5 = Run with debugging Control+F5 = Run without debugging Quote: Actually, in VC++ 2005 Express beta 1, you can't do that! Works for me.
  14. Function to take a screen shot

    Quote:Which amounts to the HDC of the window. Look up GetDC() in your local win32.hlp.
  15. When Did You Start Programming?

    I'm one of the few people here who started with C, and not BASIC. I started C at 12, C++ at 13, and now I'm 14 at C#.