创建用于在 MFC 控件上使用 printf 样式字符串格式显示状态更新的函数
Creating a function for displaying status updates with printf style string formatting on an MFC control
我需要在对话框中显示状态更新,并希望能够向其发送 printf
样式的格式化字符串。此外,我希望该函数调用一个类似的函数,将格式化的数据添加到日志文件中。
假设我的 Static 控件被调用 IDC_MYSTATUSBAR
,函数如下所示:
void MyDialog::ShowStatus(LPCWSTR lpText, ...)
{
CString sMsg;
va_list ptr;
va_start(ptr, lpText);
if (*ptr == 0)
sMsg = lpText;
else
sMsg.FormatV(lpText, ptr);
va_end(ptr);
CWinApp *myApp = AfxGetApp();
if (myApp)
{
CWnd *pWnd = myApp->m_pMainWnd;
if (pWnd)
{
::SetDlgItemText(pWnd->GetSafeHwnd(), IDC_MYSTATUSBAR, sMsg.GetString());
}
}
myLogFunction(L"%s", sMsg.GetString());
}
函数调用如下:
ShowStatus(L"The results are %d.", 100);
或
ShowStatus(L"Server returned the following result: %s", L"result");
但是其他模块和库通过一个名为 logfunc
的成员变量调用我的函数,该变量定义为:
typedef void(*logFunc)(LPCWSTR lpText, ...);
在这种情况下,来自另一个模块的代码将如下所示:
logFunc m_logfunc;
if (m_logfunc) m_logfunc(L"Internet Time: (%d) %s Local Time = %s", result, TimeResult.FormatGmt(L"%d.%m.%Y %H:%M"), CurrentTime.FormatGmt(L"%d.%m.%Y %H:%M"));
在大多数情况下,它工作得很好,输出如下所示:
但是,有时会显示 %
开关而不是数据,然后它看起来像:
我试图进一步了解它失败的原因,所以失败的函数调用是:
int ct = GetCurrentTransactionNo();
if (m_logfunc)
m_logfunc(L"Checking transaction no. %d.", ct);
但是如果我把它改成:
int ct = 15;
if (m_logfunc)
m_logfunc(L"Checking transaction no. %d.", ct);
我得到了正确格式化的字符串。
我认为正如其他人所暗示的那样,问题出在未定义的行为上。
您已经确定问题是由 0
的值引起的。好吧,0
也可以被认为是一个nullptr
(或者空指针)。
我无法验证我的解释,但认为您的 0
参数列表可能被解释为空指针(无参数),因此 return 值是格式字符串不变。
当您传递的值为 0
时,也许您应该考虑定制解决方案并以不同的方式处理它。例如(我不确定你是否可以使用同名的重载函数):
void MyDialog::ShowStatus2(LPCWSTR lpText, int iValue)
{
CString sMsg;
sMsg.Format(lpText, iValue);
CWinApp *myApp = AfxGetApp();
if (myApp)
{
CWnd *pWnd = myApp->m_pMainWnd;
if (pWnd)
{
::SetDlgItemText(pWnd->GetSafeHwnd(), IDC_MYSTATUSBAR, sMsg.GetString());
}
}
myLogFunction(L"%s", sMsg.GetString());
}
I have done that and in the problematic cases I can see that after calling FormatV sMsg
contains some "%" characters. In other words FormatV
fails, but I don't know why.
当您处于调试模式时,您可能会发现查看 ptr
的值很有用,因为它包含已解析的参数。当此参数的传入值为0
时需要查看ptr
是否为null。
当传递给函数的第一个参数是 0 或 "" 时,*ptr
(实际上是 *ptr[0]
或 *(ptr + 0)
,因此内存中的第一个块)将保持null,因此该函数按字面打印字符串,而不使用 FormatV()
.
进行格式化
您需要替换以下块
if (*ptr == 0)
sMsg = lpText;
else
sMsg.FormatV(lpText, ptr);
只有
sMsg.FormatV(lpText, ptr);
您可以使用 C++11 中引入的参数包来解决此问题
class Temp
{
public:
template< typename ... Type >
static void ShowStatus( LPCWSTR lpText, Type ... args )
{
CString sMsg;
sMsg.Format( lpText, args... );
MessageBox( NULL, sMsg.GetString(), L"", MB_OK );
}
};
int main()
{
int ct = 0;
wchar_t test[] = L"this is a test";
Temp::ShowStatus( L"%d", ct );
Temp::ShowStatus( L"%s", test );
Temp::ShowStatus( L"%d %s", ct, test );
}
我需要在对话框中显示状态更新,并希望能够向其发送 printf
样式的格式化字符串。此外,我希望该函数调用一个类似的函数,将格式化的数据添加到日志文件中。
假设我的 Static 控件被调用 IDC_MYSTATUSBAR
,函数如下所示:
void MyDialog::ShowStatus(LPCWSTR lpText, ...)
{
CString sMsg;
va_list ptr;
va_start(ptr, lpText);
if (*ptr == 0)
sMsg = lpText;
else
sMsg.FormatV(lpText, ptr);
va_end(ptr);
CWinApp *myApp = AfxGetApp();
if (myApp)
{
CWnd *pWnd = myApp->m_pMainWnd;
if (pWnd)
{
::SetDlgItemText(pWnd->GetSafeHwnd(), IDC_MYSTATUSBAR, sMsg.GetString());
}
}
myLogFunction(L"%s", sMsg.GetString());
}
函数调用如下:
ShowStatus(L"The results are %d.", 100);
或
ShowStatus(L"Server returned the following result: %s", L"result");
但是其他模块和库通过一个名为 logfunc
的成员变量调用我的函数,该变量定义为:
typedef void(*logFunc)(LPCWSTR lpText, ...);
在这种情况下,来自另一个模块的代码将如下所示:
logFunc m_logfunc;
if (m_logfunc) m_logfunc(L"Internet Time: (%d) %s Local Time = %s", result, TimeResult.FormatGmt(L"%d.%m.%Y %H:%M"), CurrentTime.FormatGmt(L"%d.%m.%Y %H:%M"));
在大多数情况下,它工作得很好,输出如下所示:
但是,有时会显示 %
开关而不是数据,然后它看起来像:
我试图进一步了解它失败的原因,所以失败的函数调用是:
int ct = GetCurrentTransactionNo();
if (m_logfunc)
m_logfunc(L"Checking transaction no. %d.", ct);
但是如果我把它改成:
int ct = 15;
if (m_logfunc)
m_logfunc(L"Checking transaction no. %d.", ct);
我得到了正确格式化的字符串。
我认为正如其他人所暗示的那样,问题出在未定义的行为上。
您已经确定问题是由 0
的值引起的。好吧,0
也可以被认为是一个nullptr
(或者空指针)。
我无法验证我的解释,但认为您的 0
参数列表可能被解释为空指针(无参数),因此 return 值是格式字符串不变。
当您传递的值为 0
时,也许您应该考虑定制解决方案并以不同的方式处理它。例如(我不确定你是否可以使用同名的重载函数):
void MyDialog::ShowStatus2(LPCWSTR lpText, int iValue)
{
CString sMsg;
sMsg.Format(lpText, iValue);
CWinApp *myApp = AfxGetApp();
if (myApp)
{
CWnd *pWnd = myApp->m_pMainWnd;
if (pWnd)
{
::SetDlgItemText(pWnd->GetSafeHwnd(), IDC_MYSTATUSBAR, sMsg.GetString());
}
}
myLogFunction(L"%s", sMsg.GetString());
}
I have done that and in the problematic cases I can see that after calling
FormatV sMsg
contains some "%" characters. In other wordsFormatV
fails, but I don't know why.
当您处于调试模式时,您可能会发现查看 ptr
的值很有用,因为它包含已解析的参数。当此参数的传入值为0
时需要查看ptr
是否为null。
当传递给函数的第一个参数是 0 或 "" 时,*ptr
(实际上是 *ptr[0]
或 *(ptr + 0)
,因此内存中的第一个块)将保持null,因此该函数按字面打印字符串,而不使用 FormatV()
.
您需要替换以下块
if (*ptr == 0)
sMsg = lpText;
else
sMsg.FormatV(lpText, ptr);
只有
sMsg.FormatV(lpText, ptr);
您可以使用 C++11 中引入的参数包来解决此问题
class Temp
{
public:
template< typename ... Type >
static void ShowStatus( LPCWSTR lpText, Type ... args )
{
CString sMsg;
sMsg.Format( lpText, args... );
MessageBox( NULL, sMsg.GetString(), L"", MB_OK );
}
};
int main()
{
int ct = 0;
wchar_t test[] = L"this is a test";
Temp::ShowStatus( L"%d", ct );
Temp::ShowStatus( L"%s", test );
Temp::ShowStatus( L"%d %s", ct, test );
}