C++ 字符串参数const char*和const wchar\t*

C++ 字符串参数const char*和const wchar\t*,c++,winapi,windows-installer,C++,Winapi,Windows Installer,正在尝试为msi安装程序设置外部ui 我从这里复制并粘贴了以下代码,我希望它可以开箱即用: 错误是: invalid conversion from 'int (__attribute__((__stdcall__)) *)(LPVOID, UINT, LPCSTR) {aka int (__attribute__((__stdcall__)) *)(void*, unsigned int, const char*)}' to 'INSTALLUI_HANDLERW {aka int (__a

正在尝试为msi安装程序设置外部ui

我从这里复制并粘贴了以下代码,我希望它可以开箱即用:

错误是:

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"));
                                         ^
因此,该错误似乎是由于需要一个常量wchar\u t*,但只传递了一个常量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而不是TEXTTEST调用了该方法

代码如下:

// 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 '\0')
//            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 = '\0';
    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;
    }
}
文本宏根据UNICODE项目设置更改常量文本

如果您知道需要什么,请通过unicode字符串aka const wchar\u t*的LTest或ANSI字符串aka const char*的测试

再次阅读错误消息后:您的错误正在通过TestMyBasicUIHandler

我认识到您是从官方示例来源复制的:

int _stdcall TestMyBasicUIHandler(LPVOID pvContext, UINT iMessageType, LPCSTR szMessage) 
这显然是错误的,只是在项目中随机设置了正确的标志

正确的版本是:

int _stdcall TestMyBasicUIHandler(LPVOID pvContext, UINT iMessageType, LPCTSTR szMessage);
请注意,LPCTSTR与LPCSTR类型不同,它将符合您的UNICODE设置

这可以从下面的例子中看出。坏例子。坏的,坏的例子


示例代码似乎从未使用UNICODE进行过测试。经历所有必要的潜在变化是毫无意义的。如果要编译示例,请将项目设置为ANSI。如果您需要UNICODE,那么首先了解它,您应该能够自己修复所有弹出的错误

我发布了另一个答案,因为我不同意另一个答案中的建议

错误是:

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"));
                                         ^
`从“int attributestdcall*LPVOID,UINT,LPCSTR{aka int attributestdcall void,unsigned int,const char*}”到“INSTALLUI\u HANDLERW{aka int attributestdcall void,unsigned int,const wchar\u t*}”的转换无效[-fppermissive]

让我们来分析一下这个错误。它说它不能将TestMyBasicUIHandler,一个int*LPVOID,UINT,LPCSTR转换成int*LPVOID,UINT,LPCWSTR。唯一的区别是传递给MsiSetExternalUI的函数的最终参数接受的字符指针类型

这是因为在配置选项中,您实际上是在调用MsiSetExternalUIW,它需要一个处理Unicode文本的函数,而不是MsiSetExternalUIA,它需要一个处理ANSI文本的函数。但您传递的是INSTALLUI\u handler,而不是INSTALLUI\u handler。到目前为止,我100%同意另一个答案


但这是我不同意的地方。ANSI支持既古老又恶劣,只有在支持Windows 9x系统时才需要。我希望你没有那样做。因此,您应该将其转换为支持Unicode。这个例子不能正确处理Unicode,这太糟糕了。但是,当有更好的选择时,使用MsiSetExteralUI也不太好:。代码已准备好Unicode。我建议适应使用它。唯一的缺点是它依赖于Windows Installer 3.1或更高版本,但这在所有当前支持的Windows版本中都是隐含的。

用LTEST替换TEXTTEST是否可以做到这一点?不,它说粘贴L和LTEST不会提供有效的预处理令牌TEXTLTEST;^我没有被声明这个范围不是TEXTLTEST,只有LTEST。你的代码有一部分是不可读的。看起来您不了解Windows字符集。与其胡思乱想,猜测可能的变化,应用尝试和错误,为什么你不寻求理解呢?我为不可读的部分感到抱歉,我在结尾时很匆忙。我试着去理解。至少最初的问题是因为一个错误的官方例子。这种带有自定义typedef的奇怪Winapi风格还真的不在我的脑海中,我对Qt更满意。。。我应该为在问答网站上提问道歉吗?我尝试了两种建议,但都没有奏效。它抛出了与上面相同的错误,另外还有这个错误:从“const void*”到“LPVOID{aka void*}”[-fppermissive]LTEST的转换无效^我是否需要包含网站上未列出的特殊标题?我加入msi.h是为了让大家知道这些方法,但也许还有别的什么?此外,我确实包含windows.h,并且我链接到user32、gdi32和dwmapi。@user2366975更新了我的答案。谢谢,更改签名后,我收到以下错误:从“const void*”到“LPVOID{aka void*}”[-fpermissive]TEXTTEST;^的转换无效@user2366975请发布您的代码,我猜不出您可能写了什么。A会有帮助的。
int _stdcall TestMyBasicUIHandler(LPVOID pvContext, UINT iMessageType, LPCTSTR szMessage);
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"));