2007-10-18 为支持通过 IErrorRecords::GetErrorParameters 方法填充参数列表 "[,,,,,]",而修改 EnumResNameProc 函数的实现。
为使应用程序在使用 SQL Server 2005 Compact Edition (简称 SSCE) 时,能调用 ReportEvent 向 EventLog 服务报告 SSCE 相关的错误事件,我们需要一个 EventID 与 sqlceer30CN.dll 内的错误描述字符串 StringID 关联的事件消息文件。
33 | 2 | 2 | 2 2 2 2 2 2 2 2 1 1 1 1 | 1 1 1 1 1 1 |
10 | 9 | 8 | 7 6 5 4 3 2 1 0 9 8 7 6 | 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 |
Sev | C | R | Facility | Code |
EventID 的 Code 部分仍与 StringID 一致,Severity 部分则是 11B (Error)。
以下就是从 sqlceer30CN.dll 的字符串依次表读取字符串并依次写入消息文件的代码:
BOOL CALLBACK EnumResNameProc(HMODULE hModule, LPCTSTR lpszType, LPTSTR lpszName, LONG_PTR lParam)
... {
HRSRC hResInfo(FindResource(hModule, lpszName, lpszType)); // 查找字符串表
if (!hResInfo) return FALSE;
LPCWSTR lpResData((LPCWSTR)LockResource(LoadResource(hModule, hResInfo))); // 读取字符串表
if (!lpResData) return FALSE;
DWORD cbSize(SizeofResource(hModule, hResInfo) / sizeof(WCHAR)); // 获取字符串表的大小
if (!cbSize) return FALSE;
DWORD dwMsgId(((DWORD)lpszName - 1) * 16); // 计算字符串表首个字符串的ID
CAtlFile *pFile((CAtlFile*)lParam);
for (DWORD i(0); i < cbSize; i += *(LPWORD)(lpResData + i) + 1, dwMsgId ++)
...{
DWORD dwStrLen(*(LPWORD)(lpResData + i)); // 读取字符串的长度
if (dwStrLen)
...{
CStringA strString(lpResData + i + 1, dwStrLen);
CStringA strMessage;
int iStart(strString.Find('['));
strMessage = strString.Left(++ iStart);
int i(0);
int iCurrent(strString.Find(',', iStart));
while (iCurrent != -1)
...{
if (iCurrent > iStart + 1)
strMessage.AppendFormat("%%%u", ++ i);
strMessage.AppendChar(',');
iStart = iCurrent + 1;
iCurrent = strString.Find(',', iStart);
}
iCurrent = strString.Find(']', iStart);
if (iCurrent != -1)
...{
if (iCurrent > iStart + 1)
strMessage.AppendFormat("%%%u", ++ i);
strMessage.AppendChar(']');
}
CStringA strOutput;
strOutput.Format(
"MessageId=0x%x Severity=Error Facility=Application SymbolicName=MSG_%u "
"Language=CHS %s . ", dwMsgId, dwMsgId, strMessage); // 按 message file 的格式要求进行格式化
ATLVERIFY(SUCCEEDED(pFile->Write(strOutput, strOutput.GetLength())));
}
}
return TRUE;
}
int _tmain( int argc, _TCHAR * argv[])
... {
if (argc == 3)
...{
CSafeHandle<HMODULE, FreeLibrary> hModule(LoadLibrary(argv[1])); // 加载 sqlceer30CN.dll
CAtlFile file;
ATLVERIFY(SUCCEEDED(file.Create(argv[2], GENERIC_WRITE, FILE_SHARE_READ, CREATE_ALWAYS))); // 创建消息文件
LPCSTR pszLangNames(
"; // This is the header section. "
"LanguageNames=(CHS=0x804:MSG00804) "
"; // The following are message definitions. "); // 消息文件头
ATLVERIFY(SUCCEEDED(file.Write(pszLangNames, strlen(pszLangNames))));
if (!EnumResourceNames(hModule, RT_STRING, EnumResNameProc, (LONG_PTR)&file)) // 枚举字符串表
DeleteFile(argv[2]);
}
return 0;
}
... {
HRSRC hResInfo(FindResource(hModule, lpszName, lpszType)); // 查找字符串表
if (!hResInfo) return FALSE;
LPCWSTR lpResData((LPCWSTR)LockResource(LoadResource(hModule, hResInfo))); // 读取字符串表
if (!lpResData) return FALSE;
DWORD cbSize(SizeofResource(hModule, hResInfo) / sizeof(WCHAR)); // 获取字符串表的大小
if (!cbSize) return FALSE;
DWORD dwMsgId(((DWORD)lpszName - 1) * 16); // 计算字符串表首个字符串的ID
CAtlFile *pFile((CAtlFile*)lParam);
for (DWORD i(0); i < cbSize; i += *(LPWORD)(lpResData + i) + 1, dwMsgId ++)
...{
DWORD dwStrLen(*(LPWORD)(lpResData + i)); // 读取字符串的长度
if (dwStrLen)
...{
CStringA strString(lpResData + i + 1, dwStrLen);
CStringA strMessage;
int iStart(strString.Find('['));
strMessage = strString.Left(++ iStart);
int i(0);
int iCurrent(strString.Find(',', iStart));
while (iCurrent != -1)
...{
if (iCurrent > iStart + 1)
strMessage.AppendFormat("%%%u", ++ i);
strMessage.AppendChar(',');
iStart = iCurrent + 1;
iCurrent = strString.Find(',', iStart);
}
iCurrent = strString.Find(']', iStart);
if (iCurrent != -1)
...{
if (iCurrent > iStart + 1)
strMessage.AppendFormat("%%%u", ++ i);
strMessage.AppendChar(']');
}
CStringA strOutput;
strOutput.Format(
"MessageId=0x%x Severity=Error Facility=Application SymbolicName=MSG_%u "
"Language=CHS %s . ", dwMsgId, dwMsgId, strMessage); // 按 message file 的格式要求进行格式化
ATLVERIFY(SUCCEEDED(pFile->Write(strOutput, strOutput.GetLength())));
}
}
return TRUE;
}
int _tmain( int argc, _TCHAR * argv[])
... {
if (argc == 3)
...{
CSafeHandle<HMODULE, FreeLibrary> hModule(LoadLibrary(argv[1])); // 加载 sqlceer30CN.dll
CAtlFile file;
ATLVERIFY(SUCCEEDED(file.Create(argv[2], GENERIC_WRITE, FILE_SHARE_READ, CREATE_ALWAYS))); // 创建消息文件
LPCSTR pszLangNames(
"; // This is the header section. "
"LanguageNames=(CHS=0x804:MSG00804) "
"; // The following are message definitions. "); // 消息文件头
ATLVERIFY(SUCCEEDED(file.Write(pszLangNames, strlen(pszLangNames))));
if (!EnumResourceNames(hModule, RT_STRING, EnumResNameProc, (LONG_PTR)&file)) // 枚举字符串表
DeleteFile(argv[2]);
}
return 0;
}
由于消息文件是直接从 PE 生成,没有其他信息用来生成 SymbolicName,故仅用 MSG_<StringID> 方式意思一下。