Guest User

Untitled

a guest
Apr 2nd, 2020
24
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 18.11 KB | None | 0 0
  1. (*
  2.  
  3. SafeMM Debug Memory Manager 0.4
  4.  
  5. (C) Ben Taylor
  6.  
  7. based on code from:
  8. (C) 2000 Per B. Larsen & TurboPower Software Co.
  9. All rights reserved.
  10. Used with permission.
  11.  
  12. Description:
  13. A "proof of concept" unit for using OS functionality to raise an
  14. AV when an improper attempt is made to read or write memory.
  15.  
  16. Usage:
  17. To use SafeMM as a replacement memory manager in a standalone application
  18. add the SafeMMInstall as the first unit in the project uses clause.
  19. Then run the application under the debugger. If an invalid
  20. memory access is detected an Access Violation will be generated.
  21.  
  22. For applications using ShareMem, compile the borlndmm.dpr project
  23. and copy the resulting borlndmm.dll into the application bin directory.
  24.  
  25. To use SafeMM with the IDE (BDS.exe), backup and replace the borlndmm.dll
  26. file in the directory where BDS.exe is installed.
  27.  
  28. Notes:
  29. - A problem is that many functions require/assume 4/8-byte
  30. alignment. enabling alignment in the mm means that its guarding
  31. ability is reduced.
  32.  
  33. - A typical app might experience 4x the normal memory usage.
  34. Depending on how many small-block pools are preallocated, there may
  35. also be a minimum memory usage of about 60Mb.
  36.  
  37. - When used with the IDE (version 8 or later), disable the exceptiondiag
  38. IDE package to prevent an infinite loop if an unhandled exception is raised.
  39. To do this, rename the exceptiondiagXXX.bpl file in the diretory with
  40. bds.exe at the same time borlndmm.dll is replaced.
  41. When the IDE is started, reply "yes" to the missing package prompt.
  42. When restoring the original borlndmm.dll file, re-enable the exceptdiag
  43. package by removing the registry entry under:
  44.  
  45. HKEY_CURRENT_USER\Software\CodeGear\BDS\X.0\Disabled IDE Packages
  46.  
  47. - Because of the increased memory requirements, when running the IDE,
  48. it may be necessary to disable unused IDE personalities and features
  49. to avoid out of memory errors. Out of memory errors may also occur
  50. after running the application for a period of time.
  51.  
  52. Examples:
  53. //with alignment=8
  54. var
  55. p:pchar;
  56. begin
  57. p:=AllocMem(2);
  58. p[0]:='a';
  59. p:=ReallocMem(p,3);
  60. Assert(p[0]='a');
  61. p[1]:='b';
  62. //ideally should fail, but wont due to alignment
  63. p[3]:='b';
  64. //should give a write-av
  65. p[10]:='c';
  66. //should give a read-av
  67. if p[10]='c' then ...
  68.  
  69. Small Block Layout:
  70. 64k is divided into 16 x 4k blocks:
  71.  
  72. info/g/mem1/g/mem2/g/mem3/g/mem4/g/mem5/g/mem6/g/mem7/g
  73.  
  74. info : contains info about allocation of the small blocks
  75. g : guard blocks. these are always marked as NoAccess
  76. memX : returned by the memory routines.
  77. marked as NoAccess when not in use.
  78. marked as ReadWrite when allocated
  79.  
  80. There are 7 info records in the info block.
  81. This gives about 500bytes/record.
  82.  
  83. Good reason to not release pool back to os when empty is that you can
  84. store info about data that was previously there.
  85.  
  86. Change log:
  87. Version 0.1
  88. - Initial release
  89.  
  90. Version 0.2
  91. - Improved memory usage. Small blocks are now reused less frequently,
  92. leading to a higher chance that improper memory usage is caught.
  93.  
  94. Version 0.3 (March 29, 2007)
  95. - Added support for delaying reuse of large-blocks.
  96.  
  97. Version 0.4 (October 16, 2009)
  98. - Mark Edington (Embarcadero): Fixed compilation with older RTL that does
  99. not have TMemoryManagerEx defined.
  100. Moved SafeMM_readme.txt contents into this file and added addition notes.
  101.  
  102. Todo (from Ben):
  103. - Implement SafeMMMode again
  104. - store stack traces for the memory alloc/free calls
  105. - Add guard bytes to allow detection of modification in the area
  106. not used due to alignment
  107. - Implement data structures to allow better management of the
  108. allocated memory. this would allow:
  109. - walking a list of allocated large and small blocks
  110. ideally these structures should be marked as NoAccess
  111. unless being changed by the memory routines.
  112. - Rewrite by someone who knows this better than i do :)
  113. - Nice to have a callback like GetPointerInfo(p:pointer;info:pchar);
  114. this would allow the rtl to ask the memory-manager for additional info
  115. about the given pointer (eg stack trace), which can then be displayed
  116. in an error message to the user.
  117.  
  118. *)
  119.  
  120. unit SafeMM;
  121. {$ASSERTIONS ON}
  122.  
  123. interface
  124.  
  125. {$IF RTLVersion >= 18.0}
  126. {$DEFINE MEMORY_MANAGER_EX}
  127. {$IFEND}
  128.  
  129. type
  130. TSafeProtect = (spReadWrite, spReadOnly, spNoAccess);
  131.  
  132. NativeIntAlloc = NativeInt;
  133.  
  134.  
  135. function SafeGetMem(Size: NativeInt): Pointer;
  136. function SafeFreeMem(P: Pointer): Integer;
  137. function SafeReallocMem(P: Pointer; Size: NativeInt): Pointer;
  138. function SafeAllocMem(ASize: NativeIntAlloc): Pointer;
  139. function SafeRegisterExpectedMemoryLeak(P: Pointer): Boolean;
  140. function SafeUnRegisterExpectedMemoryLeak(P: Pointer): Boolean;
  141.  
  142. {
  143. caller can specifically set protection on a block of memory.
  144. this is useful eg if you have a block of memory that you know
  145. shouldn't be written to.
  146. }
  147. procedure SafeMMProtect(const P: Pointer; const aProtect: TSafeProtect);
  148.  
  149. {
  150. this preallocates a number of small-block pools.
  151. this is done so that the same small-block isn't immediately
  152. reused by the next memory request.
  153. }
  154. procedure SafeMMPrepare;
  155.  
  156. const
  157. {$IFDEF MEMORY_MANAGER_EX}
  158. SafeMemoryManager: TMemoryManagerEx = (
  159. GetMem: SafeGetMem;
  160. FreeMem: SafeFreeMem;
  161. ReallocMem: SafeReallocMem;
  162. AllocMem: SafeAllocMem;
  163. RegisterExpectedMemoryLeak: SafeRegisterExpectedMemoryLeak;
  164. UnregisterExpectedMemoryLeak: SafeUnRegisterExpectedMemoryLeak;
  165. );
  166. {$ELSE}
  167. SafeMemoryManager: TMemoryManager = (
  168. GetMem: SafeGetMem;
  169. FreeMem: SafeFreeMem;
  170. ReallocMem: SafeReallocMem;
  171. );
  172.  
  173. type
  174. TMemoryManagerEx = TMemoryManager; // Simple alias to avoid IFDEF in SafeMMInstall.
  175. {$ENDIF}
  176.  
  177. type
  178.  
  179. {
  180. selects how the returned block of memory is aligned inside its
  181. guard pages.
  182.  
  183. eg header:
  184. Guard|returned block|trailing space|guard
  185. will protect against eg p[-1]:=nil;
  186.  
  187. footer:
  188. Guard|leading space|returned block|guard
  189. will protect against eg p[size+100]:=nil;
  190. }
  191. TSafeGuardMode = (
  192. gmHeader, // mostly protect against access before block
  193. gmFooter // mostly protect against access after block
  194. );
  195.  
  196. var
  197. // 0=no alignment
  198. SafeMMAlign: Integer;
  199. SafeMMMode: TSafeGuardMode;
  200.  
  201. implementation
  202.  
  203. uses
  204. Windows;
  205.  
  206. const
  207. // how many 4k blocks in a 64k pool
  208. cSubCount = 7;
  209.  
  210. type
  211.  
  212. PPoolInfo = ^TPoolInfo;
  213.  
  214. TPoolInfo = record
  215. Pool: Pointer;
  216. // is each subblock available?
  217. // could be better to use 7 bits and bitwise ops?
  218. // eg 01111111. then full=127,empty=0, inuse>0
  219. avail: array [0 .. cSubCount - 1] of boolean;
  220. // helper routines
  221. end;
  222.  
  223. PBlockInfo = ^TBlockInfo;
  224.  
  225. TBlockInfo = record
  226. // indicate valid info
  227. Magic: Cardinal;
  228. // pool. nil=it was alloc seperately
  229. Pool: PPoolInfo;
  230. PoolIndex: Integer;
  231. //
  232. Issued: boolean;
  233. // requested size
  234. RequestSize: Cardinal;
  235. // pointer to returned memory
  236. Start: Pointer;
  237. // use a fixed size list of pointers for a stack
  238. // Stack:Pointer;
  239. end;
  240.  
  241. const
  242. // should read page size from system?
  243. cPage = 4 * 1024;
  244. // used to indicate if we're located at a valid location
  245. cMagic = 123123;
  246. // how many small-block pools are allowed
  247. cMaxAvail = 30000;
  248.  
  249. var
  250. FHeap: NativeUInt;
  251. FAvailCount: Cardinal;
  252. FAvailList: array [0 .. cMaxAvail] of PPoolInfo;
  253. // used to decrease reuse of large blocks
  254. // the larger the array, the better, but also increases ram usage
  255. FLargeList: array [0 .. 100] of PBlockInfo;
  256. FLargeIndex: Integer;
  257. FHoldStart: Cardinal;
  258. FHoldEnd: Cardinal;
  259. // no reason to keep full list unless mem leak checking
  260. // FTotalList:array[1..32000] of PPoolInfo;
  261. FCritical: TRTLCriticalSection;
  262.  
  263. function SafeRegisterExpectedMemoryLeak(P: Pointer): Boolean;
  264. begin
  265. Result := False;
  266. end;
  267.  
  268. function SafeUnRegisterExpectedMemoryLeak(P: Pointer): Boolean;
  269. begin
  270. Result := False;
  271. end;
  272.  
  273. function offset(const P: Pointer; const b: Integer): Pointer;
  274. begin
  275. Assert(P <> nil);
  276. result := Pointer(NativeUInt(P) + b);
  277. end;
  278.  
  279. procedure PushAvail(const aPool: PPoolInfo);
  280. begin
  281. Assert(aPool <> nil, 'noinfo');
  282. Assert(FAvailList[FHoldEnd] = nil, 'noinfo');
  283.  
  284. FAvailList[FHoldEnd] := aPool;
  285. inc(FAvailCount);
  286. inc(FHoldEnd);
  287. Assert(FHoldStart <> FHoldEnd); // cant be empty
  288.  
  289. if FHoldEnd = High(FAvailList) then
  290. FHoldEnd := Low(FAvailList);
  291. end;
  292.  
  293. function PopAvail: PPoolInfo;
  294. begin
  295. Assert(FHoldStart <> FHoldEnd); // cant be empty
  296.  
  297. dec(FAvailCount);
  298. result := FAvailList[FHoldStart];
  299. FAvailList[FHoldStart] := nil;
  300. Assert(result <> nil);
  301. inc(FHoldStart);
  302. if FHoldStart = High(FAvailList) then
  303. FHoldStart := Low(FAvailList);
  304. end;
  305.  
  306. function IsEmpty(const aPool: PPoolInfo): boolean;
  307. var
  308. i: Integer;
  309. begin
  310. result := True;
  311. for i := low(aPool.avail) to high(aPool.avail) do
  312. if not aPool.avail[i] then
  313. begin
  314. result := False;
  315. exit;
  316. end;
  317. end;
  318.  
  319. function IsFull(const aPool: PPoolInfo): boolean;
  320. var
  321. i: Integer;
  322. begin
  323. result := True;
  324. for i := low(aPool.avail) to high(aPool.avail) do
  325. if aPool.avail[i] then
  326. begin
  327. result := False;
  328. exit;
  329. end;
  330. end;
  331.  
  332. procedure CheckValid(const aBlock: PBlockInfo);
  333. begin
  334. Assert(aBlock <> nil);
  335. Assert(aBlock.Magic = cMagic);
  336. end;
  337.  
  338. procedure Init(const aBlock: PBlockInfo);
  339. begin
  340. aBlock.Magic := cMagic;
  341. aBlock.Issued := False;
  342. end;
  343.  
  344. procedure lock(const aBlock: PBlockInfo);
  345. var
  346. old: Cardinal;
  347. begin
  348. // could also use PAGE_readonly here as a lower protection
  349. if not VirtualProtect(aBlock.Start, aBlock.RequestSize, PAGE_NOACCESS, old) then
  350. Assert(false);
  351. end;
  352.  
  353. procedure unlock(const aBlock: PBlockInfo);
  354. // unlock 1 4k page
  355. var
  356. old: Cardinal;
  357. aSuccess: boolean;
  358. begin
  359. aSuccess := VirtualProtect(aBlock.Start, 1, PAGE_READWRITE, old);
  360. Assert(aSuccess);
  361. end;
  362.  
  363. function PointerToBlock(const P: Pointer): PBlockInfo;
  364. // info|guard|mem0|guard|mem1|guard...
  365. var
  366. aBase: Pointer;
  367. aIndex: Cardinal;
  368. begin
  369. Assert(P <> nil);
  370. aBase := Pointer((NativeUInt(P) div (64 * 1024)) * (64 * 1024));
  371. // which 4kblock are we in?
  372. aIndex := (NativeUInt(P) - NativeUInt(aBase)) div (4 * 1024);
  373. // convert that to info index: 2=0,4=1,6=2
  374. aIndex := (aIndex div 2) - 1;
  375.  
  376. // then add that*infosize to the base
  377. result := offset(aBase, aIndex * SizeOf(TBlockInfo));
  378.  
  379. Assert(result.Magic = cMagic);
  380. end;
  381.  
  382. procedure InitPool(const aPool: PPoolInfo);
  383. var
  384. aSub: Pointer;
  385. old: Cardinal;
  386. i: Integer;
  387. aBlock: PBlockInfo;
  388. aSuccess: boolean;
  389. begin
  390. Assert(aPool <> nil);
  391. // msdn: Memory allocated by VirtualAlloc is automatically
  392. // initialized to zero, unless MEM_RESET is specified.
  393. aPool.Pool := VirtualAlloc(nil, 64 * 1024, MEM_COMMIT, PAGE_NOACCESS);
  394. if aPool.Pool = nil then
  395. exit;
  396.  
  397. // ensure 64k aligned. safemm routines depend on this.
  398. Assert(NativeUInt(aPool.Pool) mod (64 * 1024) = 0);
  399.  
  400. for i := Low(aPool.avail) to high(aPool.avail) do
  401. aPool.avail[i] := True;
  402.  
  403. aSub := aPool.Pool;
  404. aSuccess := VirtualProtect(aSub, 4096, PAGE_READWRITE, old);
  405. Assert(aSuccess);
  406.  
  407. // first block is info. init the 7
  408. for i := 0 to cSubCount - 1 do
  409. begin
  410. aBlock := offset(aSub, i * SizeOf(TBlockInfo));
  411. Init(aBlock);
  412. aBlock.Pool := aPool;
  413. aBlock.PoolIndex := i;
  414. // 0=2 1=4
  415. aBlock.Start := offset(aPool.Pool, (2 + (i * 2)) * 4096);
  416. end;
  417.  
  418. end;
  419.  
  420. function GetLargeBlock(const aRequest: Cardinal): PBlockInfo;
  421. var
  422. aActual: Cardinal;
  423. P: Pointer;
  424. old: Cardinal;
  425. begin
  426. aActual := aRequest div 4096;
  427. if aRequest mod 4096 > 0 then
  428. inc(aActual);
  429. aActual := aActual * 4096;
  430.  
  431. result := VirtualAlloc(nil, aActual + (3 * 4096), MEM_COMMIT, PAGE_READWRITE);
  432. if result = nil then
  433. exit;
  434.  
  435. // setup info
  436. Init(result);
  437. result.RequestSize := aRequest;
  438. result.Start := offset(result, 8 * 1024);
  439.  
  440. P := offset(result, 4096);
  441. if not VirtualProtect(P, 1, PAGE_NOACCESS, old) then
  442. Assert(false);
  443. P := offset(P, 4096 + aActual);
  444. if not VirtualProtect(P, 1, PAGE_NOACCESS, old) then
  445. Assert(false);
  446. end;
  447.  
  448. function FreeLargeBlock(const aBlock: PBlockInfo): Integer;
  449. // retain the block so it isn't immediately reallocated
  450. var
  451. P: PBlockInfo;
  452. begin
  453. Assert(aBlock <> nil);
  454.  
  455. result := 0;
  456.  
  457. lock(aBlock);
  458.  
  459. Assert(FLargeList[FLargeIndex] = nil);
  460. FLargeList[FLargeIndex] := aBlock;
  461.  
  462. if FLargeIndex = high(FLargeList) then
  463. FLargeIndex := Low(FLargeList)
  464. else
  465. inc(FLargeIndex);
  466.  
  467. P := FLargeList[FLargeIndex];
  468. if P <> nil then
  469. begin
  470. if VirtualFree(P, 0, MEM_RELEASE) then
  471. result := 0
  472. else
  473. result := -1;
  474. FLargeList[FLargeIndex] := nil;
  475. end;
  476.  
  477. end;
  478.  
  479. function CreateInfo: PPoolInfo;
  480. const
  481. HEAP_ZERO_MEMORY = $00000008; // Was not defined in older Windows.pas files
  482. begin
  483. result := HeapAlloc(FHeap, HEAP_ZERO_MEMORY, SizeOf(TPoolInfo));
  484. InitPool(result);
  485. end;
  486.  
  487. function GetSmallBlock(const ASize: Cardinal): PBlockInfo;
  488. var
  489. aPool: PPoolInfo;
  490. i: Integer;
  491. begin
  492. result := nil;
  493.  
  494. EnterCriticalSection(FCritical);
  495. try
  496.  
  497. Assert(FAvailCount < cMaxAvail);
  498.  
  499. // is there an avail pool?
  500. if FAvailCount > 0 then
  501. begin
  502. aPool := PopAvail;
  503. end
  504. else
  505. begin
  506. aPool := CreateInfo;
  507. if aPool.Pool = nil then
  508. exit;
  509. end;
  510.  
  511. Assert(aPool.Pool <> nil);
  512.  
  513. // return a block
  514. result := nil;
  515. for i := Low(aPool.avail) to high(aPool.avail) do
  516. begin
  517. if aPool.avail[i] then
  518. begin
  519. result := offset(aPool.Pool, i * SizeOf(TBlockInfo));
  520. aPool.avail[i] := False;
  521. break;
  522. end;
  523. end;
  524. Assert(result <> nil);
  525.  
  526. // if the pool is still avail then push
  527. if not IsFull(aPool) then
  528. begin
  529. PushAvail(aPool);
  530. end;
  531.  
  532. finally
  533. LeaveCriticalSection(FCritical);
  534. end;
  535.  
  536. // we've acquired small block, can now prepare it outside of lock
  537. Assert(result <> nil);
  538. CheckValid(result);
  539. result.RequestSize := ASize;
  540. unlock(result);
  541. FillChar(result.Start^, 4096, 0);
  542. end;
  543.  
  544. function FreeSmallBlock(const aBlock: PBlockInfo): Integer;
  545. var
  546. aPool: PPoolInfo;
  547. begin
  548. Assert(aBlock <> nil);
  549. result := 0;
  550. CheckValid(aBlock);
  551.  
  552. // prevent further access. should clear here too?
  553. // leave content means you could find out what was previously there when av happens?
  554. lock(aBlock);
  555.  
  556. EnterCriticalSection(FCritical);
  557. try
  558. aPool := aBlock.Pool;
  559.  
  560. // if pool was full then it goes back into avail list
  561. if IsFull(aPool) then
  562. begin
  563. PushAvail(aPool);
  564. end;
  565.  
  566. // tag the block as being available again
  567. aPool.avail[aBlock.PoolIndex] := True;
  568.  
  569. //
  570. if IsEmpty(aPool) then
  571. begin
  572. // either keep in avail list or delete based on count
  573. // VirtualFree(aPool.Pool,0,MEM_RELEASE);
  574. // also heapfree the pool info
  575. end;
  576.  
  577. finally
  578. LeaveCriticalSection(FCritical);
  579. end;
  580. end;
  581.  
  582. function BlockToPointer(const aBlock: PBlockInfo): Pointer;
  583. var
  584. aPartial, aoffset: Integer;
  585. begin
  586. Assert(aBlock <> nil);
  587. Assert(aBlock.Magic = cMagic);
  588.  
  589. // header align
  590. // result:=aBlock.Start;
  591.  
  592. // footer aligned
  593. aPartial := (aBlock.RequestSize mod cPage);
  594. if aPartial = 0 then
  595. aoffset := 0
  596. else
  597. aoffset := cPage - aPartial;
  598. if SafeMMAlign > 0 then
  599. aoffset := (aoffset div SafeMMAlign) * SafeMMAlign;
  600. result := offset(aBlock.Start, aoffset);
  601.  
  602. end;
  603.  
  604. function SafeGetMem(Size: NativeInt): Pointer;
  605. var
  606. aBlock: PBlockInfo;
  607. begin
  608. Assert(Size > 0);
  609.  
  610. result := nil;
  611.  
  612. if Size <= 4 * 1024 then
  613. aBlock := GetSmallBlock(Size)
  614. else
  615. aBlock := GetLargeBlock(Size);
  616. if aBlock = nil then
  617. exit;
  618.  
  619. // setup
  620. CheckValid(aBlock);
  621. Assert(aBlock.Issued = False);
  622. aBlock.Issued := True;
  623.  
  624. // and quit
  625. result := BlockToPointer(aBlock);
  626.  
  627. end;
  628.  
  629. function SafeAllocMem(ASize: NativeIntAlloc): Pointer;
  630. begin
  631. Assert(ASize > 0);
  632. result := SafeGetMem(ASize);
  633. end;
  634.  
  635. function Min(const i1, i2: Integer): Integer;
  636. begin
  637. result := i1;
  638. if i2 < result then
  639. result := i2;
  640. end;
  641.  
  642. function SafeReallocMem(P: Pointer; Size: NativeInt): Pointer;
  643. // force-move realloc will help find invalid pointer usage
  644. var
  645. aActual: Integer;
  646. aSource: PBlockInfo;
  647. begin
  648. Assert(P <> nil);
  649. Assert(Size > 0);
  650.  
  651. // find size of original block
  652. aSource := PointerToBlock(P);
  653.  
  654. // after checks have passed, get new block and copy
  655. result := SafeAllocMem(Size);
  656. if result = nil then
  657. exit;
  658.  
  659. aActual := Min(aSource.RequestSize, Size);
  660. Move(P^, result^, aActual);
  661.  
  662. SafeFreeMem(P);
  663. end;
  664.  
  665. function SafeFreeMem(P: Pointer): Integer;
  666. var
  667. aBlock: PBlockInfo;
  668. begin
  669. Assert(P <> nil);
  670.  
  671. // on free also check if it held a class. store classname as text in
  672. // the info block, makes easier?
  673.  
  674. aBlock := PointerToBlock(P);
  675. CheckValid(aBlock);
  676.  
  677. Assert(aBlock.Issued);
  678. aBlock.Issued := False;
  679.  
  680. if aBlock.Pool = nil then
  681. begin
  682. result := FreeLargeBlock(aBlock);
  683. end
  684. else
  685. begin
  686. result := FreeSmallBlock(aBlock);
  687. end;
  688.  
  689. end;
  690.  
  691. procedure SafeMMProtect(const P: Pointer; const aProtect: TSafeProtect);
  692. var
  693. old: Cardinal;
  694. aBlock: PBlockInfo;
  695. const
  696. cProtect: array [TSafeProtect] of Cardinal = (PAGE_READWRITE, PAGE_READONLY,
  697. PAGE_NOACCESS);
  698. begin
  699. Assert(P <> nil);
  700. aBlock := PointerToBlock(P);
  701. Assert(aBlock.Issued);
  702. VirtualProtect(aBlock.Start, aBlock.RequestSize, cProtect[aProtect], old)
  703. end;
  704.  
  705. procedure SafeMMPrepare;
  706. var
  707. aPool: PPoolInfo;
  708. i: Integer;
  709. begin
  710. for i := 1 to 1000 do
  711. begin
  712. aPool := CreateInfo;
  713. PushAvail(aPool);
  714. end;
  715. end;
  716.  
  717. initialization
  718. {$WARNINGS OFF}
  719. // check that enough info can fit into 1 page
  720. Assert(SizeOf(TBlockInfo) * (cSubCount + 1) <= (4 * 1024));
  721. {$WARNINGS ON}
  722. FHoldStart := 0;
  723. FHoldEnd := 0;
  724. FLargeIndex := 0;
  725. FAvailCount := 0;
  726. SafeMMAlign := 8;
  727. SafeMMMode := gmFooter;
  728. InitializeCriticalSection(FCritical);
  729. FHeap := GetProcessHeap;
  730. finalization
  731. DeleteCriticalSection(FCritical);
  732. end.
Advertisement
Add Comment
Please, Sign In to add comment