├── AzureStorageAccountBackup.cs ├── FastEnumIntEqualityComparer.cs ├── FastReplacer.cs ├── GroupPolicyUtilities.cs ├── VariableArguments.cs └── VariableArgumentsExampleUsage.cs /AzureStorageAccountBackup.cs: -------------------------------------------------------------------------------- 1 | 2 | public async Task Backup() 3 | { 4 | CloudBlobClient blobClient = _storageAccount.CreateCloudBlobClient(); 5 | CloudBlobClient blobBackupClient = _backupStorageAccount.CreateCloudBlobClient(); 6 | 7 | foreach (var srcContainer in blobClient.ListContainers()) 8 | { 9 | var backupTimeInTicks = DateTime.UtcNow.Ticks; 10 | var destContainerName = srcContainer.Name + "-" + backupTimeInTicks; 11 | 12 | var destContainer = blobBackupClient.GetContainerReference(destContainerName); 13 | 14 | // assume it does not exist already, 15 | // as that wouldn't make sense. 16 | await destContainer.CreateAsync(); 17 | 18 | // ensure that the container is not accessible 19 | // to the outside world, 20 | // as we want all the backups to be internal. 21 | BlobContainerPermissions destContainerPermissions = destContainer.GetPermissions(); 22 | if (destContainerPermissions.PublicAccess != BlobContainerPublicAccessType.Off) 23 | { 24 | destContainerPermissions.PublicAccess = BlobContainerPublicAccessType.Off; 25 | await destContainer.SetPermissionsAsync(destContainerPermissions); 26 | } 27 | 28 | // copy src container to dest container, 29 | // note that this is synchronous operation in reality, 30 | // as I want to only add real metadata to container 31 | // once all the blobs have been copied successfully. 32 | await CopyContainers(srcContainer, destContainer); 33 | await EnsureCopySucceeded(destContainer); 34 | 35 | // ensure we have some metadata for the container 36 | // as this will helps us to delete older containers 37 | // on a later date. 38 | await destContainer.FetchAttributesAsync(); 39 | 40 | var destContainerMetadata = destContainer.Metadata; 41 | if (!destContainerMetadata.ContainsKey("Backup-Of")) 42 | { 43 | destContainerMetadata.Add("Backup-Of", srcContainer.Name.ToLowerInvariant()); 44 | destContainerMetadata.Add("Created-At", backupTimeInTicks.ToString()); 45 | await destContainer.SetMetadataAsync(); 46 | } 47 | } 48 | 49 | // let's purge the older containers, 50 | // if we already have multiple newer backups of them. 51 | // why keep them around. 52 | // just asking for trouble. 53 | var blobGroupedContainers = blobBackupClient.ListContainers() 54 | .Where(container => container.Metadata.ContainsKey("Backup-Of")) 55 | .Select(container => new 56 | { 57 | Container = container, 58 | BackupOf = container.Metadata["Backup-Of"], 59 | CreatedAt = new DateTime(long.Parse(container.Metadata["Created-At"])) 60 | }).GroupBy(arg => arg.BackupOf); 61 | 62 | foreach (var blobGroupedContainer in blobGroupedContainers) 63 | { 64 | var containersToDelete = blobGroupedContainer.Select(arg => new 65 | { 66 | Container = arg.Container, 67 | CreatedAt = new DateTime(arg.CreatedAt.Year, arg.CreatedAt.Month, arg.CreatedAt.Day) 68 | }) 69 | .GroupBy(arg => arg.CreatedAt) 70 | .OrderByDescending(grouping => grouping.Key) 71 | .Skip(7) /* skip last 7 days worth of data */ 72 | .SelectMany(grouping => grouping) 73 | .Select(arg => arg.Container); 74 | 75 | foreach (var containerToDelete in containersToDelete) 76 | { 77 | await containerToDelete.DeleteIfExistsAsync(); 78 | } 79 | } 80 | } 81 | 82 | private async Task EnsureCopySucceeded(CloudBlobContainer destContainer) 83 | { 84 | bool pendingCopy = true; 85 | var retryCountLookup = new Dictionary(); 86 | 87 | while (pendingCopy) 88 | { 89 | pendingCopy = false; 90 | 91 | var destBlobList = destContainer.ListBlobs(null, true, BlobListingDetails.Copy); 92 | 93 | foreach (var dest in destBlobList) 94 | { 95 | var destBlob = dest as CloudBlob; 96 | if (destBlob == null) 97 | { 98 | continue; 99 | } 100 | 101 | var blobIdentifier = destBlob.Name; 102 | 103 | if (destBlob.CopyState.Status == CopyStatus.Aborted || 104 | destBlob.CopyState.Status == CopyStatus.Failed) 105 | { 106 | int retryCount; 107 | if (retryCountLookup.TryGetValue(blobIdentifier, out retryCount)) 108 | { 109 | if (retryCount > 4) 110 | { 111 | throw new Exception("[CRITICAL] Failed to copy '" 112 | + destBlob.CopyState.Source.AbsolutePath + "' to '" 113 | + destBlob.StorageUri + "' due to reason of: " + 114 | destBlob.CopyState.StatusDescription); 115 | } 116 | 117 | retryCountLookup[blobIdentifier] = retryCount + 1; 118 | } 119 | else 120 | { 121 | retryCountLookup[blobIdentifier] = 1; 122 | } 123 | 124 | pendingCopy = true; 125 | 126 | // restart the copy process for src and dest blobs. 127 | // note we also have retry count protection, 128 | // so if any of the blobs fail too much, 129 | // we'll give up. 130 | await destBlob.StartCopyAsync(destBlob.CopyState.Source); 131 | } 132 | else if (destBlob.CopyState.Status == CopyStatus.Pending) 133 | { 134 | pendingCopy = true; 135 | } 136 | } 137 | 138 | Thread.Sleep(1000); 139 | } 140 | } 141 | 142 | private async Task CopyContainers( 143 | CloudBlobContainer srcContainer, 144 | CloudBlobContainer destContainer) 145 | { 146 | // get the SAS token to use for all blobs 147 | string blobToken = srcContainer.GetSharedAccessSignature(new SharedAccessBlobPolicy() 148 | { 149 | Permissions = SharedAccessBlobPermissions.Read, 150 | SharedAccessStartTime = DateTime.Now.AddMinutes(-5), 151 | SharedAccessExpiryTime = DateTime.Now.AddHours(3) 152 | }); 153 | 154 | foreach (var srcBlob in srcContainer.ListBlobs(null, true)) 155 | { 156 | var srcCloudBlob = srcBlob as CloudBlob; 157 | if (srcCloudBlob == null) 158 | { 159 | continue; 160 | } 161 | 162 | CloudBlob destCloudBlob; 163 | 164 | if (srcCloudBlob.Properties.BlobType == BlobType.BlockBlob) 165 | { 166 | destCloudBlob = destContainer.GetBlockBlobReference(srcCloudBlob.Name); 167 | } 168 | else 169 | { 170 | destCloudBlob = destContainer.GetPageBlobReference(srcCloudBlob.Name); 171 | } 172 | 173 | await destCloudBlob.StartCopyAsync(new Uri(srcCloudBlob.Uri.AbsoluteUri + blobToken)); 174 | } 175 | } 176 | -------------------------------------------------------------------------------- /FastEnumIntEqualityComparer.cs: -------------------------------------------------------------------------------- 1 | // todo; check if your TEnum is enum && typeCode == TypeCode.Int 2 | struct FastEnumIntEqualityComparer : IEqualityComparer 3 | where TEnum : struct 4 | { 5 | static class BoxAvoidance 6 | { 7 | static readonly Func _wrapper; 8 | 9 | public static int ToInt(TEnum enu) 10 | { 11 | return _wrapper(enu); 12 | } 13 | 14 | static BoxAvoidance() 15 | { 16 | var p = Expression.Parameter(typeof(TEnum), null); 17 | var c = Expression.ConvertChecked(p, typeof(int)); 18 | 19 | _wrapper = Expression.Lambda>(c, p).Compile(); 20 | } 21 | } 22 | 23 | public bool Equals(TEnum firstEnum, TEnum secondEnum) 24 | { 25 | return BoxAvoidance.ToInt(firstEnum) == 26 | BoxAvoidance.ToInt(secondEnum); 27 | } 28 | 29 | public int GetHashCode(TEnum firstEnum) 30 | { 31 | return BoxAvoidance.ToInt(firstEnum); 32 | } 33 | } -------------------------------------------------------------------------------- /FastReplacer.cs: -------------------------------------------------------------------------------- 1 | . 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Globalization; 5 | using System.Linq; 6 | using System.Runtime.InteropServices; 7 | using System.Threading; 8 | using System.Threading.Tasks; 9 | using System.Diagnostics; 10 | using System.Text; 11 | using System.Collections.Concurrent; 12 | 13 | namespace ConsoleApplication1 14 | { 15 | internal class Program 16 | { 17 | // Used http://stackoverflow.com/a/20285267/40868 as a template 18 | private static readonly Random Seed = new Random(42); //used a constant to keep result consistant 19 | private static readonly string OutputFormat = "{0, -20} | {1, 8} | {2, -40}"; 20 | 21 | private static string BuildRandomInputString(int length) 22 | { 23 | var input = new StringBuilder(); 24 | for (var i = 0; i < length; i++) 25 | { 26 | var randomNumber = Seed.Next(0, 3); 27 | var character = randomNumber == 0 ? 'A' : randomNumber == 1 ? 'B' : 'C'; 28 | 29 | input.Append(character); 30 | } 31 | return input.ToString(); 32 | } 33 | 34 | private static List BuildRandomReplacements(int replacementsCount) 35 | { 36 | var replacements = new List(); 37 | for (var i = 0; i < replacementsCount; i++) 38 | { 39 | var randomNumber = Seed.Next(0, 3); 40 | var replaceIteration = randomNumber == 0 ? "AB" : randomNumber == 1 ? "BC" : "CD"; 41 | 42 | replacements.Add(replaceIteration); 43 | } 44 | return replacements; 45 | } 46 | 47 | private static long TestImplementation(string input, string replace, string[] replacements, 48 | Action implementation) 49 | { 50 | GC.Collect(); 51 | GC.WaitForPendingFinalizers(); 52 | 53 | var stopWatch = Stopwatch.StartNew(); 54 | implementation(input, replace, replacements); 55 | stopWatch.Stop(); 56 | 57 | return stopWatch.ElapsedMilliseconds; 58 | } 59 | 60 | private static void SimpleImplementation(string input, string replace, string[] replacements) 61 | { 62 | foreach (var replaceBy in replacements) 63 | { 64 | var result = input.Replace(replace, replacements[0]); 65 | } 66 | } 67 | 68 | private static void SimpleParallelImplementation(string input, string replace, string[] replacements) 69 | { 70 | var rangePartitioner = Partitioner.Create(0, replacements.Length); 71 | 72 | Parallel.ForEach(rangePartitioner, (range, loopState) => 73 | { 74 | for (var i = range.Item1; i < range.Item2; i++) 75 | { 76 | var result = input.Replace(replace, replacements[i]); 77 | } 78 | }); 79 | } 80 | 81 | private unsafe class FastReplacer : IDisposable 82 | { 83 | private readonly char* _oldValue; 84 | private readonly int _oldValueLength; 85 | 86 | private readonly bool* _fastTable; 87 | private readonly char* _input; 88 | 89 | public int FoundIndexes; 90 | 91 | public FastReplacer(string input, string oldValue) 92 | { 93 | var inputLength = input.Length; 94 | _oldValueLength = oldValue.Length; 95 | 96 | _oldValue = (char*) Marshal.AllocHGlobal((_oldValueLength + 1)*sizeof (char)); 97 | _input = (char*) Marshal.AllocHGlobal((inputLength + 1)*sizeof (char)); 98 | _fastTable = (bool*) Marshal.AllocHGlobal((inputLength)*sizeof (bool)); 99 | 100 | fixed (char* inputSrc = input, oldValueSrc = oldValue) 101 | { 102 | Copy(inputSrc, _input); 103 | Copy(oldValueSrc, _oldValue); 104 | } 105 | 106 | BuildMatchedIndexes(); 107 | } 108 | 109 | public void Replace(char* outputPtr, int outputLength, string newValue) 110 | { 111 | var newValueLength = newValue.Length; 112 | 113 | char* inputPtr = _input; 114 | bool* fastTablePtr = _fastTable; 115 | 116 | fixed (char* newValuePtr = newValue) 117 | { 118 | while (*inputPtr != 0) 119 | { 120 | if (*fastTablePtr) 121 | { 122 | Copy(newValuePtr, outputPtr); 123 | outputLength -= newValueLength; 124 | outputPtr += newValueLength; 125 | 126 | inputPtr += _oldValueLength; 127 | fastTablePtr += _oldValueLength; 128 | continue; 129 | } 130 | 131 | *outputPtr++ = *inputPtr++; 132 | fastTablePtr++; 133 | } 134 | } 135 | 136 | *outputPtr = '\0'; 137 | } 138 | 139 | public void Dispose() 140 | { 141 | if (_fastTable != null) Marshal.FreeHGlobal(new IntPtr(_fastTable)); 142 | if (_input != null) Marshal.FreeHGlobal(new IntPtr(_input)); 143 | if (_oldValue != null) Marshal.FreeHGlobal(new IntPtr(_oldValue)); 144 | } 145 | 146 | private void Copy(char* sourcePtr, 147 | char* targetPtr) 148 | { 149 | while ((*targetPtr++ = *sourcePtr++) != 0) ; 150 | } 151 | 152 | /// 153 | /// KMP?! 154 | /// 155 | private void BuildMatchedIndexes() 156 | { 157 | var sourcePtr = _input; 158 | var fastTablePtr = _fastTable; 159 | 160 | var i = 0; 161 | 162 | while (sourcePtr[i] != 0) 163 | { 164 | fastTablePtr[i] = false; 165 | 166 | if (sourcePtr[i] == _oldValue[0]) 167 | { 168 | var tempSourcePtr = &sourcePtr[i]; 169 | var tempOldValuePtr = _oldValue; 170 | var isMatch = true; 171 | 172 | while (isMatch && 173 | *tempSourcePtr != 0 && 174 | *tempOldValuePtr != 0) 175 | { 176 | if (*tempSourcePtr != *tempOldValuePtr) 177 | isMatch = false; 178 | 179 | tempSourcePtr++; 180 | tempOldValuePtr++; 181 | } 182 | 183 | if (isMatch) 184 | { 185 | fastTablePtr[i] = true; 186 | i += _oldValueLength; 187 | 188 | FoundIndexes++; 189 | continue; 190 | } 191 | } 192 | 193 | i++; 194 | } 195 | } 196 | } 197 | 198 | 199 | private static unsafe void UnsafeUnmanagedImplementation(string input, string oldValue, string[] newValues) 200 | { 201 | using (var fastReplacer = new FastReplacer(input, oldValue)) 202 | { 203 | var replaceLength = oldValue.Length; 204 | var inputLength = input.Length; 205 | 206 | var replacedDataLength = fastReplacer.FoundIndexes*replaceLength; 207 | 208 | var maximumParallelWork = newValues.Length > Environment.ProcessorCount 209 | ? Environment.ProcessorCount 210 | : newValues.Length; 211 | 212 | var partitions = Partitioner.Create(0, newValues.Length).GetPartitions(maximumParallelWork); 213 | var threads = new Thread[partitions.Count]; 214 | 215 | var biggestIndex = 0; 216 | for (var k = 1; k < newValues.Length; k++) 217 | { 218 | if (newValues[biggestIndex].Length < newValues[k].Length) 219 | biggestIndex = k; 220 | } 221 | 222 | var biggestStackSize = inputLength - replacedDataLength + fastReplacer.FoundIndexes*newValues[biggestIndex].Length; 223 | 224 | for (var i = 0; i < partitions.Count; i++) 225 | { 226 | var partition = partitions[i]; 227 | partition.MoveNext(); 228 | 229 | threads[i] = new Thread(() => 230 | { 231 | var range = partition.Current; 232 | 233 | var maxLength = newValues[range.Item1].Length; 234 | for (var j = range.Item1 + 1; j < range.Item2; j++) 235 | maxLength = Math.Max(newValues[j].Length, maxLength); 236 | 237 | var outputLength = inputLength - replacedDataLength + fastReplacer.FoundIndexes*maxLength; 238 | var outputPtr = stackalloc char[(outputLength + 1)]; 239 | 240 | for (var d = range.Item1; d < range.Item2; d++) 241 | { 242 | fastReplacer.Replace(outputPtr, outputLength, newValues[d]); 243 | //Console.WriteLine(new string(outputPtr)); 244 | } 245 | }, biggestStackSize + 1000 * 1000 * 1000); 246 | 247 | threads[i].Start(); 248 | } 249 | 250 | for (var k = 0; k < threads.Length; k++) 251 | threads[k].Join(); 252 | } 253 | } 254 | 255 | private static void ParallelSubstringImplementation(string input, string replace, string[] replaceBy) 256 | { 257 | var startingPosition = 0; 258 | var indexes = new List(); 259 | 260 | int currentPosition; 261 | 262 | while ((currentPosition = input.IndexOf(replace, startingPosition)) >= 0) 263 | { 264 | indexes.Add(currentPosition); 265 | startingPosition = currentPosition + 1; 266 | } 267 | 268 | var replaceByPartitioner = Partitioner.Create(0, replaceBy.Length); 269 | var rangePartitioner = Partitioner.Create(0, indexes.Count); 270 | 271 | Parallel.ForEach(replaceByPartitioner, (outerRange, outerLoopState) => 272 | { 273 | for (var g = outerRange.Item1; g < outerRange.Item2; g++) 274 | { 275 | var replaceWith = replaceBy[g]; 276 | 277 | var finalSize = input.Length - (indexes.Count * replace.Length) + (indexes.Count * replaceWith.Length); 278 | var finalResult = new char[finalSize]; 279 | 280 | Parallel.ForEach(rangePartitioner, (innerRange, innerLoopState) => 281 | { 282 | for (var i = innerRange.Item1; i < innerRange.Item2; i++) 283 | { 284 | var currentIndex = indexes[i]; 285 | var prevIndex = i > 0 ? indexes[i - 1] : -replace.Length; 286 | 287 | var n = 0; 288 | if (prevIndex >= 0) 289 | { 290 | n = prevIndex + replace.Length; 291 | 292 | // note that due to offset changes, 293 | // prevIndex doesn't map to 1:1 if lens are different 294 | if (replace.Length != replaceWith.Length) 295 | { 296 | var offset = (replace.Length - replaceWith.Length) * i; 297 | var dir = replace.Length < replaceWith.Length; 298 | n = prevIndex + offset + replaceWith.Length + (dir ? 1 : -1); 299 | } 300 | } 301 | 302 | // append everything between two indexes. 303 | for (var k = prevIndex + replace.Length; k < currentIndex; k++) 304 | finalResult[n++] = input[k]; 305 | 306 | // append replaced text. 307 | foreach (var ch in replaceWith) 308 | finalResult[n++] = ch; 309 | 310 | // if dealing with last index. 311 | // we need to append remaining parts 312 | if (currentIndex == indexes[indexes.Count - 1]) 313 | { 314 | for (var k = currentIndex + replace.Length; k < input.Length; k++) 315 | finalResult[n++] = input[k]; 316 | } 317 | } 318 | }); 319 | } 320 | }); 321 | } 322 | 323 | private unsafe static void FredouImplementation(string input, string replace, string[] replaceBy) 324 | { 325 | var inputLength = input.Length; 326 | 327 | var indexes = new List(); 328 | 329 | //my own string.indexof to save a few ms 330 | int len = inputLength; 331 | fixed (char* i = input, r = replace) 332 | { 333 | while (--len > -1) 334 | { 335 | //i wish i knew how to do this in 1 compare :-) 336 | if (i[len] == r[0] && i[len + 1] == r[1]) 337 | { 338 | indexes.Add(len--); 339 | } 340 | } 341 | } 342 | 343 | var idx = indexes.ToArray(); 344 | len = indexes.Count; 345 | 346 | Parallel.For(0, replaceBy.Length, l => 347 | Process(input, inputLength, replaceBy[l], idx, len) 348 | ); 349 | } 350 | 351 | private unsafe static void Process(string input, int len, string replaceBy, int[] idx, int idxLen) 352 | { 353 | var output = new char[len]; 354 | int l; 355 | 356 | fixed (char* o = output, i = input) 357 | { 358 | //direct copy, simulate string.copy 359 | for (l = 0; l < len; ++l) 360 | { 361 | o[l] = i[l]; 362 | } 363 | 364 | //oldValue, i also wish to know how to do this in 1 shot 365 | for (l = 0; l < idxLen; ++l) 366 | { 367 | o[idx[l]] = replaceBy[0]; 368 | o[idx[l] + 1] = replaceBy[1]; 369 | } 370 | } 371 | 372 | //Console.WriteLine(output); 373 | } 374 | 375 | 376 | private static void Main() 377 | { 378 | var implementations = new[] 379 | { 380 | new Tuple>("Simple", SimpleImplementation), 381 | new Tuple>("SimpleParallel", SimpleParallelImplementation), 382 | new Tuple>("ParallelSubstring", ParallelSubstringImplementation), 383 | new Tuple>("Fredou unsafe", FredouImplementation), 384 | new Tuple>("Unsafe+unmanaged_mem", UnsafeUnmanagedImplementation) 385 | }; 386 | 387 | Console.WriteLine(OutputFormat, "Implementation", "Average", "Seperate runs"); 388 | 389 | var replace ="BC"; 390 | var testCases = new[] 391 | { 392 | new {ReplacementCount = 500, InputLength = (int)Math.Pow(10, 6) * 2}, 393 | new {ReplacementCount = 500, InputLength = (int)Math.Pow(10, 6)}, 394 | new {ReplacementCount = 100, InputLength = (int)Math.Pow(10, 6) / 2}, 395 | new {ReplacementCount = 50, InputLength = (int)Math.Pow(10, 3)} 396 | }; 397 | 398 | var result = new Dictionary>(); 399 | foreach (var testCase in testCases) 400 | { 401 | var input = BuildRandomInputString(testCase.InputLength); 402 | var replacements = BuildRandomReplacements(testCase.ReplacementCount).ToArray(); 403 | 404 | foreach (var implementation in implementations) 405 | { 406 | var elapsedTime = TestImplementation(input, replace, replacements, implementation.Item2); 407 | if (!result.ContainsKey(implementation.Item1)) 408 | result[implementation.Item1] = new List(); 409 | 410 | result[implementation.Item1].Add(elapsedTime); 411 | } 412 | } 413 | 414 | 415 | foreach (var implementation in implementations) 416 | { 417 | var l = result[implementation.Item1]; 418 | 419 | var averageTime = (long)l.Average(); 420 | Console.WriteLine(OutputFormat, implementation.Item1, averageTime, 421 | string.Join(", ", l.Select(x => x.ToString(CultureInfo.InvariantCulture)))); 422 | } 423 | 424 | Console.ReadKey(); 425 | } 426 | } 427 | } -------------------------------------------------------------------------------- /GroupPolicyUtilities.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.ComponentModel; 3 | using System.Diagnostics; 4 | using System.IO; 5 | using System.Runtime.InteropServices; 6 | using System.Threading; 7 | using System.Windows.Forms; 8 | 9 | namespace WixCustomActions 10 | { 11 | public sealed class GroupPolicyUtilities 12 | { 13 | private GroupPolicyUtilities() 14 | { 15 | } 16 | 17 | private static void UpdateGroupPolicy() 18 | { 19 | var execFile = new FileInfo("gpupdate.exe"); 20 | var proc = new Process 21 | { 22 | StartInfo = 23 | { 24 | WindowStyle = ProcessWindowStyle.Hidden, 25 | FileName = execFile.Name, 26 | Arguments = "/force" 27 | } 28 | }; 29 | proc.Start(); 30 | 31 | //Wait for GPUpdate to finish 32 | while (!proc.HasExited) 33 | { 34 | Application.DoEvents(); 35 | Thread.Sleep(100); 36 | } 37 | } 38 | 39 | #region "Win32 stuff" 40 | 41 | private class Win32 42 | { 43 | public sealed class HandleKerberosTokenPurging 44 | { 45 | private void PurgeAllTickets( 46 | IntPtr lsaHandle, 47 | UInt32 kerberosPackageId) 48 | { 49 | IntPtr purgeCacheRequestPtr = IntPtr.Zero; 50 | IntPtr purgeCacheResponsePtr = IntPtr.Zero; 51 | 52 | try 53 | { 54 | using (var empty = new OSCalls.UNICODE_STRING("")) 55 | { 56 | var purgeCacheRequest = new OSCalls.KERB_PURGE_TKT_CACHE_REQUEST 57 | { 58 | MessageType = OSCalls.KERB_PROTOCOL_MESSAGE_TYPE.KerbPurgeTicketCacheMessage, 59 | LogonId = new OSCalls.LUID(), 60 | 61 | ServerName = empty, 62 | RealmName = empty, 63 | }; 64 | 65 | var purgeCacheRequestSize = Marshal.SizeOf(purgeCacheRequest); 66 | purgeCacheRequestPtr = Marshal.AllocHGlobal(purgeCacheRequestSize); 67 | Marshal.StructureToPtr(purgeCacheRequest, purgeCacheRequestPtr, false); 68 | 69 | OSCalls.NtStatus ntSubStatus; 70 | 71 | ulong purgeCacheResponseSize; 72 | var ntStatus = OSCalls.LsaCallAuthenticationPackage( 73 | lsaHandle, 74 | kerberosPackageId, 75 | purgeCacheRequestPtr, 76 | purgeCacheRequestSize, 77 | out purgeCacheResponsePtr, 78 | out purgeCacheResponseSize, 79 | out ntSubStatus); 80 | 81 | 82 | if (ntStatus != OSCalls.NtStatus.Success || ntSubStatus != OSCalls.NtStatus.Success) 83 | throw new Win32Exception( 84 | string.Format( 85 | "PurgeAllTickets->LsaCallAuthenticationPackage failed, ntStatus={0}, ntSubStatus={1}", 86 | ntStatus, ntSubStatus)); 87 | 88 | } 89 | } 90 | finally 91 | { 92 | if (purgeCacheRequestPtr != IntPtr.Zero) Marshal.FreeHGlobal(purgeCacheRequestPtr); 93 | if (purgeCacheResponsePtr != IntPtr.Zero) OSCalls.LsaFreeReturnBuffer(purgeCacheResponsePtr); 94 | } 95 | 96 | } 97 | 98 | 99 | public void PurgeAll() 100 | { 101 | IntPtr lsaHandle = IntPtr.Zero; 102 | OSCalls.WinStatusCodes status = OSCalls.LsaConnectUntrusted(out lsaHandle); 103 | if (status != OSCalls.WinStatusCodes.STATUS_SUCCESS) 104 | throw new Win32Exception((int) OSCalls.LsaNtStatusToWinError(status)); 105 | 106 | IntPtr cacheRequestPtr = IntPtr.Zero; 107 | 108 | try 109 | { 110 | using (var kerberosPackageName = new OSCalls.LsaStringWrapper("Kerberos")) 111 | { 112 | UInt32 kerberosPackageId; 113 | status = OSCalls.LsaLookupAuthenticationPackage(lsaHandle, 114 | ref kerberosPackageName._string, out kerberosPackageId); 115 | if (status != OSCalls.WinStatusCodes.STATUS_SUCCESS) 116 | throw new Win32Exception((int) OSCalls.LsaNtStatusToWinError(status)); 117 | 118 | PurgeAllTickets(lsaHandle, kerberosPackageId); 119 | } 120 | } 121 | finally 122 | { 123 | if (cacheRequestPtr != IntPtr.Zero) Marshal.FreeHGlobal(cacheRequestPtr); 124 | 125 | if (lsaHandle != IntPtr.Zero) OSCalls.LsaDeregisterLogonProcess(lsaHandle); 126 | } 127 | } 128 | } 129 | 130 | 131 | internal class OSCalls 132 | { 133 | 134 | public enum WinErrors : uint 135 | { 136 | NO_ERROR = 0, 137 | } 138 | 139 | public enum WinStatusCodes : uint 140 | { 141 | STATUS_SUCCESS = 0 142 | } 143 | 144 | /// 145 | /// A NT status value. 146 | /// 147 | public enum NtStatus : uint 148 | { 149 | // Success 150 | Success = 0x00000000, 151 | Wait0 = 0x00000000, 152 | Wait1 = 0x00000001, 153 | Wait2 = 0x00000002, 154 | Wait3 = 0x00000003, 155 | Wait63 = 0x0000003f, 156 | Abandoned = 0x00000080, 157 | AbandonedWait0 = 0x00000080, 158 | AbandonedWait1 = 0x00000081, 159 | AbandonedWait2 = 0x00000082, 160 | AbandonedWait3 = 0x00000083, 161 | AbandonedWait63 = 0x000000bf, 162 | UserApc = 0x000000c0, 163 | KernelApc = 0x00000100, 164 | Alerted = 0x00000101, 165 | Timeout = 0x00000102, 166 | Pending = 0x00000103, 167 | Reparse = 0x00000104, 168 | MoreEntries = 0x00000105, 169 | NotAllAssigned = 0x00000106, 170 | SomeNotMapped = 0x00000107, 171 | OpLockBreakInProgress = 0x00000108, 172 | VolumeMounted = 0x00000109, 173 | RxActCommitted = 0x0000010a, 174 | NotifyCleanup = 0x0000010b, 175 | NotifyEnumDir = 0x0000010c, 176 | NoQuotasForAccount = 0x0000010d, 177 | PrimaryTransportConnectFailed = 0x0000010e, 178 | PageFaultTransition = 0x00000110, 179 | PageFaultDemandZero = 0x00000111, 180 | PageFaultCopyOnWrite = 0x00000112, 181 | PageFaultGuardPage = 0x00000113, 182 | PageFaultPagingFile = 0x00000114, 183 | CrashDump = 0x00000116, 184 | ReparseObject = 0x00000118, 185 | NothingToTerminate = 0x00000122, 186 | ProcessNotInJob = 0x00000123, 187 | ProcessInJob = 0x00000124, 188 | ProcessCloned = 0x00000129, 189 | FileLockedWithOnlyReaders = 0x0000012a, 190 | FileLockedWithWriters = 0x0000012b, 191 | 192 | // Informational 193 | Informational = 0x40000000, 194 | ObjectNameExists = 0x40000000, 195 | ThreadWasSuspended = 0x40000001, 196 | WorkingSetLimitRange = 0x40000002, 197 | ImageNotAtBase = 0x40000003, 198 | RegistryRecovered = 0x40000009, 199 | 200 | // Warning 201 | Warning = 0x80000000, 202 | GuardPageViolation = 0x80000001, 203 | DatatypeMisalignment = 0x80000002, 204 | Breakpoint = 0x80000003, 205 | SingleStep = 0x80000004, 206 | BufferOverflow = 0x80000005, 207 | NoMoreFiles = 0x80000006, 208 | HandlesClosed = 0x8000000a, 209 | PartialCopy = 0x8000000d, 210 | DeviceBusy = 0x80000011, 211 | InvalidEaName = 0x80000013, 212 | EaListInconsistent = 0x80000014, 213 | NoMoreEntries = 0x8000001a, 214 | LongJump = 0x80000026, 215 | DllMightBeInsecure = 0x8000002b, 216 | 217 | // Error 218 | Error = 0xc0000000, 219 | Unsuccessful = 0xc0000001, 220 | NotImplemented = 0xc0000002, 221 | InvalidInfoClass = 0xc0000003, 222 | InfoLengthMismatch = 0xc0000004, 223 | AccessViolation = 0xc0000005, 224 | InPageError = 0xc0000006, 225 | PagefileQuota = 0xc0000007, 226 | InvalidHandle = 0xc0000008, 227 | BadInitialStack = 0xc0000009, 228 | BadInitialPc = 0xc000000a, 229 | InvalidCid = 0xc000000b, 230 | TimerNotCanceled = 0xc000000c, 231 | InvalidParameter = 0xc000000d, 232 | NoSuchDevice = 0xc000000e, 233 | NoSuchFile = 0xc000000f, 234 | InvalidDeviceRequest = 0xc0000010, 235 | EndOfFile = 0xc0000011, 236 | WrongVolume = 0xc0000012, 237 | NoMediaInDevice = 0xc0000013, 238 | NoMemory = 0xc0000017, 239 | NotMappedView = 0xc0000019, 240 | UnableToFreeVm = 0xc000001a, 241 | UnableToDeleteSection = 0xc000001b, 242 | IllegalInstruction = 0xc000001d, 243 | AlreadyCommitted = 0xc0000021, 244 | AccessDenied = 0xc0000022, 245 | BufferTooSmall = 0xc0000023, 246 | ObjectTypeMismatch = 0xc0000024, 247 | NonContinuableException = 0xc0000025, 248 | BadStack = 0xc0000028, 249 | NotLocked = 0xc000002a, 250 | NotCommitted = 0xc000002d, 251 | InvalidParameterMix = 0xc0000030, 252 | ObjectNameInvalid = 0xc0000033, 253 | ObjectNameNotFound = 0xc0000034, 254 | ObjectNameCollision = 0xc0000035, 255 | ObjectPathInvalid = 0xc0000039, 256 | ObjectPathNotFound = 0xc000003a, 257 | ObjectPathSyntaxBad = 0xc000003b, 258 | DataOverrun = 0xc000003c, 259 | DataLate = 0xc000003d, 260 | DataError = 0xc000003e, 261 | CrcError = 0xc000003f, 262 | SectionTooBig = 0xc0000040, 263 | PortConnectionRefused = 0xc0000041, 264 | InvalidPortHandle = 0xc0000042, 265 | SharingViolation = 0xc0000043, 266 | QuotaExceeded = 0xc0000044, 267 | InvalidPageProtection = 0xc0000045, 268 | MutantNotOwned = 0xc0000046, 269 | SemaphoreLimitExceeded = 0xc0000047, 270 | PortAlreadySet = 0xc0000048, 271 | SectionNotImage = 0xc0000049, 272 | SuspendCountExceeded = 0xc000004a, 273 | ThreadIsTerminating = 0xc000004b, 274 | BadWorkingSetLimit = 0xc000004c, 275 | IncompatibleFileMap = 0xc000004d, 276 | SectionProtection = 0xc000004e, 277 | EasNotSupported = 0xc000004f, 278 | EaTooLarge = 0xc0000050, 279 | NonExistentEaEntry = 0xc0000051, 280 | NoEasOnFile = 0xc0000052, 281 | EaCorruptError = 0xc0000053, 282 | FileLockConflict = 0xc0000054, 283 | LockNotGranted = 0xc0000055, 284 | DeletePending = 0xc0000056, 285 | CtlFileNotSupported = 0xc0000057, 286 | UnknownRevision = 0xc0000058, 287 | RevisionMismatch = 0xc0000059, 288 | InvalidOwner = 0xc000005a, 289 | InvalidPrimaryGroup = 0xc000005b, 290 | NoImpersonationToken = 0xc000005c, 291 | CantDisableMandatory = 0xc000005d, 292 | NoLogonServers = 0xc000005e, 293 | NoSuchLogonSession = 0xc000005f, 294 | NoSuchPrivilege = 0xc0000060, 295 | PrivilegeNotHeld = 0xc0000061, 296 | InvalidAccountName = 0xc0000062, 297 | UserExists = 0xc0000063, 298 | NoSuchUser = 0xc0000064, 299 | GroupExists = 0xc0000065, 300 | NoSuchGroup = 0xc0000066, 301 | MemberInGroup = 0xc0000067, 302 | MemberNotInGroup = 0xc0000068, 303 | LastAdmin = 0xc0000069, 304 | WrongPassword = 0xc000006a, 305 | IllFormedPassword = 0xc000006b, 306 | PasswordRestriction = 0xc000006c, 307 | LogonFailure = 0xc000006d, 308 | AccountRestriction = 0xc000006e, 309 | InvalidLogonHours = 0xc000006f, 310 | InvalidWorkstation = 0xc0000070, 311 | PasswordExpired = 0xc0000071, 312 | AccountDisabled = 0xc0000072, 313 | NoneMapped = 0xc0000073, 314 | TooManyLuidsRequested = 0xc0000074, 315 | LuidsExhausted = 0xc0000075, 316 | InvalidSubAuthority = 0xc0000076, 317 | InvalidAcl = 0xc0000077, 318 | InvalidSid = 0xc0000078, 319 | InvalidSecurityDescr = 0xc0000079, 320 | ProcedureNotFound = 0xc000007a, 321 | InvalidImageFormat = 0xc000007b, 322 | NoToken = 0xc000007c, 323 | BadInheritanceAcl = 0xc000007d, 324 | RangeNotLocked = 0xc000007e, 325 | DiskFull = 0xc000007f, 326 | ServerDisabled = 0xc0000080, 327 | ServerNotDisabled = 0xc0000081, 328 | TooManyGuidsRequested = 0xc0000082, 329 | GuidsExhausted = 0xc0000083, 330 | InvalidIdAuthority = 0xc0000084, 331 | AgentsExhausted = 0xc0000085, 332 | InvalidVolumeLabel = 0xc0000086, 333 | SectionNotExtended = 0xc0000087, 334 | NotMappedData = 0xc0000088, 335 | ResourceDataNotFound = 0xc0000089, 336 | ResourceTypeNotFound = 0xc000008a, 337 | ResourceNameNotFound = 0xc000008b, 338 | ArrayBoundsExceeded = 0xc000008c, 339 | FloatDenormalOperand = 0xc000008d, 340 | FloatDivideByZero = 0xc000008e, 341 | FloatInexactResult = 0xc000008f, 342 | FloatInvalidOperation = 0xc0000090, 343 | FloatOverflow = 0xc0000091, 344 | FloatStackCheck = 0xc0000092, 345 | FloatUnderflow = 0xc0000093, 346 | IntegerDivideByZero = 0xc0000094, 347 | IntegerOverflow = 0xc0000095, 348 | PrivilegedInstruction = 0xc0000096, 349 | TooManyPagingFiles = 0xc0000097, 350 | FileInvalid = 0xc0000098, 351 | InstanceNotAvailable = 0xc00000ab, 352 | PipeNotAvailable = 0xc00000ac, 353 | InvalidPipeState = 0xc00000ad, 354 | PipeBusy = 0xc00000ae, 355 | IllegalFunction = 0xc00000af, 356 | PipeDisconnected = 0xc00000b0, 357 | PipeClosing = 0xc00000b1, 358 | PipeConnected = 0xc00000b2, 359 | PipeListening = 0xc00000b3, 360 | InvalidReadMode = 0xc00000b4, 361 | IoTimeout = 0xc00000b5, 362 | FileForcedClosed = 0xc00000b6, 363 | ProfilingNotStarted = 0xc00000b7, 364 | ProfilingNotStopped = 0xc00000b8, 365 | NotSameDevice = 0xc00000d4, 366 | FileRenamed = 0xc00000d5, 367 | CantWait = 0xc00000d8, 368 | PipeEmpty = 0xc00000d9, 369 | CantTerminateSelf = 0xc00000db, 370 | InternalError = 0xc00000e5, 371 | InvalidParameter1 = 0xc00000ef, 372 | InvalidParameter2 = 0xc00000f0, 373 | InvalidParameter3 = 0xc00000f1, 374 | InvalidParameter4 = 0xc00000f2, 375 | InvalidParameter5 = 0xc00000f3, 376 | InvalidParameter6 = 0xc00000f4, 377 | InvalidParameter7 = 0xc00000f5, 378 | InvalidParameter8 = 0xc00000f6, 379 | InvalidParameter9 = 0xc00000f7, 380 | InvalidParameter10 = 0xc00000f8, 381 | InvalidParameter11 = 0xc00000f9, 382 | InvalidParameter12 = 0xc00000fa, 383 | MappedFileSizeZero = 0xc000011e, 384 | TooManyOpenedFiles = 0xc000011f, 385 | Cancelled = 0xc0000120, 386 | CannotDelete = 0xc0000121, 387 | InvalidComputerName = 0xc0000122, 388 | FileDeleted = 0xc0000123, 389 | SpecialAccount = 0xc0000124, 390 | SpecialGroup = 0xc0000125, 391 | SpecialUser = 0xc0000126, 392 | MembersPrimaryGroup = 0xc0000127, 393 | FileClosed = 0xc0000128, 394 | TooManyThreads = 0xc0000129, 395 | ThreadNotInProcess = 0xc000012a, 396 | TokenAlreadyInUse = 0xc000012b, 397 | PagefileQuotaExceeded = 0xc000012c, 398 | CommitmentLimit = 0xc000012d, 399 | InvalidImageLeFormat = 0xc000012e, 400 | InvalidImageNotMz = 0xc000012f, 401 | InvalidImageProtect = 0xc0000130, 402 | InvalidImageWin16 = 0xc0000131, 403 | LogonServer = 0xc0000132, 404 | DifferenceAtDc = 0xc0000133, 405 | SynchronizationRequired = 0xc0000134, 406 | DllNotFound = 0xc0000135, 407 | IoPrivilegeFailed = 0xc0000137, 408 | OrdinalNotFound = 0xc0000138, 409 | EntryPointNotFound = 0xc0000139, 410 | ControlCExit = 0xc000013a, 411 | PortNotSet = 0xc0000353, 412 | DebuggerInactive = 0xc0000354, 413 | CallbackBypass = 0xc0000503, 414 | PortClosed = 0xc0000700, 415 | MessageLost = 0xc0000701, 416 | InvalidMessage = 0xc0000702, 417 | RequestCanceled = 0xc0000703, 418 | RecursiveDispatch = 0xc0000704, 419 | LpcReceiveBufferExpected = 0xc0000705, 420 | LpcInvalidConnectionUsage = 0xc0000706, 421 | LpcRequestsNotAllowed = 0xc0000707, 422 | ResourceInUse = 0xc0000708, 423 | ProcessIsProtected = 0xc0000712, 424 | VolumeDirty = 0xc0000806, 425 | FileCheckedOut = 0xc0000901, 426 | CheckOutRequired = 0xc0000902, 427 | BadFileType = 0xc0000903, 428 | FileTooLarge = 0xc0000904, 429 | FormsAuthRequired = 0xc0000905, 430 | VirusInfected = 0xc0000906, 431 | VirusDeleted = 0xc0000907, 432 | TransactionalConflict = 0xc0190001, 433 | InvalidTransaction = 0xc0190002, 434 | TransactionNotActive = 0xc0190003, 435 | TmInitializationFailed = 0xc0190004, 436 | RmNotActive = 0xc0190005, 437 | RmMetadataCorrupt = 0xc0190006, 438 | TransactionNotJoined = 0xc0190007, 439 | DirectoryNotRm = 0xc0190008, 440 | CouldNotResizeLog = 0xc0190009, 441 | TransactionsUnsupportedRemote = 0xc019000a, 442 | LogResizeInvalidSize = 0xc019000b, 443 | RemoteFileVersionMismatch = 0xc019000c, 444 | CrmProtocolAlreadyExists = 0xc019000f, 445 | TransactionPropagationFailed = 0xc0190010, 446 | CrmProtocolNotFound = 0xc0190011, 447 | TransactionSuperiorExists = 0xc0190012, 448 | TransactionRequestNotValid = 0xc0190013, 449 | TransactionNotRequested = 0xc0190014, 450 | TransactionAlreadyAborted = 0xc0190015, 451 | TransactionAlreadyCommitted = 0xc0190016, 452 | TransactionInvalidMarshallBuffer = 0xc0190017, 453 | CurrentTransactionNotValid = 0xc0190018, 454 | LogGrowthFailed = 0xc0190019, 455 | ObjectNoLongerExists = 0xc0190021, 456 | StreamMiniversionNotFound = 0xc0190022, 457 | StreamMiniversionNotValid = 0xc0190023, 458 | MiniversionInaccessibleFromSpecifiedTransaction = 0xc0190024, 459 | CantOpenMiniversionWithModifyIntent = 0xc0190025, 460 | CantCreateMoreStreamMiniversions = 0xc0190026, 461 | HandleNoLongerValid = 0xc0190028, 462 | NoTxfMetadata = 0xc0190029, 463 | LogCorruptionDetected = 0xc0190030, 464 | CantRecoverWithHandleOpen = 0xc0190031, 465 | RmDisconnected = 0xc0190032, 466 | EnlistmentNotSuperior = 0xc0190033, 467 | RecoveryNotNeeded = 0xc0190034, 468 | RmAlreadyStarted = 0xc0190035, 469 | FileIdentityNotPersistent = 0xc0190036, 470 | CantBreakTransactionalDependency = 0xc0190037, 471 | CantCrossRmBoundary = 0xc0190038, 472 | TxfDirNotEmpty = 0xc0190039, 473 | IndoubtTransactionsExist = 0xc019003a, 474 | TmVolatile = 0xc019003b, 475 | RollbackTimerExpired = 0xc019003c, 476 | TxfAttributeCorrupt = 0xc019003d, 477 | EfsNotAllowedInTransaction = 0xc019003e, 478 | TransactionalOpenNotAllowed = 0xc019003f, 479 | TransactedMappingUnsupportedRemote = 0xc0190040, 480 | TxfMetadataAlreadyPresent = 0xc0190041, 481 | TransactionScopeCallbacksNotSet = 0xc0190042, 482 | TransactionRequiredPromotion = 0xc0190043, 483 | CannotExecuteFileInTransaction = 0xc0190044, 484 | TransactionsNotFrozen = 0xc0190045, 485 | 486 | MaximumNtStatus = 0xffffffff 487 | } 488 | 489 | [StructLayout(LayoutKind.Sequential)] 490 | public struct LUID 491 | { 492 | public uint LowPart; 493 | public int HighPart; 494 | } 495 | 496 | [StructLayout(LayoutKind.Sequential)] 497 | public struct KERB_QUERY_TKT_CACHE_RESPONSE 498 | { 499 | public KERB_PROTOCOL_MESSAGE_TYPE MessageType; 500 | public uint CountOfTickets; 501 | //public KERB_TICKET_CACHE_INFO[] Tickets; 502 | } 503 | 504 | [StructLayout(LayoutKind.Sequential)] 505 | public struct KERB_QUERY_TKT_CACHE_REQUEST 506 | { 507 | public KERB_PROTOCOL_MESSAGE_TYPE MessageType; 508 | public LUID LUID; 509 | } 510 | 511 | [StructLayout(LayoutKind.Sequential)] 512 | public struct UNICODE_STRING : IDisposable 513 | { 514 | public ushort Length; 515 | public ushort MaximumLength; 516 | public IntPtr buffer; 517 | 518 | public UNICODE_STRING(string s) 519 | { 520 | Length = (ushort)(s.Length * 2); 521 | MaximumLength = (ushort)(Length + 2); 522 | buffer = Marshal.StringToHGlobalUni(s); 523 | } 524 | 525 | public void Dispose() 526 | { 527 | Marshal.FreeHGlobal(buffer); 528 | buffer = IntPtr.Zero; 529 | } 530 | 531 | public override string ToString() 532 | { 533 | return Marshal.PtrToStringUni(buffer); 534 | } 535 | } 536 | 537 | [StructLayout(LayoutKind.Sequential)] 538 | public struct KERB_PURGE_TKT_CACHE_REQUEST 539 | { 540 | public KERB_PROTOCOL_MESSAGE_TYPE MessageType; 541 | public LUID LogonId; 542 | 543 | public UNICODE_STRING ServerName; 544 | public UNICODE_STRING RealmName; 545 | } 546 | 547 | public enum KERB_PROTOCOL_MESSAGE_TYPE : uint 548 | { 549 | KerbDebugRequestMessage = 0, 550 | KerbQueryTicketCacheMessage = 1, 551 | KerbChangeMachinePasswordMessage = 2, 552 | KerbVerifyPacMessage = 3, 553 | KerbRetrieveTicketMessage = 4, 554 | KerbUpdateAddressesMessage = 5, 555 | KerbPurgeTicketCacheMessage = 6, 556 | KerbChangePasswordMessage = 7, 557 | KerbRetrieveEncodedTicketMessage = 8, 558 | KerbDecryptDataMessage = 9, 559 | KerbAddBindingCacheEntryMessage = 10, 560 | KerbSetPasswordMessage = 11, 561 | KerbSetPasswordExMessage = 12, 562 | KerbVerifyCredentialsMessage = 13, 563 | KerbQueryTicketCacheExMessage = 14, 564 | KerbPurgeTicketCacheExMessage = 15, 565 | KerbRefreshSmartcardCredentialsMessage = 16, 566 | KerbAddExtraCredentialsMessage = 17, 567 | KerbQuerySupplementalCredentialsMessage = 18, 568 | KerbTransferCredentialsMessage = 19, 569 | KerbQueryTicketCacheEx2Message = 20, 570 | KerbSubmitTicketMessage = 21, 571 | KerbAddExtraCredentialsExMessage = 22 572 | } 573 | 574 | [DllImport("advapi32.dll", CharSet = CharSet.Auto, SetLastError = false)] 575 | public static extern WinErrors LsaNtStatusToWinError(WinStatusCodes status); 576 | 577 | 578 | 579 | /// 580 | /// http://msdn2.microsoft.com/en-us/library/aa378261.aspx 581 | /// 582 | /// [in] Handle obtained from a previous call to LsaRegisterLogonProcess or LsaConnectUntrusted. 583 | /// [in] Supplies the identifier of the authentication package. This value is obtained by calling LsaLookupAuthenticationPackage. 584 | /// [in] An authentication package–specific message buffer passed to the authentication package. 585 | /// [in] Indicates the length of the ProtocolSubmitBuffer buffer, in bytes. 586 | /// [out] Pointer that receives the address of the buffer returned by the authentication package. 587 | /// [out] Pointer to a ULONG that receives the length of the returned buffer, in bytes. 588 | /// [out] If the function succeeds, this parameter receives a pointer to an NSTATUS code which indicates the completion status of the authentication package. 589 | /// If the function succeeds, the return value is STATUS_SUCCESS. Check the ProtocolStatus parameter to obtain the status returned by the authentication package. 590 | /// If the function fails, the return value is an NTSTATUS code. The following are possible error codes. 591 | /// STATUS_QUOTA_EXCEEDED The call could not be completed because the client's memory quota is not sufficient to allocate the return buffer. 592 | /// STATUS_NO_SUCH_PACKAGE The specified authentication package is not recognized by the LSA. 593 | /// 594 | [DllImport("Secur32.dll", SetLastError = true)] 595 | internal static extern NtStatus LsaCallAuthenticationPackage( 596 | IntPtr LsaHandle, 597 | uint AuthenticationPackage, 598 | IntPtr ProtocolSubmitBuffer, 599 | int SubmitBufferLength, 600 | out IntPtr ProtocolReturnBuffer, 601 | out ulong ReturnBufferLength, 602 | out NtStatus ProtocolStatus 603 | ); 604 | 605 | 606 | [DllImport("secur32.dll", SetLastError = false)] 607 | public static extern WinStatusCodes LsaFreeReturnBuffer( 608 | [In] IntPtr buffer); 609 | 610 | [DllImport("secur32.dll", SetLastError = false)] 611 | public static extern WinStatusCodes LsaConnectUntrusted([Out] out IntPtr LsaHandle); 612 | 613 | [DllImport("secur32.dll", SetLastError = false)] 614 | public static extern WinStatusCodes LsaDeregisterLogonProcess([In] IntPtr LsaHandle); 615 | 616 | [DllImport("secur32.dll", SetLastError = false)] 617 | public static extern WinStatusCodes LsaLookupAuthenticationPackage([In] IntPtr LsaHandle, 618 | [In] ref LSA_STRING PackageName, [Out] out UInt32 AuthenticationPackage); 619 | 620 | 621 | [StructLayout(LayoutKind.Sequential)] 622 | public struct LSA_STRING 623 | { 624 | public UInt16 Length; 625 | public UInt16 MaximumLength; 626 | public IntPtr Buffer; 627 | } 628 | 629 | public class LsaStringWrapper : IDisposable 630 | { 631 | public LSA_STRING _string; 632 | 633 | public LsaStringWrapper(string value) 634 | { 635 | _string = new LSA_STRING(); 636 | _string.Length = (ushort) value.Length; 637 | _string.MaximumLength = (ushort) value.Length; 638 | _string.Buffer = Marshal.StringToHGlobalAnsi(value); 639 | } 640 | 641 | ~LsaStringWrapper() 642 | { 643 | Dispose(false); 644 | } 645 | 646 | private void Dispose(bool disposing) 647 | { 648 | if (_string.Buffer != IntPtr.Zero) 649 | { 650 | Marshal.FreeHGlobal(_string.Buffer); 651 | _string.Buffer = IntPtr.Zero; 652 | } 653 | if (disposing) 654 | GC.SuppressFinalize(this); 655 | } 656 | 657 | #region IDisposable Members 658 | 659 | public void Dispose() 660 | { 661 | Dispose(true); 662 | } 663 | 664 | #endregion 665 | } 666 | } 667 | } 668 | 669 | #endregion 670 | 671 | public static void ForceRefresh() 672 | { 673 | new Win32.HandleKerberosTokenPurging() 674 | .PurgeAll(); 675 | 676 | UpdateGroupPolicy(); 677 | } 678 | } 679 | } -------------------------------------------------------------------------------- /VariableArguments.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Runtime.InteropServices; 5 | 6 | // Author: Chris Eelmaa 7 | 8 | namespace ConsoleApplication1 9 | { 10 | #region VariableCombiner 11 | 12 | class CombinedVariables : IDisposable 13 | { 14 | readonly IntPtr _ptr; 15 | readonly IList _disposables; 16 | 17 | bool _disposed; 18 | 19 | public CombinedVariables(VariableArgument[] args) 20 | { 21 | _disposables = new List(); 22 | 23 | _ptr = Marshal.AllocHGlobal(args.Sum(arg => arg.GetSize())); 24 | var curPtr = _ptr; 25 | 26 | foreach (var arg in args) 27 | { 28 | _disposables.Add(arg.Write(curPtr)); 29 | curPtr += arg.GetSize(); 30 | } 31 | } 32 | 33 | public IntPtr GetPtr() 34 | { 35 | if(_disposed) 36 | throw new InvalidOperationException("Disposed already."); 37 | 38 | return _ptr; 39 | } 40 | 41 | public void Dispose() 42 | { 43 | if (!_disposed) 44 | { 45 | _disposed = true; 46 | 47 | foreach (var disposable in _disposables) 48 | disposable.Dispose(); 49 | 50 | Marshal.FreeHGlobal(_ptr); 51 | } 52 | } 53 | } 54 | 55 | #endregion 56 | 57 | #region VariableArgument 58 | 59 | abstract class VariableArgument 60 | { 61 | #region SentinelDispose 62 | 63 | protected static readonly IDisposable SentinelDisposable = new SentinelDispose(); 64 | 65 | class SentinelDispose : IDisposable 66 | { 67 | public void Dispose() 68 | { 69 | 70 | } 71 | } 72 | 73 | #endregion 74 | 75 | public abstract IDisposable Write(IntPtr buffer); 76 | 77 | public virtual int GetSize() 78 | { 79 | return IntPtr.Size; 80 | } 81 | 82 | public static implicit operator VariableArgument(int input) 83 | { 84 | return new VariableIntegerArgument(input); 85 | } 86 | 87 | public static implicit operator VariableArgument(string input) 88 | { 89 | return new VariableStringArgument(input); 90 | } 91 | 92 | public static implicit operator VariableArgument(double input) 93 | { 94 | return new VariableDoubleArgument(input); 95 | } 96 | } 97 | 98 | #endregion 99 | 100 | #region VariableIntegerArgument 101 | 102 | sealed class VariableIntegerArgument : VariableArgument 103 | { 104 | readonly int _value; 105 | 106 | public VariableIntegerArgument(int value) 107 | { 108 | _value = value; 109 | } 110 | 111 | public override IDisposable Write(IntPtr buffer) 112 | { 113 | Marshal.Copy(new[] { _value }, 0, buffer, 1); 114 | return SentinelDisposable; 115 | } 116 | } 117 | 118 | #endregion 119 | 120 | #region VariableDoubleArgument 121 | 122 | sealed class VariableDoubleArgument : VariableArgument 123 | { 124 | readonly double _value; 125 | 126 | public VariableDoubleArgument(double value) 127 | { 128 | _value = value; 129 | } 130 | 131 | public override int GetSize() 132 | { 133 | return 8; 134 | } 135 | 136 | public override IDisposable Write(IntPtr buffer) 137 | { 138 | Marshal.Copy(new[] { _value }, 0, buffer, 1); 139 | return SentinelDisposable; 140 | } 141 | } 142 | 143 | #endregion 144 | 145 | #region VariableStringArgument 146 | 147 | sealed class VariableStringArgument : VariableArgument 148 | { 149 | readonly string _value; 150 | 151 | public VariableStringArgument(string value) 152 | { 153 | _value = value; 154 | } 155 | 156 | public override IDisposable Write(IntPtr buffer) 157 | { 158 | var ptr = Marshal.StringToHGlobalAnsi(_value); 159 | 160 | Marshal.Copy(new[] {ptr}, 0, buffer, 1); 161 | 162 | return new StringArgumentDisposable(ptr); 163 | } 164 | 165 | #region StringArgumentDisposable 166 | 167 | class StringArgumentDisposable : IDisposable 168 | { 169 | IntPtr _ptr; 170 | 171 | public StringArgumentDisposable(IntPtr ptr) 172 | { 173 | _ptr = ptr; 174 | } 175 | 176 | public void Dispose() 177 | { 178 | if (_ptr != IntPtr.Zero) 179 | { 180 | Marshal.FreeHGlobal(_ptr); 181 | _ptr = IntPtr.Zero; 182 | } 183 | } 184 | } 185 | 186 | #endregion 187 | } 188 | #endregion 189 | } 190 | -------------------------------------------------------------------------------- /VariableArgumentsExampleUsage.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Runtime.InteropServices; 5 | using System.Text; 6 | 7 | namespace ConsoleApplication1 8 | { 9 | class Program 10 | { 11 | static void Main(string[] args) 12 | { 13 | Console.WriteLine(AmazingSPrintf("I am %s, %d years old, %f meters tall!", 14 | "Chris", 15 | 24, 16 | 1.94)); 17 | } 18 | 19 | static string AmazingSPrintf(string format, params VariableArgument[] args) 20 | { 21 | if (!args.Any()) 22 | return format; 23 | 24 | using (var combinedVariables = new CombinedVariables(args)) 25 | { 26 | var bufferCapacity = _vscprintf(format, combinedVariables.GetPtr()); 27 | var stringBuilder = new StringBuilder(bufferCapacity + 1); 28 | 29 | vsprintf(stringBuilder, format, combinedVariables.GetPtr()); 30 | 31 | return stringBuilder.ToString(); 32 | } 33 | } 34 | 35 | [DllImport("msvcrt.dll", CallingConvention = CallingConvention.Cdecl)] 36 | static extern int vsprintf( 37 | StringBuilder buffer, 38 | string format, 39 | IntPtr ptr); 40 | 41 | [DllImport("msvcrt.dll", CallingConvention = CallingConvention.Cdecl)] 42 | static extern int _vscprintf( 43 | string format, 44 | IntPtr ptr); 45 | 46 | } 47 | } 48 | --------------------------------------------------------------------------------