سؤال

لدي مشروع أساسي MSI. أحتاج إلى إزالة منتج MSI آخر على التثبيت المدمج الآن في تطبيقنا الرئيسي. حاولت استخدام سيناريوهات الترقية وتعاملها كترقية كبيرة. ومع ذلك، لم ينجح هذا بسبب رموز الترقية لا تتطابق معني.

بعد ذلك، قمت أيضا باتخاذ إجراءات مخصصة قمت بتشغيل MSIEXEC.EXE بعد costfinalize (أعتقد أن هذا قد ذكر في مساعدة InstallShield.) هذا يعمل تماما حتى مثبت على نظام لم يكن لديك المثبت الذي كنت أتطلع إليه. سوف يفشل المثبت الخاص بي إذا لم يتم تثبيت المنتج القديم الآخر. حاولت وضع شرط على الإجراء المخصص الذي حدده البحث عن النظام، ولكن يبدو أن البحث في النظام محدود في الوظيفة. لا يمكنني فقط التحقق من مفتاح REG وقم بتعيين خاصية Boolean.

أيه أفكار؟

هل كانت مفيدة؟

المحلول

اشياء قليلة لتأخذ في الاعتبار

1) يمكن استخدام الترقية (المنتجات المستندات / إزالة المنتجات) لإزالة مجموعات المنتجات المرتبطة بترقية منتج آخر.

2) إذا كانت الذاكرة تخدم، فلن تقوم MSI بإزالة منتج لكل مستخدم أثناء تثبيت الجهاز لكل جهاز (أو الطريقة الأخرى). السياق يجب أن يكون هو نفسه.

3) لا يعمل تسلسل UI أثناء التثبيت الصامت.

4) لا يمكنك تشغيل MSIEXEC من تسلسل التنفيذ لأن هناك نظام كبير للنظام من تسلسل تنفيذ واحد فقط لكل جهاز.

5) إذا قمت بالجدول الزمني في واجهة المستخدم (قلت لك بالفعل أنه يجب ألا تدار لأنك لا تدير أثناء التثبيت الصامت)، فهناك موبيوكس آخر يقول فقط 1 واجهة المستخدم لكل عملية.

إذا كنت تذهب من كل مستخدم لكل مستخدم أو لكل جهاز مقابل الجهاز لكل جهاز، فسأعتقد أنه من المقبول، يجب أن تكون قادرا على القيام بما تريد استخدام صفقات الترقية عناصر / طاولة دون كتابة إجراءات مخصصة. وإلا فسوف تحتاج إلى bootstrapper نمط Setup.exe للتعامل مع إلغاء التثبيت قبل إدخال عالم MSIEXEC.

نصائح أخرى

حققت هذا في InstallShield 2013 باستخدام تثبيت مخصص. يتم تنفيذ البرنامج النصي عبر إجراء مخصص في تسلسل UI، لكنني وضعت ذلك بعد مربع الحوار "SetupProgress"، أي قبل "تنفيذ الإجراء" بدلا من بعد coveFinalize (كما يقول الوثائق). أضفت الحالة "غير مثبتة" إلى الإجراء. إذا وضعت هذا في الأمر المقترح، فسوف ينطلق إلغاء التثبيت بمجرد تهيئة المثبت. إذا قمت بنقل الأمر إلى المكان الذي فعلته، فلا يجب أن ينطلق حتى يتم النقر على المستخدم الآن على زر التثبيت النهائي الآن.

السبب في وضع هذا في تسلسل UI هو الحصول على One MSI Installer (أو إلغاء التثبيت) في مشكلة في الوقت المناسب. هذا ببساطة لا يعمل في تسلسل التنفيذ بسبب هذا القيد.

الضعف الأساسي في هذه الطريقة هو أنه كما ذكر كريستوفر، فلن يعمل هذا في تثبيت صامت (موجود أيضا في الوثائق هو أيضا). هذه هي الوسائل الرسمية لتحقيق ذلك. (الدفع: http://helpnet.installshield.com/installshield16helplplib/ihelpcustomacymsiexec.htm.) إذا كنت تستطيع العيش مع ذلك (نظرا لأن التثبيت الصامت هو عموما كسيناريو خاصا خاصا)، فهذا يعمل بشكل جيد فقط.

كما قال كريس أيضا، لا يمكنك تشغيل UNSTALLER UI أثناء تشغيل UI الأساسي، ولكن هذه ليست مشكلة في البرنامج النصي لأنه يضيف مفتاح سطر الأوامر لتشغيل إلغاء التثبيت بدون UI (أي بصمت).

يتجنب النصي أيضا الاضطرار إلى معرفة GUID للتطبيق الذي تريد إلغاء تثبيته. إليك البرنامج النصي لربط الإجراء المخصص (إلغاء التثبيت هو وظيفة نقطة الدخول):

////////////////////////////////////////////////////////////////////////////////
    //                                                                            
    //  This template script provides the code necessary to build an entry-point 
    //  function to be called in an InstallScript custom action. 
    //                                                                            
    //                                                                            
    //    File Name:  Setup.rul                                                   
    //                                                                            
    //  Description:  InstallShield script                                        
    //
    ////////////////////////////////////////////////////////////////////////////////

    // Include Ifx.h for built-in InstallScript function prototypes, for Windows 
    // Installer API function prototypes and constants, and to declare code for 
    // the OnBegin and OnEnd events.
    #include "ifx.h"

    // The keyword export identifies MyFunction() as an entry-point function.
    // The argument it accepts must be a handle to the Installer database.

    export prototype UninstallPriorVersions(HWND);

    // To Do:  Declare global variables, define constants, and prototype user-
    //         defined and DLL functions here.

    prototype NUMBER UninstallApplicationByName( STRING );
    prototype NUMBER GetUninstallCmdLine( STRING, BOOL, BYREF STRING );
    prototype STRING GetUninstallKey( STRING );
    prototype NUMBER RegDBGetSubKeyNameContainingValue( NUMBER, STRING, STRING, STRING, BYREF STRING );

    // To Do:  Create a custom action for this entry-point function:
    // 1.  Right-click on "Custom Actions" in the Sequences/Actions view.
    // 2.  Select "Custom Action Wizard" from the context menu.
    // 3.  Proceed through the wizard and give the custom action a unique name.
    // 4.  Select "Run InstallScript code" for the custom action type, and in
    //     the next panel select "MyFunction" (or the new name of the entry-
    //     point function) for the source.
    // 5.  Click Next, accepting the default selections until the wizard
    //     creates the custom action.
    //
    // Once you have made a custom action, you must execute it in your setup by
    // inserting it into a sequence or making it the result of a dialog's
    // control event.


    ///////////////////////////////////////////////////////////////////////////////
    //                                                                           
    // Function:  UninstallPriorVersions
    //                                                                           
    //  Purpose:  Uninstall prior versions of this application
    //                                                                           
    ///////////////////////////////////////////////////////////////////////////////
    function UninstallPriorVersions(hMSI)
    begin

        UninstallApplicationByName( "The Name Of Some App" );           

    end;


    ///////////////////////////////////////////////////////////////////////////////
    //                                                                           
    // Function: UninstallApplicationByName
    //                                                                           
    // Purpose: Uninstall an application (without knowing the guid)
    //                        
    // Returns: (UninstCmdLine is assigned a value by referrence)
    //      >= ISERR_SUCCESS    The function successfully got the command line.
    //      < ISERR_SUCCESS     The function failed to get the command line.
    //
    ///////////////////////////////////////////////////////////////////////////////
    function NUMBER UninstallApplicationByName( AppName )
        NUMBER nReturn;
        STRING UninstCmdLine;
    begin           

        nReturn = GetUninstallCmdLine( AppName, TRUE, UninstCmdLine );  
        if( nReturn < ISERR_SUCCESS ) then
            return nReturn;
        endif;

        if( LaunchAppAndWait( "", UninstCmdLine, LAAW_OPTION_WAIT) = 0 ) then 
            return ISERR_SUCCESS;
        else
            return ISERR_SUCCESS-1;
        endif; 

    end;


    ///////////////////////////////////////////////////////////////////////////////
    //                                                                           
    // Function: GetUninstallCmdLine 
    //                                                                           
    // Purpose: Get the command line statement to uninstall an application 
    //                        
    // Returns: (UninstCmdLine is assigned a value by referrence)
    //      >= ISERR_SUCCESS    The function successfully got the command line.
    //      < ISERR_SUCCESS     The function failed to get the command line.
    //
    ///////////////////////////////////////////////////////////////////////////////
    function NUMBER GetUninstallCmdLine( AppName, Silent, UninstCmdLine )
        NUMBER nReturn;
    begin           

        nReturn = RegDBGetUninstCmdLine ( GetUninstallKey( AppName ), UninstCmdLine );
        if( nReturn < ISERR_SUCCESS ) then
            return nReturn;
        endif;

        if( Silent && StrFind( UninstCmdLine, "MsiExec.exe" ) >= 0 )then
            UninstCmdLine = UninstCmdLine + " /qn"; 
        endif; 

        return nReturn;
    end;


    ///////////////////////////////////////////////////////////////////////////////
    //                                                                           
    // Function: GetUninstallKey
    //                                                                           
    // Purpose:  Find the uninstall key in the registry for an application looked up by name
    //      
    // Returns: The uninstall key (i.e. the guid or a fall back value)
    //                                                                       
    ///////////////////////////////////////////////////////////////////////////////
    function STRING GetUninstallKey( AppName )
        STRING guid;
        STRING Key64, Key32, ValueName;
    begin

        Key64 = "SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Uninstall";
        Key32 = "SOFTWARE\\Wow6432Node\\\Microsoft\\Windows\\CurrentVersion\\Uninstall"; 
        ValueName = "DisplayName";

        if( RegDBGetSubKeyNameContainingValue( HKEY_LOCAL_MACHINE, Key64, ValueName, AppName, guid ) = 0 ) then
            return guid; // return 64 bit GUID
        endif;

        if( RegDBGetSubKeyNameContainingValue( HKEY_LOCAL_MACHINE, Key32, ValueName, AppName, guid ) = 0 ) then
            return guid; // return 32 bit GUID
        endif;

        return AppName; // return old style uninstall key (fall back value)

    end;


    ///////////////////////////////////////////////////////////////////////////////
    //                                                                           
    // Function: RegDBGetSubKeyNameContainingValue
    //                                                                           
    // Purpose:  Find a registry sub key containing a given value.
    //           Return the NAME of the subkey (NOT the entire key path)
    //
    // Returns: (SubKeyName is assigned a value by referrence)
    //      = 0     A sub key name was found with a matching value
    //      != 0    Failed to find a sub key with a matching value
    //                                                                           
    ///////////////////////////////////////////////////////////////////////////////
    function NUMBER RegDBGetSubKeyNameContainingValue( nRootKey, Key, ValueName, Value, SubKeyName )
        STRING SearchSubKey, SubKey, svValue;
        NUMBER nResult, nType, nvSize;
        LIST   listSubKeys;
    begin

        SubKeyName = "";

        listSubKeys  = ListCreate(STRINGLIST);
        if (listSubKeys = LIST_NULL) then
            MessageBox ("Unable to create necessary list.", SEVERE);
            abort;
        endif;

        RegDBSetDefaultRoot( nRootKey );

        if (RegDBQueryKey( Key, REGDB_KEYS, listSubKeys ) = 0) then    
            nResult = ListGetFirstString (listSubKeys, SubKey); 
            while (nResult != END_OF_LIST) 
                SearchSubKey = Key + "\\" + SubKey;
                nType = REGDB_STRING;
                if (RegDBGetKeyValueEx (SearchSubKey, ValueName, nType, svValue, nvSize) = 0) then
                    if( svValue = Value ) then              
                        SubKeyName = SubKey;     
                        nResult = END_OF_LIST;
                    endif;
                endif;      
                if( nResult != END_OF_LIST ) then                       
                    nResult = ListGetNextString (listSubKeys, SubKey); 
                endif;
            endwhile; 
        endif;

        ListDestroy (listSubKeys );

        if ( SubKeyName = "" ) then
            return 1;
        else
            return 0;
        endif;

    end;
مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top