aboutsummaryrefslogtreecommitdiff
path: root/src/WixToolset.Core.Native/Msi/View.cs
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--src/WixToolset.Core.Native/Msi/View.cs206
1 files changed, 206 insertions, 0 deletions
diff --git a/src/WixToolset.Core.Native/Msi/View.cs b/src/WixToolset.Core.Native/Msi/View.cs
new file mode 100644
index 00000000..6305a9de
--- /dev/null
+++ b/src/WixToolset.Core.Native/Msi/View.cs
@@ -0,0 +1,206 @@
1// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information.
2
3namespace WixToolset.Core.Native.Msi
4{
5 using System;
6 using System.Collections;
7 using System.Collections.Generic;
8 using System.Globalization;
9
10 /// <summary>
11 /// Wrapper class for MSI API views.
12 /// </summary>
13 public sealed class View : MsiHandle
14 {
15 /// <summary>
16 /// Constructor that creates a view given a database handle and a query.
17 /// </summary>
18 /// <param name="db">Handle to the database to run the query on.</param>
19 /// <param name="query">Query to be executed.</param>
20 public View(Database db, string query)
21 {
22 if (null == db)
23 {
24 throw new ArgumentNullException(nameof(db));
25 }
26
27 if (null == query)
28 {
29 throw new ArgumentNullException(nameof(query));
30 }
31
32 var error = MsiInterop.MsiDatabaseOpenView(db.Handle, query, out var handle);
33 if (0 != error)
34 {
35 throw new MsiException(error);
36 }
37
38 this.Handle = handle;
39 }
40
41 /// <summary>
42 /// Enumerator that automatically disposes of the retrieved Records.
43 /// </summary>
44 public IEnumerable<Record> Records => new ViewEnumerable(this);
45
46 /// <summary>
47 /// Executes a view with no customizable parameters.
48 /// </summary>
49 public void Execute()
50 {
51 this.Execute(null);
52 }
53
54 /// <summary>
55 /// Executes a query substituing the values from the records into the customizable parameters
56 /// in the view.
57 /// </summary>
58 /// <param name="record">Record containing parameters to be substituded into the view.</param>
59 public void Execute(Record record)
60 {
61 var error = MsiInterop.MsiViewExecute(this.Handle, null == record ? 0 : record.Handle);
62 if (0 != error)
63 {
64 throw new MsiException(error);
65 }
66 }
67
68 /// <summary>
69 /// Fetches the next row in the view.
70 /// </summary>
71 /// <returns>Returns the fetched record; otherwise null.</returns>
72 public Record Fetch()
73 {
74 var error = MsiInterop.MsiViewFetch(this.Handle, out var recordHandle);
75 if (259 == error)
76 {
77 return null;
78 }
79 else if (0 != error)
80 {
81 throw new MsiException(error);
82 }
83
84 return new Record(recordHandle);
85 }
86
87 /// <summary>
88 /// Updates a fetched record.
89 /// </summary>
90 /// <param name="type">Type of modification mode.</param>
91 /// <param name="record">Record to be modified.</param>
92 public void Modify(ModifyView type, Record record)
93 {
94 var error = MsiInterop.MsiViewModify(this.Handle, Convert.ToInt32(type, CultureInfo.InvariantCulture), record.Handle);
95 if (0 != error)
96 {
97 throw new MsiException(error);
98 }
99 }
100
101 /// <summary>
102 /// Get the column names in a record.
103 /// </summary>
104 /// <returns></returns>
105 public Record GetColumnNames()
106 {
107 return this.GetColumnInfo(MsiInterop.MSICOLINFONAMES);
108 }
109
110 /// <summary>
111 /// Get the column types in a record.
112 /// </summary>
113 /// <returns></returns>
114 public Record GetColumnTypes()
115 {
116 return this.GetColumnInfo(MsiInterop.MSICOLINFOTYPES);
117 }
118
119 /// <summary>
120 /// Returns a record containing column names or definitions.
121 /// </summary>
122 /// <param name="columnType">Specifies a flag indicating what type of information is needed. Either MSICOLINFO_NAMES or MSICOLINFO_TYPES.</param>
123 /// <returns>The record containing information about the column.</returns>
124 public Record GetColumnInfo(int columnType)
125 {
126
127 var error = MsiInterop.MsiViewGetColumnInfo(this.Handle, columnType, out var recordHandle);
128 if (0 != error)
129 {
130 throw new MsiException(error);
131 }
132
133 return new Record(recordHandle);
134 }
135
136 private class ViewEnumerable : IEnumerable<Record>
137 {
138 private readonly View view;
139
140 public ViewEnumerable(View view) => this.view = view;
141
142 public IEnumerator<Record> GetEnumerator() => new ViewEnumerator(this.view);
143
144 IEnumerator IEnumerable.GetEnumerator() => new ViewEnumerator(this.view);
145 }
146
147 private class ViewEnumerator : IEnumerator<Record>
148 {
149 private readonly View view;
150 private readonly List<Record> records = new List<Record>();
151 private int position = -1;
152 private bool disposed;
153
154 public ViewEnumerator(View view) => this.view = view;
155
156 public Record Current => this.records[this.position];
157
158 object IEnumerator.Current => this.records[this.position];
159
160 public bool MoveNext()
161 {
162 if (this.position + 1 >= this.records.Count)
163 {
164 var record = this.view.Fetch();
165
166 if (record == null)
167 {
168 return false;
169 }
170
171 this.records.Add(record);
172 this.position = this.records.Count - 1;
173 }
174 else
175 {
176 ++this.position;
177 }
178
179 return true;
180 }
181
182 public void Reset() => this.position = -1;
183
184 public void Dispose()
185 {
186 this.Dispose(true);
187 }
188
189 protected virtual void Dispose(bool disposing)
190 {
191 if (!this.disposed)
192 {
193 if (disposing)
194 {
195 foreach (var record in this.records)
196 {
197 record.Dispose();
198 }
199 }
200
201 this.disposed = true;
202 }
203 }
204 }
205 }
206}