字符串参数常量字符* 和常量 wchar_t*
String parameter const char* and const wchar_t*
尝试为 msi 安装程序设置外部 UI。
我从这里复制并粘贴了以下代码,我希望这些代码开箱即用: https://msdn.microsoft.com/en-us/library/windows/desktop/aa368786(v=vs.85).aspx
MsiSetExternalUI (TestMyBasicUIHandler,
INSTALLLOGMODE_PROGRESS|INSTALLLOGMODE_FATALEXIT|INSTALLLOGMODE_ERROR
|INSTALLLOGMODE_WARNING|INSTALLLOGMODE_USER|INSTALLLOGMODE_INFO
|INSTALLLOGMODE_RESOLVESOURCE|INSTALLLOGMODE_OUTOFDISKSPACE
|INSTALLLOGMODE_ACTIONSTART|INSTALLLOGMODE_ACTIONDATA
|INSTALLLOGMODE_COMMONDATA|INSTALLLOGMODE_PROGRESS|INSTALLLOGMODE_INITIALIZE
|INSTALLLOGMODE_TERMINATE|INSTALLLOGMODE_SHOWDIALOG,
TEXT("TEST"));
错误是:
invalid conversion from 'int (__attribute__((__stdcall__)) *)(LPVOID, UINT, LPCSTR) {aka int (__attribute__((__stdcall__)) *)(void*, unsigned int, const char*)}' to 'INSTALLUI_HANDLERW {aka int (__attribute__((__stdcall__)) *)(void*, unsigned int, const wchar_t*)}' [-fpermissive]
TEXT("TEST"));
^
因此,错误似乎是由于期望const wchar_t*
但仅传递const char*
。 我也试过这个,也不起作用:
std::string test = "TEST";
wchar_t *someString = new wchar_t[ test.length() + 1 ];
std::copy( test.begin(), test.end(), someString );
someString[ test.length() ] = 0;
然后我用someString
而不是TEXT("TEST")
调用该方法。
这是代码:
// https://msdn.microsoft.com/en-us/library/windows/desktop/aa368786(v=vs.85).aspx
// Globals
//
// common data information fields
int g_rgiField[3]; //array of fields to handle INSTALLOGMODE_COMMONDATA data
WORD g_wLANGID = LANG_NEUTRAL; // initialize to neutral language
//
// progress information fields
int iField[4]; //array of record fields to handle INSTALLOGMODE_PROGRESS data
int g_iProgressTotal = 0; // total ticks on progress bar
int g_iProgress = 0; // amount of progress
int iCurPos = 0;
BOOL bFirstTime = TRUE;
BOOL g_bForwardProgress = TRUE; //TRUE if the progress bar control should be incremented in a forward direction
BOOL g_bScriptInProgress = FALSE;
BOOL g_bEnableActionData; //TRUE if INSTALLOGMODE_ACTIONDATA messages are sending progress information
BOOL g_bCancelInstall = FALSE; //Should be set to TRUE if the user clicks Cancel button.
// In the following snippet, note that the internal user
// interface level is set to INSTALLLEVEL_NONE. If the internal
// user interface level is set to anything other than
// INSTALLUILEVEL_NONE, the user interface level is
// INSTALLUILEVEL_BASIC by default and the installer only
// displays an initial dialog. If the authored wizard
// sequence of the package is to be displayed, the user
// interface level should be set to INSTALLUILEVEL_FULL.
// If the external user interface handler is to have full
// control of the installation user interface, the user
// interface level must be set to INSTALLUILEVEL_NONE.
// Because an external UI handler cannot handle the
// INSTALLMESSAGE_RESOLVESOURCE message,
// Windows Installer allows a UI level,INSTALLUILEVEL_SOURCERESONLY
// that will allow an external UI handler to have full control while also still
// permitting an install to resolve the source
//
// FUNCTION: FGetInteger(char*& pch)
//
// PURPOSE: Converts the string (from current pos. to next whitespace or ' ')
// to an integer.
//
// COMMENTS: Assumes correct syntax. Ptr is updated to new position at whitespace
// or null terminator.
//
int FGetInteger(char*& rpch)
{
char* pchPrev = rpch;
while (*rpch && *rpch != ' ')
rpch++;
*rpch = ' ';
int i = atoi(pchPrev);
return i;
}
//
// FUNCTION: ParseProgressString(LPSTR sz)
//
// PURPOSE: Parses the progress data message sent to the INSTALLUI_HANDLER callback
//
// COMMENTS: Assumes correct syntax.
//
//
// FUNCTION: ParseCommonDataString(LPSTR sz)
//
// PURPOSE: Parses the common data message sent to the INSTALLUI_HANDLER callback
//
// COMMENTS: Ignores the 3rd field and the caption common data message. Assumes correct syntax.
//
BOOL ParseCommonDataString(LPSTR sz)
{
char *pch = sz;
if (0 == *pch)
return FALSE; // no msg
while (*pch != 0)
{
char chField = *pch++;
pch++; // for ':'
pch++; // for sp
switch (chField)
{
case '1': // field 1
{
// common data message type
g_rgiField[0] = *pch++ - '0';
if (g_rgiField[0] == 1)
return FALSE; // we are ignoring caption messages
break;
}
case '2': // field 2
{
// because we are ignoring caption msg, these are all ints
g_rgiField[1] = FGetInteger(pch);
return TRUE; // done processing
}
default: // unknown field
{
return FALSE;
}
}
pch++; // for space (' ') between fields
}
return TRUE;
}
BOOL ParseProgressString(LPSTR sz)
{
char *pch = sz;
if (0 == *pch)
return FALSE; // no msg
while (*pch != 0)
{
char chField = *pch++;
pch++; // for ':'
pch++; // for sp
switch (chField)
{
case '1': // field 1
{
// progress message type
if (0 == isdigit(*pch))
return FALSE; // blank record
iField[0] = *pch++ - '0';
break;
}
case '2': // field 2
{
iField[1] = FGetInteger(pch);
if (iField[0] == 2 || iField[0] == 3)
return TRUE; // done processing
break;
}
case '3': // field 3
{
iField[2] = FGetInteger(pch);
if (iField[0] == 1)
return TRUE; // done processing
break;
}
case '4': // field 4
{
iField[3] = FGetInteger(pch);
return TRUE; // done processing
}
default: // unknown field
{
return FALSE;
}
}
pch++; // for space (' ') between fields
}
return TRUE;
}
int _stdcall TestMyBasicUIHandler(LPVOID pvContext, UINT iMessageType, LPCTSTR szMessage)
{
// File costing is skipped when applying Patch(es) and INSTALLUILEVEL is NONE.
// Workaround: Set INSTALLUILEVEL to anything but NONE only once.
if (bFirstTime == TRUE)
{
UINT r1 = MsiSetInternalUI(INSTALLUILEVEL_BASIC, NULL);
bFirstTime = FALSE;
}
if (!szMessage)
return 0;
INSTALLMESSAGE mt;
UINT uiFlags;
mt = (INSTALLMESSAGE)(0xFF000000 & (UINT)iMessageType);
uiFlags = 0x00FFFFFF & iMessageType;
switch (mt)
{
//Premature termination
case INSTALLMESSAGE_FATALEXIT:
/* Get fatal error message here and display it*/
return 0; //MessageBox(0, szMessage, TEXT("FatalError"), uiFlags);
case INSTALLMESSAGE_ERROR:
{
/* Get error message here and display it*/
// language and caption can be obtained from common data msg
MessageBeep(uiFlags & MB_ICONMASK);
return 0; //MessageBoxEx(0, szMessage, TEXT("Error"), uiFlags, g_wLANGID);
}
case INSTALLMESSAGE_WARNING:
/* Get warning message here and display it */
return 0; //MessageBox(0, szMessage, TEXT("Warning"), uiFlags);
case INSTALLMESSAGE_USER:
/* Get user message here */
// parse uiFlags to get Message Box Styles Flag and return appopriate value, IDOK, IDYES, etc.
return IDOK;
case INSTALLMESSAGE_INFO:
return IDOK;
case INSTALLMESSAGE_FILESINUSE:
/* Display FilesInUse dialog */
// parse the message text to provide the names of the
// applications that the user can close so that the
// files are no longer in use.
return 0;
case INSTALLMESSAGE_RESOLVESOURCE:
/* ALWAYS return 0 for ResolveSource */
return 0;
case INSTALLMESSAGE_OUTOFDISKSPACE:
/* Get user message here */
return IDOK;
case INSTALLMESSAGE_ACTIONSTART:
/* New action started, any action data is sent by this new action */
g_bEnableActionData = FALSE;
return IDOK;
case INSTALLMESSAGE_ACTIONDATA:
// only act if progress total has been initialized
if (0 == g_iProgressTotal)
return IDOK;
// SetDlgItemText(/*handle to your dialog*/,/*identifier of your actiontext control*/, szMessage);
if(g_bEnableActionData)
{
// SendMessage(/*handle to your progress control*/,PBM_STEPIT,0,0);
}
return IDOK;
case INSTALLMESSAGE_PROGRESS:
{
#if (0)
if(ParseProgressString(const_cast<LPSTR>(szMessage)))
{
// all fields off by 1 due to c array notation
switch(iField[0])
{
case 0: // Reset progress bar
{
//field 1 = 0, field 2 = total number of ticks, field 3 = direction, field 4 = in progress
/* get total number of ticks in progress bar */
g_iProgressTotal = iField[1];
/* determine direction */
if (iField[2] == 0)
g_bForwardProgress = TRUE;
else // iField[2] == 1
g_bForwardProgress = FALSE;
/* get current position of progress bar, depends on direction */
// if Forward direction, current position is 0
// if Backward direction, current position is Total # ticks
g_iProgress = g_bForwardProgress ? 0 : g_iProgressTotal;
// SendMessage(/*handle to your progress control*/, PBM_SETRANGE32, 0, g_iProgressTotal);
// if g_bScriptInProgress, finish progress bar, else reset (and set up according to direction)
// SendMessage(/*handle to your progress control*/, PBM_SETPOS, g_bScriptInProgress ? g_iProgressTotal : g_iProgress, 0);
iCurPos = 0;
/* determine new state */
// if new state = 1 (script in progress), could send a "Please wait..." msg
// new state = 1 means the total # of progress ticks is an estimate, and may not add up correctly
g_bScriptInProgress = (iField[3] == 1) ? TRUE : FALSE;
break;
}
case 1: // ActionInfo
{
//field 1 = 1, field 2 will contain the number of ticks to increment the bar
//ignore if field 3 is zero
if(iField[2])
{
// movement direction determined by g_bForwardProgress set by reset progress msg
// SendMessage(/*handle to your progress control*/, PBM_SETSTEP, g_bForwardProgress ? iField[1] : -1*iField[1], 0);
g_bEnableActionData = TRUE;
}
else
{
g_bEnableActionData = FALSE;
}
break;
}
case 2: //ProgressReport
{
// only act if progress total has been initialized
if (0 == g_iProgressTotal)
break;
iCurPos += iField[1];
//field 1 = 2,field 2 will contain the number of ticks the bar has moved
// movement direction determined by g_bForwardProgress set by reset progress msg
cout /*<< PBM_SETPOS << ", " */<< iCurPos << endl;
// SendMessage(/*handle to your progress control*/, PBM_SETPOS, g_bForwardProgress ? iCurPos : -1*iCurPos, 0);
break;
}
case 3: // ProgressAddition - fall through (we don't care to handle it -- total tick count adjustment)
default:
{
break;
}
}
}
#endif
if(g_bCancelInstall == TRUE)
{
return IDCANCEL;
}
else
return IDOK;
}
case INSTALLMESSAGE_COMMONDATA:
{
#if ( 0)
if (ParseCommonDataString(const_cast<LPSTR>(szMessage)))
{
// all fields off by 1 due to c array notation
switch (g_rgiField[0])
{
case 0:
// field 1 = 0, field 2 = LANGID, field 3 = CodePage
g_wLANGID = g_rgiField[1];
break;
case 1:
// field 1 = 1, field 2 = CAPTION
/* you could use this as the caption for MessageBoxes */
break;
case 2:
// field 1 = 2, field 2 = 0 (hide cancel button) OR 1 (show cancel button)
// ShowWindow(/*handle to cancel button control on the progress indicator dialog box*/, g_rgiField[1] == 0 ? SW_HIDE : SW_SHOW);
break;
default:
break;
}
}
#endif
return IDOK;
}
// this message is received prior to internal UI initialization, no string data
case INSTALLMESSAGE_INITIALIZE:
return IDOK;
// Sent after UI termination, no string data
case INSTALLMESSAGE_TERMINATE:
return IDOK;
//Sent prior to display of authored dialog or wizard
case INSTALLMESSAGE_SHOWDIALOG:
return IDOK;
default:
return 0;
}
}
这就是我所说的:
// https://msdn.microsoft.com/en-us/library/windows/desktop/aa370053(v=vs.85).aspx
// https://msdn.microsoft.com/en-us/library/windows/desktop/aa368786(v=vs.85).aspx
MsiSetInternalUI(INSTALLUILEVEL(INSTALLUILEVEL_NONE|INSTALLUILEVEL_SOURCERESONLY), NULL);
// std::string test = "TEST";
// wchar_t *someString = new wchar_t[ test.length() + 1 ];
// std::copy( test.begin(), test.end(), someString );
// someString[ test.length() ] = 0;
MsiSetExternalUI (TestMyBasicUIHandler,
INSTALLLOGMODE_PROGRESS|INSTALLLOGMODE_FATALEXIT|INSTALLLOGMODE_ERROR
|INSTALLLOGMODE_WARNING|INSTALLLOGMODE_USER|INSTALLLOGMODE_INFO
|INSTALLLOGMODE_RESOLVESOURCE|INSTALLLOGMODE_OUTOFDISKSPACE
|INSTALLLOGMODE_ACTIONSTART|INSTALLLOGMODE_ACTIONDATA
|INSTALLLOGMODE_COMMONDATA|INSTALLLOGMODE_PROGRESS|INSTALLLOGMODE_INITIALIZE
|INSTALLLOGMODE_TERMINATE|INSTALLLOGMODE_SHOWDIALOG,
TEXT("TEST"));
MsiInstallProduct(save_path,NULL);
TEXT
宏会根据UNICODE
项目设置更改常量文本。
如果您知道自己想要什么,请为 unicode 字符串(又名const wchar_t*
)传递L"Test"
,为 ANSI 字符串(又名const char*
)传递"Test"
。
再次阅读您的错误消息后:您的错误正在通过TestMyBasicUIHandler
。
这个(我承认你从官方示例来源复制了它):
int _stdcall TestMyBasicUIHandler(LPVOID pvContext, UINT iMessageType, LPCSTR szMessage)
是完全错误的,并且只能通过随机机会在他们的项目中设置正确的标志。
正确的版本是:
int _stdcall TestMyBasicUIHandler(LPVOID pvContext, UINT iMessageType, LPCTSTR szMessage);
请注意符合UNICODE
设置的LPCTSTR
(与LPCSTR
相反)类型。
通过查看它的实际文档可以看出。不好的例子。坏,坏例子。
示例代码似乎从未使用UNICODE
进行测试。经历所有必要的潜在变化真的没有意义。如果要编译示例,请将项目设置为ANSI
。如果您需要UNICODE
,请先了解一下,您应该能够自己修复所有弹出的错误。
我发布了一个替代答案,因为我不同意另一个答案中的建议。
MsiSetExternalUI (TestMyBasicUIHandler, INSTALLLOGMODE_PROGRESS|INSTALLLOGMODE_FATALEXIT|INSTALLLOGMODE_ERROR |INSTALLLOGMODE_WARNING|INSTALLLOGMODE_USER|INSTALLLOGMODE_INFO |INSTALLLOGMODE_RESOLVESOURCE|INSTALLLOGMODE_OUTOFDISKSPACE |INSTALLLOGMODE_ACTIONSTART|INSTALLLOGMODE_ACTIONDATA |INSTALLLOGMODE_COMMONDATA|INSTALLLOGMODE_PROGRESS|INSTALLLOGMODE_INITIALIZE |INSTALLLOGMODE_TERMINATE|INSTALLLOGMODE_SHOWDIALOG, TEXT("TEST"));
错误是:
'从'int (attribute((stdcall)) *)(LPVOID, UINT, LPCSTR) {aka int (attribute((stdcall)) )(void, unsigned int, const char*)}'到 'INSTALLUI_HANDLERW {aka int (attribute((stdcall)))(void, unsignedint, const wchar_t*)}' [-fpermissive]
让我们分解这个错误。它说它不能将TestMyBasicUIHandler
、int (*)(LPVOID, UINT, LPCSTR)
转换为int (*)(LPVOID, UINT, LPCWSTR)
。唯一的区别是函数的最终参数接受的字符指针的类型(您要将其指针传递给 MsiSetExternalUI)。
这是因为通过配置选项,您实际上是在调用 MsiSetExternalUIW(它需要处理 Unicode 文本的函数)而不是 MsiSetExternalUIA(它需要处理 ANSI 文本的函数)。然而,你传递的是一个INSTALLUI_HANLDERA而不是一个INSTALLUI_HANDLERW。到目前为止,我 100% 同意另一个答案。
但这是我不同意的地方。ANSI支持是陈旧而令人讨厌的,只有在支持Windows 9x系统时才需要。我希望你不要那样做。因此,您应该转换内容以支持Unicode。这个例子不能正确处理 Unicode 真是太糟糕了。但是它使用MsiSetExteralUI也不是很好,当有一个更好的选择时:MsiSetExternalUIRecord。MsiSetExternalUIRecord 的示例代码是 Unicode 就绪的。我建议适应使用它。唯一的缺点是它依赖于Windows Installer 3.1或更高版本(但这在所有当前支持的Windows版本上都是隐含的)。
- #定义c-预处理器常量..我做错了什么
- 用C++中的一个变量定义一个常量
- 什么时候在C++中返回常量引用是个好主意
- 代理对象的常量正确性
- 我想将一个对T类型的非常量左值引用绑定到一个T类型的临时值
- 通过多个头文件使用常量变量
- 在cuda线程之间共享大量常量数据
- 不能在初始值设定项列表中将非常量表达式从类型 'int' 缩小到'unsigned long long'
- 有没有什么方法可以使用一个函数中定义的常量变量,也可以由c++中同一程序中的其他函数使用
- 是默认情况下分配给char数组常量的值
- 私有类型的静态常量成员
- 类似枚举的计算常量
- 递归模板化函数不能分配给具有常量限定类型"const tt &"的变量"state"
- MacOS通过在莫哈韦"wchar.h"下破碎的自制啤酒发出叮当声
- 无法将参数 1 从 WCHAR 转换为常量字符 *
- 如何在 C++ 中的 wcstok 中使用常量 WCHAR* 变量?
- 将字符串文本常量定义为 char const* 和 wchar const*
- VC++ 2017 如何将常量wchar_t转换为 WCHAR *.
- C++ 需要解释“静态常量 WCHAR*”
- "strcpy":无法将参数 2 从"WCHAR *"转换为"常量字符 *