Thread: [VC++/WinAPI] DDB to DIB

  1. #1
    Registered User jagi's Avatar
    Join Date
    Mar 2005
    Location
    Poland
    Posts
    21

    [VC++/WinAPI] DDB to DIB

    Hello. (sorry for mistakes) I'm taking part of screen and saving it as DDB. And now I'm trying to convert DDB to DIB using GetDIBits function

    Code:
    int GetDIBits(
      HDC hdc,           // handle to DC
      HBITMAP hbmp,      // handle to bitmap
      UINT uStartScan,   // first scan line to set
      UINT cScanLines,   // number of scan lines to copy
      LPVOID lpvBits,    // array for bitmap bits
      LPBITMAPINFO lpbi, // bitmap data buffer
      UINT uUsage        // RGB or palette index
    );
    but it doesn't work. If I run program, it chucks out error (that application will be close). I tryed use GetDIBits with different parameters. Propably is something wrong with HDC. I send hdc as a parametr of function in which I use GetDIBits. I don't know what to do. Maybe did you have that problem. If you can't help me, meybe you know how to convert DDB to DIB different way...
    Last edited by jagi; 03-25-2005 at 06:15 AM.

  2. #2
    Yes, my avatar is stolen anonytmouse's Avatar
    Join Date
    Dec 2002
    Posts
    2,544
    You'll need to post your code. It's possible that you are not retrieving the screen shot correctly. However, here is a typical call to GetDIBits:
    Code:
    PDWORD GetBitmapPixels(HBITMAP hBmp, HDC hdc)
    {
    	BITMAP             bmo       = { 0 };
    	BITMAPINFO         bmi       = { 0 };
    	PDWORD             pixels    = NULL;
    
     	if (GetObject(hBmp, sizeof(BITMAP), &bmo))
    	{
    		bmi.bmiHeader.biSize        = sizeof(BITMAPINFOHEADER);
    		bmi.bmiHeader.biWidth       = bmo.bmWidth;
    		bmi.bmiHeader.biHeight      = -bmo.bmHeight;
    		bmi.bmiHeader.biPlanes      = 1;
    		bmi.bmiHeader.biBitCount    = 32; /* Request a 32bpp bitmap. */
    		bmi.bmiHeader.biCompression = BI_RGB;
    
    		/* BEWARE: Size calculation needs to take account of padding if we are
    		 * not requesting a 32bpp bitmap. */
    		pixels = (PDWORD) malloc(bmo.bmWidth * bmo.bmHeight * sizeof(DWORD));
    
    		if (pixels)
    		{
    		 	if (GetDIBits(hdc, hBmp, 0, bmo.bmHeight, pixels, &bmi, DIB_RGB_COLORS))
    			{
    				return pixels;
    			}
    		}
    	}
    
    	free(pixels);
    	return NULL;
    }

  3. #3
    Registered User jagi's Avatar
    Join Date
    Mar 2005
    Location
    Poland
    Posts
    21
    Code:
    #include <windows.h>
    #include "engine.h"
    
    BOOL epfShowBitmap(HDC hdc, int xPos, int yPos,BITMAPFILEHEADER * pbmfh, BITMAPFILEHEADER * pbmfhMask, int iFlag)
    {
    	static BITMAPINFO * pbmi,  * pbmiMask,  * pbmiBk;
    	static BYTE       * pBits, * pBitsMask, * pBitsBk;
    
    	static HDC          hdcMem;
    	static HBITMAP      hBitmap;
    
    	static TCHAR szBuffer[100];
    
    	RGBQUAD      rgb[3];
    
    	if(pbmfh == NULL)
    	{
    		MessageBox(NULL, TEXT("Nie można wczytać bitmapy!"), NULL, 0);
    		return FALSE;
    	}
    
    	switch(iFlag)
    	{
    	case SB_COPYBITMAP:
    		pbmi  = (BITMAPINFO *) (pbmfh + 1);
    		pBits = (BYTE *) pbmfh + pbmfh->bfOffBits;
    
    		rgb[0] = *((RGBQUAD *) pBits);
    
    		wsprintf(szBuffer, TEXT("%i %i %i"), rgb[0].rgbRed, rgb[0].rgbGreen, rgb[0].rgbBlue);
    		MessageBox(NULL, szBuffer, NULL, 0);
    
    		return TRUE;
    
    	case SB_COPYBITMAPWITHMASK:
    		pbmi  = (BITMAPINFO *) (pbmfh + 1);
    		pBits = (BYTE *) pbmfh + pbmfh->bfOffBits;
    
    		pbmiMask  = (BITMAPINFO *) (pbmfhMask + 1);
    		pBitsMask = (BYTE *) pbmfhMask + pbmfhMask->bfOffBits;
    
    		hdcMem    = CreateCompatibleDC(hdc);
    		hBitmap   = CreateCompatibleBitmap(hdc, 1, 1);
    		SelectObject(hdcMem, hBitmap);
    		BitBlt(hdcMem, 0, 0, 1, 1, hdc, 0, 0, SRCCOPY);
    
    		GetDIBits(hdc, hBitmap, 0, 1, pBitsBk, pbmiBk, 0);
    
    		rgb[0] = * ((RGBQUAD *) pBits);
    		rgb[1] = * ((RGBQUAD *) pBitsMask);
    		rgb[2] = * ((RGBQUAD *) pBitsBk);
    
    		wsprintf(szBuffer, TEXT("%i %i %i\n%i %i %i\n%i %i %i"), rgb[0].rgbRed, rgb[0].rgbGreen, rgb[0].rgbBlue,
     rgb[1].rgbRed, rgb[1].rgbGreen, rgb[1].rgbBlue,
     rgb[2].rgbRed, rgb[2].rgbGreen, rgb[2].rgbBlue);
    		MessageBox(NULL, szBuffer, NULL, 0);
    
    		return TRUE;
    	}
    
    	return TRUE;
    }
    Code:
    LRESULT CALLBACK WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
    {
    	HDC          hdc;
        PAINTSTRUCT  ps;
    	static TCHAR szFileName[MAX_PATH], szTitleName[MAX_PATH], szFileName2[MAX_PATH], szTitleName2[MAX_PATH];
      
        switch(message)
    	{
        case WM_CREATE:
    		FileInitialize(hwnd);
    		FileOpenDlg(hwnd, szFileName, szTitleName);
    		FileOpenDlg(hwnd, szFileName2, szTitleName2);
    		return 0;
    
        case WM_PAINT:
            hdc = BeginPaint(hwnd, &ps);
    		epfShowBitmap(hdc, 0, 0, epfLoadImage(szFileName), epfLoadImage(szFileName2), SB_COPYBITMAPWITHMASK);
            EndPaint(hwnd, &ps);
            return 0;
              
        case WM_DESTROY:
            PostQuitMessage(0);
            return 0;
        }
        return DefWindowProc(hwnd, message, wParam, lParam);
    }
    I don't attach FileInitialize(), FileOpenDlg() and epfLoadImage() function's structure because it works corectly...
    Last edited by jagi; 03-25-2005 at 03:16 AM.
    [I'm from Poland]
    [Sorry for my English, because I just learn]

  4. #4
    Yes, my avatar is stolen anonytmouse's Avatar
    Join Date
    Dec 2002
    Posts
    2,544
    What are you trying to achieve?

    I can spot a few issues with your code.
    I don't believe copying from the HDC returned by BeginPaint is going to work.
    You can't pass uninitialised pointers to the GetDIBits, or any other, function.
    Windows provides an API, LoadImage, to load bitmaps. You don't need to roll your own function.
    Showing a MessageBox from inside WM_PAINT is a bad idea.

  5. #5
    Registered User jagi's Avatar
    Join Date
    Mar 2005
    Location
    Poland
    Posts
    21
    I don't believe copying from the HDC returned by BeginPaint is going to work.
    Why?

    You can't pass uninitialised pointers to the GetDIBits, or any other, function.
    BYTE * pBitsBk; // wrong ?
    BYTE * pBitsBk = 0; // corect ?

    Windows provides an API, LoadImage, to load bitmaps. You don't need to roll your own function.
    It doesn't load DDB. I use it to Load DIB bitmap - API doesn't include it.

    Showing a MessageBox from inside WM_PAINT is a bad idea.
    I know that, but it's only for test. In final wersion, I'll delete it.
    [I'm from Poland]
    [Sorry for my English, because I just learn]

  6. #6
    Registered User jagi's Avatar
    Join Date
    Mar 2005
    Location
    Poland
    Posts
    21
    Ok. I know what was wrong, before send pointer as function's parameter I must book memory...
    Code:
    BITMAPINFO * pbmiBk = new BITMAPINFO;
    BYTE * pBitsBk = new BYTE[size];
    But something is still wrong... If I take part of screen, save it as DDB and convert to DIB, count of red, green and blue color of first pixel is wrong. RGB first pixel always is 205,205,205 but true value is other. If I change background of client screen, first pixel's RGB is always the same...
    Last edited by jagi; 03-25-2005 at 08:59 AM.
    [I'm from Poland]
    [Sorry for my English, because I just learn]

  7. #7
    Registered User
    Join Date
    Aug 2003
    Posts
    288
    heres what i used, might help you:

    Code:
    //credit to Charles Petzold
    
    BYTE * ConvertDDBtoDIB (HWND hwnd, HBITMAP hBitmap, HPALETTE hPalette)
    {
         BITMAP       bm ;
         BITMAPINFO * pbmi ;
         BYTE       * pBits ;
         HDC          hdc ;
         WORD         wBitCount, wNumColor, wColorEntries, wRowLength ;
    
              // Get information about the DDB & calculate bits per pixel
    
         GetObject (hBitmap, sizeof (BITMAP), (PSTR) &bm) ;
    
         wBitCount = bm.bmPlanes * bm.bmBitsPixel ;
    
              // Figure out the DIB bit count and number of colors.
              //   (Probably more generalized than it needs to be)
    
         if (wBitCount > 16)
         {
              wNumColor = 0 ;
              wBitCount = 24 ;
         }
         else if (wBitCount > 8)
         {
              wNumColor = 0 ;
              wBitCount = 16 ;
         }
         else if (wBitCount > 4)
         {
              wNumColor = 1 << wBitCount ;
              wBitCount = 8 ;
         }
         else if (wBitCount > 2)
         {
              wNumColor = 1 << wBitCount ;
              wBitCount = 4 ;
         }
         else if (wBitCount == 1)
         {
              wNumColor = 2 ;
         }
    
              // Determine the number of entries in the DIB color table
    
         wColorEntries = wNumColor ;
    
         if (wColorEntries == 0 && wBitCount <= 8)
              wColorEntries = 1 << wBitCount ;
    
              // DIB rows are always multiple of 4 bytes long.
    
         wRowLength = ((bm.bmWidth * wBitCount + 31) & ~31) >> 3 ;
    
              // Allocate memory for the entire DIB.
    
         pbmi = (BITMAPINFO*)malloc (sizeof (BITMAPINFOHEADER) + 
                        sizeof (RGBQUAD) * wColorEntries +
                        wRowLength * bm.bmHeight) ;
    
         if (pbmi == NULL)
              return NULL ;
    
              // Initialize the BITMAPINFOHEADER
    
         pbmi->bmiHeader.biSize          = sizeof (BITMAPINFOHEADER) ;
         pbmi->bmiHeader.biWidth         = bm.bmWidth ;
         pbmi->bmiHeader.biHeight        = bm.bmHeight ;
         pbmi->bmiHeader.biPlanes        = 1 ;
         pbmi->bmiHeader.biBitCount      = wBitCount ;
         pbmi->bmiHeader.biCompression   = BI_RGB ;
         pbmi->bmiHeader.biSizeImage     = 0 ;
         pbmi->bmiHeader.biXPelsPerMeter = 0 ;
         pbmi->bmiHeader.biYPelsPerMeter = 0 ;
         pbmi->bmiHeader.biClrUsed       = wNumColor ;
         pbmi->bmiHeader.biClrImportant  = 0 ;
    
              // Determine the address of the pixel bits in the DIB
    
         pBits = (BYTE *) pbmi + sizeof (BITMAPINFOHEADER) + 
                                 sizeof (RGBQUAD) * wColorEntries ;
    
              // Get a video device context and select the palette into it
    
         hdc = GetDC (hwnd) ;
         SelectPalette (hdc, hPalette, FALSE) ;
         RealizePalette (hdc) ;
    
              // Here's the function we've all been waiting for.
    
         GetDIBits (hdc, hBitmap, 0, bm.bmHeight, pBits, pbmi, DIB_RGB_COLORS) ;
              
              // Clean up and get out.
    
         ReleaseDC (hwnd, hdc) ;
    
         return (BYTE *) pbmi ;
    }

  8. #8
    Registered User jagi's Avatar
    Join Date
    Mar 2005
    Location
    Poland
    Posts
    21
    Code:
    // epfTakeScreenShot.cpp
    
    #include <windows.h>
    #include "engine.h"
    
    HBITMAP epfTakeScreenShot(HWND hwnd, int xPos, int yPos, int cx, int cy)
    {
    	HDC     hdc, hdcMem;
    
    	HBITMAP hBitmap;
    
    	hdc = GetDC(hwnd);
    	hdcMem  = CreateCompatibleDC(hdc);
    
    	hBitmap = CreateCompatibleBitmap(hdc, cx, cy);
    
    	SelectObject(hdcMem, hBitmap);
    	BitBlt(hdcMem, 0, 0, cx, cy, hdc, xPos, yPos, SRCCOPY);
    
    	DeleteDC(hdcMem);
    	ReleaseDC(hwnd, hdc);
    
    	return hBitmap;
    }
    Code:
    // epfConvertDDBtoDIB.cpp
    
    #include <windows.h>
    #include "engine.h"
    
    // Function epfConvertDDBtoDIB takes as "hBitmap" parameter only 24bpp bitmaps
    
    BITMAPINFO * epfConvertDDBtoDIB(HWND hwnd, HBITMAP hBitmap)
    {
    	if(hBitmap == NULL)
    	{
    		MessageBox(hwnd, TEXT("Brak bitmapy DDB do konwersji"), NULL, MB_OK | MB_ICONERROR);
    		return NULL;
    	}
    
    	HDC          hdc;
    
    	BITMAP       bm;
    	BITMAPINFO * pBmi;
    	BYTE       * pBits;
    
    	WORD         wRowLength;
    
    	// Initialize the BITMAPINFOHEADER
    
    	GetObject(hBitmap, sizeof(BITMAP), &bm);
    
    	wRowLength = ((bm.bmWidth * 24 + 31) & ~31) >> 3;
    
    	pBmi = (BITMAPINFO *) malloc(sizeof(BITMAPINFO) + wRowLength * bm.bmHeight);
    
    	pBmi->bmiHeader.biSize          = sizeof(BITMAPINFOHEADER);
    	pBmi->bmiHeader.biWidth         = bm.bmWidth;
    	pBmi->bmiHeader.biHeight        = bm.bmHeight;
    	pBmi->bmiHeader.biPlanes        = 1;
    	pBmi->bmiHeader.biBitCount      = 24;
    	pBmi->bmiHeader.biCompression   = BI_RGB;
    	pBmi->bmiHeader.biSizeImage     = 0;
    	pBmi->bmiHeader.biXPelsPerMeter = 0;
    	pBmi->bmiHeader.biYPelsPerMeter = 0;
    	pBmi->bmiHeader.biClrUsed       = 0;
    	pBmi->bmiHeader.biClrImportant  = 0;
    
    	// Take pointer to Bits of Pixels
    
    	pBits = (BYTE *) (pBmi + 1);
    
    	// Convert DDB to DIB
    
    	hdc = GetDC(hwnd);
    
    	GetDIBits(hdc, hBitmap, 0, bm.bmHeight, pBits, pBmi, DIB_RGB_COLORS);
    
    	ReleaseDC(hwnd, hdc);
    
    	free(pBmi);
    
    	return pBmi;
    }
    Code:
    // epulsfoto.cpp
    
    #include <windows.h>
    #include "engine.h"
    
    LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
    
    int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, PSTR szCmdLine, int iCmdShow)
    {
        static TCHAR szAppName[] = TEXT("ePulsFoto");
        HWND         hwnd;
        MSG          msg;
        WNDCLASS     wndclass;
    
        wndclass.style         = CS_HREDRAW | CS_VREDRAW;
        wndclass.lpfnWndProc   = WndProc;
        wndclass.cbClsExtra    = 0;
        wndclass.cbWndExtra    = 0;
        wndclass.hInstance     = hInstance;
        wndclass.hIcon         = LoadIcon(NULL, IDI_APPLICATION);
        wndclass.hCursor       = LoadCursor(NULL, IDC_ARROW);
        wndclass.hbrBackground = (HBRUSH) CreateSolidBrush(RGB(100, 200, 0));
        wndclass.lpszMenuName  = NULL;
        wndclass.lpszClassName = szAppName;
    
        if(!RegisterClass(&wndclass))
        {
    		MessageBox(NULL, TEXT("Ten program wymaga Windows NT / XP!"), szAppName, MB_ICONERROR);
            return 0;
        }
    
        hwnd = CreateWindow(szAppName,
                            TEXT("ePulsFoto [engine]"),
                            WS_OVERLAPPEDWINDOW,
                            CW_USEDEFAULT,
                            CW_USEDEFAULT,
                            CW_USEDEFAULT,
                            CW_USEDEFAULT,
                            NULL,
                            NULL,
                            hInstance,
                            NULL);
    
        ShowWindow(hwnd, iCmdShow);
        UpdateWindow(hwnd);
         
        while(GetMessage(&msg, NULL, 0, 0))
        {
    		TranslateMessage(&msg);
            DispatchMessage(&msg);
        }
        return msg.wParam;
    }
    
    LRESULT CALLBACK WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
    {
    	static HDC          hdc, hdcMem;
        static PAINTSTRUCT  ps;
    
    	static HBITMAP hBitmap;
    	static BITMAPINFO * pBmi;
    	static BYTE       * pBits;
    
        switch(message)
    	{
        case WM_CREATE:
    		return 0;
    
    	case WM_PAINT:
    		hdc = BeginPaint(hwnd, &ps);
    		TextOut(hdc, 0, 0, TEXT("dupa"), 4);
    
    		hBitmap = epfTakeScreenShot(hwnd, 0, 0, 100, 100);
    		pBmi    = epfConvertDDBtoDIB(hwnd, hBitmap);
    		pBits   = (BYTE *) (pBmi + 1);
    
    		SetDIBitsToDevice(hdc, 0, 0, 100, 100, 0, 0, 0, 100, pBits, pBmi, DIB_RGB_COLORS);
    
    		EndPaint(hwnd, &ps);
    		return 0;
           
        case WM_DESTROY:
            PostQuitMessage(0);
            return 0;
        }
        return DefWindowProc(hwnd, message, wParam, lParam);
    }
    + header files which I don't include.

    Thanks X PaYnE X for code. Basing on your code I wrote function epfConvertDDBtoDIB, but it doesn't work :/. I think I'v done everyting correctly. Function epfTakeScreenShot works good... What is wrong in converting function... ?
    [I'm from Poland]
    [Sorry for my English, because I just learn]

  9. #9
    Registered User
    Join Date
    Aug 2003
    Posts
    288
    I tested the function before i posted it and it worked perfectly.. heres the code i used to capture the screen (try this instead of epfTakeScreenShot)..

    Code:
    HBITMAP CreateBitmapFromScreen (RECT * prect)
    {
         HBITMAP hBitmap ;
         HDC     hdcScr, hdcMem ;
    
              // Check if the bitmap has a zero dimension
    
         if (prect->top == prect->bottom || prect->left == prect->right)
              return NULL ;
    
              // Don't look at me like that! This is a documented use of the
              //   GetDC call to return a device context for the display!
    
         hdcScr = GetDC (NULL) ;
    
              // Create a memory DC and a compatible bitmap
    
         hdcMem = CreateCompatibleDC (hdcScr) ;
    
         hBitmap = CreateCompatibleBitmap (hdcScr, abs (prect->right - prect->left), 
                                                   abs (prect->bottom - prect->top)) ;
    
              // Return NULL if there's not enough memory to create the bitmap
    
         if (hBitmap == NULL)
         {
              ReleaseDC (NULL, hdcScr) ;
              DeleteDC (hdcMem) ;
              return NULL ;
         }
    
              // Select the bitmap into the memory DC and copy the screen to it
              // Note: Bitmap will be flipped if prect->left > prect->right or
              //   prect->top > prect->bottom. Use abs() or BitBlt() to avoid
              //   flipping.
    
         SelectObject (hdcMem, hBitmap) ;
    
         StretchBlt (hdcMem, 0, 0, abs (prect->right - prect->left),
                                   abs (prect->bottom - prect->top), 
                     hdcScr, prect->left, prect->top, 
                                   prect->right - prect->left,
                                   prect->bottom - prect->top, SRCCOPY) ;
    
    
              // Clean up and return the handle to the bitmap
    
         DeleteDC (hdcMem) ;
         ReleaseDC (NULL, hdcScr) ;
    
         return hBitmap ;
    }
    you would call it like this:

    Code:
    RECT rWindow;
    
    rWindow.left = 0;
    rWindow.top = 0;
    rWindow.right = 1024;
    rWindow.bottom = 768;
    
    HBITMAP hBitmap = CreateBitmapFromScreen(&rWindow);
    BYTE *pDib = ConvertDDBtoDIB(hWindow, hBitmap, NULL);
    
    //that should turn the screen to a DIB, now u need to save it to a file..
    //for that you would use something like DibSave(pDib, "test.bmp");
    edit:

    if you want to display the DIB in your form by painting it, why not just paint is as a DDB? why bother converting it to DIB.. only reason i can think of for a DIB is if you want to save it to a file, painting a DDB is much easier.

  10. #10
    Registered User jagi's Avatar
    Join Date
    Mar 2005
    Location
    Poland
    Posts
    21
    You have right... I'll do that

    epfTakeScreenShot Function works correctly, I tested it and display screens as DDB, but epfConvertDDBtoDIB don't work...
    [I'm from Poland]
    [Sorry for my English, because I just learn]

  11. #11
    Registered User jagi's Avatar
    Join Date
    Mar 2005
    Location
    Poland
    Posts
    21
    I have one more question. I want to display bitmaps using grayscale mask.
    I have 3 bitmaps.
    First - created by epfTakeScreenShot
    Second - mask bitmap from resource
    Third - bitmap to display with mask (from resource)

    I have pointers to bits to each other bitmap.

    And I must to make operation on red, green and blue color betwen first, second and third bitmap on any pixel. Where mask is black pixel won't be display, if white pixel will be display, and if gray pixel will be semi-transparent... Can you help me ?
    [I'm from Poland]
    [Sorry for my English, because I just learn]

  12. #12
    Registered User
    Join Date
    Aug 2003
    Posts
    288
    so basically:

    first + second = third?

    first being the image, second being the mask, and third being the product, right?

    you could use BitBlt with the SRCCOPY parameter and then with the SRCINVERT param..

  13. #13
    Registered User jagi's Avatar
    Join Date
    Mar 2005
    Location
    Poland
    Posts
    21
    first = bitmap
    second = mask
    third = background
    fourth = last effect

    I want to display bitmap using mask on every background

    I'm attaching example
    I tried to use ROP code but it doesn't work...
    I read somewhere, that to do it I have to use binary operations, but I don't know how...
    Last edited by jagi; 03-26-2005 at 05:38 AM.
    [I'm from Poland]
    [Sorry for my English, because I just learn]

  14. #14
    Registered User
    Join Date
    Aug 2003
    Posts
    288
    binary operations?

    well my guess would be, loop through all of the pixels in the mask, and everytime its a white pixel, you would set it to the corresponding pixel in the bitmap, but if its black you set it to the corresponding pixel in the background.. this is probably not the best way to do it, but i know it will work, BitBlt also works just as well...

    e.g.

    if pixel 10, 10 in the mask is black, set it to pixel 10, 10 of the background.. if pixel 11, 11 is white, set it to pixel 11, 11 of the bitmap.. etc

  15. #15
    Registered User jagi's Avatar
    Join Date
    Mar 2005
    Location
    Poland
    Posts
    21
    but what if 12x12 pixel in the mask is gray ? it doesn't work...
    [I'm from Poland]
    [Sorry for my English, because I just learn]

Popular pages Recent additions subscribe to a feed

Similar Threads

  1. DDB vs. DIB (Clarification needed)
    By Kurisu33 in forum Windows Programming
    Replies: 2
    Last Post: 08-27-2006, 07:22 PM
  2. DIB & DDB
    By samsam1 in forum Windows Programming
    Replies: 3
    Last Post: 01-19-2003, 12:33 PM
  3. How can I do the following..
    By Dual-Catfish in forum C++ Programming
    Replies: 14
    Last Post: 05-01-2002, 03:38 AM