1 #define WIN32_LEAN_AND_MEAN
2 
3 #include <windows.h>
4 #include <msiquery.h>
5 #include <wcautil.h>
6 
7 #define GUID_BUFFER_SIZE 39 // {8-4-4-4-12}\0
8 
9 
SetInstallScope(MSIHANDLE hInstall)10 extern "C" UINT WINAPI SetInstallScope(MSIHANDLE hInstall) {
11   HRESULT hr = S_OK;
12   UINT er = ERROR_SUCCESS;
13   PMSIHANDLE hDB;
14   PMSIHANDLE hView;
15   PMSIHANDLE hRecord;
16 
17   hr = WcaInitialize(hInstall, "SetInstallScope");
18   ExitOnFailure(hr, "Failed to initialize");
19 
20   hDB = MsiGetActiveDatabase(hInstall);
21   ExitOnNull(hDB, hr, S_FALSE, "Failed to get active database");
22 
23   LPCTSTR query = TEXT("SELECT DISTINCT UpgradeCode FROM Upgrade");
24   er = MsiDatabaseOpenView(hDB, query, &hView);
25   ExitOnWin32Error(er, hr, "Failed MsiDatabaseOpenView");
26 
27   er = MsiViewExecute(hView, 0);
28   ExitOnWin32Error(er, hr, "Failed MsiViewExecute");
29 
30   for (;;) {
31     er = MsiViewFetch(hView, &hRecord);
32     if (er == ERROR_NO_MORE_ITEMS) break;
33     ExitOnWin32Error(er, hr, "Failed MsiViewFetch");
34 
35     TCHAR upgrade_code[GUID_BUFFER_SIZE];
36     DWORD upgrade_code_len = GUID_BUFFER_SIZE;
37     er = MsiRecordGetString(hRecord, 1, upgrade_code, &upgrade_code_len);
38     ExitOnWin32Error(er, hr, "Failed to read UpgradeCode");
39 
40     DWORD iProductIndex;
41     for (iProductIndex = 0;; iProductIndex++) {
42       TCHAR product_code[GUID_BUFFER_SIZE];
43       er = MsiEnumRelatedProducts(upgrade_code, 0, iProductIndex,
44                                   product_code);
45       if (er == ERROR_NO_MORE_ITEMS) break;
46       ExitOnWin32Error(er, hr, "Failed to get related product code");
47 
48       TCHAR assignment_type[2];
49       DWORD assignment_type_len = 2;
50       er = MsiGetProductInfo(product_code, INSTALLPROPERTY_ASSIGNMENTTYPE,
51                              assignment_type, &assignment_type_len);
52       ExitOnWin32Error(er, hr, "Failed to get the assignment type property "
53                        "from related product");
54 
55       // '0' = per-user; '1' = per-machine
56       if (assignment_type[0] == '0') {
57         /* When old versions which were installed as per-user are detected,
58          * the installation scope has to be set to per-user to be able to do
59          * an upgrade. If not, two versions will be installed side-by-side:
60          * one as per-user and the other as per-machine.
61          *
62          * If we wanted to disable backward compatibility, the installer
63          * should abort here, and request the previous version to be manually
64          * uninstalled before installing this one.
65          */
66         er = MsiSetProperty(hInstall, TEXT("ALLUSERS"), TEXT(""));
67         ExitOnWin32Error(er, hr, "Failed to set the install scope to per-user");
68         goto LExit;
69       }
70     }
71   }
72 
73 LExit:
74   // Always succeed. This should not block the installation.
75   return WcaFinalize(ERROR_SUCCESS);
76 }
77 
78 
BroadcastEnvironmentUpdate(MSIHANDLE hInstall)79 extern "C" UINT WINAPI BroadcastEnvironmentUpdate(MSIHANDLE hInstall) {
80   HRESULT hr = S_OK;
81   UINT er = ERROR_SUCCESS;
82 
83   hr = WcaInitialize(hInstall, "BroadcastEnvironmentUpdate");
84   ExitOnFailure(hr, "Failed to initialize");
85 
86   SendMessageTimeoutW(HWND_BROADCAST,
87                       WM_SETTINGCHANGE,
88                       0,
89                       (LPARAM) L"Environment",
90                       SMTO_ABORTIFHUNG,
91                       5000,
92                       NULL);
93 
94 LExit:
95   er = SUCCEEDED(hr) ? ERROR_SUCCESS : ERROR_INSTALL_FAILURE;
96   return WcaFinalize(er);
97 }
98 
99 
DllMain(HINSTANCE hInst,ULONG ulReason,VOID * dummy)100 extern "C" BOOL WINAPI DllMain(HINSTANCE hInst, ULONG ulReason, VOID* dummy) {
101   switch (ulReason) {
102     case DLL_PROCESS_ATTACH:
103       WcaGlobalInitialize(hInst);
104       break;
105 
106     case DLL_PROCESS_DETACH:
107       WcaGlobalFinalize();
108       break;
109   }
110 
111   return TRUE;
112 }
113