1 /******************************************************************************
5 * This is the implementation of a file that consists of blocks of
6 * a predetermined size.
7 * This class is used in the Compound File implementation of the
8 * IStorage and IStream interfaces. It provides the functionality
9 * to read and write any blocks in the file as well as setting and
10 * obtaining the size of the file.
11 * The blocks are indexed sequentially from the start of the file
15 * - Support for a transacted mode
17 * Copyright 1999 Thuy Nguyen
19 * This library is free software; you can redistribute it and/or
20 * modify it under the terms of the GNU Lesser General Public
21 * License as published by the Free Software Foundation; either
22 * version 2.1 of the License, or (at your option) any later version.
24 * This library is distributed in the hope that it will be useful,
25 * but WITHOUT ANY WARRANTY; without even the implied warranty of
26 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
27 * Lesser General Public License for more details.
29 * You should have received a copy of the GNU Lesser General Public
30 * License along with this library; if not, write to the Free Software
31 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
42 #define NONAMELESSUNION
43 #define NONAMELESSSTRUCT
52 #include "storage32.h"
54 #include "wine/debug.h"
56 WINE_DEFAULT_DEBUG_CHANNEL(storage
);
58 /***********************************************************
59 * Data structures used internally by the BigBlockFile
63 /* We map in PAGE_SIZE-sized chunks. Must be a multiple of 4096. */
64 #define PAGE_SIZE 131072
66 #define BLOCKS_PER_PAGE (PAGE_SIZE / BIG_BLOCK_SIZE)
68 /* We keep a list of recently-discarded pages. This controls the
69 * size of that list. */
70 #define MAX_VICTIM_PAGES 16
72 /* This structure provides one bit for each block in a page.
73 * Use BIGBLOCKFILE_{Test,Set,Clear}Bit to manipulate it. */
76 unsigned int bits
[BLOCKS_PER_PAGE
/ (CHAR_BIT
* sizeof(unsigned int))];
80 * This structure identifies the paged that are mapped
81 * from the file and their position in memory. It is
82 * also used to hold a reference count to those pages.
84 * page_index identifies which PAGE_SIZE chunk from the
85 * file this mapping represents. (The mappings are always
98 BlockBits readable_blocks
;
99 BlockBits writable_blocks
;
102 /***********************************************************
103 * Prototypes for private methods
105 static void* BIGBLOCKFILE_GetMappedView(LPBIGBLOCKFILE This
,
107 static void BIGBLOCKFILE_ReleaseMappedPage(LPBIGBLOCKFILE This
,
109 static void BIGBLOCKFILE_FreeAllMappedPages(LPBIGBLOCKFILE This
);
110 static void BIGBLOCKFILE_UnmapAllMappedPages(LPBIGBLOCKFILE This
);
111 static void BIGBLOCKFILE_RemapAllMappedPages(LPBIGBLOCKFILE This
);
112 static MappedPage
* BIGBLOCKFILE_CreatePage(LPBIGBLOCKFILE This
,
114 static DWORD
BIGBLOCKFILE_GetProtectMode(DWORD openFlags
);
115 static BOOL
BIGBLOCKFILE_FileInit(LPBIGBLOCKFILE This
, HANDLE hFile
);
116 static BOOL
BIGBLOCKFILE_MemInit(LPBIGBLOCKFILE This
, ILockBytes
* plkbyt
);
117 static void BIGBLOCKFILE_DeleteList(LPBIGBLOCKFILE This
, MappedPage
*list
);
119 /* Note that this evaluates a and b multiple times, so don't
120 * pass expressions with side effects. */
121 #define ROUND_UP(a, b) ((((a) + (b) - 1)/(b))*(b))
123 /***********************************************************
124 * Blockbits functions.
126 static inline BOOL
BIGBLOCKFILE_TestBit(const BlockBits
*bb
,
129 unsigned int array_index
= index
/ (CHAR_BIT
* sizeof(unsigned int));
130 unsigned int bit_index
= index
% (CHAR_BIT
* sizeof(unsigned int));
132 return bb
->bits
[array_index
] & (1 << bit_index
);
135 static inline void BIGBLOCKFILE_SetBit(BlockBits
*bb
, unsigned int index
)
137 unsigned int array_index
= index
/ (CHAR_BIT
* sizeof(unsigned int));
138 unsigned int bit_index
= index
% (CHAR_BIT
* sizeof(unsigned int));
140 bb
->bits
[array_index
] |= (1 << bit_index
);
143 static inline void BIGBLOCKFILE_ClearBit(BlockBits
*bb
, unsigned int index
)
145 unsigned int array_index
= index
/ (CHAR_BIT
* sizeof(unsigned int));
146 unsigned int bit_index
= index
% (CHAR_BIT
* sizeof(unsigned int));
148 bb
->bits
[array_index
] &= ~(1 << bit_index
);
151 static inline void BIGBLOCKFILE_Zero(BlockBits
*bb
)
153 memset(bb
->bits
, 0, sizeof(bb
->bits
));
156 /******************************************************************************
157 * BIGBLOCKFILE_Construct
159 * Construct a big block file. Create the file mapping object.
160 * Create the read only mapped pages list, the writable mapped page list
161 * and the blocks in use list.
163 BigBlockFile
* BIGBLOCKFILE_Construct(
172 This
= HeapAlloc(GetProcessHeap(), 0, sizeof(BigBlockFile
));
177 This
->fileBased
= fileBased
;
179 This
->flProtect
= BIGBLOCKFILE_GetProtectMode(openFlags
);
181 This
->blocksize
= blocksize
;
183 This
->maplist
= NULL
;
184 This
->victimhead
= NULL
;
185 This
->victimtail
= NULL
;
186 This
->num_victim_pages
= 0;
190 if (!BIGBLOCKFILE_FileInit(This
, hFile
))
192 HeapFree(GetProcessHeap(), 0, This
);
198 if (!BIGBLOCKFILE_MemInit(This
, pLkByt
))
200 HeapFree(GetProcessHeap(), 0, This
);
208 /******************************************************************************
209 * BIGBLOCKFILE_FileInit
211 * Initialize a big block object supported by a file.
213 static BOOL
BIGBLOCKFILE_FileInit(LPBIGBLOCKFILE This
, HANDLE hFile
)
216 This
->hbytearray
= 0;
217 This
->pbytearray
= NULL
;
221 if (This
->hfile
== INVALID_HANDLE_VALUE
)
224 This
->filesize
.u
.LowPart
= GetFileSize(This
->hfile
,
225 &This
->filesize
.u
.HighPart
);
227 if( This
->filesize
.u
.LowPart
|| This
->filesize
.u
.HighPart
)
229 /* create the file mapping object
231 This
->hfilemap
= CreateFileMappingA(This
->hfile
,
239 CloseHandle(This
->hfile
);
244 This
->hfilemap
= NULL
;
246 This
->maplist
= NULL
;
248 TRACE("file len %u\n", This
->filesize
.u
.LowPart
);
253 /******************************************************************************
254 * BIGBLOCKFILE_MemInit
256 * Initialize a big block object supported by an ILockBytes on HGLOABL.
258 static BOOL
BIGBLOCKFILE_MemInit(LPBIGBLOCKFILE This
, ILockBytes
* plkbyt
)
264 * Retrieve the handle to the byte array from the LockByte object.
266 if (GetHGlobalFromILockBytes(plkbyt
, &(This
->hbytearray
)) != S_OK
)
268 FIXME("May not be an ILockBytes on HGLOBAL\n");
272 This
->pLkbyt
= plkbyt
;
275 * Increment the reference count of the ILockByte object since
276 * we're keeping a reference to it.
278 ILockBytes_AddRef(This
->pLkbyt
);
280 This
->filesize
.u
.LowPart
= GlobalSize(This
->hbytearray
);
281 This
->filesize
.u
.HighPart
= 0;
283 This
->pbytearray
= GlobalLock(This
->hbytearray
);
285 TRACE("mem on %p len %u\n", This
->pbytearray
, This
->filesize
.u
.LowPart
);
290 /******************************************************************************
291 * BIGBLOCKFILE_Destructor
293 * Destructor. Clean up, free memory.
295 void BIGBLOCKFILE_Destructor(
298 BIGBLOCKFILE_FreeAllMappedPages(This
);
302 CloseHandle(This
->hfilemap
);
303 CloseHandle(This
->hfile
);
307 GlobalUnlock(This
->hbytearray
);
308 ILockBytes_Release(This
->pLkbyt
);
313 HeapFree(GetProcessHeap(), 0, This
);
316 /******************************************************************************
317 * BIGBLOCKFILE_EnsureExists
319 * Grows the file if necessary to make sure the block is valid.
321 void BIGBLOCKFILE_EnsureExists(LPBIGBLOCKFILE This
, ULONG index
)
324 * block index starts at -1
325 * translate to zero based index
327 if (index
== 0xffffffff)
333 * make sure that the block physically exists
335 if ((This
->blocksize
* (index
+ 1)) > This
->filesize
.u
.LowPart
)
337 ULARGE_INTEGER newSize
;
339 newSize
.u
.HighPart
= 0;
340 newSize
.u
.LowPart
= This
->blocksize
* (index
+ 1);
342 BIGBLOCKFILE_SetSize(This
, newSize
);
346 /******************************************************************************
347 * BIGBLOCKFILE_SetSize
349 * Sets the size of the file.
352 void BIGBLOCKFILE_SetSize(LPBIGBLOCKFILE This
, ULARGE_INTEGER newSize
)
354 if (This
->filesize
.u
.LowPart
== newSize
.u
.LowPart
)
357 TRACE("from %u to %u\n", This
->filesize
.u
.LowPart
, newSize
.u
.LowPart
);
359 * unmap all views, must be done before call to SetEndFile
361 * Just ditch the victim list because there is no guarantee we will need them
362 * and it is not worth the performance hit to unmap and remap them all.
364 BIGBLOCKFILE_DeleteList(This
, This
->victimhead
);
365 This
->victimhead
= NULL
;
366 This
->victimtail
= NULL
;
367 This
->num_victim_pages
= 0;
369 BIGBLOCKFILE_UnmapAllMappedPages(This
);
373 LARGE_INTEGER newpos
;
375 newpos
.QuadPart
= newSize
.QuadPart
;
376 if (SetFilePointerEx(This
->hfile
, newpos
, NULL
, FILE_BEGIN
))
378 if( This
->hfilemap
) CloseHandle(This
->hfilemap
);
380 SetEndOfFile(This
->hfile
);
383 * re-create the file mapping object
385 This
->hfilemap
= CreateFileMappingA(This
->hfile
,
394 GlobalUnlock(This
->hbytearray
);
397 * Resize the byte array object.
399 ILockBytes_SetSize(This
->pLkbyt
, newSize
);
402 * Re-acquire the handle, it may have changed.
404 GetHGlobalFromILockBytes(This
->pLkbyt
, &This
->hbytearray
);
405 This
->pbytearray
= GlobalLock(This
->hbytearray
);
408 This
->filesize
.u
.LowPart
= newSize
.u
.LowPart
;
409 This
->filesize
.u
.HighPart
= newSize
.u
.HighPart
;
411 BIGBLOCKFILE_RemapAllMappedPages(This
);
414 /******************************************************************************
415 * BIGBLOCKFILE_FindPageInList [PRIVATE]
418 static MappedPage
*BIGBLOCKFILE_FindPageInList(MappedPage
*head
,
421 for (; head
!= NULL
; head
= head
->next
)
423 if (head
->page_index
== page_index
)
425 InterlockedIncrement(&head
->refcnt
);
434 static void BIGBLOCKFILE_UnlinkPage(MappedPage
*page
)
436 if (page
->next
) page
->next
->prev
= page
->prev
;
437 if (page
->prev
) page
->prev
->next
= page
->next
;
440 static void BIGBLOCKFILE_LinkHeadPage(MappedPage
**head
, MappedPage
*page
)
442 if (*head
) (*head
)->prev
= page
;
448 /******************************************************************************
449 * BIGBLOCKFILE_GetMappedView [PRIVATE]
451 * Gets the page requested if it is already mapped.
452 * If it's not already mapped, this method will map it
454 static void * BIGBLOCKFILE_GetMappedView(
460 page
= BIGBLOCKFILE_FindPageInList(This
->maplist
, page_index
);
463 page
= BIGBLOCKFILE_FindPageInList(This
->victimhead
, page_index
);
466 This
->num_victim_pages
--;
468 BIGBLOCKFILE_Zero(&page
->readable_blocks
);
469 BIGBLOCKFILE_Zero(&page
->writable_blocks
);
475 /* If the page is not already at the head of the list, move
476 * it there. (Also moves pages from victim to main list.) */
477 if (This
->maplist
!= page
)
479 if (This
->victimhead
== page
) This
->victimhead
= page
->next
;
480 if (This
->victimtail
== page
) This
->victimtail
= page
->prev
;
482 BIGBLOCKFILE_UnlinkPage(page
);
484 BIGBLOCKFILE_LinkHeadPage(&This
->maplist
, page
);
490 page
= BIGBLOCKFILE_CreatePage(This
, page_index
);
491 if (!page
) return NULL
;
493 BIGBLOCKFILE_LinkHeadPage(&This
->maplist
, page
);
498 static BOOL
BIGBLOCKFILE_MapPage(LPBIGBLOCKFILE This
, MappedPage
*page
)
500 DWORD lowoffset
= PAGE_SIZE
* page
->page_index
;
505 DWORD desired_access
;
507 if( !This
->hfilemap
)
510 if (lowoffset
+ PAGE_SIZE
> This
->filesize
.u
.LowPart
)
511 numBytesToMap
= This
->filesize
.u
.LowPart
- lowoffset
;
513 numBytesToMap
= PAGE_SIZE
;
515 if (This
->flProtect
== PAGE_READONLY
)
516 desired_access
= FILE_MAP_READ
;
518 desired_access
= FILE_MAP_WRITE
;
520 page
->lpBytes
= MapViewOfFile(This
->hfilemap
, desired_access
, 0,
521 lowoffset
, numBytesToMap
);
522 page
->mapped_bytes
= numBytesToMap
;
526 page
->lpBytes
= (LPBYTE
)This
->pbytearray
+ lowoffset
;
527 page
->mapped_bytes
= PAGE_SIZE
;
530 TRACE("mapped page %u to %p\n", page
->page_index
, page
->lpBytes
);
532 return page
->lpBytes
!= NULL
;
535 static MappedPage
*BIGBLOCKFILE_CreatePage(LPBIGBLOCKFILE This
,
540 page
= HeapAlloc(GetProcessHeap(), 0, sizeof(MappedPage
));
544 page
->page_index
= page_index
;
550 if (!BIGBLOCKFILE_MapPage(This
, page
))
552 HeapFree(GetProcessHeap(),0,page
);
556 BIGBLOCKFILE_Zero(&page
->readable_blocks
);
557 BIGBLOCKFILE_Zero(&page
->writable_blocks
);
562 static void BIGBLOCKFILE_UnmapPage(LPBIGBLOCKFILE This
, MappedPage
*page
)
564 TRACE("%d at %p\n", page
->page_index
, page
->lpBytes
);
565 if (page
->refcnt
> 0)
566 ERR("unmapping inuse page %p\n", page
->lpBytes
);
568 if (This
->fileBased
&& page
->lpBytes
)
569 UnmapViewOfFile(page
->lpBytes
);
571 page
->lpBytes
= NULL
;
574 static void BIGBLOCKFILE_DeletePage(LPBIGBLOCKFILE This
, MappedPage
*page
)
576 BIGBLOCKFILE_UnmapPage(This
, page
);
578 HeapFree(GetProcessHeap(), 0, page
);
581 /******************************************************************************
582 * BIGBLOCKFILE_ReleaseMappedPage [PRIVATE]
584 * Decrements the reference count of the mapped page.
586 static void BIGBLOCKFILE_ReleaseMappedPage(
590 assert(This
!= NULL
);
591 assert(page
!= NULL
);
593 /* If the page is no longer refenced, move it to the victim list.
594 * If the victim list is too long, kick somebody off. */
595 if (!InterlockedDecrement(&page
->refcnt
))
597 if (This
->maplist
== page
) This
->maplist
= page
->next
;
599 BIGBLOCKFILE_UnlinkPage(page
);
601 if (MAX_VICTIM_PAGES
> 0)
603 if (This
->num_victim_pages
>= MAX_VICTIM_PAGES
)
605 MappedPage
*victim
= This
->victimtail
;
608 This
->victimtail
= victim
->prev
;
609 if (This
->victimhead
== victim
)
610 This
->victimhead
= victim
->next
;
612 BIGBLOCKFILE_UnlinkPage(victim
);
613 BIGBLOCKFILE_DeletePage(This
, victim
);
616 else This
->num_victim_pages
++;
618 BIGBLOCKFILE_LinkHeadPage(&This
->victimhead
, page
);
619 if (This
->victimtail
== NULL
) This
->victimtail
= page
;
622 BIGBLOCKFILE_DeletePage(This
, page
);
626 static void BIGBLOCKFILE_DeleteList(LPBIGBLOCKFILE This
, MappedPage
*list
)
630 MappedPage
*next
= list
->next
;
632 BIGBLOCKFILE_DeletePage(This
, list
);
638 /******************************************************************************
639 * BIGBLOCKFILE_FreeAllMappedPages [PRIVATE]
641 * Unmap all currently mapped pages.
642 * Empty mapped pages list.
644 static void BIGBLOCKFILE_FreeAllMappedPages(
647 BIGBLOCKFILE_DeleteList(This
, This
->maplist
);
648 BIGBLOCKFILE_DeleteList(This
, This
->victimhead
);
650 This
->maplist
= NULL
;
651 This
->victimhead
= NULL
;
652 This
->victimtail
= NULL
;
653 This
->num_victim_pages
= 0;
656 static void BIGBLOCKFILE_UnmapList(LPBIGBLOCKFILE This
, MappedPage
*list
)
658 for (; list
!= NULL
; list
= list
->next
)
660 BIGBLOCKFILE_UnmapPage(This
, list
);
664 static void BIGBLOCKFILE_UnmapAllMappedPages(LPBIGBLOCKFILE This
)
666 BIGBLOCKFILE_UnmapList(This
, This
->maplist
);
667 BIGBLOCKFILE_UnmapList(This
, This
->victimhead
);
670 static void BIGBLOCKFILE_RemapList(LPBIGBLOCKFILE This
, MappedPage
*list
)
674 MappedPage
*next
= list
->next
;
676 if (list
->page_index
* PAGE_SIZE
> This
->filesize
.u
.LowPart
)
678 TRACE("discarding %u\n", list
->page_index
);
680 /* page is entirely outside of the file, delete it */
681 BIGBLOCKFILE_UnlinkPage(list
);
682 BIGBLOCKFILE_DeletePage(This
, list
);
686 /* otherwise, remap it */
687 BIGBLOCKFILE_MapPage(This
, list
);
694 static void BIGBLOCKFILE_RemapAllMappedPages(LPBIGBLOCKFILE This
)
696 BIGBLOCKFILE_RemapList(This
, This
->maplist
);
697 BIGBLOCKFILE_RemapList(This
, This
->victimhead
);
700 /****************************************************************************
701 * BIGBLOCKFILE_GetProtectMode
703 * This function will return a protection mode flag for a file-mapping object
704 * from the open flags of a file.
706 static DWORD
BIGBLOCKFILE_GetProtectMode(DWORD openFlags
)
708 switch(STGM_ACCESS_MODE(openFlags
))
712 return PAGE_READWRITE
;
714 return PAGE_READONLY
;
718 /* ILockByte Interfaces */
720 /******************************************************************************
721 * This method is part of the ILockBytes interface.
723 * It reads a block of information from the byte array at the specified
726 * See the documentation of ILockBytes for more info.
728 static HRESULT WINAPI
ImplBIGBLOCKFILE_ReadAt(
729 BigBlockFile
* const This
,
730 ULARGE_INTEGER ulOffset
, /* [in] */
731 void* pv
, /* [length_is][size_is][out] */
733 ULONG
* pcbRead
) /* [out] */
735 ULONG first_page
= ulOffset
.u
.LowPart
/ PAGE_SIZE
;
736 ULONG offset_in_page
= ulOffset
.u
.LowPart
% PAGE_SIZE
;
737 ULONG bytes_left
= cb
;
738 ULONG page_index
= first_page
;
739 ULONG bytes_from_page
;
740 LPVOID writePtr
= pv
;
744 TRACE("(%p)-> %i %p %i %p\n",This
, ulOffset
.u
.LowPart
, pv
, cb
, pcbRead
);
746 /* verify a sane environment */
747 if (!This
) return E_FAIL
;
749 if (offset_in_page
+ bytes_left
> PAGE_SIZE
)
750 bytes_from_page
= PAGE_SIZE
- offset_in_page
;
752 bytes_from_page
= bytes_left
;
761 MappedPage
*page
= BIGBLOCKFILE_GetMappedView(This
, page_index
);
763 if (!page
|| !page
->lpBytes
)
765 rc
= STG_E_READFAULT
;
769 TRACE("page %i, offset %u, bytes_from_page %u, bytes_left %u\n",
770 page
->page_index
, offset_in_page
, bytes_from_page
, bytes_left
);
772 if (page
->mapped_bytes
< bytes_from_page
)
775 bytes_from_page
= page
->mapped_bytes
;
778 readPtr
= (BYTE
*)page
->lpBytes
+ offset_in_page
;
779 memcpy(writePtr
,readPtr
,bytes_from_page
);
780 BIGBLOCKFILE_ReleaseMappedPage(This
, page
);
783 *pcbRead
+= bytes_from_page
;
784 bytes_left
-= bytes_from_page
;
786 if (bytes_left
&& !eof
)
788 writePtr
= (LPBYTE
)writePtr
+ bytes_from_page
;
791 if (bytes_left
> PAGE_SIZE
)
792 bytes_from_page
= PAGE_SIZE
;
794 bytes_from_page
= bytes_left
;
798 rc
= STG_E_READFAULT
;
807 /******************************************************************************
808 * This method is part of the ILockBytes interface.
810 * It writes the specified bytes at the specified offset.
811 * position. If the file is too small, it will be resized.
813 * See the documentation of ILockBytes for more info.
815 static HRESULT WINAPI
ImplBIGBLOCKFILE_WriteAt(
816 BigBlockFile
* const This
,
817 ULARGE_INTEGER ulOffset
, /* [in] */
818 const void* pv
, /* [size_is][in] */
820 ULONG
* pcbWritten
) /* [out] */
822 ULONG size_needed
= ulOffset
.u
.LowPart
+ cb
;
823 ULONG first_page
= ulOffset
.u
.LowPart
/ PAGE_SIZE
;
824 ULONG offset_in_page
= ulOffset
.u
.LowPart
% PAGE_SIZE
;
825 ULONG bytes_left
= cb
;
826 ULONG page_index
= first_page
;
828 LPCVOID readPtr
= pv
;
832 TRACE("(%p)-> %i %p %i %p\n",This
, ulOffset
.u
.LowPart
, pv
, cb
, pcbWritten
);
834 /* verify a sane environment */
835 if (!This
) return E_FAIL
;
837 if (This
->flProtect
!= PAGE_READWRITE
)
838 return STG_E_ACCESSDENIED
;
840 if (size_needed
> This
->filesize
.u
.LowPart
)
842 ULARGE_INTEGER newSize
;
843 newSize
.u
.HighPart
= 0;
844 newSize
.u
.LowPart
= size_needed
;
845 BIGBLOCKFILE_SetSize(This
, newSize
);
848 if (offset_in_page
+ bytes_left
> PAGE_SIZE
)
849 bytes_to_page
= PAGE_SIZE
- offset_in_page
;
851 bytes_to_page
= bytes_left
;
859 MappedPage
*page
= BIGBLOCKFILE_GetMappedView(This
, page_index
);
861 TRACE("page %i, offset %u, bytes_to_page %u, bytes_left %u\n",
862 page
? page
->page_index
: 0, offset_in_page
, bytes_to_page
, bytes_left
);
866 ERR("Unable to get a page to write. This should never happen\n");
871 if (page
->mapped_bytes
< bytes_to_page
)
873 ERR("Not enough bytes mapped to the page. This should never happen\n");
878 writePtr
= (BYTE
*)page
->lpBytes
+ offset_in_page
;
879 memcpy(writePtr
,readPtr
,bytes_to_page
);
880 BIGBLOCKFILE_ReleaseMappedPage(This
, page
);
883 *pcbWritten
+= bytes_to_page
;
884 bytes_left
-= bytes_to_page
;
888 readPtr
= (const BYTE
*)readPtr
+ bytes_to_page
;
891 if (bytes_left
> PAGE_SIZE
)
892 bytes_to_page
= PAGE_SIZE
;
894 bytes_to_page
= bytes_left
;
902 HRESULT
BIGBLOCKFILE_ReadAt(LPBIGBLOCKFILE This
, ULARGE_INTEGER offset
,
903 void* buffer
, ULONG size
, ULONG
* bytesRead
)
906 return ImplBIGBLOCKFILE_ReadAt(This
,offset
,buffer
,size
,bytesRead
);
908 return ILockBytes_ReadAt(This
->pLkbyt
,offset
,buffer
,size
,bytesRead
);
911 HRESULT
BIGBLOCKFILE_WriteAt(LPBIGBLOCKFILE This
, ULARGE_INTEGER offset
,
912 const void* buffer
, ULONG size
, ULONG
* bytesRead
)
915 return ImplBIGBLOCKFILE_WriteAt(This
,offset
,buffer
,size
,bytesRead
);
917 return ILockBytes_WriteAt(This
->pLkbyt
,offset
,buffer
,size
,bytesRead
);