[Okta Webinar] Learn how to a build a cloud-first strategyRegister Now

x
?
Solved

Wrong Background color on window capture.

Posted on 2005-04-21
21
Medium Priority
?
598 Views
Last Modified: 2013-12-03
I have a program that is capturing the screen of another program using the Window handle.
The image it's capturing is just a black and white image displayed in the window.
When I have the desktop on 16 bit or 24 bit color resulotion, I'm able to capture the screen just fine, but when I change the resulotion to 256 colors (8-bit), then the background comes out wrong.

The captured image ends up with a background that is grey or blue, even though the original window has a white background.

Here's the code I'm using, which saves a window image to a BMP file.

int WindowToBmpFile(LPCTSTR szFName, HWND hWnd)
{
      
      HDC      hDC,  hMemDC;
      HANDLE  hBits, hFile;
      HBITMAP  hBitmap, hTmpBmp;
      RGBQUAD  RGBQuad;
      DWORD    ImgSize, plSize, dwWritten;
      int      i, CRes, Height, Width;
      BITMAPFILEHEADER bmFH = {0x4d42, 0, 0,0};
      LPBITMAPINFO    pBmInfo, pBmInfoMem;
      
      if((hFile = CreateFile(szFName, GENERIC_WRITE,0,NULL,CREATE_ALWAYS,FILE_ATTRIBUTE_NORMAL,NULL)) == NULL) return 1;
      
      SetCursor(LoadCursor(NULL, IDC_WAIT));
      
      if(hWnd==HWND_DESKTOP)
      {
            Width  = GetSystemMetrics(SM_CXSCREEN);
            Height = GetSystemMetrics(SM_CYSCREEN);
      }
      else
      {
            RECT rc;
            ::GetClientRect(hWnd,&rc);
            Width  = rc.right-rc.left;
            Height = rc.bottom-rc.top;
      }
      
      hDC    = ::GetDC(hWnd);
      hMemDC = CreateCompatibleDC(hDC);
      hBitmap= CreateCompatibleBitmap(hDC, Width, Height);
      hTmpBmp= CreateCompatibleBitmap(hDC, 8, 8);
      pBmInfoMem = (LPBITMAPINFO)GlobalAlloc(GHND, sizeof(BITMAPINFO)+256*sizeof(RGBQUAD));
      pBmInfo = (LPBITMAPINFO)GlobalLock(pBmInfoMem);
      ZeroMemory(pBmInfo, sizeof(BITMAPINFO)+256*sizeof(RGBQUAD));
      SelectObject(hMemDC, hBitmap);
      BitBlt(hMemDC,0,0,Width,Height,hDC,0,0,SRCCOPY);
      SelectObject(hMemDC, hTmpBmp);
      
      pBmInfo->bmiHeader.biSize = (DWORD)sizeof(BITMAPINFOHEADER);
      pBmInfo->bmiHeader.biWidth      = Width;
      pBmInfo->bmiHeader.biHeight      = Height;
      pBmInfo->bmiHeader.biPlanes      = 1;
      pBmInfo->bmiHeader.biBitCount    = (WORD)GetDeviceCaps(hDC, BITSPIXEL);
      pBmInfo->bmiHeader.biCompression = BI_RGB;
      GetDIBits(hDC, hBitmap, 0,Height, NULL, pBmInfo, DIB_RGB_COLORS);
      if(!pBmInfo->bmiHeader.biSizeImage)
            pBmInfo->bmiHeader.biSizeImage = ((((pBmInfo->bmiHeader.biWidth * pBmInfo->bmiHeader.biBitCount) + 31) & ~31) / 8)
            * pBmInfo->bmiHeader.biHeight;
      CRes  = GetDeviceCaps(hDC, SIZEPALETTE);
      plSize = CRes*sizeof(RGBQUAD);
      ImgSize= pBmInfo->bmiHeader.biSizeImage;
      
      bmFH.bfOffBits  = plSize + sizeof(BITMAPINFOHEADER) + sizeof(BITMAPFILEHEADER);
      bmFH.bfSize      = ImgSize + bmFH.bfOffBits;
      
      WriteFile(hFile, &bmFH, sizeof(BITMAPFILEHEADER), &dwWritten, NULL);
      WriteFile(hFile, &(pBmInfo->bmiHeader), sizeof(BITMAPINFOHEADER), &dwWritten, NULL);
      
      if(CRes)
      {
            HPALETTE hPal;
            hPal=(HPALETTE)GlobalAlloc(GHND, sizeof(LOGPALETTE) + (CRes*sizeof(PALETTEENTRY)));
            LPLOGPALETTE    lp = (LPLOGPALETTE)GlobalLock(hPal);
            ZeroMemory(lp, sizeof(LOGPALETTE) + (CRes*sizeof(PALETTEENTRY)));
            lp->palNumEntries=(WORD)CRes;
            lp->palVersion=0x0300;
            GetSystemPaletteEntries(hDC, 0, CRes, lp->palPalEntry);
            RGBQuad.rgbReserved=0;
            for(i=0; i<CRes; i++)
            {
                  RGBQuad.rgbRed  = lp->palPalEntry[i].peRed;
                  RGBQuad.rgbGreen = lp->palPalEntry[i].peGreen;
                  RGBQuad.rgbBlue  = lp->palPalEntry[i].peBlue;
                  WriteFile(hFile, &RGBQuad, sizeof(RGBQUAD), &dwWritten, NULL);
            }
            GlobalUnlock(hPal);
            GlobalFree(hPal);
            GlobalUnlock(lp);
      }
      
      hBits = GlobalAlloc(GHND, pBmInfo->bmiHeader.biSizeImage);
      LPVOID  lpBits = (LPVOID)GlobalLock(hBits);
      ZeroMemory(lpBits, pBmInfo->bmiHeader.biSizeImage);
      GetDIBits(hDC, hBitmap, 0,Height, lpBits, pBmInfo, DIB_RGB_COLORS);
      WriteFile(hFile,lpBits,ImgSize,&dwWritten,NULL);
      GlobalUnlock(lpBits);

      GlobalUnlock(hBits);
      GlobalFree(hBits);
      
      GlobalUnlock(pBmInfo);
      GlobalFree(pBmInfoMem);
      DeleteObject(hTmpBmp);
      DeleteObject(hBitmap);
      DeleteDC(hMemDC);
      ::ReleaseDC(hWnd,hDC);
      CloseHandle(hFile);
      SetCursor(LoadCursor(NULL,IDC_ARROW));
      
      return 0;
}
0
Comment
Question by:Axter
  • 11
  • 10
21 Comments
 
LVL 8

Expert Comment

by:_corey_
ID: 13839506
I don't think you're using the right palette.  Since you're using DIB_RGB_COLORS to get the bitmap data, it should create the palette for you in the resulting BITMAPINFO structure.

corey
0
 
LVL 30

Author Comment

by:Axter
ID: 13839567
I kind of had the feeling that had something to do with it, but I don't have a clue as to how to fix it.

How would I fix the code so as to make it work with both 256 colors and 16/24 bit colors?
0
 
LVL 8

Expert Comment

by:_corey_
ID: 13839857
Well, for 256 colors, you should check for a value of 8 in the biBitCount member of BITMAPINFOHEADER structure.  Then, I believe you should write biClrUsed number of RGBQUAD values from the BITMAPINFO structure.  Currently you write these quads before you get the dibits.  Be sure to get the dibits first so the BITMAPINFO structure is filled out with the appropriate palette.

corey
0
Veeam and MySQL: How to Perform Backup & Recovery

MySQL and the MariaDB variant are among the most used databases in Linux environments, and many critical applications support their data on them. Watch this recorded webinar to find out how Veeam Backup & Replication allows you to get consistent backups of MySQL databases.

 
LVL 30

Author Comment

by:Axter
ID: 13843978
>>Well, for 256 colors, you should check for a value of 8 in the biBitCount member of BITMAPINFOHEADER structure.
That's getting set in the following line:
pBmInfo->bmiHeader.biBitCount    = (WORD)GetDeviceCaps(hDC, BITSPIXEL);

And
CRes  = GetDeviceCaps(hDC, SIZEPALETTE);
plSize = CRes*sizeof(RGBQUAD);


>>Then, I believe you should write biClrUsed number of RGBQUAD values from the BITMAPINFO structure.  Currently you write these
>>quads before you get the dibits.  Be sure to get the dibits first so the BITMAPINFO structure is filled out with the appropriate palette.

I'm not sure what you're referring to here.  Can you be more specific, with exact position in the example code?
0
 
LVL 8

Expert Comment

by:_corey_
ID: 13844114
Axter,

  Sorry for the confusion, hopefully I can test this soon when I am able to setup 256-color mode for a bit:

>>>Well, for 256 colors, you should check for a value of 8 in the biBitCount member of BITMAPINFOHEADER structure.
>>That's getting set in the following line:
>>pBmInfo->bmiHeader.biBitCount    = (WORD)GetDeviceCaps(hDC, BITSPIXEL);

  That's probably fine.


>>And
>>CRes  = GetDeviceCaps(hDC, SIZEPALETTE);
>>plSize = CRes*sizeof(RGBQUAD);


>>>Then, I believe you should write biClrUsed number of RGBQUAD values from the BITMAPINFO structure.  Currently you write these
>>>quads before you get the dibits.  Be sure to get the dibits first so the BITMAPINFO structure is filled out with the appropriate palette.

Ah, this is where I think it should change.  Instead of checking the device caps like that, I would take the value in the BITMAPINFO structure taken at

hBits = GlobalAlloc(GHND, pBmInfo->bmiHeader.biSizeImage);
LPVOID  lpBits = (LPVOID)GlobalLock(hBits);
ZeroMemory(lpBits, pBmInfo->bmiHeader.biSizeImage);
GetDIBits(hDC, hBitmap, 0,Height, lpBits, pBmInfo, DIB_RGB_COLORS);
WriteFile(hFile,lpBits,ImgSize,&dwWritten,NULL);
GlobalUnlock(lpBits);

However, currently you're obtaining this after you get and write the RGBQUAD palette.  I would read this before, and if you're in 256-color mode, then I would write the values in the BITMAPINFO RGBQUAD array as the palette.  

I mention the biClrUsed member of that BITMAPINFO output because in 256-color mode it should give you the proper number of RGBQUADs in that BITMAPINFO structure.  I just thought it'd be a safe way to access the proper amount of memory.

corey

0
 
LVL 30

Author Comment

by:Axter
ID: 13844192
In testing the code, I find the only difference in flow logic between 256 colors and 16/24 bits, is that the CRes  value is non-zero in the 256 color mode.
And therefore, it jumps into the follownig if condition only on the 256 color mode:
      if(CRes)
      {
            HPALETTE hPal;
            hPal=(HPALETTE)GlobalAlloc(GHND, sizeof(LOGPALETTE) + (CRes*sizeof(PALETTEENTRY)));
            LPLOGPALETTE    lp = (LPLOGPALETTE)GlobalLock(hPal);
            ZeroMemory(lp, sizeof(LOGPALETTE) + (CRes*sizeof(PALETTEENTRY)));
            lp->palNumEntries=(WORD)CRes;
            lp->palVersion=0x0300;
            int NumEntriesRetriev = GetSystemPaletteEntries(hDC, 0, CRes, lp->palPalEntry);
            RGBQuad.rgbReserved=0;
            for(i=0; i<CRes; i++)
            {
                  RGBQuad.rgbRed  = lp->palPalEntry[i].peRed;
                  RGBQuad.rgbGreen = lp->palPalEntry[i].peGreen;
                  RGBQuad.rgbBlue  = lp->palPalEntry[i].peBlue;
                  WriteFile(hFile, &RGBQuad, sizeof(RGBQUAD), &dwWritten, NULL);
            }
            GlobalUnlock(hPal);
            GlobalFree(hPal);
            GlobalUnlock(lp);
      }

So I'm thinking that something in that section of the code is causing the problem.
I'm wondering if the GetSystemPaletteEntries should be replace with something that gets the palette for the specific window instead of teh system palette.
I'm not sure if there is a difference, or if there is an API function to get just a specific Window palette.
0
 
LVL 8

Expert Comment

by:_corey_
ID: 13844477
Yea, you shouldn't be using some logical palette I don't think since you're retrieving the bitmap values using DIB_RGB_COLORS.  This should mean that the BITMAPINFO structure was filled out with the on-the-fly created palette for you to use.

corey
0
 
LVL 8

Expert Comment

by:_corey_
ID: 13844957
Axter,

  Unfortunately, I'm unable to go into 256-color mode to test this.

corey
0
 
LVL 30

Author Comment

by:Axter
ID: 13845073
Well, now I have some code I pulled from CodeGuru, which works with 256 colors and 24-bits, but does not work for 16-bit color settings.
See following link:
http://www.codeguru.com/Cpp/G-M/bitmap/capturing/article.php/c4919/

What I think I'm going to do is just add logic to the function to call the CodeGuru function if it detects 256 color mode.
0
 
LVL 8

Expert Comment

by:_corey_
ID: 13845278
Interesting, it will require some more analysis later to find all the correlations between the bmp read/write and window read/write, but I did see one thing.

In their BITMAPFILEHEADER structure, their offset size included the RGBQUAD array which I don't think you do?

corey
0
 
LVL 30

Author Comment

by:Axter
ID: 13845338
Are you talking about the following:
hdr.bfOffBits=(DWORD) sizeof(MyHeaderU.hdr) + nColors * sizeof(RGBQUAD);
0
 
LVL 30

Author Comment

by:Axter
ID: 13845429
>>RGBQUAD array which I don't think you do?

It does in the following:
      plSize = CRes*sizeof(RGBQUAD);
      ImgSize= pBmInfo->bmiHeader.biSizeImage;
      
      bmFH.bfOffBits  = plSize + sizeof(BITMAPINFOHEADER) + sizeof(BITMAPFILEHEADER);
      bmFH.bfSize      = ImgSize + bmFH.bfOffBits;

However, mine has the extra sizeof(BITMAPINFOHEADER)
I also notice that the bfSize  value is getting computed differently.
0
 
LVL 8

Expert Comment

by:_corey_
ID: 13846278
Yea, but see their code includes the bmiColors section of BITMAPINFO too.

I really wish I could prove this more and create a test program.

It seems to me that despite all that you could be creating the bitmap properly ( I haven't seen your output of 256 yet).  But I think you're writing the system palette to the bitmap header while the values in the bitmap are indicies into the palette originally created when you called GetDIBits the second time with DIB_RGB_COLORS.

corey
0
 
LVL 30

Author Comment

by:Axter
ID: 13846317
>>I really wish I could prove this more and create a test program
Why is it that you can't change to 256-color mode?

In my test code, I have it change automatically to 256 color mode via following wrapper function:

bool SetDesktopScreenColorResulotion(DWORD BitsPerPel, DWORD PelsWidth = 1024, DWORD PelsHeight = 768)
{
      DEVMODE my_DEVMODE;
      EnumDisplaySettings(NULL, ENUM_CURRENT_SETTINGS, &my_DEVMODE);
      my_DEVMODE.dmBitsPerPel            = BitsPerPel;
      if (my_DEVMODE.dmPelsWidth < PelsWidth) my_DEVMODE.dmPelsWidth            = PelsWidth;
      if (my_DEVMODE.dmPelsHeight < PelsHeight) my_DEVMODE.dmPelsHeight            = PelsHeight;
      return (ChangeDisplaySettings(&my_DEVMODE, CDS_FULLSCREEN) == DISP_CHANGE_SUCCESSFUL);
}



To change it to 256 colors, just call
SetDesktopScreenColorResulotion(8);

What's nice about this, is that when the program exit's, the resultion automatically changes back to original settings.
0
 
LVL 8

Expert Comment

by:_corey_
ID: 13848425
Axter,

  Yes, I should have just programmed the resolution change from the beginning but as I never used 256 color mode on this machine and didn't see it in the display settings i figured it wasn't available until I checked the resolution enumerations.

  This works for me now.  You'll see that I hardcoded 256 written RGBQUAD's.  This is because I'm getting a value of 0 in biClrUsed which is unusual for 8-bit mode according to docs.

--

int WindowToBmpFile(LPCTSTR szFName, HWND hWnd)
{
     
     HDC      hDC,  hMemDC;
     HANDLE  hBits, hFile;
     HBITMAP  hBitmap, hTmpBmp;
     RGBQUAD  RGBQuad;
     DWORD    ImgSize, plSize, dwWritten;
     int      i, CRes, Height, Width;
     BITMAPFILEHEADER bmFH = {0x4d42, 0, 0,0};
     LPBITMAPINFO    pBmInfo, pBmInfoMem;
     
     if((hFile = CreateFile(szFName, GENERIC_WRITE,0,NULL,CREATE_ALWAYS,FILE_ATTRIBUTE_NORMAL,NULL)) == NULL) return 1;
     
     SetCursor(LoadCursor(NULL, IDC_WAIT));
     
     if(hWnd==HWND_DESKTOP)
     {
          Width  = GetSystemMetrics(SM_CXSCREEN);
          Height = GetSystemMetrics(SM_CYSCREEN);
     }
     else
     {
          RECT rc;
          ::GetClientRect(hWnd,&rc);
          Width  = rc.right-rc.left;
          Height = rc.bottom-rc.top;
     }
     
     hDC    = ::GetDC(hWnd);
     hMemDC = CreateCompatibleDC(hDC);
     hBitmap= CreateCompatibleBitmap(hDC, Width, Height);
     //hTmpBmp= CreateCompatibleBitmap(hDC, 8, 8);
     pBmInfoMem = (LPBITMAPINFO)GlobalAlloc(GHND, sizeof(BITMAPINFO)+256*sizeof(RGBQUAD));
     pBmInfo = (LPBITMAPINFO)GlobalLock(pBmInfoMem);
     ZeroMemory(pBmInfo, sizeof(BITMAPINFO)+256*sizeof(RGBQUAD));
     SelectObject(hMemDC, hBitmap);
     BitBlt(hMemDC,0,0,Width,Height,hDC,0,0,SRCCOPY);
     //SelectObject(hMemDC, hTmpBmp);
     
     pBmInfo->bmiHeader.biSize = (DWORD)sizeof(BITMAPINFOHEADER);
     pBmInfo->bmiHeader.biWidth      = Width;
     pBmInfo->bmiHeader.biHeight      = Height;
     pBmInfo->bmiHeader.biPlanes      = 1;
     pBmInfo->bmiHeader.biBitCount    = (WORD)GetDeviceCaps(hDC, BITSPIXEL);
     pBmInfo->bmiHeader.biCompression = BI_RGB;
     pBmInfo->bmiHeader.biClrUsed = 256;
     GetDIBits(hDC, hBitmap, 0,Height, NULL, pBmInfo, DIB_RGB_COLORS);
     if(!pBmInfo->bmiHeader.biSizeImage)
          pBmInfo->bmiHeader.biSizeImage = ((((pBmInfo->bmiHeader.biWidth * pBmInfo->bmiHeader.biBitCount) + 31) & ~31) / 8)
          * pBmInfo->bmiHeader.biHeight;
     CRes  = GetDeviceCaps(hDC, SIZEPALETTE);
     plSize = CRes*sizeof(RGBQUAD);
     ImgSize= pBmInfo->bmiHeader.biSizeImage;
     
     bmFH.bfOffBits  = plSize + sizeof(BITMAPINFOHEADER) + sizeof(BITMAPFILEHEADER);
     bmFH.bfSize      = ImgSize + bmFH.bfOffBits;
     
     WriteFile(hFile, &bmFH, sizeof(BITMAPFILEHEADER), &dwWritten, NULL);
     WriteFile(hFile, &(pBmInfo->bmiHeader), sizeof(BITMAPINFOHEADER), &dwWritten, NULL);
     
     hBits = GlobalAlloc(GHND, pBmInfo->bmiHeader.biSizeImage);
     LPVOID  lpBits = (LPVOID)GlobalLock(hBits);
     ZeroMemory(lpBits, pBmInfo->bmiHeader.biSizeImage);
     GetDIBits(hDC, hBitmap, 0,Height, lpBits, pBmInfo, DIB_RGB_COLORS);

     if(CRes)
     {
//          HPALETTE hPal;
//          hPal=(HPALETTE)GlobalAlloc(GHND, sizeof(LOGPALETTE) + (CRes*sizeof(PALETTEENTRY)));
//          LPLOGPALETTE    lp = (LPLOGPALETTE)GlobalLock(hPal);
//          ZeroMemory(lp, sizeof(LOGPALETTE) + (CRes*sizeof(PALETTEENTRY)));
//          lp->palNumEntries=(WORD)CRes;
//          lp->palVersion=0x0300;
//          GetSystemPaletteEntries(hDC, 0, CRes, lp->palPalEntry);
//          RGBQuad.rgbReserved=0;
//          for(i=0; i<CRes; i++)
//          {
//               RGBQuad.rgbRed  = lp->palPalEntry[i].peRed;
//               RGBQuad.rgbGreen = lp->palPalEntry[i].peGreen;
//               RGBQuad.rgbBlue  = lp->palPalEntry[i].peBlue;
            WriteFile(hFile, pBmInfo->bmiColors, 256 * sizeof(RGBQUAD), &dwWritten, NULL);
//          }
//          GlobalUnlock(hPal);
//          GlobalFree(hPal);
//          GlobalUnlock(lp);
     }
     
     WriteFile(hFile,lpBits,ImgSize,&dwWritten,NULL);
     GlobalUnlock(lpBits);

     GlobalUnlock(hBits);
     GlobalFree(hBits);
     
     GlobalUnlock(pBmInfo);
     GlobalFree(pBmInfoMem);
     //DeleteObject(hTmpBmp);
     DeleteObject(hBitmap);
     DeleteDC(hMemDC);
     ::ReleaseDC(hWnd,hDC);
     CloseHandle(hFile);
     SetCursor(LoadCursor(NULL,IDC_ARROW));
     
     return 0;
}


corey
0
 
LVL 8

Accepted Solution

by:
_corey_ earned 2000 total points
ID: 13852158
Does the above code work for you too, Axter?

corey
0
 
LVL 30

Author Comment

by:Axter
ID: 13852248
I'm going to wait until Monday to test this out.
I rather tested it in an environment that I'm sure it previously failed in.
0
 
LVL 8

Expert Comment

by:_corey_
ID: 13852600
Ok, no problem.  Maybe I can find a reason why colors used is 0 in this case.  

I have just realized my mistake in that!  The clause is that 0 means maximum values (or 256) in this case.  I got that mixed up with another >8bit case, sorry.

So, instead of:

WriteFile(hFile, pBmInfo->bmiColors, 256 * sizeof(RGBQUAD), &dwWritten, NULL);

I should have used (and did in my tests but forgot the meaning of 0):

int palSize = (pBmInfo->bmiHeader.biClrUsed == 0) ? 256 : pBmInfo->bmiHeader.biClrUsed;
WriteFile(hFile, pBmInfo->bmiColors, palSize * sizeof(RGBQUAD), &dwWritten, NULL);


Sorry about the confusion!

corey
0
 
LVL 30

Author Comment

by:Axter
ID: 13861334
Thanks _corey_

I wasted too many hours trying to figure this out, but failed to come up with anything.

I just tested the code in 256, 16-bit, and 24-bit.  It works great now on all three.

Thanks again
0
 
LVL 8

Expert Comment

by:_corey_
ID: 13861352
No problem, glad to give back some.

Bitmaps are probably one of the simplest and trickiest items in imaging :)  You see what I did, I hope?

corey
0
 
LVL 30

Author Comment

by:Axter
ID: 13861443
>>You see what I did, I hope?

Yes.  I did a file DIF with Visual Sorce safe, and was able to see what changed.

One thing that initially through me off, is that when I looked at bmiColors type, I notice it was not in RGB order.
typedef struct tagRGBQUAD {
        BYTE    rgbBlue;
        BYTE    rgbGreen;
        BYTE    rgbRed;
        BYTE    rgbReserved;
} RGBQUAD;

It's in BGR order instead of RGB order.  And I completely overlooked that the type was RGBQUAD.
So before saving the data to the file, I tried to rearange the colors so that they were in RGB order, thinking that RGBQUAD was a different type from that of bmiColors.

After looking at your code changes, I realize they're the same type, and I'm surprise it works that way.


0

Featured Post

Fill in the form and get your FREE NFR key NOW!

Veeam is happy to provide a FREE NFR server license to certified engineers, trainers, and bloggers.  It allows for the non‑production use of Veeam Agent for Microsoft Windows. This license is valid for five workstations and two servers.

Question has a verified solution.

If you are experiencing a similar issue, please ask a related question

This article describes how to add a user-defined command button to the Windows 7 Explorer toolbar.  In the previous article (http://www.experts-exchange.com/A_2172.html), we saw how to put the Delete button back there where it belongs.  "Delete" is …
What my article will show is if you ever had to do processing to a listbox without being able to just select all the items in it. My software Visual Studio 2008 crystal report v11 My issue was I wanted to add crystal report to a form and show…
This is Part 3 in a 3-part series on Experts Exchange to discuss error handling in VBA code written for Excel. Part 1 of this series discussed basic error handling code using VBA. http://www.experts-exchange.com/videos/1478/Excel-Error-Handlin…
Please read the paragraph below before following the instructions in the video — there are important caveats in the paragraph that I did not mention in the video. If your PaperPort 12 or PaperPort 14 is failing to start, or crashing, or hanging, …

873 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question