LLVM 19.0.0git
MSVCSetupApi.h
Go to the documentation of this file.
1// <copyright file="Program.cpp" company="Microsoft Corporation">
2// Copyright (C) Microsoft Corporation. All rights reserved.
3// Licensed under the MIT license.
4// </copyright>
5// <license>
6// The MIT License (MIT)
7//
8// Copyright (C) Microsoft Corporation. All rights reserved.
9//
10// Permission is hereby granted, free of charge, to any person obtaining
11// a copy of this software and associated documentation files (the "Software"),
12// to deal in the Software without restriction, including without limitation the
13// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
14// sell copies of the Software, and to permit persons to whom the Software is
15// furnished to do so, subject to the following conditions:
16//
17// The above copyright notice and this permission notice shall be included in
18// all copies or substantial portions of the Software.
19//
20// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
21// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
22// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
23// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
24// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
25// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
26// SOFTWARE.
27// </license>
28
29#pragma once
30
31#ifdef __clang__
32#pragma clang diagnostic push
33#pragma clang diagnostic ignored "-Wnon-virtual-dtor"
34#endif
35
36// Constants
37//
38#ifndef E_NOTFOUND
39#define E_NOTFOUND HRESULT_FROM_WIN32(ERROR_NOT_FOUND)
40#endif
41
42#ifndef E_FILENOTFOUND
43#define E_FILENOTFOUND HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND)
44#endif
45
46// Enumerations
47//
48/// <summary>
49/// The state of an instance.
50/// </summary>
51enum InstanceState : unsigned {
52 /// <summary>
53 /// The instance state has not been determined.
54 /// </summary>
55 eNone = 0,
56
57 /// <summary>
58 /// The instance installation path exists.
59 /// </summary>
60 eLocal = 1,
61
62 /// <summary>
63 /// A product is registered to the instance.
64 /// </summary>
66
67 /// <summary>
68 /// No reboot is required for the instance.
69 /// </summary>
71
72 /// <summary>
73 /// The instance represents a complete install.
74 /// </summary>
75 eComplete = MAXUINT,
76};
77
78// Forward interface declarations
79//
80#ifndef __ISetupInstance_FWD_DEFINED__
81#define __ISetupInstance_FWD_DEFINED__
83#endif
84
85#ifndef __ISetupInstance2_FWD_DEFINED__
86#define __ISetupInstance2_FWD_DEFINED__
88#endif
89
90#ifndef __IEnumSetupInstances_FWD_DEFINED__
91#define __IEnumSetupInstances_FWD_DEFINED__
93#endif
94
95#ifndef __ISetupConfiguration_FWD_DEFINED__
96#define __ISetupConfiguration_FWD_DEFINED__
98#endif
99
100#ifndef __ISetupConfiguration2_FWD_DEFINED__
101#define __ISetupConfiguration2_FWD_DEFINED__
103#endif
104
105#ifndef __ISetupPackageReference_FWD_DEFINED__
106#define __ISetupPackageReference_FWD_DEFINED__
108#endif
109
110#ifndef __ISetupHelper_FWD_DEFINED__
111#define __ISetupHelper_FWD_DEFINED__
113#endif
114
115// Forward class declarations
116//
117#ifndef __SetupConfiguration_FWD_DEFINED__
118#define __SetupConfiguration_FWD_DEFINED__
119
120#ifdef __cplusplus
121typedef class SetupConfiguration SetupConfiguration;
122#endif
123
124#endif
125
126#ifdef __cplusplus
127extern "C" {
128#endif
129
130// Interface definitions
131//
132EXTERN_C const IID IID_ISetupInstance;
133
134#if defined(__cplusplus) && !defined(CINTERFACE)
135/// <summary>
136/// Information about an instance of a product.
137/// </summary>
138struct DECLSPEC_UUID("B41463C3-8866-43B5-BC33-2B0676F7F42E")
139 DECLSPEC_NOVTABLE ISetupInstance : public IUnknown {
140 /// <summary>
141 /// Gets the instance identifier (should match the name of the parent instance
142 /// directory).
143 /// </summary>
144 /// <param name="pbstrInstanceId">The instance identifier.</param>
145 /// <returns>Standard HRESULT indicating success or failure, including
146 /// E_FILENOTFOUND if the instance state does not exist.</returns>
147 STDMETHOD(GetInstanceId)(_Out_ BSTR *pbstrInstanceId) = 0;
148
149 /// <summary>
150 /// Gets the local date and time when the installation was originally
151 /// installed.
152 /// </summary>
153 /// <param name="pInstallDate">The local date and time when the installation
154 /// was originally installed.</param>
155 /// <returns>Standard HRESULT indicating success or failure, including
156 /// E_FILENOTFOUND if the instance state does not exist and E_NOTFOUND if the
157 /// property is not defined.</returns>
158 STDMETHOD(GetInstallDate)(_Out_ LPFILETIME pInstallDate) = 0;
159
160 /// <summary>
161 /// Gets the unique name of the installation, often indicating the branch and
162 /// other information used for telemetry.
163 /// </summary>
164 /// <param name="pbstrInstallationName">The unique name of the installation,
165 /// often indicating the branch and other information used for
166 /// telemetry.</param>
167 /// <returns>Standard HRESULT indicating success or failure, including
168 /// E_FILENOTFOUND if the instance state does not exist and E_NOTFOUND if the
169 /// property is not defined.</returns>
170 STDMETHOD(GetInstallationName)(_Out_ BSTR *pbstrInstallationName) = 0;
171
172 /// <summary>
173 /// Gets the path to the installation root of the product.
174 /// </summary>
175 /// <param name="pbstrInstallationPath">The path to the installation root of
176 /// the product.</param>
177 /// <returns>Standard HRESULT indicating success or failure, including
178 /// E_FILENOTFOUND if the instance state does not exist and E_NOTFOUND if the
179 /// property is not defined.</returns>
180 STDMETHOD(GetInstallationPath)(_Out_ BSTR *pbstrInstallationPath) = 0;
181
182 /// <summary>
183 /// Gets the version of the product installed in this instance.
184 /// </summary>
185 /// <param name="pbstrInstallationVersion">The version of the product
186 /// installed in this instance.</param>
187 /// <returns>Standard HRESULT indicating success or failure, including
188 /// E_FILENOTFOUND if the instance state does not exist and E_NOTFOUND if the
189 /// property is not defined.</returns>
190 STDMETHOD(GetInstallationVersion)(_Out_ BSTR *pbstrInstallationVersion) = 0;
191
192 /// <summary>
193 /// Gets the display name (title) of the product installed in this instance.
194 /// </summary>
195 /// <param name="lcid">The LCID for the display name.</param>
196 /// <param name="pbstrDisplayName">The display name (title) of the product
197 /// installed in this instance.</param>
198 /// <returns>Standard HRESULT indicating success or failure, including
199 /// E_FILENOTFOUND if the instance state does not exist and E_NOTFOUND if the
200 /// property is not defined.</returns>
201 STDMETHOD(GetDisplayName)(_In_ LCID lcid, _Out_ BSTR *pbstrDisplayName) = 0;
202
203 /// <summary>
204 /// Gets the description of the product installed in this instance.
205 /// </summary>
206 /// <param name="lcid">The LCID for the description.</param>
207 /// <param name="pbstrDescription">The description of the product installed in
208 /// this instance.</param>
209 /// <returns>Standard HRESULT indicating success or failure, including
210 /// E_FILENOTFOUND if the instance state does not exist and E_NOTFOUND if the
211 /// property is not defined.</returns>
212 STDMETHOD(GetDescription)(_In_ LCID lcid, _Out_ BSTR *pbstrDescription) = 0;
213
214 /// <summary>
215 /// Resolves the optional relative path to the root path of the instance.
216 /// </summary>
217 /// <param name="pwszRelativePath">A relative path within the instance to
218 /// resolve, or NULL to get the root path.</param>
219 /// <param name="pbstrAbsolutePath">The full path to the optional relative
220 /// path within the instance. If the relative path is NULL, the root path will
221 /// always terminate in a backslash.</param>
222 /// <returns>Standard HRESULT indicating success or failure, including
223 /// E_FILENOTFOUND if the instance state does not exist and E_NOTFOUND if the
224 /// property is not defined.</returns>
225 STDMETHOD(ResolvePath)
226 (_In_opt_z_ LPCOLESTR pwszRelativePath, _Out_ BSTR *pbstrAbsolutePath) = 0;
227};
228#endif
229
230EXTERN_C const IID IID_ISetupInstance2;
231
232#if defined(__cplusplus) && !defined(CINTERFACE)
233/// <summary>
234/// Information about an instance of a product.
235/// </summary>
236struct DECLSPEC_UUID("89143C9A-05AF-49B0-B717-72E218A2185C")
237 DECLSPEC_NOVTABLE ISetupInstance2 : public ISetupInstance {
238 /// <summary>
239 /// Gets the state of the instance.
240 /// </summary>
241 /// <param name="pState">The state of the instance.</param>
242 /// <returns>Standard HRESULT indicating success or failure, including
243 /// E_FILENOTFOUND if the instance state does not exist.</returns>
244 STDMETHOD(GetState)(_Out_ InstanceState *pState) = 0;
245
246 /// <summary>
247 /// Gets an array of package references registered to the instance.
248 /// </summary>
249 /// <param name="ppsaPackages">Pointer to an array of <see
250 /// cref="ISetupPackageReference"/>.</param>
251 /// <returns>Standard HRESULT indicating success or failure, including
252 /// E_FILENOTFOUND if the instance state does not exist and E_NOTFOUND if the
253 /// packages property is not defined.</returns>
254 STDMETHOD(GetPackages)(_Out_ LPSAFEARRAY *ppsaPackages) = 0;
255
256 /// <summary>
257 /// Gets a pointer to the <see cref="ISetupPackageReference"/> that represents
258 /// the registered product.
259 /// </summary>
260 /// <param name="ppPackage">Pointer to an instance of <see
261 /// cref="ISetupPackageReference"/>. This may be NULL if <see
262 /// cref="GetState"/> does not return <see cref="eComplete"/>.</param>
263 /// <returns>Standard HRESULT indicating success or failure, including
264 /// E_FILENOTFOUND if the instance state does not exist and E_NOTFOUND if the
265 /// packages property is not defined.</returns>
266 STDMETHOD(GetProduct)
267 (_Outptr_result_maybenull_ ISetupPackageReference **ppPackage) = 0;
268
269 /// <summary>
270 /// Gets the relative path to the product application, if available.
271 /// </summary>
272 /// <param name="pbstrProductPath">The relative path to the product
273 /// application, if available.</param>
274 /// <returns>Standard HRESULT indicating success or failure, including
275 /// E_FILENOTFOUND if the instance state does not exist.</returns>
276 STDMETHOD(GetProductPath)
277 (_Outptr_result_maybenull_ BSTR *pbstrProductPath) = 0;
278};
279#endif
280
281EXTERN_C const IID IID_IEnumSetupInstances;
282
283#if defined(__cplusplus) && !defined(CINTERFACE)
284/// <summary>
285/// A enumerator of installed <see cref="ISetupInstance"/> objects.
286/// </summary>
287struct DECLSPEC_UUID("6380BCFF-41D3-4B2E-8B2E-BF8A6810C848")
288 DECLSPEC_NOVTABLE IEnumSetupInstances : public IUnknown {
289 /// <summary>
290 /// Retrieves the next set of product instances in the enumeration sequence.
291 /// </summary>
292 /// <param name="celt">The number of product instances to retrieve.</param>
293 /// <param name="rgelt">A pointer to an array of <see
294 /// cref="ISetupInstance"/>.</param>
295 /// <param name="pceltFetched">A pointer to the number of product instances
296 /// retrieved. If celt is 1 this parameter may be NULL.</param>
297 /// <returns>S_OK if the number of elements were fetched, S_FALSE if nothing
298 /// was fetched (at end of enumeration), E_INVALIDARG if celt is greater than
299 /// 1 and pceltFetched is NULL, or E_OUTOFMEMORY if an <see
300 /// cref="ISetupInstance"/> could not be allocated.</returns>
301 STDMETHOD(Next)
302 (_In_ ULONG celt, _Out_writes_to_(celt, *pceltFetched) ISetupInstance **rgelt,
303 _Out_opt_ _Deref_out_range_(0, celt) ULONG *pceltFetched) = 0;
304
305 /// <summary>
306 /// Skips the next set of product instances in the enumeration sequence.
307 /// </summary>
308 /// <param name="celt">The number of product instances to skip.</param>
309 /// <returns>S_OK if the number of elements could be skipped; otherwise,
310 /// S_FALSE;</returns>
311 STDMETHOD(Skip)(_In_ ULONG celt) = 0;
312
313 /// <summary>
314 /// Resets the enumeration sequence to the beginning.
315 /// </summary>
316 /// <returns>Always returns S_OK;</returns>
317 STDMETHOD(Reset)(void) = 0;
318
319 /// <summary>
320 /// Creates a new enumeration object in the same state as the current
321 /// enumeration object: the new object points to the same place in the
322 /// enumeration sequence.
323 /// </summary>
324 /// <param name="ppenum">A pointer to a pointer to a new <see
325 /// cref="IEnumSetupInstances"/> interface. If the method fails, this
326 /// parameter is undefined.</param>
327 /// <returns>S_OK if a clone was returned; otherwise, E_OUTOFMEMORY.</returns>
328 STDMETHOD(Clone)(_Deref_out_opt_ IEnumSetupInstances **ppenum) = 0;
329};
330#endif
331
332EXTERN_C const IID IID_ISetupConfiguration;
333
334#if defined(__cplusplus) && !defined(CINTERFACE)
335/// <summary>
336/// Gets information about product instances set up on the machine.
337/// </summary>
338struct DECLSPEC_UUID("42843719-DB4C-46C2-8E7C-64F1816EFD5B")
339 DECLSPEC_NOVTABLE ISetupConfiguration : public IUnknown {
340 /// <summary>
341 /// Enumerates all completed product instances installed.
342 /// </summary>
343 /// <param name="ppEnumInstances">An enumeration of completed, installed
344 /// product instances.</param>
345 /// <returns>Standard HRESULT indicating success or failure.</returns>
346 STDMETHOD(EnumInstances)(_Out_ IEnumSetupInstances **ppEnumInstances) = 0;
347
348 /// <summary>
349 /// Gets the instance for the current process path.
350 /// </summary>
351 /// <param name="ppInstance">The instance for the current process
352 /// path.</param>
353 /// <returns>The instance for the current process path, or E_NOTFOUND if not
354 /// found.</returns>
355 STDMETHOD(GetInstanceForCurrentProcess)
356 (_Out_ ISetupInstance **ppInstance) = 0;
357
358 /// <summary>
359 /// Gets the instance for the given path.
360 /// </summary>
361 /// <param name="ppInstance">The instance for the given path.</param>
362 /// <returns>The instance for the given path, or E_NOTFOUND if not
363 /// found.</returns>
364 STDMETHOD(GetInstanceForPath)
365 (_In_z_ LPCWSTR wzPath, _Out_ ISetupInstance **ppInstance) = 0;
366};
367#endif
368
369EXTERN_C const IID IID_ISetupConfiguration2;
370
371#if defined(__cplusplus) && !defined(CINTERFACE)
372/// <summary>
373/// Gets information about product instances.
374/// </summary>
375struct DECLSPEC_UUID("26AAB78C-4A60-49D6-AF3B-3C35BC93365D")
376 DECLSPEC_NOVTABLE ISetupConfiguration2 : public ISetupConfiguration {
377 /// <summary>
378 /// Enumerates all product instances.
379 /// </summary>
380 /// <param name="ppEnumInstances">An enumeration of all product
381 /// instances.</param>
382 /// <returns>Standard HRESULT indicating success or failure.</returns>
383 STDMETHOD(EnumAllInstances)(_Out_ IEnumSetupInstances **ppEnumInstances) = 0;
384};
385#endif
386
387EXTERN_C const IID IID_ISetupPackageReference;
388
389#if defined(__cplusplus) && !defined(CINTERFACE)
390/// <summary>
391/// A reference to a package.
392/// </summary>
393struct DECLSPEC_UUID("da8d8a16-b2b6-4487-a2f1-594ccccd6bf5")
394 DECLSPEC_NOVTABLE ISetupPackageReference : public IUnknown {
395 /// <summary>
396 /// Gets the general package identifier.
397 /// </summary>
398 /// <param name="pbstrId">The general package identifier.</param>
399 /// <returns>Standard HRESULT indicating success or failure.</returns>
400 STDMETHOD(GetId)(_Out_ BSTR *pbstrId) = 0;
401
402 /// <summary>
403 /// Gets the version of the package.
404 /// </summary>
405 /// <param name="pbstrVersion">The version of the package.</param>
406 /// <returns>Standard HRESULT indicating success or failure.</returns>
407 STDMETHOD(GetVersion)(_Out_ BSTR *pbstrVersion) = 0;
408
409 /// <summary>
410 /// Gets the target process architecture of the package.
411 /// </summary>
412 /// <param name="pbstrChip">The target process architecture of the
413 /// package.</param>
414 /// <returns>Standard HRESULT indicating success or failure.</returns>
415 STDMETHOD(GetChip)(_Out_ BSTR *pbstrChip) = 0;
416
417 /// <summary>
418 /// Gets the language and optional region identifier.
419 /// </summary>
420 /// <param name="pbstrLanguage">The language and optional region
421 /// identifier.</param>
422 /// <returns>Standard HRESULT indicating success or failure.</returns>
423 STDMETHOD(GetLanguage)(_Out_ BSTR *pbstrLanguage) = 0;
424
425 /// <summary>
426 /// Gets the build branch of the package.
427 /// </summary>
428 /// <param name="pbstrBranch">The build branch of the package.</param>
429 /// <returns>Standard HRESULT indicating success or failure.</returns>
430 STDMETHOD(GetBranch)(_Out_ BSTR *pbstrBranch) = 0;
431
432 /// <summary>
433 /// Gets the type of the package.
434 /// </summary>
435 /// <param name="pbstrType">The type of the package.</param>
436 /// <returns>Standard HRESULT indicating success or failure.</returns>
437 STDMETHOD(GetType)(_Out_ BSTR *pbstrType) = 0;
438
439 /// <summary>
440 /// Gets the unique identifier consisting of all defined tokens.
441 /// </summary>
442 /// <param name="pbstrUniqueId">The unique identifier consisting of all
443 /// defined tokens.</param>
444 /// <returns>Standard HRESULT indicating success or failure, including
445 /// E_UNEXPECTED if no Id was defined (required).</returns>
446 STDMETHOD(GetUniqueId)(_Out_ BSTR *pbstrUniqueId) = 0;
447};
448#endif
449
450EXTERN_C const IID IID_ISetupHelper;
451
452#if defined(__cplusplus) && !defined(CINTERFACE)
453/// <summary>
454/// Helper functions.
455/// </summary>
456/// <remarks>
457/// You can query for this interface from the <see cref="SetupConfiguration"/>
458/// class.
459/// </remarks>
460struct DECLSPEC_UUID("42b21b78-6192-463e-87bf-d577838f1d5c")
461 DECLSPEC_NOVTABLE ISetupHelper : public IUnknown {
462 /// <summary>
463 /// Parses a dotted quad version string into a 64-bit unsigned integer.
464 /// </summary>
465 /// <param name="pwszVersion">The dotted quad version string to parse, e.g.
466 /// 1.2.3.4.</param>
467 /// <param name="pullVersion">A 64-bit unsigned integer representing the
468 /// version. You can compare this to other versions.</param>
469 /// <returns>Standard HRESULT indicating success or failure.</returns>
470 STDMETHOD(ParseVersion)
471 (_In_ LPCOLESTR pwszVersion, _Out_ PULONGLONG pullVersion) = 0;
472
473 /// <summary>
474 /// Parses a dotted quad version string into a 64-bit unsigned integer.
475 /// </summary>
476 /// <param name="pwszVersionRange">The string containing 1 or 2 dotted quad
477 /// version strings to parse, e.g. [1.0,) that means 1.0.0.0 or newer.</param>
478 /// <param name="pullMinVersion">A 64-bit unsigned integer representing the
479 /// minimum version, which may be 0. You can compare this to other
480 /// versions.</param>
481 /// <param name="pullMaxVersion">A 64-bit unsigned integer representing the
482 /// maximum version, which may be MAXULONGLONG. You can compare this to other
483 /// versions.</param>
484 /// <returns>Standard HRESULT indicating success or failure.</returns>
485 STDMETHOD(ParseVersionRange)
486 (_In_ LPCOLESTR pwszVersionRange, _Out_ PULONGLONG pullMinVersion,
487 _Out_ PULONGLONG pullMaxVersion) = 0;
488};
489#endif
490
491// Class declarations
492//
493EXTERN_C const CLSID CLSID_SetupConfiguration;
494
495#ifdef __cplusplus
496/// <summary>
497/// This class implements <see cref="ISetupConfiguration"/>, <see
498/// cref="ISetupConfiguration2"/>, and <see cref="ISetupHelper"/>.
499/// </summary>
500class DECLSPEC_UUID("177F0C4A-1CD3-4DE7-A32C-71DBBB9FA36D") SetupConfiguration;
501#endif
502
503// Function declarations
504//
505/// <summary>
506/// Gets an <see cref="ISetupConfiguration"/> that provides information about
507/// product instances installed on the machine.
508/// </summary>
509/// <param name="ppConfiguration">The <see cref="ISetupConfiguration"/> that
510/// provides information about product instances installed on the
511/// machine.</param>
512/// <param name="pReserved">Reserved for future use.</param>
513/// <returns>Standard HRESULT indicating success or failure.</returns>
514STDMETHODIMP GetSetupConfiguration(_Out_ ISetupConfiguration **ppConfiguration,
515 _Reserved_ LPVOID pReserved);
516
517#ifdef __cplusplus
518}
519#endif
520
521#ifdef __clang__
522#pragma clang diagnostic pop
523#endif
struct ISetupInstance2 ISetupInstance2
Definition: MSVCSetupApi.h:87
EXTERN_C const IID IID_ISetupConfiguration2
Definition: MSVCSetupApi.h:369
struct ISetupInstance ISetupInstance
Definition: MSVCSetupApi.h:82
EXTERN_C const IID IID_ISetupInstance
Definition: MSVCSetupApi.h:132
EXTERN_C const IID IID_IEnumSetupInstances
Definition: MSVCSetupApi.h:281
EXTERN_C const IID IID_ISetupHelper
Definition: MSVCSetupApi.h:450
InstanceState
The state of an instance.
Definition: MSVCSetupApi.h:51
@ eNone
The instance state has not been determined.
Definition: MSVCSetupApi.h:55
@ eComplete
The instance represents a complete install.
Definition: MSVCSetupApi.h:75
@ eRegistered
A product is registered to the instance.
Definition: MSVCSetupApi.h:65
@ eLocal
The instance installation path exists.
Definition: MSVCSetupApi.h:60
@ eNoRebootRequired
No reboot is required for the instance.
Definition: MSVCSetupApi.h:70
struct ISetupPackageReference ISetupPackageReference
Definition: MSVCSetupApi.h:107
struct ISetupConfiguration ISetupConfiguration
Definition: MSVCSetupApi.h:97
struct ISetupHelper ISetupHelper
Definition: MSVCSetupApi.h:112
EXTERN_C const CLSID CLSID_SetupConfiguration
Definition: MSVCSetupApi.h:493
struct ISetupConfiguration2 ISetupConfiguration2
Definition: MSVCSetupApi.h:102
EXTERN_C const IID IID_ISetupConfiguration
Definition: MSVCSetupApi.h:332
EXTERN_C const IID IID_ISetupInstance2
Definition: MSVCSetupApi.h:230
STDMETHODIMP GetSetupConfiguration(_Out_ ISetupConfiguration **ppConfiguration, _Reserved_ LPVOID pReserved)
Gets an ISetupConfiguration that provides information about product instances installed on the machin...
struct IEnumSetupInstances IEnumSetupInstances
Definition: MSVCSetupApi.h:92
EXTERN_C const IID IID_ISetupPackageReference
Definition: MSVCSetupApi.h:387