OpenVPN
msiex.c
Go to the documentation of this file.
1/*
2 * openvpnmsica -- Custom Action DLL to provide OpenVPN-specific support to MSI packages
3 * https://community.openvpn.net/openvpn/wiki/OpenVPNMSICA
4 *
5 * Copyright (C) 2018-2025 Simon Rozman <simon@rozman.si>
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License version 2
9 * as published by the Free Software Foundation.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License along
17 * with this program; if not, see <https://www.gnu.org/licenses/>.
18 */
19
20#ifdef HAVE_CONFIG_H
21#include <config.h>
22#endif
23
24#include "msiex.h"
25#include "../tapctl/error.h"
26
27#include <windows.h>
28#include <malloc.h>
29#include <memory.h>
30#include <msiquery.h>
31#ifdef _MSC_VER
32#pragma comment(lib, "msi.lib")
33#endif
34
35
36UINT
37msi_get_string(_In_ MSIHANDLE hInstall, _In_z_ LPCWSTR szName, _Out_ LPWSTR *pszValue)
38{
39 if (pszValue == NULL)
40 {
41 return ERROR_BAD_ARGUMENTS;
42 }
43
44 /* Try with stack buffer first. */
45 WCHAR szBufStack[128];
46 DWORD dwLength = _countof(szBufStack);
47 UINT uiResult = MsiGetProperty(hInstall, szName, szBufStack, &dwLength);
48 if (uiResult == ERROR_SUCCESS)
49 {
50 /* Copy from stack. */
51 *pszValue = (LPWSTR)malloc(++dwLength * sizeof(WCHAR));
52 if (*pszValue == NULL)
53 {
54 msg(M_FATAL, "%s: malloc(%u) failed", dwLength * sizeof(WCHAR));
55 return ERROR_OUTOFMEMORY;
56 }
57
58 memcpy(*pszValue, szBufStack, dwLength * sizeof(WCHAR));
59 return ERROR_SUCCESS;
60 }
61 else if (uiResult == ERROR_MORE_DATA)
62 {
63 /* Allocate on heap and retry. */
64 LPWSTR szBufHeap = (LPWSTR)malloc(++dwLength * sizeof(WCHAR));
65 if (szBufHeap == NULL)
66 {
67 msg(M_FATAL, "%s: malloc(%u) failed", __FUNCTION__, dwLength * sizeof(WCHAR));
68 return ERROR_OUTOFMEMORY;
69 }
70
71 uiResult = MsiGetProperty(hInstall, szName, szBufHeap, &dwLength);
72 if (uiResult == ERROR_SUCCESS)
73 {
74 *pszValue = szBufHeap;
75 }
76 else
77 {
78 free(szBufHeap);
79 }
80 return uiResult;
81 }
82 else
83 {
84 SetLastError(uiResult); /* MSDN does not mention MsiGetProperty() to set GetLastError(). But
85 we do have an error code. Set last error manually. */
86 msg(M_NONFATAL | M_ERRNO, "%s: MsiGetProperty failed", __FUNCTION__);
87 return uiResult;
88 }
89}
90
91
92UINT
93msi_get_record_string(_In_ MSIHANDLE hRecord, _In_ unsigned int iField, _Out_ LPWSTR *pszValue)
94{
95 if (pszValue == NULL)
96 {
97 return ERROR_BAD_ARGUMENTS;
98 }
99
100 /* Try with stack buffer first. */
101 WCHAR szBufStack[128];
102 DWORD dwLength = _countof(szBufStack);
103 UINT uiResult = MsiRecordGetString(hRecord, iField, szBufStack, &dwLength);
104 if (uiResult == ERROR_SUCCESS)
105 {
106 /* Copy from stack. */
107 *pszValue = (LPWSTR)malloc(++dwLength * sizeof(WCHAR));
108 if (*pszValue == NULL)
109 {
110 msg(M_FATAL, "%s: malloc(%u) failed", __FUNCTION__, dwLength * sizeof(WCHAR));
111 return ERROR_OUTOFMEMORY;
112 }
113
114 memcpy(*pszValue, szBufStack, dwLength * sizeof(WCHAR));
115 return ERROR_SUCCESS;
116 }
117 else if (uiResult == ERROR_MORE_DATA)
118 {
119 /* Allocate on heap and retry. */
120 LPWSTR szBufHeap = (LPWSTR)malloc(++dwLength * sizeof(WCHAR));
121 if (szBufHeap == NULL)
122 {
123 msg(M_FATAL, "%s: malloc(%u) failed", __FUNCTION__, dwLength * sizeof(WCHAR));
124 return ERROR_OUTOFMEMORY;
125 }
126
127 uiResult = MsiRecordGetString(hRecord, iField, szBufHeap, &dwLength);
128 if (uiResult == ERROR_SUCCESS)
129 {
130 *pszValue = szBufHeap;
131 }
132 else
133 {
134 free(szBufHeap);
135 }
136 return uiResult;
137 }
138 else
139 {
140 SetLastError(uiResult); /* MSDN does not mention MsiRecordGetString() to set GetLastError().
141 But we do have an error code. Set last error manually. */
142 msg(M_NONFATAL | M_ERRNO, "%s: MsiRecordGetString failed", __FUNCTION__);
143 return uiResult;
144 }
145}
146
147
148UINT
149msi_format_record(_In_ MSIHANDLE hInstall, _In_ MSIHANDLE hRecord, _Out_ LPWSTR *pszValue)
150{
151 if (pszValue == NULL)
152 {
153 return ERROR_BAD_ARGUMENTS;
154 }
155
156 /* Try with stack buffer first. */
157 WCHAR szBufStack[128];
158 DWORD dwLength = _countof(szBufStack);
159 UINT uiResult = MsiFormatRecord(hInstall, hRecord, szBufStack, &dwLength);
160 if (uiResult == ERROR_SUCCESS)
161 {
162 /* Copy from stack. */
163 *pszValue = (LPWSTR)malloc(++dwLength * sizeof(WCHAR));
164 if (*pszValue == NULL)
165 {
166 msg(M_FATAL, "%s: malloc(%u) failed", __FUNCTION__, dwLength * sizeof(WCHAR));
167 return ERROR_OUTOFMEMORY;
168 }
169
170 memcpy(*pszValue, szBufStack, dwLength * sizeof(WCHAR));
171 return ERROR_SUCCESS;
172 }
173 else if (uiResult == ERROR_MORE_DATA)
174 {
175 /* Allocate on heap and retry. */
176 LPWSTR szBufHeap = (LPWSTR)malloc(++dwLength * sizeof(WCHAR));
177 if (szBufHeap == NULL)
178 {
179 msg(M_FATAL, "%s: malloc(%u) failed", __FUNCTION__, dwLength * sizeof(WCHAR));
180 return ERROR_OUTOFMEMORY;
181 }
182
183 uiResult = MsiFormatRecord(hInstall, hRecord, szBufHeap, &dwLength);
184 if (uiResult == ERROR_SUCCESS)
185 {
186 *pszValue = szBufHeap;
187 }
188 else
189 {
190 free(szBufHeap);
191 }
192 return uiResult;
193 }
194 else
195 {
196 SetLastError(uiResult); /* MSDN does not mention MsiFormatRecord() to set GetLastError().
197 But we do have an error code. Set last error manually. */
198 msg(M_NONFATAL | M_ERRNO, "%s: MsiFormatRecord failed", __FUNCTION__);
199 return uiResult;
200 }
201}
202
203
204UINT
205msi_format_field(_In_ MSIHANDLE hInstall, _In_ MSIHANDLE hRecord, _In_ unsigned int iField,
206 _Out_ LPWSTR *pszValue)
207{
208 if (pszValue == NULL)
209 {
210 return ERROR_BAD_ARGUMENTS;
211 }
212
213 /* Read string to format. */
214 LPWSTR szValue = NULL;
215 UINT uiResult = msi_get_record_string(hRecord, iField, &szValue);
216 if (uiResult != ERROR_SUCCESS)
217 {
218 return uiResult;
219 }
220 if (szValue[0] == 0)
221 {
222 /* The string is empty. There's nothing left to do. */
223 *pszValue = szValue;
224 return ERROR_SUCCESS;
225 }
226
227 /* Create a temporary record. */
228 MSIHANDLE hRecordEx = MsiCreateRecord(1);
229 if (!hRecordEx)
230 {
231 uiResult = ERROR_INVALID_HANDLE;
232 msg(M_NONFATAL, "%s: MsiCreateRecord failed", __FUNCTION__);
233 goto cleanup_szValue;
234 }
235
236 /* Populate the record with data. */
237 uiResult = MsiRecordSetString(hRecordEx, 0, szValue);
238 if (uiResult != ERROR_SUCCESS)
239 {
240 SetLastError(uiResult); /* MSDN does not mention MsiRecordSetString() to set GetLastError().
241 But we do have an error code. Set last error manually. */
242 msg(M_NONFATAL | M_ERRNO, "%s: MsiRecordSetString failed", __FUNCTION__);
243 goto cleanup_hRecordEx;
244 }
245
246 /* Do the formatting. */
247 uiResult = msi_format_record(hInstall, hRecordEx, pszValue);
248
249cleanup_hRecordEx:
250 MsiCloseHandle(hRecordEx);
251cleanup_szValue:
252 free(szValue);
253 return uiResult;
254}
UINT msi_format_record(_In_ MSIHANDLE hInstall, _In_ MSIHANDLE hRecord, _Out_ LPWSTR *pszValue)
Formats MSI record.
Definition msiex.c:149
UINT msi_format_field(_In_ MSIHANDLE hInstall, _In_ MSIHANDLE hRecord, _In_ unsigned int iField, _Out_ LPWSTR *pszValue)
Formats MSI record field.
Definition msiex.c:205
UINT msi_get_record_string(_In_ MSIHANDLE hRecord, _In_ unsigned int iField, _Out_ LPWSTR *pszValue)
Gets MSI record string value.
Definition msiex.c:93
UINT msi_get_string(_In_ MSIHANDLE hInstall, _In_z_ LPCWSTR szName, _Out_ LPWSTR *pszValue)
Gets MSI property value.
Definition msiex.c:37
#define M_FATAL
Definition error.h:88
#define M_NONFATAL
Definition error.h:89
#define msg(flags,...)
Definition error.h:150
#define M_ERRNO
Definition error.h:93
#define _In_z_
Definition basic.h:47
#define _Out_
Definition basic.h:56
#define _In_
Definition basic.h:41