Common/Types/Gpo.ps1

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
$gpoType = @'
using System;
using System.Collections.Generic;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading;
using Microsoft.Win32;

namespace GPO
{
    /// <summary>
    /// Represent the result of group policy operations.
    /// </summary>
    public enum ResultCode
    {
        Succeed = 0,
        CreateOrOpenFailed = -1,
        SetFailed = -2,
        SaveFailed = -3
    }

    /// <summary>
    /// The WinAPI handler for GroupPlicy operations.
    /// </summary>
    public class WinAPIForGroupPolicy
    {
        // Group Policy Object open / creation flags
        const UInt32 GPO_OPEN_LOAD_REGISTRY = 0x00000001; // Load the registry files
        const UInt32 GPO_OPEN_READ_ONLY = 0x00000002; // Open the GPO as read only

        // Group Policy Object option flags
        const UInt32 GPO_OPTION_DISABLE_USER = 0x00000001; // The user portion of this GPO is disabled
        const UInt32 GPO_OPTION_DISABLE_MACHINE = 0x00000002; // The machine portion of this GPO is disabled

        const UInt32 REG_OPTION_NON_VOLATILE = 0x00000000;

        const UInt32 ERROR_MORE_DATA = 234;

        // You can find the Guid in <Gpedit.h>
        static readonly Guid REGISTRY_EXTENSION_GUID = new Guid("35378EAC-683F-11D2-A89A-00C04FBBCFA2");
        static readonly Guid CLSID_GPESnapIn = new Guid("8FC0B734-A0E1-11d1-A7D3-0000F87571E3");

        /// <summary>
        /// Group Policy Object type.
        /// </summary>
        enum GROUP_POLICY_OBJECT_TYPE
        {
            GPOTypeLocal = 0, // Default GPO on the local machine
            GPOTypeRemote, // GPO on a remote machine
            GPOTypeDS, // GPO in the Active Directory
            GPOTypeLocalUser, // User-specific GPO on the local machine
            GPOTypeLocalGroup // Group-specific GPO on the local machine
        }

        #region COM

        /// <summary>
        /// Group Policy Interface definition from COM.
        /// You can find the Guid in <Gpedit.h>
        /// </summary>
        [Guid("EA502723-A23D-11d1-A7D3-0000F87571E3"),
        InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
        interface IGroupPolicyObject
        {
            void New(
            [MarshalAs(UnmanagedType.LPWStr)] String pszDomainName,
            [MarshalAs(UnmanagedType.LPWStr)] String pszDisplayName,
            UInt32 dwFlags);

            void OpenDSGPO(
                [MarshalAs(UnmanagedType.LPWStr)] String pszPath,
                UInt32 dwFlags);

            void OpenLocalMachineGPO(UInt32 dwFlags);

            void OpenRemoteMachineGPO(
                [MarshalAs(UnmanagedType.LPWStr)] String pszComputerName,
                UInt32 dwFlags);

            void Save(
                [MarshalAs(UnmanagedType.Bool)] bool bMachine,
                [MarshalAs(UnmanagedType.Bool)] bool bAdd,
                [MarshalAs(UnmanagedType.LPStruct)] Guid pGuidExtension,
                [MarshalAs(UnmanagedType.LPStruct)] Guid pGuid);

            void Delete();

            void GetName(
                [MarshalAs(UnmanagedType.LPWStr)] StringBuilder pszName,
                Int32 cchMaxLength);

            void GetDisplayName(
                [MarshalAs(UnmanagedType.LPWStr)] StringBuilder pszName,
                Int32 cchMaxLength);

            void SetDisplayName([MarshalAs(UnmanagedType.LPWStr)] String pszName);

            void GetPath(
                [MarshalAs(UnmanagedType.LPWStr)] StringBuilder pszPath,
                Int32 cchMaxPath);

            void GetDSPath(
                UInt32 dwSection,
                [MarshalAs(UnmanagedType.LPWStr)] StringBuilder pszPath,
                Int32 cchMaxPath);

            void GetFileSysPath(
                UInt32 dwSection,
                [MarshalAs(UnmanagedType.LPWStr)] StringBuilder pszPath,
                Int32 cchMaxPath);

            UInt32 GetRegistryKey(UInt32 dwSection);

            Int32 GetOptions();

            void SetOptions(UInt32 dwOptions, UInt32 dwMask);

            void GetType(out GROUP_POLICY_OBJECT_TYPE gpoType);

            void GetMachineName(
                [MarshalAs(UnmanagedType.LPWStr)] StringBuilder pszName,
                Int32 cchMaxLength);

            UInt32 GetPropertySheetPages(out IntPtr hPages);
        }

        /// <summary>
        /// Group Policy Class definition from COM.
        /// You can find the Guid in <Gpedit.h>
        /// </summary>
        [ComImport, Guid("EA502722-A23D-11d1-A7D3-0000F87571E3")]
        class GroupPolicyObject { }

        #endregion

        #region WinAPI You can find definition of API for C# on: http://pinvoke.net/

        /// <summary>
        /// Opens the specified registry key. Note that key names are not case sensitive.
        /// </summary>
        /// See http://msdn.microsoft.com/en-us/library/ms724897(VS.85).aspx for more info about the parameters.<br/>
        [DllImport("advapi32.dll", CharSet = CharSet.Auto)]
        public static extern Int32 RegOpenKeyEx(
        UIntPtr hKey,
        String subKey,
        Int32 ulOptions,
        RegSAM samDesired,
        out UIntPtr hkResult);

        /// <summary>
        /// Retrieves the type and data for the specified value name associated with an open registry key.
        /// </summary>
        /// See http://msdn.microsoft.com/en-us/library/ms724911(VS.85).aspx for more info about the parameters and return value.<br/>
        [DllImport("advapi32.dll", CharSet = CharSet.Unicode, EntryPoint = "RegQueryValueExW", SetLastError = true)]
        static extern Int32 RegQueryValueEx(
        UIntPtr hKey,
        String lpValueName,
        Int32 lpReserved,
        out UInt32 lpType,
        [Out] byte[] lpData,
        ref UInt32 lpcbData);

        /// <summary>
        /// Sets the data and type of a specified value under a registry key.
        /// </summary>
        /// See http://msdn.microsoft.com/en-us/library/ms724923(VS.85).aspx for more info about the parameters and return value.<br/>
        [DllImport("advapi32.dll", SetLastError = true)]
        static extern Int32 RegSetValueEx(
        UInt32 hKey,
        [MarshalAs(UnmanagedType.LPStr)] String lpValueName,
        Int32 Reserved,
        Microsoft.Win32.RegistryValueKind dwType,
        IntPtr lpData,
        Int32 cbData);

        /// <summary>
        /// Creates the specified registry key. If the key already exists, the function opens it. Note that key names are not case sensitive.
        /// </summary>
        /// See http://msdn.microsoft.com/en-us/library/ms724844(v=VS.85).aspx for more info about the parameters and return value.<br/>
        [DllImport("advapi32.dll", SetLastError = true)]
        static extern Int32 RegCreateKeyEx(
        UInt32 hKey,
        String lpSubKey,
        UInt32 Reserved,
        String lpClass,
        RegOption dwOptions,
        RegSAM samDesired,
        IntPtr lpSecurityAttributes,
        out UInt32 phkResult,
        out RegResult lpdwDisposition);

        /// <summary>
        /// Closes a handle to the specified registry key.
        /// </summary>
        /// See http://msdn.microsoft.com/en-us/library/ms724837(VS.85).aspx for more info about the parameters and return value.<br/>
        [DllImport("advapi32.dll", SetLastError = true)]
        static extern Int32 RegCloseKey(
        UInt32 hKey);

        /// <summary>
        /// Deletes a subkey and its values from the specified platform-specific view of the registry. Note that key names are not case sensitive.
        /// </summary>
        /// See http://msdn.microsoft.com/en-us/library/ms724847(VS.85).aspx for more info about the parameters and return value.<br/>
        [DllImport("advapi32.dll", EntryPoint = "RegDeleteKeyEx", SetLastError = true)]
        public static extern Int32 RegDeleteKeyEx(
        UInt32 hKey,
        String lpSubKey,
        RegSAM samDesired,
        UInt32 Reserved);

        /// <summary>
        /// Removes the specified value from the specified registry key and subkey.
        /// </summary>
        /// See https://msdn.microsoft.com/en-us/library/windows/desktop/ms724848(v=vs.85).aspx for more info about the parameters and return value.<br/>
        [DllImport("advapi32.dll", EntryPoint = "RegDeleteKeyValue", SetLastError = true)]
        public static extern Int32 RegDeleteKeyValue(
        UInt32 hKey,
        String lpSubKey,
        string lpValueName);

        #endregion

        /// <summary>
        /// Registry creating volatile check.
        /// </summary>
        [Flags]
        public enum RegOption
        {
            NonVolatile = 0x0,
            Volatile = 0x1,
            CreateLink = 0x2,
            BackupRestore = 0x4,
            OpenLink = 0x8
        }

        /// <summary>
        /// Access mask the specifies the platform-specific view of the registry.
        /// </summary>
        [Flags]
        public enum RegSAM
        {
            QueryValue = 0x00000001,
            SetValue = 0x00000002,
            CreateSubKey = 0x00000004,
            EnumerateSubKeys = 0x00000008,
            Notify = 0x00000010,
            CreateLink = 0x00000020,
            WOW64_32Key = 0x00000200,
            WOW64_64Key = 0x00000100,
            WOW64_Res = 0x00000300,
            Read = 0x00020019,
            Write = 0x00020006,
            Execute = 0x00020019,
            AllAccess = 0x000f003f
        }

        /// <summary>
        /// Structure for security attributes.
        /// </summary>
        [StructLayout(LayoutKind.Sequential)]
        public struct SECURITY_ATTRIBUTES
        {
            public Int32 nLength;
            public IntPtr lpSecurityDescriptor;
            public Int32 bInheritHandle;
        }

        /// <summary>
        /// Flag returned by calling RegCreateKeyEx.
        /// </summary>
        public enum RegResult
        {
            CreatedNewKey = 0x00000001,
            OpenedExistingKey = 0x00000002
        }

        /// <summary>
        /// Class to create an object to handle the group policy operation.
        /// </summary>
        public class GroupPolicyObjectHandler
        {
            public const Int32 REG_NONE = 0;
            public const Int32 REG_SZ = 1;
            public const Int32 REG_EXPAND_SZ = 2;
            public const Int32 REG_BINARY = 3;
            public const Int32 REG_DWORD = 4;
            public const Int32 REG_DWORD_BIG_ENDIAN = 5;
            public const Int32 REG_MULTI_SZ = 7;
            public const Int32 REG_QWORD = 11;

            // Group Policy interface handler
            IGroupPolicyObject iGroupPolicyObject;
            // Group Policy object handler.
            GroupPolicyObject groupPolicyObject;

            #region constructor

            /// <summary>
            /// Constructor.
            /// </summary>
            /// <param name="remoteMachineName">Target machine name to operate group policy</param>
            /// <exception cref="System.Runtime.InteropServices.COMException">Throw when com execution throws exceptions</exception>
            public GroupPolicyObjectHandler(String remoteMachineName)
            {
                groupPolicyObject = new GroupPolicyObject();
                iGroupPolicyObject = (IGroupPolicyObject)groupPolicyObject;
                try
                {
                    if (String.IsNullOrEmpty(remoteMachineName))
                    {
                        iGroupPolicyObject.OpenLocalMachineGPO(GPO_OPEN_LOAD_REGISTRY);
                    }
                    else
                    {
                        iGroupPolicyObject.OpenRemoteMachineGPO(remoteMachineName, GPO_OPEN_LOAD_REGISTRY);
                    }
                }
                catch (COMException e)
                {
                    throw e;
                }
            }

            #endregion

            #region interface related methods

            /// <summary>
            /// Retrieves the display name for the GPO.
            /// </summary>
            /// <returns>Display name</returns>
            /// <exception cref="System.Runtime.InteropServices.COMException">Throw when com execution throws exceptions</exception>
            public String GetDisplayName()
            {
                StringBuilder pszName = new StringBuilder(Byte.MaxValue);
                try
                {
                    iGroupPolicyObject.GetDisplayName(pszName, Byte.MaxValue);
                }
                catch (COMException e)
                {
                    throw e;
                }
                return pszName.ToString();
            }

            /// <summary>
            /// Retrieves the computer name of the remote GPO.
            /// </summary>
            /// <returns>Machine name</returns>
            /// <exception cref="System.Runtime.InteropServices.COMException">Throw when com execution throws exceptions</exception>
            public String GetMachineName()
            {
                StringBuilder pszName = new StringBuilder(Byte.MaxValue);
                try
                {
                    iGroupPolicyObject.GetMachineName(pszName, Byte.MaxValue);
                }
                catch (COMException e)
                {
                    throw e;
                }
                return pszName.ToString();
            }

            /// <summary>
            /// Retrieves the options for the GPO.
            /// </summary>
            /// <returns>Options flag</returns>
            /// <exception cref="System.Runtime.InteropServices.COMException">Throw when com execution throws exceptions</exception>
            public Int32 GetOptions()
            {
                try
                {
                    return iGroupPolicyObject.GetOptions();
                }
                catch (COMException e)
                {
                    throw e;
                }
            }

            /// <summary>
            /// Retrieves the path to the GPO.
            /// </summary>
            /// <returns>The path to the GPO</returns>
            /// <exception cref="System.Runtime.InteropServices.COMException">Throw when com execution throws exceptions</exception>
            public String GetPath()
            {
                StringBuilder pszName = new StringBuilder(Byte.MaxValue);
                try
                {
                    iGroupPolicyObject.GetPath(pszName, Byte.MaxValue);
                }
                catch (COMException e)
                {
                    throw e;
                }
                return pszName.ToString();
            }

            /// <summary>
            /// Retrieves a handle to the root of the registry key for the machine section.
            /// </summary>
            /// <returns>A handle to the root of the registry key for the specified GPO computer section</returns>
            /// <exception cref="System.Runtime.InteropServices.COMException">Throw when com execution throws exceptions</exception>
            public UInt32 GetMachineRegistryKey()
            {
                UInt32 handle;
                try
                {
                    handle = iGroupPolicyObject.GetRegistryKey(GPO_OPTION_DISABLE_MACHINE);
                }
                catch (COMException e)
                {
                    throw e;
                }
                return handle;
            }

            /// <summary>
            /// Retrieves a handle to the root of the registry key for the user section.
            /// </summary>
            /// <returns>A handle to the root of the registry key for the specified GPO user section</returns>
            /// <exception cref="System.Runtime.InteropServices.COMException">Throw when com execution throws exceptions</exception>
            public UInt32 GetUserRegistryKey()
            {
                UInt32 handle;
                try
                {
                    handle = iGroupPolicyObject.GetRegistryKey(GPO_OPTION_DISABLE_USER);
                }
                catch (COMException e)
                {
                    throw e;
                }
                return handle;
            }

            /// <summary>
            /// Saves the specified registry policy settings to disk and updates the revision number of the GPO.
            /// </summary>
            /// <param name="isMachine">Specifies the registry policy settings to be saved. If this parameter is TRUE, the computer policy settings are saved. Otherwise, the user policy settings are saved.</param>
            /// <param name="isAdd">Specifies whether this is an add or delete operation. If this parameter is FALSE, the last policy setting for the specified extension pGuidExtension is removed. In all other cases, this parameter is TRUE.</param>
            /// <exception cref="System.Runtime.InteropServices.COMException">Throw when com execution throws exceptions</exception>
            public void Save(bool isMachine, bool isAdd)
            {
                try
                {
                    iGroupPolicyObject.Save(isMachine, isAdd, REGISTRY_EXTENSION_GUID, CLSID_GPESnapIn);
                }
                catch (COMException e)
                {
                    throw e;
                }
            }

            #endregion

            #region customized methods

            /// <summary>
            /// Set the group policy value.
            /// </summary>
            /// <param name="isMachine">Specifies the registry policy settings to be saved. If this parameter is TRUE, the computer policy settings are saved. Otherwise, the user policy settings are saved.</param>
            /// <param name="subKey">Group policy config full path</param>
            /// <param name="valueName">Group policy config key name</param>
            /// <param name="value">If value is null, it will envoke the delete method</param>
            /// <returns>Whether the config is successfully set</returns>
            public ResultCode SetGroupPolicy(bool isMachine, String subKey, String valueName, object value)
            {
                UInt32 gphKey = (isMachine) ? GetMachineRegistryKey() : GetUserRegistryKey();
                UInt32 gphSubKey;
                UIntPtr hKey;
                RegResult flag;

                if (null == value)
                {
                    // check the key's existance
                    if (RegOpenKeyEx((UIntPtr)gphKey, subKey, 0, RegSAM.QueryValue, out hKey) == 0)
                    {
                        RegCloseKey((UInt32)hKey);
                        // delete the GPO
                        Int32 hr = RegDeleteKeyValue(
                            gphKey,
                            subKey,
                            valueName);
                        if (0 != hr)
                        {
                            RegCloseKey(gphKey);
                            return ResultCode.CreateOrOpenFailed;
                        }
                        Save(isMachine, false);
                    }
                    else
                    {
                        // not exist
                    }

                }
                else
                {
                    // set the GPO
                    Int32 hr = RegCreateKeyEx(
                    gphKey,
                    subKey,
                    0,
                    null,
                    RegOption.NonVolatile,
                    RegSAM.Write,
                    IntPtr.Zero,
                    out gphSubKey,
                    out flag);
                    if (0 != hr)
                    {
                        RegCloseKey(gphSubKey);
                        RegCloseKey(gphKey);
                        return ResultCode.CreateOrOpenFailed;
                    }

                    Int32 cbData = 4;
                    IntPtr keyValue = IntPtr.Zero;

                    if (value.GetType() == typeof(Int32))
                    {
                        keyValue = Marshal.AllocHGlobal(cbData);
                        Marshal.WriteInt32(keyValue, (Int32)value);
                        hr = RegSetValueEx(gphSubKey, valueName, 0, RegistryValueKind.DWord, keyValue, cbData);
                    }
                    else if (value.GetType() == typeof(String))
                    {
                        keyValue = Marshal.StringToHGlobalAnsi(value.ToString());
                        cbData = System.Text.Encoding.UTF8.GetByteCount(value.ToString()) + 1;
                        hr = RegSetValueEx(gphSubKey, valueName, 0, RegistryValueKind.String, keyValue, cbData);
                    }
                    else
                    {
                        RegCloseKey(gphSubKey);
                        RegCloseKey(gphKey);
                        return ResultCode.SetFailed;
                    }

                    if (0 != hr)
                    {
                        RegCloseKey(gphSubKey);
                        RegCloseKey(gphKey);
                        return ResultCode.SetFailed;
                    }
                    try
                    {
                        Save(isMachine, true);
                    }
                    catch (COMException e)
                    {
                        RegCloseKey(gphSubKey);
                        RegCloseKey(gphKey);
                        return ResultCode.SaveFailed;
                    }
                    RegCloseKey(gphSubKey);
                    RegCloseKey(gphKey);
                }

                return ResultCode.Succeed;
            }

            /// <summary>
            /// Get the config of the group policy.
            /// </summary>
            /// <param name="isMachine">Specifies the registry policy settings to be saved. If this parameter is TRUE, get from the computer policy settings. Otherwise, get from the user policy settings.</param>
            /// <param name="subKey">Group policy config full path</param>
            /// <param name="valueName">Group policy config key name</param>
            /// <returns>The setting of the specified config</returns>
            public object GetGroupPolicy(bool isMachine, String subKey, String valueName)
            {
                UIntPtr gphKey = (UIntPtr)((isMachine) ? GetMachineRegistryKey() : GetUserRegistryKey());
                UIntPtr hKey;
                object keyValue = null;
                UInt32 size = 1;

                if (RegOpenKeyEx(gphKey, subKey, 0, RegSAM.QueryValue, out hKey) == 0)
                {
                    UInt32 type;
                    byte[] data = new byte[size]; // to store retrieved the value's data

                    if (RegQueryValueEx(hKey, valueName, 0, out type, data, ref size) == 234)
                    {
                        //size retreived
                        data = new byte[size]; //redefine data
                    }

                    if (RegQueryValueEx(hKey, valueName, 0, out type, data, ref size) != 0)
                    {
                        return null;
                    }

                    switch (type)
                    {
                        case REG_NONE:
                        case REG_BINARY:
                            keyValue = data;
                            break;
                        case REG_DWORD:
                            keyValue = (((data[0] | (data[1] << 8)) | (data[2] << 16)) | (data[3] << 24));
                            break;
                        case REG_DWORD_BIG_ENDIAN:
                            keyValue = (((data[3] | (data[2] << 8)) | (data[1] << 16)) | (data[0] << 24));
                            break;
                        case REG_QWORD:
                            {
                                UInt32 numLow = (UInt32)(((data[0] | (data[1] << 8)) | (data[2] << 16)) | (data[3] << 24));
                                UInt32 numHigh = (UInt32)(((data[4] | (data[5] << 8)) | (data[6] << 16)) | (data[7] << 24));
                                keyValue = (long)(((ulong)numHigh << 32) | (ulong)numLow);
                                break;
                            }
                        case REG_SZ:
                            var s = Encoding.Unicode.GetString(data, 0, (Int32)size);
                            keyValue = s.Substring(0, s.Length - 1);
                            break;
                        case REG_EXPAND_SZ:
                            keyValue = Environment.ExpandEnvironmentVariables(Encoding.Unicode.GetString(data, 0, (Int32)size));
                            break;
                        case REG_MULTI_SZ:
                            {
                                List<string> strings = new List<String>();
                                String packed = Encoding.Unicode.GetString(data, 0, (Int32)size);
                                Int32 start = 0;
                                Int32 end = packed.IndexOf("", start);
                                while (end > start)
                                {
                                    strings.Add(packed.Substring(start, end - start));
                                    start = end + 1;
                                    end = packed.IndexOf("", start);
                                }
                                keyValue = strings.ToArray();
                                break;
                            }
                        default:
                            throw new NotSupportedException();
                    }

                    RegCloseKey((UInt32)hKey);
                }

                return keyValue;
            }

            #endregion

        }
    }

    public class Helper
    {
        private static object _returnValueFromSet, _returnValueFromGet;

        /// <summary>
        /// Set policy config
        /// It will start a single thread to set group policy.
        /// </summary>
        /// <param name="isMachine">Whether is machine config</param>
        /// <param name="configFullPath">The full path configuration</param>
        /// <param name="configKey">The configureation key name</param>
        /// <param name="value">The value to set, boxed with proper type [ String, Int32 ]</param>
        /// <returns>Whether the config is successfully set</returns>
        [MethodImplAttribute(MethodImplOptions.Synchronized)]
        public static ResultCode SetGroupPolicy(bool isMachine, String configFullPath, String configKey, object value)
        {
            Thread worker = new Thread(SetGroupPolicy);
            worker.SetApartmentState(ApartmentState.STA);
            worker.Start(new object[] { isMachine, configFullPath, configKey, value });
            worker.Join();
            return (ResultCode)_returnValueFromSet;
        }

        /// <summary>
        /// Thread start for seting group policy.
        /// Called by public static ResultCode SetGroupPolicy(bool isMachine, WinRMGPConfigName configName, object value)
        /// </summary>
        /// <param name="values">
        /// values[0] - isMachine<br/>
        /// values[1] - configFullPath<br/>
        /// values[2] - configKey<br/>
        /// values[3] - value<br/>
        /// </param>
        private static void SetGroupPolicy(object values)
        {
            object[] valueList = (object[])values;
            bool isMachine = (bool)valueList[0];
            String configFullPath = (String)valueList[1];
            String configKey = (String)valueList[2];
            object value = valueList[3];

            WinAPIForGroupPolicy.GroupPolicyObjectHandler gpHandler = new WinAPIForGroupPolicy.GroupPolicyObjectHandler(null);

            _returnValueFromSet = gpHandler.SetGroupPolicy(isMachine, configFullPath, configKey, value);
        }

        /// <summary>
        /// Get policy config.
        /// It will start a single thread to get group policy
        /// </summary>
        /// <param name="isMachine">Whether is machine config</param>
        /// <param name="configFullPath">The full path configuration</param>
        /// <param name="configKey">The configureation key name</param>
        /// <returns>The group policy setting</returns>
        [MethodImplAttribute(MethodImplOptions.Synchronized)]
        public static object GetGroupPolicy(bool isMachine, String configFullPath, String configKey)
        {
            Thread worker = new Thread(GetGroupPolicy);
            worker.SetApartmentState(ApartmentState.STA);
            worker.Start(new object[] { isMachine, configFullPath, configKey });
            worker.Join();
            return _returnValueFromGet;
        }

        /// <summary>
        /// Thread start for geting group policy.
        /// Called by public static object GetGroupPolicy(bool isMachine, WinRMGPConfigName configName)
        /// </summary>
        /// <param name="values">
        /// values[0] - isMachine<br/>
        /// values[1] - configFullPath<br/>
        /// values[2] - configKey<br/>
        /// </param>
        public static void GetGroupPolicy(object values)
        {
            object[] valueList = (object[])values;
            bool isMachine = (bool)valueList[0];
            String configFullPath = (String)valueList[1];
            String configKey = (String)valueList[2];

            WinAPIForGroupPolicy.GroupPolicyObjectHandler gpHandler = new WinAPIForGroupPolicy.GroupPolicyObjectHandler(null);

            _returnValueFromGet = gpHandler.GetGroupPolicy(isMachine, configFullPath, configKey);
        }
    }
}
'@


Add-Type -TypeDefinition $gpoType -IgnoreWarnings