975 lines
30 KiB
C++
975 lines
30 KiB
C++
#include <windows.h>
|
|
#include <objbase.h>
|
|
#include "fusenet.h"
|
|
|
|
#define INITGUID
|
|
#include <guiddef.h>
|
|
|
|
// Update services
|
|
#include "server.h"
|
|
DEFINE_GUID(IID_IAssemblyUpdate,
|
|
0x301b3415,0xf52d,0x4d40,0xbd,0xf7,0x31,0xd8,0x27,0x12,0xc2,0xdc);
|
|
|
|
DEFINE_GUID(CLSID_CAssemblyUpdate,
|
|
0x37b088b8,0x70ef,0x4ecf,0xb1,0x1e,0x1f,0x3f,0x4d,0x10,0x5f,0xdd);
|
|
|
|
// CLR Hosting
|
|
#import "..\..\clrhost\asmexec.tlb" raw_interfaces_only
|
|
using namespace asmexec;
|
|
|
|
|
|
// note: a bit hacky to use something that's implemented under shell\shortcut\util.cpp
|
|
#include "project.hpp" // for extern HRESULT GetLastWin32Error(); only
|
|
|
|
#define WZ_TYPE_DOTNET L".NetAssembly"
|
|
#define WZ_TYPE_WIN32 L"win32Executable"
|
|
#define WZ_TYPE_AVALON L"avalon"
|
|
#define TYPE_DOTNET 1
|
|
#define TYPE_WIN32 2
|
|
#define TYPE_AVALON 3
|
|
|
|
IInternetSecurityManager* g_pSecurityMgr = NULL;
|
|
|
|
|
|
#include <stdio.h> // for _snwprintf
|
|
|
|
|
|
// debug msg stuff
|
|
void
|
|
Msg(LPWSTR pwz)
|
|
{
|
|
MessageBox(NULL, pwz, L"Fusion Manifest Host", 0);
|
|
}
|
|
|
|
// ----------------------------------------------------------------------------
|
|
|
|
HRESULT
|
|
RunCommand(LPWSTR wzCmdLine, LPCWSTR wzCurrentDir, BOOL fWait)
|
|
{
|
|
HRESULT hr = S_OK;
|
|
STARTUPINFO si;
|
|
PROCESS_INFORMATION pi;
|
|
|
|
ZeroMemory(&si, sizeof(si));
|
|
ZeroMemory(&pi, sizeof(pi));
|
|
|
|
si.cb = sizeof(si);
|
|
|
|
// wzCurrentDir: The string must be a full path and file name that includes a drive letter; or NULL
|
|
if(!CreateProcess(NULL, wzCmdLine, NULL, NULL, TRUE, 0, NULL, wzCurrentDir, &si, &pi))
|
|
{
|
|
hr = GetLastWin32Error();
|
|
goto exit;
|
|
}
|
|
|
|
if (fWait)
|
|
{
|
|
if(WaitForSingleObject(pi.hProcess, 180000L) == WAIT_TIMEOUT)
|
|
{
|
|
hr = HRESULT_FROM_WIN32(ERROR_TIMEOUT);
|
|
goto exit;
|
|
}
|
|
}
|
|
|
|
exit:
|
|
if(pi.hProcess) CloseHandle(pi.hProcess);
|
|
if(pi.hThread) CloseHandle(pi.hThread);
|
|
|
|
return hr;
|
|
}
|
|
|
|
// ----------------------------------------------------------------------------
|
|
|
|
HRESULT
|
|
CopyToUSStartMenu(LPCWSTR pwzFilePath, LPCWSTR pwzRealFilename, BOOL bOverwrite)
|
|
{
|
|
HRESULT hr = S_OK;
|
|
WCHAR wzPath[MAX_PATH];
|
|
|
|
// BUGBUG?: use SHGetFolderPath()
|
|
if(GetEnvironmentVariable(L"USERPROFILE", wzPath, MAX_PATH-1) == 0)
|
|
{
|
|
hr = CO_E_PATHTOOLONG;
|
|
goto exit;
|
|
}
|
|
|
|
if (!PathAppend(wzPath, L"Start Menu\\Programs\\"))
|
|
{
|
|
hr = E_FAIL;
|
|
goto exit;
|
|
}
|
|
|
|
if (!PathAppend(wzPath, pwzRealFilename))
|
|
{
|
|
hr = E_FAIL;
|
|
goto exit;
|
|
}
|
|
|
|
if (CopyFile(pwzFilePath, wzPath, !bOverwrite) == 0)
|
|
{
|
|
hr = GetLastWin32Error();
|
|
//goto exit;
|
|
}
|
|
|
|
exit:
|
|
return hr;
|
|
}
|
|
|
|
// ----------------------------------------------------------------------------
|
|
|
|
// BUGBUG: should this be in-sync with what server does to register update?
|
|
#define REG_KEY_FUSION_SETTINGS L"Software\\Microsoft\\Fusion\\Installer\\1.0.0.0\\Policy"
|
|
|
|
#define REG_VAL_INTRANET_DISALLOW L"Download from Intranet Disallowed"
|
|
#define REG_VAL_TRUSTED_DISALLOW L"Download from Trusted Zone Disallowed"
|
|
#define REG_VAL_INTERNET_DISALLOW L"Download from Internet Disallowed"
|
|
#define REG_VAL_UNTRUSTED_DISALLOW L"Download from Untrusted Zone Disallowed"
|
|
|
|
|
|
// BUGBUG hacky should move this from extricon.cpp to util.cpp and declare in project.hpp
|
|
extern LONG GetRegKeyValue(HKEY hkeyParent, PCWSTR pcwzSubKey,
|
|
PCWSTR pcwzValue, PDWORD pdwValueType,
|
|
PBYTE pbyteBuf, PDWORD pdwcbBufLen);
|
|
|
|
// return: S_OK for yes/ok, E_ABORT for no/abort
|
|
// default is allow all
|
|
HRESULT
|
|
CheckZonePolicy(LPWSTR pwzURL)
|
|
{
|
|
HRESULT hr = S_OK;
|
|
DWORD dwZone = 0;
|
|
DWORD dwType = 0;
|
|
DWORD dwValue = -1;
|
|
DWORD dwSize = sizeof(dwValue);
|
|
|
|
if (g_pSecurityMgr == NULL)
|
|
{
|
|
// lazy init.
|
|
|
|
hr = CoCreateInstance(CLSID_InternetSecurityManager, NULL, CLSCTX_INPROC_SERVER,
|
|
IID_IInternetSecurityManager, (void**)&g_pSecurityMgr);
|
|
if (FAILED(hr))
|
|
{
|
|
g_pSecurityMgr = NULL;
|
|
goto exit;
|
|
}
|
|
}
|
|
|
|
if (SUCCEEDED(hr = g_pSecurityMgr->MapUrlToZone(pwzURL, &dwZone, 0)))
|
|
{
|
|
// BUGBUG: hack up code here... not much error checking...
|
|
switch(dwZone)
|
|
{
|
|
case 1: // Intranet Zone
|
|
// Get registry entry
|
|
if (GetRegKeyValue(HKEY_CURRENT_USER,
|
|
REG_KEY_FUSION_SETTINGS, REG_VAL_INTRANET_DISALLOW,
|
|
&dwType, (PBYTE) &dwValue, &dwSize)
|
|
== ERROR_SUCCESS)
|
|
{
|
|
if (dwValue == 1)
|
|
{
|
|
hr = E_ABORT;
|
|
Msg(L"Zone policy: Download from Intranet is disallowed. Aborting...");
|
|
}
|
|
}
|
|
break;
|
|
case 2: // Trusted Zone
|
|
// Get registry entry
|
|
if (GetRegKeyValue(HKEY_CURRENT_USER,
|
|
REG_KEY_FUSION_SETTINGS, REG_VAL_TRUSTED_DISALLOW,
|
|
&dwType, (PBYTE) &dwValue, &dwSize)
|
|
== ERROR_SUCCESS)
|
|
{
|
|
if (dwValue == 1)
|
|
{
|
|
hr = E_ABORT;
|
|
Msg(L"Zone policy: Download from Trusted Zone is disallowed. Aborting...");
|
|
}
|
|
}
|
|
break;
|
|
case 3: // Internet Zone
|
|
// Get registry entry
|
|
if (GetRegKeyValue(HKEY_CURRENT_USER,
|
|
REG_KEY_FUSION_SETTINGS, REG_VAL_INTERNET_DISALLOW,
|
|
&dwType, (PBYTE) &dwValue, &dwSize)
|
|
== ERROR_SUCCESS)
|
|
{
|
|
if (dwValue == 1)
|
|
{
|
|
hr = E_ABORT;
|
|
Msg(L"Zone policy: Download from Internet is disallowed. Aborting...");
|
|
}
|
|
}
|
|
break;
|
|
case 4: // Untrusted Zone
|
|
// Get registry entry
|
|
if (GetRegKeyValue(HKEY_CURRENT_USER,
|
|
REG_KEY_FUSION_SETTINGS, REG_VAL_UNTRUSTED_DISALLOW,
|
|
&dwType, (PBYTE) &dwValue, &dwSize)
|
|
== ERROR_SUCCESS)
|
|
{
|
|
if (dwValue == 1)
|
|
{
|
|
hr = E_ABORT;
|
|
Msg(L"Zone policy: Download from Untrusted Zone is disallowed. Aborting...");
|
|
}
|
|
}
|
|
break;
|
|
case 0: //Local machine
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
exit:
|
|
return hr;
|
|
}
|
|
|
|
// ----------------------------------------------------------------------------
|
|
|
|
// note: parameter pwzCodebase can be NULL
|
|
HRESULT
|
|
ResolveAndInstall(LPASSEMBLY_IDENTITY pAsmId, LPWSTR pwzCodebase, LPWSTR *ppwzManifestFileDir, LPWSTR *ppwzManifestFilePath, BOOL* pbIs1stTimeInstall)
|
|
{
|
|
HRESULT hr = S_OK;
|
|
LPASSEMBLY_CACHE_IMPORT pCacheImport = NULL;
|
|
DWORD dwCC = 0;
|
|
|
|
*pbIs1stTimeInstall = FALSE;
|
|
|
|
// look into the cache
|
|
|
|
if (FAILED(hr=CreateAssemblyCacheImport(&pCacheImport, pAsmId, CACHEIMP_CREATE_RESOLVE_REF)))
|
|
goto exit;
|
|
|
|
// hr from CreateAssemblyCacheImport() above
|
|
|
|
// Case 1, cached copy exist
|
|
// hr == S_OK
|
|
|
|
// Case 2, cached copy (of the referenced version) not exist
|
|
if (hr == S_FALSE)
|
|
{
|
|
LPASSEMBLY_DOWNLOAD pAsmDownload = NULL;
|
|
|
|
// BUGBUG?: what if it is not a partial ref but there's actually another version installed?
|
|
|
|
if (pwzCodebase == NULL)
|
|
{
|
|
Msg(L"No completed cached version of this application found and this manifest cannot be used to initiate an install. Cannot continue.");
|
|
hr = E_FAIL;
|
|
goto exit;
|
|
}
|
|
|
|
*pbIs1stTimeInstall = TRUE;
|
|
|
|
// check policy before download
|
|
if (FAILED(hr=CheckZonePolicy(pwzCodebase)))
|
|
goto exit;
|
|
|
|
if (FAILED(hr=CreateAssemblyDownload(&pAsmDownload)))
|
|
goto exit;
|
|
|
|
//BUGBUG: need ref def matching checks for desktop->subscription->app manifests' ids
|
|
|
|
// (synchronous & ui) download from codebase
|
|
hr=pAsmDownload->DownloadManifestAndDependencies(pwzCodebase, NULL, DOWNLOAD_FLAGS_PROGRESS_UI);
|
|
pAsmDownload->Release();
|
|
|
|
// hr from DownloadManifestAndDependencies() above
|
|
if (FAILED(hr))
|
|
{
|
|
if (hr == E_ABORT)
|
|
Msg(L"File download canceled.");
|
|
else
|
|
Msg(L"Error in file download. Cannot continue.");
|
|
|
|
goto exit;
|
|
}
|
|
|
|
// another version might have been completed at this time...
|
|
// get cache dir again to ensure running the highest version
|
|
if (FAILED(hr=CreateAssemblyCacheImport(&pCacheImport, pAsmId, CACHEIMP_CREATE_RESOLVE_REF)))
|
|
goto exit;
|
|
|
|
if (hr == S_FALSE)
|
|
{
|
|
Msg(L"No completed cached version found. Possible error in download cache commit. Cannot continue.");
|
|
hr = E_ABORT;
|
|
goto exit;
|
|
}
|
|
}
|
|
|
|
pCacheImport->GetManifestFileDir(ppwzManifestFileDir, &dwCC);
|
|
if (dwCC < 2)
|
|
{
|
|
// this should never happen
|
|
hr = E_FAIL;
|
|
goto exit;
|
|
}
|
|
|
|
pCacheImport->GetManifestFilePath(ppwzManifestFilePath, &dwCC);
|
|
if (dwCC < 2)
|
|
{
|
|
hr = E_FAIL;
|
|
goto exit;
|
|
}
|
|
|
|
exit:
|
|
if (pCacheImport)
|
|
pCacheImport->Release();
|
|
|
|
if (FAILED(hr))
|
|
{
|
|
if (*ppwzManifestFileDir)
|
|
{
|
|
delete *ppwzManifestFileDir;
|
|
*ppwzManifestFileDir = NULL;
|
|
}
|
|
if (*ppwzManifestFilePath)
|
|
{
|
|
delete *ppwzManifestFilePath;
|
|
*ppwzManifestFilePath = NULL;
|
|
}
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
// ----------------------------------------------------------------------------
|
|
|
|
/*void _stdcall
|
|
EntryPoint(HWND hwnd, HINSTANCE hinst, LPSTR lpszCmdLine, int nCmdShow);
|
|
|
|
hwnd - window handle that should be used as the owner window for
|
|
any windows your DLL creates
|
|
hinst - your DLL's instance handle
|
|
lpszCmdLine - ASCIIZ command line your DLL should parse
|
|
nCmdShow - describes how your DLL's windows should be displayed
|
|
*/
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// DisableCurrentVersionW
|
|
// The rundll32 entry point for rollback to previous version
|
|
// The function should be named as 'DisableCurrentVersion' on rundll32's command line
|
|
// ---------------------------------------------------------------------------
|
|
|
|
// BUGBUG: WARNING!! this is **major hacked up code** and does open up some holes in the logic
|
|
// depends on the timing of this and check for max version in cache in app start....
|
|
// ie. SHOULD NEVER BE RUNNING THIS AND START DOWNLOAD/UPDATE!
|
|
//
|
|
// also, this depends on a few things such as how update service check if the
|
|
// version exists etc..
|
|
void CALLBACK
|
|
DisableCurrentVersionW(HWND hwnd, HINSTANCE hinst, LPWSTR lpszCmdLine, int nCmdShow)
|
|
{
|
|
HRESULT hr = S_OK;
|
|
LPASSEMBLY_MANIFEST_IMPORT pManImport = NULL;
|
|
LPASSEMBLY_IDENTITY pAsmId = NULL;
|
|
LPMANIFEST_APPLICATION_INFO pAppInfo = NULL;
|
|
LPASSEMBLY_CACHE_IMPORT pCacheImport = NULL;
|
|
LPWSTR pwzCacheDir = NULL;
|
|
LPWSTR pwzDisplayName = NULL;
|
|
DWORD dwCC = 0;
|
|
WCHAR wzBuf[1024];
|
|
LONG lResult;
|
|
HKEY hkeySubKey;
|
|
|
|
if (FAILED(hr = CoInitialize(NULL)))
|
|
goto exit;
|
|
|
|
// hacked up commandline parsing code
|
|
lpszCmdLine = lpszCmdLine+1;
|
|
*(lpszCmdLine+lstrlen(lpszCmdLine)-1) = L'\0';
|
|
|
|
if (FAILED(hr=CreateAssemblyManifestImport(&pManImport, lpszCmdLine)))
|
|
{
|
|
// temp msg
|
|
Msg(L"ManImport error.");
|
|
goto exit;
|
|
}
|
|
|
|
if (FAILED(hr=pManImport->GetManifestApplicationInfo(&pAppInfo)) || hr==S_FALSE)
|
|
{
|
|
// can't continue without this...
|
|
hr = E_ABORT;
|
|
Msg(L"This manifest does not have the proper format and cannot be used to start an application. Thus there is nothing to disable here.");
|
|
goto exit;
|
|
}
|
|
|
|
if (FAILED(hr=pManImport->GetAssemblyIdentity(&pAsmId)))
|
|
{
|
|
// temp msg
|
|
Msg(L"AsmId error.");
|
|
goto exit;
|
|
}
|
|
|
|
if (FAILED(hr=CreateAssemblyCacheImport(&pCacheImport, pAsmId, CACHEIMP_CREATE_RESOLVE_REF)))
|
|
{
|
|
// temp msg
|
|
Msg(L"CacheImport error.");
|
|
goto exit;
|
|
}
|
|
|
|
if (hr == S_FALSE)
|
|
{
|
|
Msg(L"No completed cached version found. Nothing to disable.");
|
|
hr = E_ABORT;
|
|
goto exit;
|
|
}
|
|
|
|
pCacheImport->GetManifestFileDir(&pwzCacheDir, &dwCC);
|
|
if (dwCC < 2)
|
|
{
|
|
// this should never happen
|
|
hr = E_FAIL;
|
|
goto exit;
|
|
}
|
|
// remove last L'\\'
|
|
*(pwzCacheDir+dwCC-2) = L'\0';
|
|
// find the name to use from the cache path
|
|
pwzDisplayName = wcsrchr(pwzCacheDir, L'\\');
|
|
|
|
// this has to be the same as how assemblycache does it!
|
|
lstrcpy(wzBuf, L"Software\\Microsoft\\Fusion\\Installer\\1.0.0.0\\Cache\\");
|
|
lstrcat(wzBuf, pwzDisplayName);
|
|
|
|
lResult = RegOpenKeyEx(HKEY_CURRENT_USER, wzBuf, 0, KEY_SET_VALUE,
|
|
&hkeySubKey);
|
|
|
|
if (lResult == ERROR_SUCCESS)
|
|
{
|
|
DWORD dwValue = 0;
|
|
|
|
// set to 0 to make it 'incomplete' so that StartW/host/cache will ignore it
|
|
// when executing the app but keep the dir name so that download
|
|
// will assume it is handled - assemblycache.cpp & assemblydownload.cpp's check
|
|
lResult = RegSetValueEx(hkeySubKey, L"Complete", NULL, REG_DWORD,
|
|
(PBYTE) &dwValue, sizeof(dwValue));
|
|
|
|
if (lResult == ERROR_SUCCESS)
|
|
{
|
|
// no error checking!
|
|
pCacheImport->Release();
|
|
|
|
hr=CreateAssemblyCacheImport(&pCacheImport, pAsmId, CACHEIMP_CREATE_RESOLVE_REF);
|
|
if (hr == S_FALSE)
|
|
{
|
|
dwValue = 1;
|
|
|
|
// change it back to what it was if no other completed version found
|
|
// no error checking!
|
|
lResult = RegSetValueEx(hkeySubKey, L"Complete", NULL, REG_DWORD,
|
|
(PBYTE) &dwValue, sizeof(dwValue));
|
|
|
|
// BUGBUG: known problem for fully qualified ref, eg. app manifest's asm id
|
|
// Should check for it and display and different msg here.
|
|
Msg(L"No other completed cached version found. Disabling current version cannot be done. Cancelling...");
|
|
}
|
|
else
|
|
{
|
|
Msg(L"Current version disabled. Next time another version of the app will run instead.");
|
|
}
|
|
}
|
|
|
|
RegCloseKey(hkeySubKey);
|
|
}
|
|
else
|
|
{
|
|
Msg(L"Registry error.");
|
|
}
|
|
|
|
exit:
|
|
if (pCacheImport)
|
|
pCacheImport->Release();
|
|
|
|
if (pAsmId)
|
|
pAsmId->Release();
|
|
|
|
if (pAppInfo)
|
|
pAppInfo->Release();
|
|
|
|
if (pManImport)
|
|
pManImport->Release();
|
|
|
|
if (pwzCacheDir)
|
|
delete pwzCacheDir;
|
|
|
|
if (FAILED(hr))
|
|
{
|
|
if (hr != E_ABORT)
|
|
Msg(L"Error occured.");
|
|
}
|
|
|
|
CoUninitialize();
|
|
|
|
return;
|
|
}
|
|
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// StartW
|
|
// The single rundll32 entry point for both shell (file type host) and mimefilter/url
|
|
// The function should be named as 'Start' on rundll32's command line
|
|
// ---------------------------------------------------------------------------
|
|
void CALLBACK
|
|
StartW(HWND hwnd, HINSTANCE hinst, LPWSTR lpszCmdLine, int nCmdShow)
|
|
{
|
|
HRESULT hr = S_OK;
|
|
LPASSEMBLY_MANIFEST_IMPORT pManImport = NULL;
|
|
LPASSEMBLY_IDENTITY pAsmId = NULL;
|
|
LPMANIFEST_APPLICATION_INFO pAppInfo = NULL;
|
|
LPDEPENDENT_ASSEMBLY_INFO pDependAsmInfo = NULL;
|
|
LPWSTR pwzShortcutPath = NULL;
|
|
LPWSTR pwzShortcutUrl = NULL;
|
|
LPWSTR pwzAppRootDir = NULL;
|
|
LPWSTR pwzAppManifestPath = NULL;
|
|
LPWSTR pwzEntrypoint = NULL;
|
|
LPWSTR pwzType = NULL;
|
|
LPWSTR pwzCmdLine = NULL;
|
|
LPWSTR pwzCodebase = NULL;
|
|
DWORD dwCC = 0;
|
|
DWORD dwManifestType = MANIFEST_TYPE_UNKNOWN;
|
|
BOOL bIsFromMimeFilter = FALSE;
|
|
BOOL bIs1stTimeInstall = FALSE;
|
|
int iAppType = 0;
|
|
|
|
if (FAILED(hr = CoInitialize(NULL)))//CoInitializeEx(NULL, COINIT_MULTITHREADED);
|
|
goto exit;
|
|
|
|
// parse commandline
|
|
// accepted formats: "Path" <OR> "Path" "URL"
|
|
if (*lpszCmdLine == L'\"')
|
|
{
|
|
LPWSTR pwz = NULL;
|
|
|
|
pwz = wcschr(lpszCmdLine+1, L'\"');
|
|
if (pwz != NULL)
|
|
{
|
|
*pwz = L'\0';
|
|
|
|
// case 1 desktop/local, path to shortcut only
|
|
pwzShortcutPath = lpszCmdLine+1;
|
|
|
|
pwz = wcschr(pwz+1, L'\"');
|
|
if (pwz != NULL)
|
|
{
|
|
pwzShortcutUrl = pwz+1;
|
|
|
|
pwz = wcschr(pwzShortcutUrl, L'\"');
|
|
if (pwz != NULL)
|
|
{
|
|
*pwz = L'\0';
|
|
// case 2 url/mimefilter, path to temp shortcut and source URL
|
|
bIsFromMimeFilter = TRUE;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// exit if invalid arguments. ShortcutUrl is incomplete if bIsFromMimeFilter is FALSE
|
|
if (pwzShortcutPath == NULL || (pwzShortcutUrl != NULL && !bIsFromMimeFilter))
|
|
{
|
|
hr = E_INVALIDARG;
|
|
goto exit;
|
|
}
|
|
|
|
// valid start conditions to invoke this function, passing
|
|
// 1. path to desktop manifest (install redirect to subscription manifest on server)
|
|
// 2. path to desktop manifest (install redirect to applicaion manifest on server)
|
|
// 3. BUGBUG TO-BE-FIXED//path to application (no install)
|
|
// 4. URL to subscription manifest on server
|
|
// 5. URL to application manifest on server
|
|
|
|
if (FAILED(hr=CreateAssemblyManifestImport(&pManImport, pwzShortcutPath)))
|
|
{
|
|
Msg(L"Error in loading and parsing the manifest file.");
|
|
goto exit;
|
|
}
|
|
|
|
pManImport->ReportManifestType(&dwManifestType);
|
|
if (dwManifestType == MANIFEST_TYPE_UNKNOWN)
|
|
{
|
|
Msg(L"This manifest does not have a known format type.");
|
|
hr = E_ABORT;
|
|
goto exit;
|
|
}
|
|
|
|
// allow only valid start conditions
|
|
|
|
if (bIsFromMimeFilter &&
|
|
dwManifestType != MANIFEST_TYPE_SUBSCRIPTION &&
|
|
dwManifestType != MANIFEST_TYPE_APPLICATION)
|
|
{
|
|
Msg(L"Not supported: URL pointing to a desktop manifest.");
|
|
hr = E_ABORT;
|
|
goto exit;
|
|
}
|
|
|
|
if (!bIsFromMimeFilter &&
|
|
dwManifestType != MANIFEST_TYPE_DESKTOP)
|
|
//&& dwManifestType != MANIFEST_TYPE_APPLICATION)
|
|
{
|
|
Msg(L"This manifest does not have the proper format and cannot be used to start an application.");
|
|
hr = E_ABORT;
|
|
goto exit;
|
|
}
|
|
|
|
// get data from the manifest file
|
|
|
|
if (dwManifestType != MANIFEST_TYPE_SUBSCRIPTION)
|
|
{
|
|
if (FAILED(hr=pManImport->GetAssemblyIdentity(&pAsmId)))
|
|
{
|
|
Msg(L"This manifest does not have the proper format and contains no assembly identity.");
|
|
goto exit;
|
|
}
|
|
}
|
|
|
|
if (dwManifestType != MANIFEST_TYPE_APPLICATION)
|
|
{
|
|
// BUGBUG: hardcoded index '0'
|
|
pManImport->GetNextAssembly(0, &pDependAsmInfo);
|
|
if (pDependAsmInfo)
|
|
{
|
|
if (dwManifestType == MANIFEST_TYPE_SUBSCRIPTION)
|
|
{
|
|
pDependAsmInfo->GetAssemblyIdentity(&pAsmId);
|
|
}
|
|
|
|
pDependAsmInfo->Get(DEPENDENT_ASM_CODEBASE, &pwzCodebase, &dwCC);
|
|
}
|
|
|
|
if (!pAsmId || !pwzCodebase)
|
|
{
|
|
Msg(L"This subscription manifest contains no dependent assembly identity or a subscription codebase.");
|
|
hr = E_FAIL;
|
|
goto exit;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// if (bIsFromMimeFilter)
|
|
// {
|
|
// if URL->app manifest (case 5), codebase is that URL
|
|
// note: if path->app manifest (case 3), this does NOT apply
|
|
|
|
// BUGBUG: HACK: this implies re-download of the app manifest. pref?
|
|
|
|
size_t ccCodebase = wcslen(pwzShortcutUrl)+1;
|
|
pwzCodebase = new WCHAR[ccCodebase];
|
|
|
|
if (pwzCodebase == NULL)
|
|
{
|
|
hr = E_OUTOFMEMORY;
|
|
goto exit;
|
|
}
|
|
|
|
memcpy(pwzCodebase, pwzShortcutUrl, ccCodebase * sizeof(WCHAR));
|
|
// }
|
|
}
|
|
|
|
// search cache, download/install if necessary
|
|
|
|
if (FAILED(hr = ResolveAndInstall(pAsmId, pwzCodebase, &pwzAppRootDir, &pwzAppManifestPath, &bIs1stTimeInstall)))
|
|
goto exit;
|
|
|
|
// register for updates
|
|
|
|
if (bIs1stTimeInstall && bIsFromMimeFilter && dwManifestType == MANIFEST_TYPE_SUBSCRIPTION)
|
|
{
|
|
// note: this code must be identical to what assemblydownload.cpp DoCacheUpdate() does!
|
|
LPWSTR pwzName = NULL;
|
|
|
|
if ((hr = pAsmId->GetAttribute(SXS_ASSEMBLY_IDENTITY_STD_ATTRIBUTE_NAME_NAME, &pwzName, &dwCC)) != S_OK)
|
|
{
|
|
Msg(L"Error in retrieving assembly name. Cannot register subscription for updates.");
|
|
// note: This- no asm name- should not be allowed!
|
|
}
|
|
else
|
|
{
|
|
IAssemblyUpdate *pAssemblyUpdate = NULL;
|
|
|
|
// register for updates
|
|
hr = CoCreateInstance(CLSID_CAssemblyUpdate, NULL, CLSCTX_LOCAL_SERVER,
|
|
IID_IAssemblyUpdate, (void**)&pAssemblyUpdate);
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
DWORD pollingInterval;
|
|
hr = pManImport->GetPollingInterval (&pollingInterval);
|
|
|
|
hr = pAssemblyUpdate->RegisterAssemblySubscription(pwzName,
|
|
pwzShortcutUrl, pollingInterval);
|
|
|
|
pAssemblyUpdate->Release();
|
|
}
|
|
|
|
if (FAILED(hr))
|
|
{
|
|
Msg(L"Error in update services. Cannot register subscription for updates.");
|
|
//goto exit; do not terminate!
|
|
}
|
|
// else
|
|
// Error in update services. Cannot register subscription for updates - fail gracefully
|
|
// BUGBUG: need a way to recover from this and register later
|
|
|
|
delete pwzName;
|
|
}
|
|
}
|
|
|
|
// execute the app
|
|
|
|
if (bIsFromMimeFilter)
|
|
{
|
|
// if URL, crack the app manifest to get shell state info
|
|
|
|
// BUGBUG: if URL->app manifest (case 5), pwzShortcutPath is a copy and is already cracked-so no need in that case?
|
|
|
|
pManImport->Release();
|
|
if (FAILED(hr=CreateAssemblyManifestImport(&pManImport, pwzAppManifestPath)))
|
|
{
|
|
Msg(L"Error in loading and parsing the application manifest file.");
|
|
goto exit;
|
|
}
|
|
}
|
|
|
|
if (FAILED(hr=pManImport->GetManifestApplicationInfo(&pAppInfo)) || hr==S_FALSE)
|
|
{
|
|
// can't continue without this...
|
|
hr = E_ABORT;
|
|
Msg(L"This manifest does not have the shell information and cannot be used to start an application.");
|
|
goto exit;
|
|
}
|
|
|
|
if (FAILED(hr = pAppInfo->Get(MAN_APPLICATION_ENTRYPOINT, &pwzEntrypoint, &dwCC)))
|
|
{
|
|
Msg(L"This application does not have an entrypoint specified.");
|
|
goto exit;
|
|
}
|
|
|
|
if (FAILED(hr = pAppInfo->Get(MAN_APPLICATION_ENTRYIMAGETYPE, &pwzType, &dwCC)))
|
|
{
|
|
Msg(L"Error in retrieving application type. Cannot continue.");
|
|
goto exit;
|
|
}
|
|
|
|
// BUGBUG: use wcscmp case sensitive comparison?
|
|
if (_wcsicmp(pwzType, WZ_TYPE_DOTNET) == 0)
|
|
iAppType = TYPE_DOTNET;
|
|
else if (_wcsicmp(pwzType, WZ_TYPE_WIN32) == 0)
|
|
iAppType = TYPE_WIN32;
|
|
else if (_wcsicmp(pwzType, WZ_TYPE_AVALON) == 0)
|
|
iAppType = TYPE_AVALON;
|
|
|
|
if (iAppType == TYPE_DOTNET || iAppType == TYPE_WIN32 || iAppType == TYPE_AVALON)
|
|
{
|
|
size_t ccWorkingDir = wcslen(pwzAppRootDir);
|
|
size_t ccEntryPoint = wcslen(pwzEntrypoint)+1;
|
|
pwzCmdLine = new WCHAR[ccWorkingDir+ccEntryPoint]; // 2 strings + '\0'
|
|
|
|
if (pwzCmdLine == NULL)
|
|
{
|
|
hr = E_OUTOFMEMORY;
|
|
goto exit;
|
|
}
|
|
|
|
memcpy(pwzCmdLine, pwzAppRootDir, ccWorkingDir * sizeof(WCHAR));
|
|
memcpy(pwzCmdLine+ccWorkingDir, pwzEntrypoint, ccEntryPoint * sizeof(WCHAR));
|
|
|
|
// check if the entry point is in cache or not
|
|
if (GetFileAttributes(pwzCmdLine) == (DWORD)-1)
|
|
{
|
|
Msg(L"Entry point does not exist. Cannot continue.");
|
|
hr = E_ABORT;
|
|
goto exit;
|
|
}
|
|
}
|
|
|
|
if (iAppType == TYPE_DOTNET)// || iAppType == TYPE_AVALON)
|
|
{
|
|
DWORD dwZone;
|
|
//conexec WCHAR wzCmdLine[1025];
|
|
|
|
// note: at this point the codebase can be: URL to app manifest _or_ URL to subscription manifest
|
|
// (depends on how 1st time install is started with)
|
|
if (pwzCodebase == NULL)
|
|
{
|
|
Msg(L"This application does not have a codebase specified. Cannot continue to execute .NetAssembly.");
|
|
goto exit;
|
|
}
|
|
|
|
if (g_pSecurityMgr == NULL)
|
|
{
|
|
// lazy init.
|
|
|
|
hr = CoCreateInstance(CLSID_InternetSecurityManager, NULL, CLSCTX_INPROC_SERVER,
|
|
IID_IInternetSecurityManager, (void**)&g_pSecurityMgr);
|
|
if (FAILED(hr))
|
|
{
|
|
g_pSecurityMgr = NULL;
|
|
goto exit;
|
|
}
|
|
}
|
|
|
|
// BUGBUG?: shouldn't use codebase from ref manifest to set zone?
|
|
if (FAILED(hr = g_pSecurityMgr->MapUrlToZone(pwzCodebase, &dwZone, 0)))
|
|
{
|
|
goto exit;
|
|
}
|
|
|
|
/*conexec code path
|
|
if (_snwprintf(wzCmdLine, sizeof(wzCmdLine)/sizeof(WCHAR),
|
|
L"conexec.exe \"%s\" 3 %d %s", pwzCmdLine, dwZone, pwzCodebase) < 0)
|
|
{
|
|
hr = CO_E_PATHTOOLONG;
|
|
goto exit;
|
|
}
|
|
|
|
// BUGBUG: start directory: what if the exe is in a subdir of pwzAppRootDir?
|
|
|
|
// CreateProcess dislike having the filename in the path for the start directory
|
|
if (FAILED(hr=RunCommand(wzCmdLine, pwzAppRootDir, FALSE)))
|
|
goto exit;*/
|
|
|
|
try
|
|
{
|
|
IAsmExecutePtr pIAsmExecute(__uuidof(AsmExecute));
|
|
long lRetVal = -1;
|
|
LPWSTR pwzArg = NULL;
|
|
|
|
// BUGBUG: change AsmExec if it's no longer needed to send commandline argument
|
|
// clean up interface
|
|
|
|
if (iAppType == TYPE_AVALON)
|
|
{
|
|
// call with manifest file path as a parameter
|
|
|
|
pwzArg = pwzAppManifestPath;
|
|
}
|
|
|
|
//parameters: Codebase/filepath Flag Zone Url Arg
|
|
// BUGBUG: DWORD is unsigned and long is signed
|
|
|
|
hr = pIAsmExecute->Execute(_bstr_t(pwzCmdLine), 3, dwZone, _bstr_t(pwzCodebase), _bstr_t(pwzArg), &lRetVal);
|
|
|
|
// BUGBUG: do something about lRetVal
|
|
}
|
|
catch (_com_error &e)
|
|
{
|
|
hr = e.Error();
|
|
Msg((LPWSTR)e.ErrorMessage());
|
|
}
|
|
|
|
// hr from Execute() or inside catch(){} above
|
|
if (FAILED(hr))
|
|
goto exit;
|
|
}
|
|
else if (iAppType == TYPE_AVALON) //BUGBUG: a hack for avalon
|
|
{
|
|
WCHAR wzCmdLine[2048];
|
|
|
|
if (_snwprintf(wzCmdLine, sizeof(wzCmdLine)/sizeof(WCHAR),
|
|
L"\"%s\" \"%s\"", pwzCmdLine, pwzAppManifestPath) < 0)
|
|
{
|
|
hr = CO_E_PATHTOOLONG;
|
|
goto exit;
|
|
}
|
|
|
|
// BUGBUG: start directory: what if the exe is in a subdir of pwzAppRootDir?
|
|
|
|
// CreateProcess dislike having the filename in the path for the start directory
|
|
if (FAILED(hr=RunCommand(wzCmdLine, pwzAppRootDir, FALSE)))
|
|
{
|
|
Msg(L"Avalon: Create process error.");
|
|
goto exit;
|
|
}
|
|
|
|
}
|
|
else if (iAppType == TYPE_WIN32)
|
|
{
|
|
// BUGBUG: Win32 app has no sandboxing... use SAFER?
|
|
|
|
// BUGBUG: start directory: what if the exe is in a subdir of pwzAppRootDir?
|
|
|
|
// CreateProcess dislike having the filename in the path for the start directory
|
|
if (FAILED(hr=RunCommand(pwzCmdLine, pwzAppRootDir, FALSE)))
|
|
{
|
|
Msg(L"Win32Executable: Create process error.");
|
|
goto exit;
|
|
}
|
|
}
|
|
//else
|
|
// unknown type....
|
|
|
|
exit:
|
|
if (g_pSecurityMgr != NULL)
|
|
{
|
|
g_pSecurityMgr->Release();
|
|
g_pSecurityMgr = NULL;
|
|
}
|
|
|
|
if (pAsmId)
|
|
pAsmId->Release();
|
|
|
|
if (pDependAsmInfo)
|
|
pDependAsmInfo->Release();
|
|
|
|
if (pAppInfo)
|
|
pAppInfo->Release();
|
|
|
|
if (pManImport)
|
|
pManImport->Release();
|
|
|
|
if (pwzAppManifestPath)
|
|
delete pwzAppManifestPath;
|
|
|
|
if (pwzAppRootDir)
|
|
delete pwzAppRootDir;
|
|
|
|
if (pwzEntrypoint)
|
|
delete pwzEntrypoint;
|
|
|
|
if (pwzType)
|
|
delete pwzType;
|
|
|
|
if (pwzCmdLine)
|
|
delete pwzCmdLine;
|
|
|
|
if (pwzCodebase)
|
|
delete pwzCodebase;
|
|
|
|
if (FAILED(hr))
|
|
{
|
|
if (hr != E_ABORT)
|
|
Msg(L"Error occured.");
|
|
}
|
|
else
|
|
{
|
|
if (bIsFromMimeFilter && bIs1stTimeInstall)
|
|
{
|
|
// BUGBUG: should generate the desktop manifest file
|
|
|
|
/* // assume it's an URL
|
|
LPWSTR pwzFilename = wcsrchr(pwzShortcutUrl, L'/');
|
|
if (pwzFilename != NULL)
|
|
{
|
|
pwzFilename++;
|
|
CopyToUSStartMenu(pwzShortcutPath, pwzFilename, FALSE);
|
|
}
|
|
//else
|
|
// error as filename not found but ignore...*/
|
|
}
|
|
}
|
|
|
|
if (bIsFromMimeFilter)
|
|
{
|
|
// delete the temp file from the mimefilter
|
|
// ignore return value
|
|
DeleteFile(pwzShortcutPath);
|
|
}
|
|
|
|
CoUninitialize();
|
|
|
|
return;
|
|
}
|
|
|