MSI: implement CreateShortcut and PublishProduct

Mike McCormack mike at codeweavers.com
Sat Jul 3 20:45:03 CDT 2004


ChangeLog:
<aric at codeweavers.com>
* implement CreateShortcut and PublishProduct
-------------- next part --------------
diff -ur dlls/msi.old/action.c dlls/msi/action.c
--- dlls/msi.old/action.c	2004-07-03 20:41:29.000000000 -0500
+++ dlls/msi/action.c	2004-07-03 20:41:44.000000000 -0500
@@ -145,6 +145,8 @@
 static UINT ACTION_RegisterTypeLibraries(MSIHANDLE hPackage);
 static UINT ACTION_RegisterClassInfo(MSIHANDLE hPackage);
 static UINT ACTION_RegisterProgIdInfo(MSIHANDLE hPackage);
+static UINT ACTION_CreateShortcuts(MSIHANDLE hPackage);
+static UINT ACTION_PublishProduct(MSIHANDLE hPackage);
 
 static UINT HANDLE_CustomType1(MSIHANDLE hPackage, const LPWSTR source, 
                                 const LPWSTR target, const INT type);
@@ -199,6 +201,10 @@
 {'R','e','g','i','s','t','e','r','C','l','a','s','s','I','n','f','o',0};
 const static WCHAR szRegisterProgIdInfo[] = 
 {'R','e','g','i','s','t','e','r','P','r','o','g','I','d','I','n','f','o',0};
+const static WCHAR szCreateShortcuts[] = 
+{'C','r','e','a','t','e','S','h','o','r','t','c','u','t','s',0};
+const static WCHAR szPublishProduct[] = 
+{'P','u','b','l','i','s','h','P','r','o','d','u','c','t',0};
 
 /******************************************************** 
  * helper functions to get around current HACKS and such
@@ -852,12 +858,15 @@
         rc = ACTION_RegisterClassInfo(hPackage);
      else if (strcmpW(action,szRegisterProgIdInfo)==0)
         rc = ACTION_RegisterProgIdInfo(hPackage);
+     else if (strcmpW(action,szCreateShortcuts)==0)
+        rc = ACTION_CreateShortcuts(hPackage);
+    else if (strcmpW(action,szPublishProduct)==0)
+        rc = ACTION_PublishProduct(hPackage);
 
     /*
      Called during itunes but unimplemented and seem important
 
      ResolveSource  (sets SourceDir)
-     CreateShortcuts (would be nice to have soon)
      RegisterProduct
      InstallFinalize
      */
@@ -3049,6 +3058,13 @@
     }
 }
 
+/*
+ * Ok further analysis makes me think that this work is
+ * actually done in the PublishComponents and PublishFeatures
+ * step. And not here.  It appears like the keypath and all that is
+ * resolved in this step, however actaully written in the Publish steps.
+ * But we will leave it here for now 
+ */
 static UINT ACTION_ProcessComponents(MSIHANDLE hPackage)
 {
     MSIPACKAGE* package;
@@ -3712,14 +3728,328 @@
         }
         
         register_progid(hPackage,row,clsid);
+        ui_actiondata(hPackage,szRegisterProgIdInfo,row);
+
+        MsiCloseHandle(row);
+    }
+    MsiViewClose(view);
+    MsiCloseHandle(view);
+    return rc;
+}
+
+static UINT build_icon_path(MSIHANDLE hPackage, LPCWSTR icon_name, 
+                            LPWSTR FilePath)
+{
+    WCHAR ProductCode[0x100];
+    WCHAR SystemFolder[MAX_PATH];
+    DWORD sz;
+
+    static const WCHAR szInstaller[] = 
+{'I','n','s','t','a','l','l','e','r','\\',0};
+    static const WCHAR szProductCode[] =
+{'P','r','o','d','u','c','t','C','o','d','e',0};
+    static const WCHAR szFolder[] =
+{'W','i','n','d','o','w','s','F','o','l','d','e','r',0};
+
+    sz = 0x100;
+    MsiGetPropertyW(hPackage,szProductCode,ProductCode,&sz);
+    if (strlenW(ProductCode)==0)
+        return ERROR_FUNCTION_FAILED;
+
+    sz = MAX_PATH;
+    MsiGetPropertyW(hPackage,szFolder,SystemFolder,&sz);
+    strcatW(SystemFolder,szInstaller); 
+    strcatW(SystemFolder,ProductCode);
+    create_full_pathW(SystemFolder);
+
+    strcpyW(FilePath,SystemFolder);
+    strcatW(FilePath,cszbs);
+    strcatW(FilePath,icon_name);
+    return ERROR_SUCCESS;
+}
+
+static UINT ACTION_CreateShortcuts(MSIHANDLE hPackage)
+{
+    UINT rc;
+    MSIHANDLE view;
+    MSIHANDLE row = 0;
+    static const CHAR *Query = "SELECT * from Shortcut";
+    MSIPACKAGE* package;
+
+    IShellLinkW *sl;
+    IPersistFile *pf;
+    HRESULT res;
+
+    package = msihandle2msiinfo(hPackage, MSIHANDLETYPE_PACKAGE);
+    if (!package)
+        return ERROR_INVALID_HANDLE;
+
+    res = CoInitialize( NULL );
+    if (FAILED (res))
+    {
+        ERR("CoInitialize failed\n");
+        return ERROR_FUNCTION_FAILED;
+    }
+
+    rc = MsiDatabaseOpenViewA(package->db, Query, &view);
+
+    if (rc != ERROR_SUCCESS)
+        return rc;
+
+    rc = MsiViewExecute(view, 0);
+    if (rc != ERROR_SUCCESS)
+    {
+        MsiViewClose(view);
+        MsiCloseHandle(view);
+        return rc;
+    }
+
+    while (1)
+    {
+        WCHAR target_file[MAX_PATH];
+        WCHAR buffer[0x1000];
+        DWORD sz;
+        DWORD index;
+        static const WCHAR szlnk[]={'.','l','n','k',0};
+
+        rc = MsiViewFetch(view,&row);
+        if (rc != ERROR_SUCCESS)
+        {
+            rc = ERROR_SUCCESS;
+            break;
+        }
+        
+        sz = 0x1000;
+        MsiRecordGetStringW(row,4,buffer,&sz);
+
+        index = get_loaded_component(package,buffer);
+
+        if (index < 0)
+        {
+            MsiCloseHandle(row);
+            continue;
+        }
+
+        if (!package->components[index].Enabled ||
+            !package->components[index].FeatureState)
+        {
+            TRACE("Skipping shortcut creation due to disabled component\n");
+            MsiCloseHandle(row);
+            continue;
+        }
+
+        ui_actiondata(hPackage,szCreateShortcuts,row);
+
+        res = CoCreateInstance( &CLSID_ShellLink, NULL, CLSCTX_INPROC_SERVER,
+                              &IID_IShellLinkW, (LPVOID *) &sl );
+
+        if (FAILED(res))
+        {
+            ERR("Is IID_IShellLink\n");
+            MsiCloseHandle(row);
+            continue;
+        }
+
+        res = IShellLinkW_QueryInterface( sl, &IID_IPersistFile,(LPVOID*) &pf );
+        if( FAILED( res ) )
+        {
+            ERR("Is IID_IPersistFile\n");
+            MsiCloseHandle(row);
+            continue;
+        }
+
+        sz = 0x1000;
+        MsiRecordGetStringW(row,2,buffer,&sz);
+        resolve_folder(hPackage, buffer,target_file,FALSE,FALSE,NULL);
+
+        sz = 0x1000;
+        MsiRecordGetStringW(row,3,buffer,&sz);
+        reduce_to_longfilename(buffer);
+        strcatW(target_file,buffer);
+        if (!strchrW(target_file,'.'))
+            strcatW(target_file,szlnk);
+
+        sz = 0x1000;
+        MsiRecordGetStringW(row,5,buffer,&sz);
+        if (strchrW(buffer,'['))
+        {
+            LPWSTR deformated;
+            deformat_string(hPackage,buffer,&deformated);
+            IShellLinkW_SetPath(sl,deformated);
+            HeapFree(GetProcessHeap(),0,deformated);
+        }
+        else
+        {
+            FIXME("UNHANDLED shortcut format, advertised shortcut\n");
+            IPersistFile_Release( pf );
+            IShellLinkW_Release( sl );
+            MsiCloseHandle(row);
+            continue;
+        }
+
+        if (!MsiRecordIsNull(row,6))
+        {
+            LPWSTR deformated;
+            sz = 0x1000;
+            MsiRecordGetStringW(row,6,buffer,&sz);
+            deformat_string(hPackage,buffer,&deformated);
+            IShellLinkW_SetArguments(sl,deformated);
+            HeapFree(GetProcessHeap(),0,deformated);
+        }
+
+        if (!MsiRecordIsNull(row,7))
+        {
+            LPWSTR deformated;
+            sz = 0;
+            MsiRecordGetStringW(row,7,NULL,&sz);
+            sz++;
+            deformated = HeapAlloc(GetProcessHeap(),0,sz * sizeof(WCHAR));
+            MsiRecordGetStringW(row,7,deformated,&sz);
+            IShellLinkW_SetDescription(sl,deformated);
+            HeapFree(GetProcessHeap(),0,deformated);
+        }
+
+        if (!MsiRecordIsNull(row,8))
+            IShellLinkW_SetHotkey(sl,MsiRecordGetInteger(row,8));
+
+        if (!MsiRecordIsNull(row,9))
+        {
+            WCHAR Path[MAX_PATH];
+            INT index; 
+
+            sz = 0x1000;
+            MsiRecordGetStringW(row,9,buffer,&sz);
+
+            build_icon_path(hPackage,buffer,Path);
+            index = MsiRecordGetInteger(row,10);
+
+            IShellLinkW_SetIconLocation(sl,Path,index);
+        }
+
+        if (!MsiRecordIsNull(row,11))
+            IShellLinkW_SetShowCmd(sl,MsiRecordGetInteger(row,11));
+
+        if (!MsiRecordIsNull(row,12))
+        {
+            WCHAR Path[MAX_PATH];
+
+            sz = 0x1000;
+            MsiRecordGetStringW(row,12,buffer,&sz);
+            resolve_folder(hPackage, buffer, Path, FALSE, FALSE, NULL);
+            IShellLinkW_SetWorkingDirectory(sl,Path);
+        }
+
+        TRACE("Writing shortcut to %s\n",debugstr_w(target_file));
+        IPersistFile_Save(pf,target_file,FALSE);
+        
+        IPersistFile_Release( pf );
+        IShellLinkW_Release( sl );
 
         MsiCloseHandle(row);
     }
     MsiViewClose(view);
     MsiCloseHandle(view);
+
+
+    CoUninitialize();
+
     return rc;
 }
 
+
+/*
+ * 99% of the work done here is only done for 
+ * advertised installs. However this is where the
+ * Icon table is processed and written out
+ * so that is waht i am going to do here
+ */
+static UINT ACTION_PublishProduct(MSIHANDLE hPackage)
+{
+    UINT rc;
+    MSIHANDLE view;
+    MSIHANDLE row = 0;
+    static const CHAR *Query="SELECT * from Icon";
+    MSIPACKAGE* package;
+    DWORD sz;
+
+    package = msihandle2msiinfo(hPackage, MSIHANDLETYPE_PACKAGE);
+    if (!package)
+        return ERROR_INVALID_HANDLE;
+
+    rc = MsiDatabaseOpenViewA(package->db, Query, &view);
+
+    if (rc != ERROR_SUCCESS)
+        return rc;
+
+    rc = MsiViewExecute(view, 0);
+    if (rc != ERROR_SUCCESS)
+    {
+        MsiViewClose(view);
+        MsiCloseHandle(view);
+        return rc;
+    }
+
+    while (1)
+    {
+        HANDLE the_file;
+        WCHAR FilePath[MAX_PATH];
+        WCHAR FileName[MAX_PATH];
+        CHAR buffer[1024];
+
+        rc = MsiViewFetch(view,&row);
+        if (rc != ERROR_SUCCESS)
+        {
+            rc = ERROR_SUCCESS;
+            break;
+        }
+    
+        sz = MAX_PATH;
+        MsiRecordGetStringW(row,1,FileName,&sz);
+        if (sz == 0)
+        {
+            ERR("Unable to get FileName\n");
+            MsiCloseHandle(row);
+            continue;
+        }
+
+        build_icon_path(hPackage,FileName,FilePath);
+
+        TRACE("Creating icon file at %s\n",debugstr_w(FilePath));
+        
+        the_file = CreateFileW(FilePath, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS,
+                           FILE_ATTRIBUTE_NORMAL, NULL);
+
+        if (the_file == INVALID_HANDLE_VALUE)
+        {
+            ERR("Unable to create file %s\n",debugstr_w(FilePath));
+            MsiCloseHandle(row);
+            continue;
+        }
+
+        do 
+        {
+            DWORD write;
+            sz = 1024;
+            rc = MsiRecordReadStream(row,2,buffer,&sz);
+            if (rc != ERROR_SUCCESS)
+            {
+                ERR("Failed to get stream\n");
+                CloseHandle(the_file);  
+                DeleteFileW(FilePath);
+                break;
+            }
+            WriteFile(the_file,buffer,sz,&write,NULL);
+        } while (sz == 1024);
+
+        CloseHandle(the_file);
+        MsiCloseHandle(row);
+    }
+    MsiViewClose(view);
+    MsiCloseHandle(view);
+    return rc;
+
+}
+
 /* Msi functions that seem approperate here */
 UINT WINAPI MsiDoActionA( MSIHANDLE hInstall, LPCSTR szAction )
 {
diff -ur dlls/msi.old/package.c dlls/msi/package.c
--- dlls/msi.old/package.c	2004-07-03 20:41:29.000000000 -0500
+++ dlls/msi/package.c	2004-07-03 20:41:44.000000000 -0500
@@ -173,27 +173,6 @@
     static const WCHAR TF[]=
 {'T','e','m','p','F','o','l','d','e','r',0};
 
-/* Not yet set ...  but needed by iTunes
- *
-DesktopFolder
-FavoritesFolder
-FontsFolder
-PrimaryVolumePath
-ProgramFiles64Folder
-ProgramMenuFolder
-SendToFolder
-StartMenuFolder
-StartupFolder
-System16Folder
-System64Folder
-TemplateFolder
- */
-
-/* asked for by iTunes ... but are they installer set? 
- *
- *  GlobalAssemblyCache
- */
-
 /*
  * Other things i notice set
  *
@@ -266,6 +245,7 @@
     GetTempPathW(MAX_PATH,pth);
     MsiSetPropertyW(hPackage, TF, pth);
 
+
     /* in a wine enviroment the user is always admin and privlaged */
     MsiSetPropertyA(hPackage,"AdminUser","1");
     MsiSetPropertyA(hPackage,"Privileged","1");
@@ -288,8 +268,26 @@
     MsiSetPropertyA(hPackage,"WindowsBuild",verstr);
     /* just fudge this */
     MsiSetPropertyA(hPackage,"ServicePackLevel","6");
-}
 
+    /* FIXME: these need to be set properly */
+
+    MsiSetPropertyA(hPackage,"ProgramMenuFolder",
+                    "C:\\Windows\\Start Menu\\Programs\\");
+    MsiSetPropertyA(hPackage,"FavoritesFolder",
+                    "C:\\Windows\\Favorites\\");
+    MsiSetPropertyA(hPackage,"FontsFolder",
+                    "C:\\Windows\\Fonts\\");
+    MsiSetPropertyA(hPackage,"SendToFolder",
+                    "C:\\Windows\\SendTo\\");
+    MsiSetPropertyA(hPackage,"StartMenuFolder",
+                    "C:\\Windows\\Start Menu\\");
+    MsiSetPropertyA(hPackage,"StartupFolder",
+                    "C:\\Windows\\Start Menu\\Programs\\Startup\\");
+    MsiSetPropertyA(hPackage,"TemplateFolder",
+                    "C:\\Windows\\ShellNew\\");
+    MsiSetPropertyA(hPackage, "DesktopFolder",
+                    "C:\\Windows\\Desktop\\");
+}
 
 UINT WINAPI MsiOpenPackageW(LPCWSTR szPackage, MSIHANDLE *phPackage)
 {


More information about the wine-patches mailing list