PlaceholderManager.cs
1 using Ryujinx.Common.Collections; 2 using Ryujinx.Common.Memory.PartialUnmaps; 3 using System; 4 using System.Diagnostics; 5 using System.Runtime.CompilerServices; 6 using System.Runtime.Versioning; 7 using System.Threading; 8 9 namespace Ryujinx.Memory.WindowsShared 10 { 11 /// <summary> 12 /// Windows memory placeholder manager. 13 /// </summary> 14 [SupportedOSPlatform("windows")] 15 class PlaceholderManager 16 { 17 private const int InitialOverlapsSize = 10; 18 19 private readonly MappingTree<ulong> _mappings; 20 private readonly MappingTree<MemoryPermission> _protections; 21 private readonly IntPtr _partialUnmapStatePtr; 22 private readonly Thread _partialUnmapTrimThread; 23 24 /// <summary> 25 /// Creates a new instance of the Windows memory placeholder manager. 26 /// </summary> 27 public PlaceholderManager() 28 { 29 _mappings = new MappingTree<ulong>(); 30 _protections = new MappingTree<MemoryPermission>(); 31 32 _partialUnmapStatePtr = PartialUnmapState.GlobalState; 33 34 _partialUnmapTrimThread = new Thread(TrimThreadLocalMapLoop) 35 { 36 Name = "CPU.PartialUnmapTrimThread", 37 IsBackground = true, 38 }; 39 _partialUnmapTrimThread.Start(); 40 } 41 42 /// <summary> 43 /// Gets a reference to the partial unmap state struct. 44 /// </summary> 45 /// <returns>A reference to the partial unmap state struct</returns> 46 private unsafe ref PartialUnmapState GetPartialUnmapState() 47 { 48 return ref Unsafe.AsRef<PartialUnmapState>((void*)_partialUnmapStatePtr); 49 } 50 51 /// <summary> 52 /// Trims inactive threads from the partial unmap state's thread mapping every few seconds. 53 /// Should be run in a Background thread so that it doesn't stop the program from closing. 54 /// </summary> 55 private void TrimThreadLocalMapLoop() 56 { 57 while (true) 58 { 59 Thread.Sleep(2000); 60 GetPartialUnmapState().TrimThreads(); 61 } 62 } 63 64 /// <summary> 65 /// Reserves a range of the address space to be later mapped as shared memory views. 66 /// </summary> 67 /// <param name="address">Start address of the region to reserve</param> 68 /// <param name="size">Size in bytes of the region to reserve</param> 69 public void ReserveRange(ulong address, ulong size) 70 { 71 lock (_mappings) 72 { 73 _mappings.Add(new RangeNode<ulong>(address, address + size, ulong.MaxValue)); 74 } 75 76 lock (_protections) 77 { 78 _protections.Add(new RangeNode<MemoryPermission>(address, address + size, MemoryPermission.None)); 79 } 80 } 81 82 /// <summary> 83 /// Unreserves a range of memory that has been previously reserved with <see cref="ReserveRange"/>. 84 /// </summary> 85 /// <param name="address">Start address of the region to unreserve</param> 86 /// <param name="size">Size in bytes of the region to unreserve</param> 87 /// <exception cref="WindowsApiException">Thrown when the Windows API returns an error unreserving the memory</exception> 88 public void UnreserveRange(ulong address, ulong size) 89 { 90 ulong endAddress = address + size; 91 92 lock (_mappings) 93 { 94 RangeNode<ulong> node = _mappings.GetNodeByKey(address); 95 RangeNode<ulong> successorNode; 96 97 for (; node != null; node = successorNode) 98 { 99 successorNode = node.Successor; 100 101 if (IsMapped(node.Value)) 102 { 103 if (!WindowsApi.UnmapViewOfFile2(WindowsApi.CurrentProcessHandle, (IntPtr)node.Start, 2)) 104 { 105 throw new WindowsApiException("UnmapViewOfFile2"); 106 } 107 } 108 109 _mappings.Remove(node); 110 111 if (node.End >= endAddress) 112 { 113 break; 114 } 115 } 116 } 117 118 RemoveProtection(address, size); 119 } 120 121 /// <summary> 122 /// Maps a shared memory view on a previously reserved memory region. 123 /// </summary> 124 /// <param name="sharedMemory">Shared memory that will be the backing storage for the view</param> 125 /// <param name="srcOffset">Offset in the shared memory to map</param> 126 /// <param name="location">Address to map the view into</param> 127 /// <param name="size">Size of the view in bytes</param> 128 /// <param name="owner">Memory block that owns the mapping</param> 129 public void MapView(IntPtr sharedMemory, ulong srcOffset, IntPtr location, IntPtr size, MemoryBlock owner) 130 { 131 ref var partialUnmapLock = ref GetPartialUnmapState().PartialUnmapLock; 132 partialUnmapLock.AcquireReaderLock(); 133 134 try 135 { 136 UnmapViewInternal(sharedMemory, location, size, owner, updateProtection: false); 137 MapViewInternal(sharedMemory, srcOffset, location, size, updateProtection: true); 138 } 139 finally 140 { 141 partialUnmapLock.ReleaseReaderLock(); 142 } 143 } 144 145 /// <summary> 146 /// Maps a shared memory view on a previously reserved memory region. 147 /// </summary> 148 /// <param name="sharedMemory">Shared memory that will be the backing storage for the view</param> 149 /// <param name="srcOffset">Offset in the shared memory to map</param> 150 /// <param name="location">Address to map the view into</param> 151 /// <param name="size">Size of the view in bytes</param> 152 /// <param name="updateProtection">Indicates if the memory protections should be updated after the map</param> 153 /// <exception cref="WindowsApiException">Thrown when the Windows API returns an error mapping the memory</exception> 154 private void MapViewInternal(IntPtr sharedMemory, ulong srcOffset, IntPtr location, IntPtr size, bool updateProtection) 155 { 156 SplitForMap((ulong)location, (ulong)size, srcOffset); 157 158 var ptr = WindowsApi.MapViewOfFile3( 159 sharedMemory, 160 WindowsApi.CurrentProcessHandle, 161 location, 162 srcOffset, 163 size, 164 0x4000, 165 MemoryProtection.ReadWrite, 166 IntPtr.Zero, 167 0); 168 169 if (ptr == IntPtr.Zero) 170 { 171 throw new WindowsApiException("MapViewOfFile3"); 172 } 173 174 if (updateProtection) 175 { 176 UpdateProtection((ulong)location, (ulong)size, MemoryPermission.ReadAndWrite); 177 } 178 } 179 180 /// <summary> 181 /// Splits a larger placeholder, slicing at the start and end address, for a new memory mapping. 182 /// </summary> 183 /// <param name="address">Address to split</param> 184 /// <param name="size">Size of the new region</param> 185 /// <param name="backingOffset">Offset in the shared memory that will be mapped</param> 186 private void SplitForMap(ulong address, ulong size, ulong backingOffset) 187 { 188 ulong endAddress = address + size; 189 190 var overlaps = new RangeNode<ulong>[InitialOverlapsSize]; 191 192 lock (_mappings) 193 { 194 int count = _mappings.GetNodes(address, endAddress, ref overlaps); 195 196 Debug.Assert(count == 1); 197 Debug.Assert(!IsMapped(overlaps[0].Value)); 198 199 var overlap = overlaps[0]; 200 201 ulong overlapStart = overlap.Start; 202 ulong overlapEnd = overlap.End; 203 ulong overlapValue = overlap.Value; 204 205 _mappings.Remove(overlap); 206 207 bool overlapStartsBefore = overlapStart < address; 208 bool overlapEndsAfter = overlapEnd > endAddress; 209 210 if (overlapStartsBefore && overlapEndsAfter) 211 { 212 CheckFreeResult(WindowsApi.VirtualFree( 213 (IntPtr)address, 214 (IntPtr)size, 215 AllocationType.Release | AllocationType.PreservePlaceholder)); 216 217 _mappings.Add(new RangeNode<ulong>(overlapStart, address, overlapValue)); 218 _mappings.Add(new RangeNode<ulong>(endAddress, overlapEnd, AddBackingOffset(overlapValue, endAddress - overlapStart))); 219 } 220 else if (overlapStartsBefore) 221 { 222 ulong overlappedSize = overlapEnd - address; 223 224 CheckFreeResult(WindowsApi.VirtualFree( 225 (IntPtr)address, 226 (IntPtr)overlappedSize, 227 AllocationType.Release | AllocationType.PreservePlaceholder)); 228 229 _mappings.Add(new RangeNode<ulong>(overlapStart, address, overlapValue)); 230 } 231 else if (overlapEndsAfter) 232 { 233 ulong overlappedSize = endAddress - overlapStart; 234 235 CheckFreeResult(WindowsApi.VirtualFree( 236 (IntPtr)overlapStart, 237 (IntPtr)overlappedSize, 238 AllocationType.Release | AllocationType.PreservePlaceholder)); 239 240 _mappings.Add(new RangeNode<ulong>(endAddress, overlapEnd, AddBackingOffset(overlapValue, overlappedSize))); 241 } 242 243 _mappings.Add(new RangeNode<ulong>(address, endAddress, backingOffset)); 244 } 245 } 246 247 /// <summary> 248 /// Unmaps a view that has been previously mapped with <see cref="MapView"/>. 249 /// </summary> 250 /// <remarks> 251 /// For "partial unmaps" (when not the entire mapped range is being unmapped), it might be 252 /// necessary to unmap the whole range and then remap the sub-ranges that should remain mapped. 253 /// </remarks> 254 /// <param name="sharedMemory">Shared memory that the view being unmapped belongs to</param> 255 /// <param name="location">Address to unmap</param> 256 /// <param name="size">Size of the region to unmap in bytes</param> 257 /// <param name="owner">Memory block that owns the mapping</param> 258 public void UnmapView(IntPtr sharedMemory, IntPtr location, IntPtr size, MemoryBlock owner) 259 { 260 ref var partialUnmapLock = ref GetPartialUnmapState().PartialUnmapLock; 261 partialUnmapLock.AcquireReaderLock(); 262 263 try 264 { 265 UnmapViewInternal(sharedMemory, location, size, owner, updateProtection: true); 266 } 267 finally 268 { 269 partialUnmapLock.ReleaseReaderLock(); 270 } 271 } 272 273 /// <summary> 274 /// Unmaps a view that has been previously mapped with <see cref="MapView"/>. 275 /// </summary> 276 /// <remarks> 277 /// For "partial unmaps" (when not the entire mapped range is being unmapped), it might be 278 /// necessary to unmap the whole range and then remap the sub-ranges that should remain mapped. 279 /// </remarks> 280 /// <param name="sharedMemory">Shared memory that the view being unmapped belongs to</param> 281 /// <param name="location">Address to unmap</param> 282 /// <param name="size">Size of the region to unmap in bytes</param> 283 /// <param name="owner">Memory block that owns the mapping</param> 284 /// <param name="updateProtection">Indicates if the memory protections should be updated after the unmap</param> 285 /// <exception cref="WindowsApiException">Thrown when the Windows API returns an error unmapping or remapping the memory</exception> 286 private void UnmapViewInternal(IntPtr sharedMemory, IntPtr location, IntPtr size, MemoryBlock owner, bool updateProtection) 287 { 288 ulong startAddress = (ulong)location; 289 ulong unmapSize = (ulong)size; 290 ulong endAddress = startAddress + unmapSize; 291 292 var overlaps = new RangeNode<ulong>[InitialOverlapsSize]; 293 int count; 294 295 lock (_mappings) 296 { 297 count = _mappings.GetNodes(startAddress, endAddress, ref overlaps); 298 } 299 300 for (int index = 0; index < count; index++) 301 { 302 var overlap = overlaps[index]; 303 304 if (IsMapped(overlap.Value)) 305 { 306 lock (_mappings) 307 { 308 _mappings.Remove(overlap); 309 _mappings.Add(new RangeNode<ulong>(overlap.Start, overlap.End, ulong.MaxValue)); 310 } 311 312 bool overlapStartsBefore = overlap.Start < startAddress; 313 bool overlapEndsAfter = overlap.End > endAddress; 314 315 if (overlapStartsBefore || overlapEndsAfter) 316 { 317 // If the overlap extends beyond the region we are unmapping, 318 // then we need to re-map the regions that are supposed to remain mapped. 319 // This is necessary because Windows does not support partial view unmaps. 320 // That is, you can only fully unmap a view that was previously mapped, you can't just unmap a chunck of it. 321 322 ref var partialUnmapState = ref GetPartialUnmapState(); 323 ref var partialUnmapLock = ref partialUnmapState.PartialUnmapLock; 324 partialUnmapLock.UpgradeToWriterLock(); 325 326 try 327 { 328 partialUnmapState.PartialUnmapsCount++; 329 330 if (!WindowsApi.UnmapViewOfFile2(WindowsApi.CurrentProcessHandle, (IntPtr)overlap.Start, 2)) 331 { 332 throw new WindowsApiException("UnmapViewOfFile2"); 333 } 334 335 if (overlapStartsBefore) 336 { 337 ulong remapSize = startAddress - overlap.Start; 338 339 MapViewInternal(sharedMemory, overlap.Value, (IntPtr)overlap.Start, (IntPtr)remapSize, updateProtection: false); 340 RestoreRangeProtection(overlap.Start, remapSize); 341 } 342 343 if (overlapEndsAfter) 344 { 345 ulong overlappedSize = endAddress - overlap.Start; 346 ulong remapBackingOffset = overlap.Value + overlappedSize; 347 ulong remapAddress = overlap.Start + overlappedSize; 348 ulong remapSize = overlap.End - endAddress; 349 350 MapViewInternal(sharedMemory, remapBackingOffset, (IntPtr)remapAddress, (IntPtr)remapSize, updateProtection: false); 351 RestoreRangeProtection(remapAddress, remapSize); 352 } 353 } 354 finally 355 { 356 partialUnmapLock.DowngradeFromWriterLock(); 357 } 358 } 359 else if (!WindowsApi.UnmapViewOfFile2(WindowsApi.CurrentProcessHandle, (IntPtr)overlap.Start, 2)) 360 { 361 throw new WindowsApiException("UnmapViewOfFile2"); 362 } 363 } 364 } 365 366 CoalesceForUnmap(startAddress, unmapSize, owner); 367 368 if (updateProtection) 369 { 370 UpdateProtection(startAddress, unmapSize, MemoryPermission.None); 371 } 372 } 373 374 /// <summary> 375 /// Coalesces adjacent placeholders after unmap. 376 /// </summary> 377 /// <param name="address">Address of the region that was unmapped</param> 378 /// <param name="size">Size of the region that was unmapped in bytes</param> 379 /// <param name="owner">Memory block that owns the mapping</param> 380 private void CoalesceForUnmap(ulong address, ulong size, MemoryBlock owner) 381 { 382 ulong endAddress = address + size; 383 ulong blockAddress = (ulong)owner.Pointer; 384 ulong blockEnd = blockAddress + owner.Size; 385 int unmappedCount = 0; 386 387 lock (_mappings) 388 { 389 RangeNode<ulong> node = _mappings.GetNodeByKey(address); 390 391 if (node == null) 392 { 393 // Nothing to coalesce if we have no overlaps. 394 return; 395 } 396 397 RangeNode<ulong> predecessor = node.Predecessor; 398 RangeNode<ulong> successor = null; 399 400 for (; node != null; node = successor) 401 { 402 successor = node.Successor; 403 var overlap = node; 404 405 if (!IsMapped(overlap.Value)) 406 { 407 address = Math.Min(address, overlap.Start); 408 endAddress = Math.Max(endAddress, overlap.End); 409 410 _mappings.Remove(overlap); 411 unmappedCount++; 412 } 413 414 if (node.End >= endAddress) 415 { 416 break; 417 } 418 } 419 420 if (predecessor != null && !IsMapped(predecessor.Value) && predecessor.Start >= blockAddress) 421 { 422 address = Math.Min(address, predecessor.Start); 423 424 _mappings.Remove(predecessor); 425 unmappedCount++; 426 } 427 428 if (successor != null && !IsMapped(successor.Value) && successor.End <= blockEnd) 429 { 430 endAddress = Math.Max(endAddress, successor.End); 431 432 _mappings.Remove(successor); 433 unmappedCount++; 434 } 435 436 _mappings.Add(new RangeNode<ulong>(address, endAddress, ulong.MaxValue)); 437 } 438 439 if (unmappedCount > 1) 440 { 441 size = endAddress - address; 442 443 CheckFreeResult(WindowsApi.VirtualFree( 444 (IntPtr)address, 445 (IntPtr)size, 446 AllocationType.Release | AllocationType.CoalescePlaceholders)); 447 } 448 } 449 450 /// <summary> 451 /// Reprotects a region of memory that has been mapped. 452 /// </summary> 453 /// <param name="address">Address of the region to reprotect</param> 454 /// <param name="size">Size of the region to reprotect in bytes</param> 455 /// <param name="permission">New permissions</param> 456 /// <returns>True if the reprotection was successful, false otherwise</returns> 457 public bool ReprotectView(IntPtr address, IntPtr size, MemoryPermission permission) 458 { 459 ref var partialUnmapLock = ref GetPartialUnmapState().PartialUnmapLock; 460 partialUnmapLock.AcquireReaderLock(); 461 462 try 463 { 464 return ReprotectViewInternal(address, size, permission, false); 465 } 466 finally 467 { 468 partialUnmapLock.ReleaseReaderLock(); 469 } 470 } 471 472 /// <summary> 473 /// Reprotects a region of memory that has been mapped. 474 /// </summary> 475 /// <param name="address">Address of the region to reprotect</param> 476 /// <param name="size">Size of the region to reprotect in bytes</param> 477 /// <param name="permission">New permissions</param> 478 /// <param name="throwOnError">Throw an exception instead of returning an error if the operation fails</param> 479 /// <returns>True if the reprotection was successful or if <paramref name="throwOnError"/> is true, false otherwise</returns> 480 /// <exception cref="WindowsApiException">If <paramref name="throwOnError"/> is true, it is thrown when the Windows API returns an error reprotecting the memory</exception> 481 private bool ReprotectViewInternal(IntPtr address, IntPtr size, MemoryPermission permission, bool throwOnError) 482 { 483 ulong reprotectAddress = (ulong)address; 484 ulong reprotectSize = (ulong)size; 485 ulong endAddress = reprotectAddress + reprotectSize; 486 487 bool success = true; 488 489 lock (_mappings) 490 { 491 RangeNode<ulong> node = _mappings.GetNodeByKey(reprotectAddress); 492 RangeNode<ulong> successorNode; 493 494 for (; node != null; node = successorNode) 495 { 496 successorNode = node.Successor; 497 var overlap = node; 498 499 ulong mappedAddress = overlap.Start; 500 ulong mappedSize = overlap.End - overlap.Start; 501 502 if (mappedAddress < reprotectAddress) 503 { 504 ulong delta = reprotectAddress - mappedAddress; 505 mappedAddress = reprotectAddress; 506 mappedSize -= delta; 507 } 508 509 ulong mappedEndAddress = mappedAddress + mappedSize; 510 511 if (mappedEndAddress > endAddress) 512 { 513 ulong delta = mappedEndAddress - endAddress; 514 mappedSize -= delta; 515 } 516 517 if (!WindowsApi.VirtualProtect((IntPtr)mappedAddress, (IntPtr)mappedSize, WindowsApi.GetProtection(permission), out _)) 518 { 519 if (throwOnError) 520 { 521 throw new WindowsApiException("VirtualProtect"); 522 } 523 524 success = false; 525 } 526 527 if (node.End >= endAddress) 528 { 529 break; 530 } 531 } 532 } 533 534 UpdateProtection(reprotectAddress, reprotectSize, permission); 535 536 return success; 537 } 538 539 /// <summary> 540 /// Checks the result of a VirtualFree operation, throwing if needed. 541 /// </summary> 542 /// <param name="success">Operation result</param> 543 /// <exception cref="WindowsApiException">Thrown if <paramref name="success"/> is false</exception> 544 private static void CheckFreeResult(bool success) 545 { 546 if (!success) 547 { 548 throw new WindowsApiException("VirtualFree"); 549 } 550 } 551 552 /// <summary> 553 /// Adds an offset to a backing offset. This will do nothing if the backing offset is the special "unmapped" value. 554 /// </summary> 555 /// <param name="backingOffset">Backing offset</param> 556 /// <param name="offset">Offset to be added</param> 557 /// <returns>Added offset or just <paramref name="backingOffset"/> if the region is unmapped</returns> 558 private static ulong AddBackingOffset(ulong backingOffset, ulong offset) 559 { 560 if (backingOffset == ulong.MaxValue) 561 { 562 return backingOffset; 563 } 564 565 return backingOffset + offset; 566 } 567 568 /// <summary> 569 /// Checks if a region is unmapped. 570 /// </summary> 571 /// <param name="backingOffset">Backing offset to check</param> 572 /// <returns>True if the backing offset is the special "unmapped" value, false otherwise</returns> 573 private static bool IsMapped(ulong backingOffset) 574 { 575 return backingOffset != ulong.MaxValue; 576 } 577 578 /// <summary> 579 /// Adds a protection to the list of protections. 580 /// </summary> 581 /// <param name="address">Address of the protected region</param> 582 /// <param name="size">Size of the protected region in bytes</param> 583 /// <param name="permission">Memory permissions of the region</param> 584 private void UpdateProtection(ulong address, ulong size, MemoryPermission permission) 585 { 586 ulong endAddress = address + size; 587 588 lock (_protections) 589 { 590 RangeNode<MemoryPermission> node = _protections.GetNodeByKey(address); 591 592 if (node != null && 593 node.Start <= address && 594 node.End >= endAddress && 595 node.Value == permission) 596 { 597 return; 598 } 599 600 RangeNode<MemoryPermission> successorNode; 601 602 ulong startAddress = address; 603 604 for (; node != null; node = successorNode) 605 { 606 successorNode = node.Successor; 607 var protection = node; 608 609 ulong protAddress = protection.Start; 610 ulong protEndAddress = protection.End; 611 MemoryPermission protPermission = protection.Value; 612 613 _protections.Remove(protection); 614 615 if (protPermission == permission) 616 { 617 if (startAddress > protAddress) 618 { 619 startAddress = protAddress; 620 } 621 622 if (endAddress < protEndAddress) 623 { 624 endAddress = protEndAddress; 625 } 626 } 627 else 628 { 629 if (startAddress > protAddress) 630 { 631 _protections.Add(new RangeNode<MemoryPermission>(protAddress, startAddress, protPermission)); 632 } 633 634 if (endAddress < protEndAddress) 635 { 636 _protections.Add(new RangeNode<MemoryPermission>(endAddress, protEndAddress, protPermission)); 637 } 638 } 639 640 if (node.End >= endAddress) 641 { 642 break; 643 } 644 } 645 646 _protections.Add(new RangeNode<MemoryPermission>(startAddress, endAddress, permission)); 647 } 648 } 649 650 /// <summary> 651 /// Removes protection from the list of protections. 652 /// </summary> 653 /// <param name="address">Address of the protected region</param> 654 /// <param name="size">Size of the protected region in bytes</param> 655 private void RemoveProtection(ulong address, ulong size) 656 { 657 ulong endAddress = address + size; 658 659 lock (_protections) 660 { 661 RangeNode<MemoryPermission> node = _protections.GetNodeByKey(address); 662 RangeNode<MemoryPermission> successorNode; 663 664 for (; node != null; node = successorNode) 665 { 666 successorNode = node.Successor; 667 var protection = node; 668 669 ulong protAddress = protection.Start; 670 ulong protEndAddress = protection.End; 671 MemoryPermission protPermission = protection.Value; 672 673 _protections.Remove(protection); 674 675 if (address > protAddress) 676 { 677 _protections.Add(new RangeNode<MemoryPermission>(protAddress, address, protPermission)); 678 } 679 680 if (endAddress < protEndAddress) 681 { 682 _protections.Add(new RangeNode<MemoryPermission>(endAddress, protEndAddress, protPermission)); 683 } 684 685 if (node.End >= endAddress) 686 { 687 break; 688 } 689 } 690 } 691 } 692 693 /// <summary> 694 /// Restores the protection of a given memory region that was remapped, using the protections list. 695 /// </summary> 696 /// <param name="address">Address of the remapped region</param> 697 /// <param name="size">Size of the remapped region in bytes</param> 698 private void RestoreRangeProtection(ulong address, ulong size) 699 { 700 ulong endAddress = address + size; 701 var overlaps = new RangeNode<MemoryPermission>[InitialOverlapsSize]; 702 int count; 703 704 lock (_protections) 705 { 706 count = _protections.GetNodes(address, endAddress, ref overlaps); 707 } 708 709 for (int index = 0; index < count; index++) 710 { 711 var protection = overlaps[index]; 712 713 // If protection is R/W we don't need to reprotect as views are initially mapped as R/W. 714 if (protection.Value == MemoryPermission.ReadAndWrite) 715 { 716 continue; 717 } 718 719 ulong protAddress = protection.Start; 720 ulong protEndAddress = protection.End; 721 722 if (protAddress < address) 723 { 724 protAddress = address; 725 } 726 727 if (protEndAddress > endAddress) 728 { 729 protEndAddress = endAddress; 730 } 731 732 ReprotectViewInternal((IntPtr)protAddress, (IntPtr)(protEndAddress - protAddress), protection.Value, true); 733 } 734 } 735 } 736 }