I'm on windows 8.1, visual studio 2017.
I'm using this pricedown font in a directx project I'm working on.
I load it with AddFontResourceEx and create a font for it with D3DXCreateFont.
When I hit "Local Windows Debugger" everything is fine, font renders. Be it in release or in debug mode.
Problem arises when I go through any executable, it never renders said font, be it release or debug.
So I went reading, I read the articles on msdn, this one and others whenever needed.
I don't think I'm doing anything wrong, my Resource View looks like this:
, and IDR_FONT1 looks like this:
The file is automatically loaded into the solution explorer (I didn't add it, VS did from the Resource.rc file), as you can see here:
With these proprieties:
I add it like so:
AddFontResourceEx("pricedown.ttf", FR_PRIVATE, 0);
this->createFont("Pricedown", 60, true, false);
Where createfont is my function to add the font (stripped down, it has arrays):
bool D3D9Render::createFont(char *name, int size, bool bold, bool italic)
{
D3DXCreateFont(m_pD3dDev, size, 0, (bold) ? FW_BOLD : FW_NORMAL, 0, (italic) ? 1 : 0, DEFAULT_CHARSET, OUT_DEFAULT_PRECIS, ANTIALIASED_QUALITY, DEFAULT_PITCH, name, &m_pFont);
return true;
}
I'm compiling it as x64 release.
As I've said, it works and renders the font when I press "Local Windows Debugger" (in any mode including x64 release), but when I go to project/x64/Release, it just won't render the font. Even the executable size is adequate.
GetLastError on the AddFontResource is 2 (ERROR_FILE_NOT_FOUND)
What am I doing wrong?
(Read the answer until the end, or you'll waste a lot of time.)
I got it. I had read over this blog post.
Here is an example on how to use AddFontMemResourceEx on a font file embedded in the resource.
HINSTANCE hResInstance = AfxGetResourceHandle( ); //Read the edit
HRSRC res = FindResource(
hResInstance,
MAKEINTRESOURCE(IDR_MYFONT),
L"BINARY" //Read The Edit
);
if (res)
{
HGLOBAL mem = LoadResource(hResInstance, res);
void *data = LockResource(mem);
size_t len = SizeofResource(hResInstance, res);
DWORD nFonts;
m_fonthandle = AddFontMemResourceEx(
data, // font resource
len, // number of bytes in font resource
NULL, // Reserved. Must be 0.
&nFonts // number of fonts installed
);
if(m_fonthandle==0)
{
MessageBox(L"Font add fails", L"Error");
}
}
Though you need afxwin.h, and from here:
afxwin.h is MFC and MFC is not included in the free version of VC++
(Express Edition)
EDIT:
You do not need to use AfxGetResourceHandle (why you would need afxwin.h), you can simply do:
HINSTANCE hResInstance = (HINSTANCE)GetModuleHandle(NULL);
And in FindResource, the 3rd parameter should be RT_FONT, and so you'd get:
HRSRC res = FindResource(hResInstance, MAKEINTRESOURCE(IDR_FONT1), RT_FONT);
Related
Im trying to read the value of a TextEdit box in a windows dialog and display the results using a MessageBox, however the results being displayed is just “Error”, when I run the program, even though there are no exceptions or error message in the debug section in visual studio. What am i doing wrong? Here is the subject code:
LPWSTR path;
GetDlgItemText(hDlg, IDC_PROGRAM, path, sizeof(path));
MessageBox(hDlg, NULL, path, MB_OK);
You are passing an uninitialized pointer and an incorrect buffer size to GetDlgItemText().
You need to allocate memory for a buffer for GetDlgItemText() to write into, and specify the max size of that buffer, eg:
WHAR path[MAX_PATH] = {};
GetDlgItemText(hDlg, IDC_PROGRAM, path, MAX_PATH);
MessageBox(hDlg, NULL, path, MB_OK);
Just from the start: Since March 1st 2017 this is a bug confirmed by Microsoft. Read comments at the end.
Short description:
I have random crashes in larger application using MFC, ATL. In all such cases after ATL subclassing was used for a window upon simple actions with a window (moving, resizing, setting the focus, painting etc.) I get a crash on a random execution address.
First it looked like a wild pointer or heap corruption but I narrowed the complete scenario down to a very simple application using pure ATL and only Windows API.
Requirements / my used scenarios:
The application was created with VS 2015 Enterprise Update 3.
The program should be compiled as 32bit.
Test application uses CRT as a shared DLL.
The application runs under Windows 10 Build 14393.693 64bit (but we have repros under Windows 8.1 and Windows Server 2012 R2, all 64bit)
atlthunk.dll has version 10.0.14393.0
What the application does:
It simply creates a frame window and tries to create many static windows with the windows API.
After the static window is created, this window is subclassed with the ATL CWindowImpl::SubclassWindow method.
After the subclass operation a simple window message is sent.
What happens:
Not on every run, but very often the application crashes upon SendMessage to the subclassed window.
On the 257 window ( or another multiple of 256+1) the subclass fails in some way. The ATL thunk that is created is invalid. It seems that the stored execution address of the new subclass-function isn't correct.
Sending any the message to the window causes a crash.
The callstack is always the same. The last visible and known address in the callstack is in the atlthunk.dll
atlthunk.dll!AtlThunk_Call(unsigned int,unsigned int,unsigned int,long) Unknown
atlthunk.dll!AtlThunk_0x00(struct HWND__ *,unsigned int,unsigned int,long) Unknown
user32.dll!__InternalCallWinProc#20() Unknown
user32.dll!UserCallWinProcCheckWow() Unknown
user32.dll!SendMessageWorker() Unknown
user32.dll!SendMessageW() Unknown
CrashAtlThunk.exe!WindowCheck() Line 52 C++
The thrown exception in the debugger is shown as:
Exception thrown at 0x0BF67000 in CrashAtlThunk.exe:
0xC0000005: Access violation executing location 0x0BF67000.
or another sample
Exception thrown at 0x2D75E06D in CrashAtlThunk.exe:
0xC0000005: Access violation executing location 0x2D75E06D.
What I know about atlthunk.dll:
Atlthunk.dll seems to be only part of 64bit OS. I found it on a Win 8.1 and Win 10 systems.
If atlthunk.dll is available (all Windows 10 machines), this DLL cares about the thunking. If the DLL isn't present, thunking is done in the standard way: allocating a block on the heap, marking it as executable, adding some load and a jump statement.
If the DLL is present. It contains 256 predefined slots for subclassing. If 256 subclasses are done, the DLL reloads itself a second time into memory and uses the next 256 available slots in the DLL.
As far as I see, the atlthunk.dll belongs to the Windows 10 and isn't exchangeable or redistributable.
Things checked:
Antivirus system was turned of or on, no change
Data execution protection doesn't matter. (/NXCOMPAT:NO and the EXE is defined as an exclusion in the system settings, crashes too)
Additional calls to FlushInstructionCache or Sleep calls after the subclass doesn't have any effect.
Heap integrity isn't a problem here, I rechecked it with more than one tool.
and a thousands more (I may already forgot what I tested)... ;)
Reproducibility:
The problem is somehow reproducible. It doesn't crashes all the time, it crashes randomly. I have a machine were the code crashes on every third execution.
I can repro it on two desktop stations with i7-4770 and a i7-6700.
Other machines seem not to be affected at all (works always on a Laptop i3-3217, or desktop with i7-870)
About the sample:
For simplicity I use a SEH handler to catch the error. If you debug the application the debugger will show the callstack mentioned above.
The program can be launched with an integer on the command line.In this case the program launches itself again with the count decremented by 1.So if you launch CrashAtlThunk 100 it will launch the application 100 times. Upon an error the SEH handler will catch the error and shows the text "Crash" in a message box. If the application runs without errors, the application shows "Succeeded" in a message box.
If the application is started without a parameter it is just executed once.
Questions:
Does anybody else can repro this?
Does anybody saw similar effects?
Does anybody know or can imagine a reason for this?
Does anybody know how to get around this problem?
Notes:
2017-01-20 Support case at Microsoft opened.
The code
// CrashAtlThunk.cpp : Defines the entry point for the application.
//
// Windows Header Files:
#include <windows.h>
// C RunTime Header Files
#include <stdlib.h>
#include <malloc.h>
#include <memory.h>
#include <tchar.h>
#define _ATL_CSTRING_EXPLICIT_CONSTRUCTORS // some CString constructors will be explicit
#include <atlbase.h>
#include <atlstr.h>
#include <atlwin.h>
// Global Variables:
HINSTANCE hInst; // current instance
const int NUM_WINDOWS = 1000;
//------------------------------------------------------
// The problematic code
// After the 256th subclass the application randomly crashes.
class CMyWindow : public CWindowImpl<CMyWindow>
{
public:
virtual BOOL ProcessWindowMessage(_In_ HWND hWnd, _In_ UINT uMsg, _In_ WPARAM wParam, _In_ LPARAM lParam, _Inout_ LRESULT& lResult, _In_ DWORD dwMsgMapID) override
{
return FALSE;
}
};
void WindowCheck()
{
HWND ahwnd[NUM_WINDOWS];
CMyWindow subclass[_countof(ahwnd)];
HWND hwndFrame;
ATLVERIFY(hwndFrame = ::CreateWindow(_T("Static"), _T("Frame"), SS_SIMPLE, 0, 0, 10, 10, NULL, NULL, hInst, NULL));
for (int i = 0; i<_countof(ahwnd); ++i)
{
ATLVERIFY(ahwnd[i] = ::CreateWindow(_T("Static"), _T("DummyWindow"), SS_SIMPLE|WS_CHILD, 0, 0, 10, 10, hwndFrame, NULL, hInst, NULL));
if (ahwnd[i])
{
subclass[i].SubclassWindow(ahwnd[i]);
ATLVERIFY(SendMessage(ahwnd[i], WM_GETTEXTLENGTH, 0, 0)!=0);
}
}
for (int i = 0; i<_countof(ahwnd); ++i)
{
if (ahwnd[i])
::DestroyWindow(ahwnd[i]);
}
::DestroyWindow(hwndFrame);
}
//------------------------------------------------------
int APIENTRY wWinMain(_In_ HINSTANCE hInstance,
_In_opt_ HINSTANCE hPrevInstance,
_In_ LPWSTR lpCmdLine,
_In_ int nCmdShow)
{
hInst = hInstance;
int iCount = _tcstol(lpCmdLine, nullptr, 10);
__try
{
WindowCheck();
if (iCount==0)
{
::MessageBox(NULL, _T("Succeeded"), _T("CrashAtlThunk"), MB_OK|MB_ICONINFORMATION);
}
else
{
TCHAR szFileName[_MAX_PATH];
TCHAR szCount[16];
_itot_s(--iCount, szCount, 10);
::GetModuleFileName(NULL, szFileName, _countof(szFileName));
::ShellExecute(NULL, _T("open"), szFileName, szCount, nullptr, SW_SHOW);
}
}
__except (EXCEPTION_EXECUTE_HANDLER)
{
::MessageBox(NULL, _T("Crash"), _T("CrashAtlThunk"), MB_OK|MB_ICONWARNING);
return FALSE;
}
return 0;
}
Comment after answered by Eugene (Feb. 24th 2017):
I don't want to change my original question, but I want to add some additional information how to get this into a 100% Repro.
1, Change the main function to
int APIENTRY wWinMain(_In_ HINSTANCE hInstance,
_In_opt_ HINSTANCE hPrevInstance,
_In_ LPWSTR lpCmdLine,
_In_ int nCmdShow)
{
// Get the load address of ATLTHUNK.DLL
// HMODULE hMod = LoadLibrary(_T("atlThunk.dll"));
// Now allocate a page at the prefered start address
void* pMem = VirtualAlloc(reinterpret_cast<void*>(0x0f370000), 0x10000, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);
DWORD dwLastError = ::GetLastError();
hInst = hInstance;
WindowCheck();
return 0;
}
Uncomment the LoadLibrary call. Compile.
Run the programm once and stop in the debugger. Note the address where the library was loaded (hMod).
Stop the program. Now comment the Library call again and change the VirtualAlloc call to the address of the previous hMod value, this is the prefered load address in this window session.
Recompile and run. CRASH!
Thanks to eugene.
Up to now. Microsoft ist still investigating about this. They have dumps and all code. But I don't have a final answer. Fact is we have a fatal bug in some Windows 64bit OS.
I currently made the following changes to get around this
Open atlstdthunk.h of VS-2015.
Uncomment the #ifdef block completely that defines USE_ATL_THUNK2. Code lines 25 to 27.
Recompile your program.
This enables the old thunking mechanism well known from VC-2010, VC-2013... and this works crash free for me. As long as there are no other already compiled libraries involved that may subclass or use 256 windows via ATL in any way.
Comment (Mar. 1st 2017):
Microsoft confirmed that this is a bug. It should be fixed in Windows 10 RS2.
Mircrosoft agrees that editing the headers in the atlstdthunk.h is a workaround for the problem.
In fact this says. As long as there is no stable patch I can never use the normal ATL thunking again, because I will never know what Window versions out in the world will use my program. Because Windows 8 and Windows 8.1 and Windows 10 prior to RS2 will suffer on this bug.
Final Comment (Mar. 9th 2017):
Builds with VS-2017 are affected too, there is no difference between VS-2015 and VS-2017
Microsoft decided that there will be no fix for older OS, regarding this case.
Neither Windows 8.1, Windows Server 2012 RC2 or other Windows 10 builds will get a patch to fix this issue.
The issue is to rare and the impact for our company is to small. Also the fix from our side is to simple. Other reports of this bug are not known.
The case is closed.
My advice for all programers: Change the the atlstdthunk.h in your Visual Studio version VS-2015, VS-2017 (see above). I don't understand Microsoft. This bug is a serious problem in the ATL thunking. It may hit every programmer that uses a greater number of windows and/or subclassing.
We only know of a fix in Windows 10 RS2. So all older OS are affected! So I recommend to disable the use of the atlthunk.dll by commenting out the define noted above.
This is the bug inside atlthunk.dll. When it loads itself second time and further this happens manually via MapViewOfFile call. In this case not every address relative to the module base is properly changed (when DLL loaded by LoadLibarary/LoadLibraryEx calls system loader does this automatically). Then if the first time DLL was loaded on preferred base address everything works fine as unchanged addresses point to the similar code or data. But if not you got crash when 257th subclassed window handles messages.
Since Vista we have "address space layout randomization" feature this explains why your code crashes randomly. To have crash every time you have to discover atlthunk.dll base address on your OS (it differs on different OS versions) and do one memory page address space reservation at this address using VirtualAlloc call before the first subclass. To find the base address you can use dumpbin /headers atlthunk.dll command or parse PE headers manually.
My test shows that on Windows 10 build 14393.693 x32 version is affected but x64 is not. On Server 2012R2 with latest updates both (x32 and x64) versions are affected.
BTW, atlthunk.dll code has around 10 times more CPU instructions per thunk call as previous implementation. It may be not very significant but it slows down the message processing.
Slightly more automatic form of what was already described:
// A minimum ATL program with more than 256 windows. In practise they would not be toplevel, but e.g. buttons.
// Thanks to https://www.codeguru.com/cpp/com-tech/atl/article.php/c3605/Using-the-ATL-Windowing-Classes.htm
// for helping with ATL.
// You need to be up to date, like have KB3030947 or KB3061512. Otherwise asserts will fail instead.
#undef _DEBUG
#include <atlbase.h>
ATL::CComModule _Module;
#include <atlwin.h>
#include <assert.h>
#include <string>
BEGIN_OBJECT_MAP(ObjectMap) END_OBJECT_MAP()
struct CMyWindow : CWindowImpl<CMyWindow>
{
BEGIN_MSG_MAP(CMyWindow) END_MSG_MAP()
};
int __cdecl wmain()
{
// Exacerbate the problem, which can happen more like if by chance.
PROCESS_INFORMATION process = { 0 };
{
// Be sure another process has atlthunk loaded.
WCHAR cmd[] = L"rundll32 atlthunk,x";
STARTUPINFOW startup = { sizeof(startup) };
BOOL success = CreateProcessW(0, cmd, 0, 0, 0, 0, 0, 0, &startup, &process);
assert(success && process.hProcess);
CloseHandle(process.hThread);
// Get atlthunk's usual address.
HANDLE file = CreateFileW((std::wstring(_wgetenv(L"SystemRoot")) + L"\\system32\\atlthunk.dll").c_str(), GENERIC_READ,
FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, 0, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
assert(file != INVALID_HANDLE_VALUE);
HANDLE mapping = CreateFileMappingW(file, 0, PAGE_READONLY | SEC_IMAGE, 0, 0, 0);
assert(mapping);
void* view = MapViewOfFile(mapping, 0, 0, 0, 0);
assert(view);
UnmapViewOfFile(view);
VirtualAlloc(view, 1, MEM_COMMIT | MEM_RESERVE, PAGE_NOACCESS);
}
_Module.Init(0, 0);
const int N = 300;
CMyWindow wnd[N];
for (int i = 0; i < N; ++i)
{
wnd[i].Create(0, CWindow::rcDefault, L"Hello", (i < N - 1) ? 0 : (WS_OVERLAPPEDWINDOW | WS_VISIBLE));
wnd[i].DestroyWindow();
}
TerminateProcess(process.hProcess, 0);
CloseHandle(process.hProcess);
MSG msg;
while (GetMessageW(&msg, 0, 0, 0))
{
TranslateMessage(&msg);
DispatchMessageW(&msg);
}
_Module.Term();
}
I call standard ShellExecuteW call on Windows8.1 to open PPS (powerpoint slide) file.
This works just fine on Windows 7. On Windows 8.1. it reports "There is no program associated to open the file". Of course, the file association is set and if file is saved and run from Explorer (double clicked) it opens just fine. I also tried to change association and to associate another program and then associate back to PPS viewer, no improvement. It just doesn't work for W8.1 but the same call works on earlier Windows.
Can anyone give me a clue what might be wrong here?
The code used to open file is very simple and I see no errors with it:
HINSTANCE hinst = ShellExecuteW(NULL, L"open", L"C:\\path\\to\\file.pps", NULL, NULL, SW_SHOWNORMAL);
// Check if result is error
if ((int)hinst <= 32)
{
wchar_t buf[512] = { 0 };
FormatMessageW(FORMAT_MESSAGE_FROM_SYSTEM, NULL, GetLastError(), MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), buf, 512, NULL);
MSGBOX(buf);
}
I use free PPS viewer as found here:
http://www.microsoft.com/en-us/download/details.aspx?id=13
I found something similar which points to that this could be a bug in Win8.1. Can anyone confirm this? Or reveal a workaround?
I found the solution myself.
The problem on W8.1 was that the verb open was not registered to the application so it used different default verb. So if the ShellExecute call is replaced with:
HINSTANCE hinst = ShellExecuteW(NULL, NULL, L"C:\\path\\to\\file.pps", NULL, NULL, SW_SHOWNORMAL);
Then the system looks for a default verb which may or may not be open (usually is), so by not using this verb explicitly it leaves this decision to the system.
To be specific, I'm trying to write a windows program running on different windows systems, mostly XP and Win7 or Win8. I want it to load a system default icon from somewhere in the system files(which I don't know neither). Is that possible and how can I do that?
for example, a Text.txt file has its icon on windows XP, but a different icon on Win7. XP's recycle bin is different from Win7's recycle bin, too. So where can I load this system-dependent icon to my program?? ps I 'm using VC6, not .NET.
If you want the icon for a particular type of file you can use SHGetFileInfo. For example, to get the icon for a text file:
SHFILEINFO sfi;
if (SHGetFileInfo(L".txt", FILE_ATTRIBUTE_NORMAL, &sfi, sizeof(sfi), SHGFI_ICON | SHGFI_USEFILEATTRIBUTES ))
{
// you now have the icon in sfi.hIcon
// remember to destroy it when you are done with it
DestroyIcon(sfi.hIcon);
}
The SHGFI_ICON flag indicates we want the large icon. You can add the SHGFI_SMALLICON flag to get the small icon.
SHGFI_USEFILEATTRIBUTES indicates that the filename we pass does not have to exist - the function will return the icon for the file class rather than for a specific existing file. If you want the icon for a particular file then pass the full pathname of the file to the SHGetFileInfo function and omit this flag.
If you want the icon for a system folder like recycle bin, this function can do that too. For example:
LPITEMIDLIST pidl;
if (SUCCEEDED(SHGetFolderLocation(0, CSIDL_BITBUCKET, 0, 0, &pidl)))
{
SHFILEINFO sfi;
if (SHGetFileInfo((LPCWSTR)pidl, 0, &sfi, sizeof(sfi), SHGFI_ICON | SHGFI_PIDL))
{
// you now have the icon in sfi.hIcon
// remember to destroy it when you are done with it
DestroyIcon(sfi.hIcon);
}
CoTaskMemFree(pidl);
}
See the documentation for SHGetFileInfo for a full list of flags. And make sure you have called OleInitialize() or CoInitializeEx() to initialise COM before using these functions.
How do I change the font in my C++ Windows console app?
It doesn't seem to use the font cmd.exe uses by default (Lucida Console). When I run my app through an existing cmd.exe (typing name.exe) it looks like this: http://dathui.mine.nu/konsol3.png which is entierly correct.
But when I run my app seperatly (double-click the .exe) it looks like this: http://dathui.mine.nu/konsol2.png.
Same code, two different looks.
So now I wonder how I can change the font so it always looks correctly regardless of how it's run.
EDIT:
Ok, some more information. When I just use this little snippet:
SetConsoleOutputCP(CP_UTF8);
wchar_t s[] = L"èéøÞǽлљΣæča";
int bufferSize = WideCharToMultiByte(CP_UTF8, 0, s, -1, NULL, 0, NULL, NULL);
char* m = new char[bufferSize];
WideCharToMultiByte(CP_UTF8, 0, s, -1, m, bufferSize, NULL, NULL);
wprintf(L"%S", m);
it works with the correct font. But in my real application I use WriteConsoleOutput() to print strings instead:
CHAR_INFO* info = new CHAR_INFO[mWidth * mHeight];
for(unsigned int a = 0; a < mWidth*mHeight; ++a) {
info[a].Char.UnicodeChar = mWorld.getSymbol(mWorldX + (a % mWidth), mWorldY + (a / mWidth));
info[a].Attributes = mWorld.getColour(mWorldX + (a % mWidth), mWorldY + (a / mWidth));
}
COORD zero;
zero.X = zero.Y = 0;
COORD buffSize;
buffSize.X = mWidth;
buffSize.Y = mHeight;
if(!WriteConsoleOutputW(window, info, buffSize, zero, &rect)) {
exit(-1);
}
and then it uses the wrong font. I use two different windows, created like this:
mHandleA = CreateConsoleScreenBuffer(GENERIC_READ | GENERIC_WRITE, 0,
NULL, CONSOLE_TEXTMODE_BUFFER, NULL);
Might I be setting the codepage for just the standard output or something?
Windows stores the cmd settings (including the font) in the registry using the exe path as the key. The root key is 'HKEY_CURRENT_USER\Console' so if you take a look in there with regedit you should see several sub-keys named after varous exe's.
To copy the settings of an existing exe, you can export the key to a text file, then edit the file to change the key name to that of your exe, then reimport it.
You can also progmatically modify the registry though i doubt that would take immediate effect w.r.t. to your console window.
You could try the SetCurrentConsoleFontEx() function.
For Vista and above, there is SetCurrentConsoleFontEx, as already has been said.
For 2K and XP, there is an undocumented function SetConsoleFont; e.g. read here.
typedef BOOL (WINAPI *FN_SETCONSOLEFONT)(HANDLE, DWORD);
FN_SETCONSOLEFONT SetConsoleFont;
..........
HMODULE hm = GetModuleHandle(_T("KERNEL32.DLL"));
SetConsoleFont = (FN_SETCONSOLEFONT) GetProcAddress(hm, "SetConsoleFont");
// add error checking
..........
SetConsoleFont(GetStdHandle(STD_OUTPUT_HANDLE), console_font_index);
Now, console_font_index is an index into console font table, definition of which is unknown. However, console_font_index == 10 is known to identify Lucida Console (a Unicode font). I'm not sure how stable is this value across different OS versions.
UPDATE
After dutt's comment, I've run an experiment on a clean XP SP2 setup.
Initially, GetNumberOfConsoleFonts(), indeed, returns 10, and font indices 0..9 specify various raster fonts.
After I open a console with Lucida font selected in its properties (just once; I can close it immediately after opening but the effect is the same), suddenly GetNumberOfConsoleFonts() starts to return 12, and indices 10 and 11 select Lucida of different sizes.
So it seems this trick worked for me when I played with it because I always had running at least one console app with Lucida font selected.
Thus, for practical purposes, jon hanson's answer seems better. Besides offering better control, it actually works. :)