While Windows supports dozens or even hundreds of languages, its localization APIs require quite a bit of getting used to. Below is how I solved some common problems related to formatting and parsing a number for a specific locale.
Formatting a Number for a Locale
The function GetNumberFormat() formats a number for a particular locale. Its simplest usage looks something like:
buf now contains the number 1234567.89 formatted for the user’s default locale. For example, for the English-United States locale, buf will contain “1,234,567.89”; for German-Germany, “1.234.567,89”; for Hindi, “12,34,567.89”.
XmlTextWriter is .NET’s class for writing XML in a forward-only streaming manner. It is highly efficient and is the preferred way to generate XML in .NET in most circumstances. I find XmlTextWriter so useful I wrote a partial C++ implementation of it in Implenting IXmlWriter Series.
Unfortunately, XmlTextWriter isn’t quite as strict as it could be. It will let slip some invalid XML such as duplicate attributes, invalid Unicode characters in the range 0×0 to 0×20, and invalid element and attribute names. You can read about XmlTextWriter’s limitations in the article Customized XML Writer Creation.
// Creates a shell link (a.k.a. shortcut) located at swzLinkFile that points to
// szTargetFile with a description of szDescription.
BOOL CreateLink(LPCTSTR szTargetFile, LPCTSTR szDescription, LPCOLESTR swzLinkFile)
{
BOOL bRet = FALSE;
IShellLink* psl;
HRESULT hr =::CoCreateInstance(CLSID_ShellLink, NULL, CLSCTX_INPROC_SERVER,
IID_IShellLink, (void**) &psl);
if (SUCCEEDED(hr))
{
IPersistFile* ppf;
hr = psl->QueryInterface(IID_IPersistFile, (void**) &ppf);
if (SUCCEEDED(hr))
{
hr = psl->SetPath(szTargetFile);
if (SUCCEEDED(hr))
{
hr = psl->SetDescription(szDescription);
if (SUCCEEDED(hr))
{
hr = ppf->Save(swzLinkFile, TRUE);
if (SUCCEEDED(hr))
{
bSuccess = TRUE;
}
}
}
ppf->Release();
}
psl->Release();
}
return bSuccess;
}
// NOTE: Hardcoding C:\WINDOWS and C:\Program Files is a bad practice. Use
// something like ::SHGetFolderPath().
BOOL bSuccess = CreateLink
(
_T("C:\\WINDOWS\\SYSTEM32\\SOL.EXE"),
_T("Shortcut to SOL.EXE"),
L"C:\\Program Files\\sol.lnk")
);
One might expect that the creation of the file C:\Program Files\sol.lnk would be silently redirected by Vista using file virtualization and CreateLink() would succeed, but it doesn’t — the call to IPersistFile::Save() returns E_ACCESSDENIED.
Windows C++ developers remain all too familiar with the standard Windows crash dialog. This post is an attempt to teach developers how to understand the data the crash dialog reports to diagnose difficult issues. A basic understanding of assembly language is assumed; for more background on these topics please read Matt Pietrek’s “Under The Hood” articles in the Microsoft Systems Journal February 1998 and June 1998 issues.
To begin with, let’s write an application that crashes with a null pointer dereference:
Apparently Microsoft has an API for just about everything. Today I read about Microsoft’s Delta Compression Application Programming Interface, an API for creating and applying binary diffs. This API looks ideal for building an application’s incremental online update facility.
The Win32 shell lightweight utility API (shlwapi.dll) is a cornucopia of useful functions. It appears to be Microsoft’s “dumping ground” for functions without a better home. (I believe Microsoft internal DLL ownership also played a part.) Had I known about shlwapi years ago, it would have saved me considerable programming effort.
However, it appears that Microsoft is slowly moving functionality out of shlwapi into other places. For example, many of shlwapi’s string handling functions have better-designed replacements in Microsoft’s safe string library strsafe. Similarly, shlwapi’s SHRegGetValue has been deprecated in favor of advapi32.dll’s RegGetValue.
Answer: my_pair cannot be used as a key for a STL map because the operator< violates the rule of strict weak ordering. More specifically, the operator is not antisymmetric. Consider the following:
I recently wrote a piece of code that looked something like the following:
1
2
3
4
5
6
7
8
9
10
11
staticconstint NUM_TOTAL_VALUES = ...;
typedef ... T;
// Create vec and reserve NUM_TOTAL_VALUES spaces for later insertion
std::vector<T> vec(NUM_TOTAL_VALUES);
// Insert values into vec
for (int i =0; i != NUM_TOTAL_VALUES; ++i)
vec.push_back(...);
// vec should now have NUM_TOTAL_VALUES values in it (but doesn't!)
#pragma unmanaged
classStream { ... }; // Conceptual stream class
classStreamWriter{
public: StreamWriter(Stream* pStream) : m_pStream(pStream) {}
~StreamWriter() { /* Use m_pStream in some way */ }
...
private: Stream* m_pStream;
};
voidf()
{
Stream stream;
StreamWriter streamWriter(&stream);
// Use streamWriter
// streamWriter is destroyed
// stream is destroyed
}
Note that StreamWriter’s destructor uses m_pStream (perhaps by flushing the stream). This means that the order of destruction is important — StreamWriter must be destroyed before its underlying Stream is.
I’m not sure if this is the “canonical” way to do it but here’s a description of how to write an ASP.NET 1.1 ASPX page which returns a XML document (e.g. when writing a home-brewed web service).
First, create a new Web Form (I will call it WebService.aspx). As we will be progamatically generating the XML in the HTTP response rather than sending the (processed) content of the ASPX file, delete everything from the ASPX file but the @Page directive, so that it looks something like: