Pallium

Bryan Ressler


Home
Studio
Synth Stuff
HOWTOs
Music
Programming
Downloads

Pallium

Programming Stuff

Here's a collection of topical programming wisdom that I keep. My operational definition of wisdom is something that took some me time to figure out, and I really don't want to figure it out again in the future. And I apparently don't have enough spare brain cells to remember the solutions to things like these, only enough to store the faint association "I'm sure this happened to me before, but how the heck did I fix it?"

So wisdom is practical problem-solving information. I update this whenever I encounter a non-project-specific bit of wisdom, so bookmark it if you like.

(If the questions seem a bit contrived or overwordy, sorry about that - I've phrased things for maximum search-engine-friendliness.)

Wisdom Index

Windows Visual C++ | Windows Programming | WTL | STL | HTML | UNIX Commands

Windows Visual C++ Wisdom

  • You're getting compile errors on MENUINFO structures, etc.
    • Install the Windows Platform SDK and point Tools/Options/Directories at its includes and libs (before the VC++ ones)

  • Your optimized build is crashing.
    • Start the #if 0 shuffle.
    • If you recently added any sscanfs, make sure the argument sizes on the stack match the format string (e.g. %u = UInt32).

  • Visual Studio INSISTS that some of my project files are in SourceSafe.
    • Go to where the VSS repository is, find the <my-username> folder, and delete the .ini file inside there. Rerun VC++, say no to the "connect next time" dialogs, and save the workspace. All should then be well.

  • You're getting weird compile error on a line that appears to be legit.
    • Like: HWND ctl1 = GetDlgItem(IDC_BT_GO_FOR_IT);
      Too bad for you Windows #defines ctl1 as some hex number, which is messing up your build. Just rename the variable and you're set.

  • Saving in VC++ takes for-bleeping-ever!
    • Do a Ctrl-B and delete all your breakpoints Homes!

  • Link fails for some subproject but I've got the latest source.
    • Try full-building that subproject.

  • Where the hell is that file that defines what you see in the debugger again?
    • For Visual Studio 6:
      C:\Program Files\Microsoft Visual Studio\Common\MSDev98\Bin\autoexp.dat
    • For Visuall Studio .NET:
      C:\Program Files\Microsoft Visual Studio .NET\Common7\Packages\Debugger\autoexp.dat

  • My Visual Studio .NET IDE is crashing immediately when I try to build. WTF?
    • Find and nuke .ncb files.
    • Find and nuke .idb files.
    • Run a second IDE and attach to the first IDE's process. Start the build in the first IDE and let it crash. In the second IDE, use a memory window to look in the memory around where each register points. Above one of them (that is, at a smaller address) you might find a UNICODE string with some file name in it. That's your clue. Try finding that and nuking it, since it's probably corrupt. How do you think I came up with the two solutions above? :-P

  • I'm using a std::map<std::string, int> (or something like that) and I'm getting a load of C4786 warnings when I compile. Basically various template expansion causes symbols that are too big (> 255 charactes) for the debug browse file. How do I get rid of these annoying warnings?
    • Naturally you'd think a simple #pragma warning(disable: 4786) would solve the problem. Too bad there's a bug in VC++ 6 that causes this to not (always) suppress the warning. I've found that if I add that pragma before I #include <map> it reduces, but does not eliminate, these warnings.
    • The only way I was able to get rid of these completely was to stop using std::strings in maps. For instance, you probalby could change the template above to std::map<const char*, int>, and depending on what your code is doing, you might not need to change anything else. This all stems from std::string, after filling in its default template parameters, becoming a really long typename.

  • Some useful regex's:
    • Find command definitions:
      \#define[ \t]+k[A-Za-z0-9]+Command[ \t]+

Windows Programming Wisdom

  • Your dialog shows up completely blank
    • You copied and pasted and didn't change the IDD = IDD_xxx constant
    • Your BEGIN_MESSAGE_MAP_EX(xxx) macro doesn't contain the right class name
    • (see below)

  • Your dialog doesn't show up at all
    • You added your resource alright, but you didn't rebuild the .rc file. (You dumbass!)

  • You're doing run-time window style manipulation and it doesn't seem to take
    • Check the resource file - probably you've got checks in the styles you're trying to inhibit or whatever.

  • Quick function reference:
    • How much memory does the machine have?
      ::GlobalMemoryStatus and ::GlobalMemoryStatusEx
    • How do you get that choose folder dialog again?
      ::SHBrowseForFolder, but don't; use WTL's CFolderDialog instead
    • Determine drive free space?
      ::GetDiskFreeSpace or ::GetDiskFreeSpaceEx
    • Determine Windows version:
      ::GetVersionEx
    • Determine instantaneous mouse position
      ::GetCursorPos
    • Determine instantaneous key state
      ::GetAsyncKeyState

  • How do I force a thread to have a message queue?
    • Simply call ::PeekMessage() once, and a message queue will be created

  • Clear calls to my read-only Edit Control aren't clearing. Sup?
    • Changing ES_READONLY in the style of the control doesn't work. Instead, use SetReadOnly(kED_ReadWrite), clear, SetReadOnly(k_EDReadOnly). That uses the EM_SETREADONLY message to change the attribute.

  • I'm using weird ROPs for PatBlit like DPa, DPo, and DPno, but since I started painting some of my source black, now the pattern obliterates the destination? WTF?
    • PatBlt is sensitive to the background color. You probably didn't set it at all, so you're getting the default black. Do a dc.SetBkColor(RGB(255, 255, 255)) call before your PatBlts and all should be well.

  • I'm trying to create a bitmap that's compatible with the screen and I keep getting one that's 1-bit deep!
    • You're probably passing your memory DC into CreateCompatibleBitmap. Do it this way instead (the ::GetDC(NULL) being the important part):
      CDC dc;
      CBitmap bm;
      dc.CreateCompatibleDC(NULL);
      bm.CreateCompatibleBitmap(::GetDC(NULL), width, height);
      HBITMAP oldBitmap = dc.SelectBitmap(bm);
      ...
  • My child window isn't getting WM_PAINT messages at all!
    • Either your window's client rectangle is in space, or
    • Your window is invisible, Homes! Windows are NOT visible by default, so if you created it without specifying WS_VISIBLE, it will be invisible and require a ShowWindow or SetWindowPos call to make it visible.

  • I'm adding a dialog template to the end of a GetOpenFileDialog (or a GetSaveFileDialog). (This goes for subclassing MFC's CFileDialog or WTL's CFileDialogImpl<> too.) I've got OFN_ENABLETEMPLATE set and my items show up ok, but there's weird painting anomalies and many of the main dialog's items get erased but then don't draw. WTF?
    • In the dialog template you're adding, set the WS_CLIPSIBLINGS flag (and while you're at it, DS_CONTROL as well).

  • I've got a modeless dialog I'm trying to create with CreateDialog, CreateDialogIndirect, or CreateDialogParam. But the function returns NULL, and GetLastError() returns 0 (how helpful). How do I figure out what the problem is with the dialog?
    • Generally this occurs because the creation of some child control of the dialog failed. But which one?
      • If you've got an RTF control, chances are, that's your man. Try deleting that control from your dialog template and re-running the application. If it works, make sure you're defining the right version constant for the 2.0 RTF control (e.g. _RICHEDIT_VER should be set to 0x0200 or more), and you did the necessary LoadLibrary call at startup.
      • Another approach to finding the baddie is to allow creation of the dialog to continue even though one or more child controls could not be created. To achieve this, add the DS_NOFAILCREATE style to your dialog template (it's called "No Fail Create" in the VS.NET IDE), and re-run your app. You'll still crash, but hopefully later when you try to do something with some dialog child control, and it isn't there. Then see if you can determine the dialog item ID from that, and you've found your baddie. Or, now that you've got the dialog's window handle, fire up Spy++ and walk the children until one you're expecting is missing: dat's da baad mon.

  • I'm trying to handle CDN_INCLUDEITEM notifications to leave out some files in a GetOpenFileDialog (or GetSaveFileDialog). I get the messages, and I do what the SDK says, but files I reject get included in the list anyway. WTF?
    • CDN_INCLUDEITEM doesn't do what you think. Your result is ignored for all file and folder objects, by design. Why? No idea. Instead, hack the list yourself. Here's some code to get you started. Put this in your handler for CDN_FOLDERCHANGE. The code shown here uses WTL. The unbelievably obscure constants like lst2 are defined in <dlgs.h>.
      // Obtain the HWND of the list. Current directory has kindly already been
      // set, so we can just stat files without full paths.
      CWindow dlg = GetFileDialogWindow();
      CWindow shellView(dlg.GetDlgItem(lst2));
      CListViewCtrl listView = ::FindWindowEx(shellView, NULL, WC_LISTVIEW, NULL);
      ATLASSERT(listView != NULL);
      
      // Iterate through the list backwards, since we'll be deleteing stuff.
      int count = listView.GetItemCount();
      for (int i = count - 1; i >= 0; i--)
      {
          CString text;
          listView.GetItemText(text);
          struct _stat itemStat;
          _stat(text, &itemStat);
          if ((itemStat.st_mode & _S_IFREG) != 0)
          {
              // Item represents a regular file... now decide what to do
              if (I dislike this file)
                  listView.DeleteItem(i);
          }
      }
  • I subclassed a static text item (LTEXT, RTEXT, CONTROL "Static" etc.) and created a tooltip control to show a nice tool tip for the static. I activated my tooltip control and I'm relaying messages like a good by with RelayEvent, but my tooltip isn't showing up. WTF?
    • In the resource file, make sure the SS_NOTIFY flag is set.

  • I'm using Windows Timers in my code (i.e. ::SetTimer, ::KillTimer, etc.) Everything was working great, then I added this new child window and all my timers stopped working! It's like they were never started in the first place. Neither WM_TIMER messages come in nor do timer callback functions get called. The timers just don't fire! What happened?
    • Take a look at your new window's WM_PAINT handler. If you've got something that looks like this:
      LRESULT MyWindow::OnPaint(HDC hdc)
      {
          // TODO I'll get to coding this window painting stuff later...
          
          return 0;
      }
      Believe it or not, that code will prevent your timers from working. The problem is that no BeginPaint/EndPaint is ever happening for this window, so the flag that Windows keeps indicating the window needs painting stays set. This, as a side effect, precludes timers from working. Go figure. If you simply change the implementation to have one extra line:
      LRESULT MyWindow::OnPaint(HDC hdc)
      {
          CPaintDC dc(m_hWnd);
          
          // TODO I'll get to coding this window painting stuff later...
          
          return 0;
      }
      then your timers will suddenly start working again. (I used WTL in the above example. CPaintDC just does a BeginPaint in the constructor and an EndPaint in the destructor.)

  • My ActiveX control is drawing a bunch of stuff in Gdiplus (aka GDI+) and nothing draws. The GDI+ routines all return Ok. My code draws offscreen and it just blits an all-black bitmap. When I hacked the code to draw on-screen, it doesn't draw anything! I've confirmed there are no clipping problems or anything like that by putting a little GDI code in there, that works fine! WTF?
    • This happened to me when I wrote a handy function to convert an OLE_COLOR into a COLORREF (by calling ::OleTranslateColor). My handy function returned happy RGB COLORREFs, and I used them in GDI+ calls. Too bad for me that in fact C++ was constructing Color objects for me from the DWORD COLORREFs. See the problem yet? All the colors I was drawing with were set to alpha = 0, which means fully transparent. Nice. So I just modified the conversion routine to construct and return a Color object with alpha 255 and all was well:
      Color OleToRgb(const OLE_COLOR& ole)
      {
          COLORREF result = 0;
          ::OleTranslateColor(ole, NULL, &result);
          return Color(255, GetRValue(result), GetGValue(result), GetBValue(result));
      }

  • I'm creating a menu at runtime (using ::InsertMenuItem) and my menu items show up in a bold font. Why the bold menu items? I didn't even think that was possible...
    • When you're creating the menu items, in the MENUITEMINFO structure, you are specifying MFS_DEFAULT as the menu item state (fState). While that seems entirely reasonable, it causes bold menu items. Use MFS_ENABLED and your menus will appear as expected.

  • I've got a worker thread that needs to report progress. It needs to set the text of a STATIC window owned by the UI thread. I know I can't SendMessage to the STATIC from my background thread, so I thought I'd use PostMessage instead. But what is the right way to allocate memory for the window text (LPARAM) parameter of the WM_SETTEXT call?
    • The question is moot because PostMessage always fails for Windows messages that take pointers as parameters. So you can't PostMessage a WM_SETTEXT no matter how you allocate the string.

      Instead you'll have to post a custom Windows message to your main thread with the progress information you want to convey, and that way you can decide how the memory is to be allocated. You weren't going to use a pointer to a buffer on the stack, or a local CString were you? Because those pointers will be invalid by the time the UI thread processes the message - I'd suggest ::SysAllocStringLen string (or the analogous CString::AllocSysString) for the text you're passing.

WTL Wisdom

  • My WTL project doesn't compile at all - headers appear missing
    • You need to go to Tools > Options, Directories tab and add the path to the WTL includes (usually %mssdk%/Src/WTL/Include).

  • I get compile errors about min(x,y) and max(x,y).
    • Open your precompiled header file (usually stdafx.h). You'll see some includes, then an extern CAppModule _Module;, then some more includes. Add #include <minmax.h> to the FIRST list of includes (the ones above the extern), but after the atl includes that are there. Full-build. Rejoice.
    • I have, unfortunately, had to modify stdafx.cpp to have the following lines at the top:
      #undef min
      #undef max
      #define max(a,b)            (((a) > (b)) ? (a) : (b))
      #define min(a,b)            (((a) < (b)) ? (a) : (b))

  • I'm building a WTL7 application and some of the include files #include <atlcrack.h> and use BEGIN_MESSAGE_MAP_EX(). But when I compile I get an error stating that SetMsgHandled and IsMsgHandled are undefined, as if I used just BEGIN_MESSAGE_MAP. What gives?
    • Move the include of atlcrack.h higher in the list of includes. The atlcrack.h header doesn't depend on any other WTL/ATL headers, so you can put it first. That will probably solve your problem.

  • I'm crashing in DlgResize_Init() (part of CDialogResize<>) Why?
    • Most likely, you've got an ID in your RESIZE_MAP that doesn't exist in the dialog. Maybe the symbol is defined, but no control with that ID is really in the dialog.

  • I'm calling SetCapture in the LBUTTONDOWN handler of a CContainedWindow, but the LBUTTONUP never gets called, so capture is never released!
    • You probably made a bare SetCapture call in the outer (containing) window, which sets capture for it, not the contained window. Do mContainedWindow.SetCapture() instead.

  • Idiom: Calling a superclass method
    • Say you are CMyView and you subclass CFrameWindowImpl. You want to call CFrameWindowImpl's PreTranslateMessage. Here's how:
      BOOL result = CFrameWindowImpl<CMyView>::PreTranslateMessage(pMsg);
    • Say you want to make a simpler Create() method. Here's how:
      HWND Create(HWND hWndParent)
      {
          CWindowImpl<CMruList, CListBox>::Create(hWndParent, rcDefault, NULL,
          	WS_POPUP | WS_THICKFRAME | WS_CLIPCHILDREN | WS_CLIPSIBLINGS |
          	WS_VSCROLL | LBS_NOINTEGRALHEIGHT, WS_EX_CLIENTEDGE);
          if(IsWindow())
          	SetFont(AtlGetStockFont(DEFAULT_GUI_FONT));
          return m_hWnd;
      }

  • I'm using WTL's CFileDialog and I'm overriding OnFileOK (which handles the CDN_ONFILEOK notification). Per the SDK docs, I'm returning 1 and doing SetWindowLong(DWL_MSGRESULT, 1) for files that I want to reject, which is supposed to keep the dialog up. But it gets closed anyway! WTF?
    • Do not ask me why, but WTL's internal function _OnFileOK negates the result of your OnFileOK function before returning. So you get the opposite of what the SDK says. Just return 0 for files you want to reject, and 1 for files you want to keep. I suspect Nenad was thinking you should return TRUE when you want to say "this file's OK", but it only created trouble for me. (P.S. Nenad is God anyway.)

  • I'm crashing when I call CTreeViewCtrl[Ex]::InsertItem. ATL asserts saying my dialog object is being deleted before the window is destroyed (as if maybe the stack got corrupted). I can trace right up to the ::SendMessage in WTL and then I crash there. WTF?
    • When you insert an item into a tree, other stuff may happen. For instance, your new tree item will get drawn. If you're using custom draw (with CCustomDraw<>, for instance), and your custom draw procedures have a bug, you'll appear to crash at the InsertItem. Just click Ignore in ATLs assert and then you'll find out where the real problem is.

  • I'm using WTL 7.5 with ATL 7.1 and I'm getting compile errors about CString being an ambiguous symbol (could be ATL, could be WTL). How do I fix this?
    • This is because someone did a #include <atlstr.h>, and that later crashes into WTL's CString in #include <atlmisc.h>. But who?

      The usual culprit: _ATL_ATTRIBUTES. If that's defined, atlbase.h will pull in atlstr.h and you'll see this problem. Make sure it's not defined in Project > Properties > C/C++ > Preprocessor > Preprocessor Definitions.


  • My message map won't compile! I'm getting this obscure error: "error C4430: missing type specifier - int assumed. Note: C++ does not support default-int". Here's my lovely message map:
        BEGIN_MESSAGE_MAP_EX(CMyClass)
            CHAIN_MSG_MAP_ALT(COwnerDraw, 1)
            DEFAULT_REFLECTION_HANDLER()
        END_MESSAGE_MAP()
    • Um, check those macros. The macro for extended message maps is BEGIN_MSG_MAP_EX, and to end them, END_MSG_MAP. You've typed "MESSAGE" out.

  • My control class acts weird. It creates OK, but afterward it doesn't draw right, and it doesn't respond to mouse clicks. What did I do wrong?
    • One thing to look for: If you're handling WM_CREATE, make sure your OnCreate method is doing SetMsgHandled(FALSE) (or if you're using old-style message maps set bHandled = FALSE). You generally need to let DefWindowProc do its thing on a WM_CREATE.

  • My WTL app hangs at exit. I see the WM_CLOSE come into the main frame window, but somewhere after that I hang. Ideas?
    • Are you handling WM_DESTROY? Your OnDestroy handler needs to do a SetMsgHandled(FALSE) or you'll hang at app exit.
    • Another source of application hangs (though not particularly at exit) is a WM_PAINT handler that doesn't do BeginPaint/EndPaint. If you just create a CPaintDC(m_hWnd) on the stack and return 0, that's good enough -- CPaintDC does the BeginPaint/EndPaint for you. But if you don't do anything in there, your window still seems to need painting, so you'll keep getting WM_PAINTs ad nauseum.

  • I'm using one of those groovy WTL splitters (a CSplitterWindow, CSplitterWindowImpl, or CSplitterImpl). It's cool, but redrawing my left/right pane windows is expensive. Can I get the WTL splitter to draw a ghost bar and then update the layout of my pane windows when the user releases the mouse button?
    • Ghost-bar drawing is built into CSplitterImpl, and it is controlled by the value of m_bFullDrag. Unfortunately for you, that boolean is set based on a query to GetSystemParametersInfo, and it happens during creation (or again whenever system settings change). You can't override CSplitterImpl::GetSystemSettings() either (your override will never get called). But, you can set m_bFullDrag to false when the left mouse button goes down, which will give you the effect you want. A simple way to do that is to override SetCapture in your derived class:
      HWND SetCapture()
      {
          HWND result = base::SetCapture();
          m_bFullDrag = false;
          return result;
      }
  • I've got a CWindowImpl-derived window and I'm destroying it like a good boy by calling its DestroyWindow method. But later when its destructor rolls around, ATL asserts with "ERROR - Object deleted before window was destroyed". I did destroy it! Why the assert?
    • Guess what! CWindowImplBaseT::DestroyWindow doesn't set your m_hWnd to NULL! Seems like an oversight in ATL. So anyway, remember to zero it yourself whenever you call DestroyWindow, e.g.:
      myWindow.DestroyWindow();
      myWindow.m_hWnd = NULL;

STL Wisdom

  • I can't find the member function stl_thing.xxx (e.g. vector::find)
    • Look for a generic algorithm in <algorithms>. Sort is a good example. If sort is provided as a method, use that, but if not, you can use sort(stl_thing.begin(), stl_thing.end()). Also look for a version that takes a binary predicate (a bool isxlessthany(x, y) function).

  • How do you remove all the, say '&' characters from an STL string?
    • The generic remove() algorithm moves items but doesn't erase the (now unused) elements at the end. Use this:
      str.erase(remove(str.begin(), str.end(), '&'), str.end());

  • Idiom: Using remove_if with a user defined function
    • To use remove_if (or any of the algorithms that take a unary function object) with a user-defined function, do something like this:
      class Foo
      {
      public:
          vector<HMENU> mMenus;
          static bool ValidMenu(HMENU menu) { return ::IsMenu(menu); }
          void RemoveInvalidMenus()
          {
              mMenus.erase(remove_if(mMenus.begin(), mMenus.end(),
                  not1(ptr_fun(ValidMenu))), mMenus.end());
          }
      }

  • Nifty: Using remove_if to remove non-hex characters from a string
    • Similar to the idiom above.
      static bool ValidHexChar(char c)
      { 
          return ::isxdigit(c) ? true : false;
      }
      
      ...elsewhere...
      
          string hexChars(argv[argIndex]);
          hexChars.erase(remove_if(hexChars.begin(), hexChars.end(),
              not1(ptr_fun(ValidHexChar))), hexChars.end());
      

  • I'm trying to do something simple, like use std::find on a vector<string>, and I'm getting this compile error:
    ...\algorithm(31) : error C2676: binary '==' : 'const std::string' does not
    define this operator or a conversion to a type acceptable to the predefined
    operator
    
    There's gotta be a string::operator==, right?
    • Well it might seem that way, but in fact operator==(string, string) is a global function, defined in <string>, not a member function of string. Anyway, your problem is that you forgot to #include <string>.

  • How do I use function objects for comparison with std::sort() again?
    • Here's an example that should help:
      typedef std::pair<std::string, MyThing*> ItemPair;
      class ItemCompare : std::binary_function<ItemPair, ItemPair, bool>
      {
      public:
          bool operator()(const ItemPair& a, const ItemPair& b)
          {
              return (::lstrcmp(a.first.c_str(), b.first.c_str()) < 0);
          }
      };
      
      .
      .
      .
      
      std::vector<ItemPair> items;
      // code to push_back things onto items here
      
      // Now sort items using ItemCompare:
      std::sort(items.begin(), items.end(), ItemCompare());
      

  • The compiler says std::setprecision (or some other stream manipulator) is not a member of std::. Yes it is!
    • No it isn't! Unless you add the following include:
      #include <iomanip>

HTML Wisdom

  • I think <button> tags are cooler than <input type='submit'> tags because you can put pretty markup in the button. But multiple submit buttons in Internet Explorer using <button type='submit'> tags is all horked. WTF?
    • Internet Explorer has some problems with <button> tags. Take a form like this:
      <form method='get' action='/mypage.html'>
          <button type='submit' name='action' value='new'>New</button>
          <button type='submit' name='action' value='edit'>Edit</button>
          <button type='submit' name='action' value='delete'>Delete</button>
      </form>
      Seems reasonable. However, clicking any of the three buttons will result in the following URL being fetched:
      http://www.me.com/mypage.html?action=New&action=Edit&action=Delete	
      Yeah, that's right, it adds all three name/value pairs to the GET request, no matter which button you press. How very...useless.

      Another thing you might not have noticed: the capitalization of the values in the URL. Why are they capitalized? The values given in the value= attributes are lowercase. Well, the answer is that IE uses the button label (that stuff between the <button> and the </button>) as the value, and ignores the value attribute. Check this slightly modified example:

      <form method='get' action='/mypage.html'>
          <button type='submit' name='action' value='new' accesskey='N'>
          	<u>N</u>ew
          </button>
          <button type='submit' name='action' value='edit' accesskey='E'>
          	<u>E</u>dit
          </button>
          <button type='submit' name='action' value='delete' accesskey='D'>
          	<u>D</u>elete
          </button>
      </form>
      Clicking any button in this form results in the the attractive URL
      http://www.me.com/mypage.html?action=%3CU%3EN%3C%2FU%3Eew&action=%3CU%3EE%3C%2FU%3Edit&
      	action=%3CU%3ED%3C%2FU%3Eelete
      You guessed it, IE even passes any markup that was between the <button> and the </button> too. Lovely. If you're using PHP, you can use strip_tags(stripslashes($_GET[...])) to clean them up, but they'll still have the text that's between the button tags, but alas, even that is not enough.

      What if you use POST instead of GET? It's the same, but the effect is even worse, because you can't easily see the POST data. IE acts basically the same way, that is, it adds all three name/value pairs to the POST data. The problem is the server side CGI code is going to ask for the value of the name "action", and it's going to get "<u>D</u>elete" no matter which button was pressed.

      When this happens, it's more than a little confusing. For most CGI interfaces what happens is that three successive values are assigned to the name "action", the last being "<u>D</u>elete", which is what your CGI will see. In fact most CGI interfaces will have the same problem with the GET method as well.

      So, what's a cross-browser way to fix it?

      If you're using <button> tags at all, you're aiming at farily recent browsers to start with, so I'll assume you can use a little JavaScript. (This whole problem doesn't happen with <input type='submit'>, only with <button>.) Here's what worked for me:

      <script language='javascript' type='text/javascript'>
      function submitAction(value)
      {
          document.forms[0].action.value = value;
          document.forms[0].submit();
      }
      </script>
      
      <form method='get' action='/mypage.html'>
          <input type='hidden' name='action' value=''>
          <button type='button' accesskey='N' onClick='submitAction("new")'>
          	<u>N</u>ew
          </button>
          <button type='button' accesskey='E' onClick='submitAction("edit")'>
          	<u>E</u>dit
          </button>
          <button type='button' accesskey='D' onClick='submitAction("delete")'>
          	<u>D</u>elete
          </button>
      </form>
      Note that the button type has changed from 'submit' to 'button'. This code will result in a single action=value pair in the GET URL or the POST data that will contain "new", "edit", or "delete" as expected, and will work in pretty much all modern browsers.

UNIX Command Wisdom

  • Find FIX ME's in a directory and subdirectories
    • find . -name "*.cpp" -exec grep -H "FIX ME" \{\} \;
  • Prepend something to the beginning of each line in a file (This example prepends "cp " to the beginning of each line.)
    • sed -e "s/^/cp /" list.txt
  • Append something to the end of each line in a file (This example appends "/c/src/sub" to the end of each line.)
    • sed -e "s/\$/ \/c\/src\/sub\/" list.txt
  • Make a shell script that will copy files containing a pattern to another directory
    • grep -l "mypattern" *.txt > copier.sh
      sed -e "s/^/cp /" copier.sh > copier1.sh
      sed -e "s/\$/ \/c\/src\/sub\//" copier1.sh > copier.sh
      rm copier1.sh
      ./copier.sh
      rm copier.sh
  • A tcsh script to do mass renaming of files
    • #!/bin/tcsh
      foreach oldname ( *.html )
        set newname=`echo $oldname | sed 's/.html/.html.tmpl/'`
        mv $oldname $newname
      end


Comments? Errors? Suggestions? Send 'em!