|
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!
- 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
STL Wisdom
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
- Prepend something to the beginning of each line in a file (This example
prepends "cp " to the beginning of each line.)
- Append something to the end of each line in a file (This example appends
"/c/src/sub" to the end of each line.)
- Make a shell script that will copy files containing a pattern to another
directory
- A tcsh script to do mass renaming of files
Comments? Errors? Suggestions?
Send 'em!
|