简体   繁体   English

字符串参数const char *和const wchar_t *

[英]String parameter const char* and const wchar_t*

Trying to set up a external ui for a msi installer. 尝试为MSI安装程序设置外部ui。

I copy & pasted following code from here, which I expected to work out-of-box: https://msdn.microsoft.com/en-us/library/windows/desktop/aa368786(v=vs.85).aspx 我从此处复制并粘贴以下代码,希望可以直接使用: https : //msdn.microsoft.com/zh-cn/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"));

Error is: 错误是:

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"));
                                         ^

So it seems the error is due to expecting a const wchar_t* but only passing a const char* . 因此似乎错误是由于期望const wchar_t*但仅传递const char* I also tried this, which did not work neither: 我也尝试了一下,但没有成功:

std::string test = "TEST";
wchar_t *someString = new wchar_t[ test.length() + 1 ];
std::copy( test.begin(), test.end(), someString );
someString[ test.length() ] = 0;

Then I called the method with someString instead of TEXT("TEST") . 然后,我使用someString而不是TEXT("TEST")调用了该方法。

Here is the code: 这是代码:

// 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;
    }
}

And this is how I call it: 这就是我所说的:

// 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);

The TEXT macro changes your constant text according to your UNICODE project settings. TEXT宏根据UNICODE项目设置更改您的常量文本。

If you know what you want, pass either L"Test" for a unicode string (aka const wchar_t* ) or "Test" for an ANSI string (aka const char* ). 如果你知道你想要什么,通过其中L"Test"的Unicode字符串(又名const wchar_t* )或"Test"为ANSI字符串(又名const char* )。


After reading your error message again: your mistake is passing TestMyBasicUIHandler . 再次阅读错误消息后:您的错误是通过TestMyBasicUIHandler

This (and I recognize you copied it from an official example source): 这(我认识到您是从官方示例源复制过来的):

int _stdcall TestMyBasicUIHandler(LPVOID pvContext, UINT iMessageType, LPCSTR szMessage) 

Is plain wrong and only working through random chance of having the correct flags set in their project. 这是完全错误的,只能通过随机的机会在项目中设置正确的标志来进行。

The correct version is: 正确的版本是:

int _stdcall TestMyBasicUIHandler(LPVOID pvContext, UINT iMessageType, LPCTSTR szMessage);

Notice the LPCTSTR (in contrast to LPCSTR ) type that will conform to your UNICODE setting. 请注意,符合您的UNICODE设置的LPCTSTR (与LPCSTR相反)。

As can be seen by looking at the actual documentation for it . 通过查看实际文档可以看出。 Bad example. 不好的例子。 Bad, bad example. 不好,不好的例子。


The example code seems to have never been tested with UNICODE . 该示例代码似乎从未使用UNICODE进行过测试。 There is really no point in going through all the potential changes necessary. 确实没有必要进行所有必要的潜在更改。 Set your project to ANSI if you want to compile the example. 如果要编译示例,请将项目设置为ANSI If you need UNICODE , then learn about that first and you should be able to fix all the mistakes that pop up yourself. 如果您需要UNICODE ,那么请先了解这一点,然后您将能够解决自己冒出的所有错误。

I'm posting an alternate answer because I disagree with the recommendation made in the other answer. 我发布了另一个答案,因为我不同意其他答案中的建议。

 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")); 

Error is: 错误是:

`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] `从'int( attribute (( stdcall ))*)(LPVOID,UINT,LPCSTR){aka int( attribute (( stdcall )) )(void ,unsigned int,const char *)} 无效转换为'INSTALLUI_HANDLERW {aka int( attribute (( stdcall )) )(void ,unsigned int,const wchar_t *)}'[-fpermissive]

Let's break this error down. 让我们分解一下这个错误。 It's saying it cannot convert TestMyBasicUIHandler , an int (*)(LPVOID, UINT, LPCSTR) to an int (*)(LPVOID, UINT, LPCWSTR) . 这表示它无法将TestMyBasicUIHandler ,一个int (*)(LPVOID, UINT, LPCSTR)为一个int (*)(LPVOID, UINT, LPCWSTR) The only difference is the type of character pointer accepted by the final parameter to the function (whose pointer you are passing to MsiSetExternalUI). 唯一的区别是该函数的最终参数接受的字符指针的类型(将其传递给MsiSetExternalUI的指针)。

This is because somehow with your configuration options, you're really calling MsiSetExternalUIW (which expects a function handling Unicode text) instead of MsiSetExternalUIA (which expects a function handling ANSI text). 这是因为以某种方式使用您的配置选项,您实际上是在调用MsiSetExternalUIW(它需要一个处理Unicode文本的函数)而不是MsiSetExternalUIA(它需要一个处理ANSI文本的函数)。 And yet you're passing an INSTALLUI_HANLDERA instead of an INSTALLUI_HANDLERW. 但是,您传递的是INSTALLUI_HANLDERA而不是INSTALLUI_HANDLERW。 So far I agree 100% with the other answer. 到目前为止,我同意其他答案的100%。

But here's where I disagree. 但是,这就是我不同意的地方。 ANSI support is old and nasty, and only required when you support a Windows 9x system. ANSI支持既旧又讨厌,仅在支持Windows 9x系统时才需要。 I hope you're not doing that. 我希望你不要那样做。 So instead you should convert things to support Unicode. 因此,您应该将其转换为支持Unicode。 It's awful that this example doesn't handle Unicode correctly. 糟糕的是,此示例未正确处理Unicode。 But it's also not great that it's using MsiSetExteralUI at all, when there's a better option out there: MsiSetExternalUIRecord . 但是,如果有更好的选择: MsiSetExternalUIRecord ,则根本不使用MsiSetExteralUI也是不好的 The example code for MsiSetExternalUIRecord is Unicode-ready. MsiSetExternalUIRecord示例代码已支持Unicode。 I would suggest adapting to use it. 我建议适应使用它。 The only down side is its dependency on Windows Installer 3.1 or later (but that's implicit on all currently supported versions of Windows). 唯一的缺点是它对Windows Installer 3.1或更高版本的依赖(但这在所有当前受支持的Windows版本中都是隐含的)。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM