Home | Info | Community | Development | myReactOS | Contact Us
ReactOS Development > Doxygenstg_stream.c
Go to the documentation of this file.
00001 /* 00002 * Compound Storage (32 bit version) 00003 * Stream implementation 00004 * 00005 * This file contains the implementation of the stream interface 00006 * for streams contained in a compound storage. 00007 * 00008 * Copyright 1999 Francis Beaudet 00009 * Copyright 1999 Thuy Nguyen 00010 * 00011 * This library is free software; you can redistribute it and/or 00012 * modify it under the terms of the GNU Lesser General Public 00013 * License as published by the Free Software Foundation; either 00014 * version 2.1 of the License, or (at your option) any later version. 00015 * 00016 * This library is distributed in the hope that it will be useful, 00017 * but WITHOUT ANY WARRANTY; without even the implied warranty of 00018 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 00019 * Lesser General Public License for more details. 00020 * 00021 * You should have received a copy of the GNU Lesser General Public 00022 * License along with this library; if not, write to the Free Software 00023 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA 00024 */ 00025 00026 #include <assert.h> 00027 #include <stdlib.h> 00028 #include <stdarg.h> 00029 #include <stdio.h> 00030 #include <string.h> 00031 00032 #define COBJMACROS 00033 #define NONAMELESSUNION 00034 #define NONAMELESSSTRUCT 00035 00036 #include "windef.h" 00037 #include "winbase.h" 00038 #include "winerror.h" 00039 #include "winternl.h" 00040 #include "wine/debug.h" 00041 00042 #include "storage32.h" 00043 00044 WINE_DEFAULT_DEBUG_CHANNEL(storage); 00045 00046 00047 /*** 00048 * This is the destructor of the StgStreamImpl class. 00049 * 00050 * This method will clean-up all the resources used-up by the given StgStreamImpl 00051 * class. The pointer passed-in to this function will be freed and will not 00052 * be valid anymore. 00053 */ 00054 static void StgStreamImpl_Destroy(StgStreamImpl* This) 00055 { 00056 TRACE("(%p)\n", This); 00057 00058 /* 00059 * Release the reference we are holding on the parent storage. 00060 * IStorage_Release((IStorage*)This->parentStorage); 00061 * 00062 * No, don't do this. Some apps call IStorage_Release without 00063 * calling IStream_Release first. If we grab a reference the 00064 * file is not closed, and the app fails when it tries to 00065 * reopen the file (Easy-PC, for example). Just inform the 00066 * storage that we have closed the stream 00067 */ 00068 00069 if(This->parentStorage) { 00070 00071 StorageBaseImpl_RemoveStream(This->parentStorage, This); 00072 00073 } 00074 00075 This->parentStorage = 0; 00076 00077 /* 00078 * Finally, free the memory used-up by the class. 00079 */ 00080 HeapFree(GetProcessHeap(), 0, This); 00081 } 00082 00083 /*** 00084 * This implements the IUnknown method QueryInterface for this 00085 * class 00086 */ 00087 static HRESULT WINAPI StgStreamImpl_QueryInterface( 00088 IStream* iface, 00089 REFIID riid, /* [in] */ 00090 void** ppvObject) /* [iid_is][out] */ 00091 { 00092 StgStreamImpl* const This=(StgStreamImpl*)iface; 00093 00094 /* 00095 * Perform a sanity check on the parameters. 00096 */ 00097 if (ppvObject==0) 00098 return E_INVALIDARG; 00099 00100 /* 00101 * Initialize the return parameter. 00102 */ 00103 *ppvObject = 0; 00104 00105 /* 00106 * Compare the riid with the interface IDs implemented by this object. 00107 */ 00108 if (IsEqualIID(&IID_IUnknown, riid) || 00109 IsEqualIID(&IID_IPersist, riid) || 00110 IsEqualIID(&IID_IPersistStream, riid) || 00111 IsEqualIID(&IID_ISequentialStream, riid) || 00112 IsEqualIID(&IID_IStream, riid)) 00113 { 00114 *ppvObject = This; 00115 } 00116 00117 /* 00118 * Check that we obtained an interface. 00119 */ 00120 if ((*ppvObject)==0) 00121 return E_NOINTERFACE; 00122 00123 /* 00124 * Query Interface always increases the reference count by one when it is 00125 * successful 00126 */ 00127 IStream_AddRef(iface); 00128 00129 return S_OK; 00130 } 00131 00132 /*** 00133 * This implements the IUnknown method AddRef for this 00134 * class 00135 */ 00136 static ULONG WINAPI StgStreamImpl_AddRef( 00137 IStream* iface) 00138 { 00139 StgStreamImpl* const This=(StgStreamImpl*)iface; 00140 return InterlockedIncrement(&This->ref); 00141 } 00142 00143 /*** 00144 * This implements the IUnknown method Release for this 00145 * class 00146 */ 00147 static ULONG WINAPI StgStreamImpl_Release( 00148 IStream* iface) 00149 { 00150 StgStreamImpl* const This=(StgStreamImpl*)iface; 00151 00152 ULONG ref; 00153 00154 ref = InterlockedDecrement(&This->ref); 00155 00156 /* 00157 * If the reference count goes down to 0, perform suicide. 00158 */ 00159 if (ref==0) 00160 { 00161 StgStreamImpl_Destroy(This); 00162 } 00163 00164 return ref; 00165 } 00166 00167 /*** 00168 * This method is part of the ISequentialStream interface. 00169 * 00170 * It reads a block of information from the stream at the current 00171 * position. It then moves the current position at the end of the 00172 * read block 00173 * 00174 * See the documentation of ISequentialStream for more info. 00175 */ 00176 static HRESULT WINAPI StgStreamImpl_Read( 00177 IStream* iface, 00178 void* pv, /* [length_is][size_is][out] */ 00179 ULONG cb, /* [in] */ 00180 ULONG* pcbRead) /* [out] */ 00181 { 00182 StgStreamImpl* const This=(StgStreamImpl*)iface; 00183 00184 ULONG bytesReadBuffer; 00185 HRESULT res; 00186 00187 TRACE("(%p, %p, %d, %p)\n", 00188 iface, pv, cb, pcbRead); 00189 00190 if (!This->parentStorage) 00191 { 00192 WARN("storage reverted\n"); 00193 return STG_E_REVERTED; 00194 } 00195 00196 /* 00197 * If the caller is not interested in the number of bytes read, 00198 * we use another buffer to avoid "if" statements in the code. 00199 */ 00200 if (pcbRead==0) 00201 pcbRead = &bytesReadBuffer; 00202 00203 res = StorageBaseImpl_StreamReadAt(This->parentStorage, 00204 This->dirEntry, 00205 This->currentPosition, 00206 cb, 00207 pv, 00208 pcbRead); 00209 00210 if (SUCCEEDED(res)) 00211 { 00212 /* 00213 * Advance the pointer for the number of positions read. 00214 */ 00215 This->currentPosition.u.LowPart += *pcbRead; 00216 } 00217 00218 TRACE("<-- %08x\n", res); 00219 return res; 00220 } 00221 00222 /*** 00223 * This method is part of the ISequentialStream interface. 00224 * 00225 * It writes a block of information to the stream at the current 00226 * position. It then moves the current position at the end of the 00227 * written block. If the stream is too small to fit the block, 00228 * the stream is grown to fit. 00229 * 00230 * See the documentation of ISequentialStream for more info. 00231 */ 00232 static HRESULT WINAPI StgStreamImpl_Write( 00233 IStream* iface, 00234 const void* pv, /* [size_is][in] */ 00235 ULONG cb, /* [in] */ 00236 ULONG* pcbWritten) /* [out] */ 00237 { 00238 StgStreamImpl* const This=(StgStreamImpl*)iface; 00239 00240 ULONG bytesWritten = 0; 00241 HRESULT res; 00242 00243 TRACE("(%p, %p, %d, %p)\n", 00244 iface, pv, cb, pcbWritten); 00245 00246 /* 00247 * Do we have permission to write to this stream? 00248 */ 00249 switch(STGM_ACCESS_MODE(This->grfMode)) 00250 { 00251 case STGM_WRITE: 00252 case STGM_READWRITE: 00253 break; 00254 default: 00255 WARN("access denied by flags: 0x%x\n", STGM_ACCESS_MODE(This->grfMode)); 00256 return STG_E_ACCESSDENIED; 00257 } 00258 00259 if (!pv) 00260 return STG_E_INVALIDPOINTER; 00261 00262 if (!This->parentStorage) 00263 { 00264 WARN("storage reverted\n"); 00265 return STG_E_REVERTED; 00266 } 00267 00268 /* 00269 * If the caller is not interested in the number of bytes written, 00270 * we use another buffer to avoid "if" statements in the code. 00271 */ 00272 if (pcbWritten == 0) 00273 pcbWritten = &bytesWritten; 00274 00275 /* 00276 * Initialize the out parameter 00277 */ 00278 *pcbWritten = 0; 00279 00280 if (cb == 0) 00281 { 00282 TRACE("<-- S_OK, written 0\n"); 00283 return S_OK; 00284 } 00285 00286 res = StorageBaseImpl_StreamWriteAt(This->parentStorage, 00287 This->dirEntry, 00288 This->currentPosition, 00289 cb, 00290 pv, 00291 pcbWritten); 00292 00293 /* 00294 * Advance the position pointer for the number of positions written. 00295 */ 00296 This->currentPosition.u.LowPart += *pcbWritten; 00297 00298 if (SUCCEEDED(res)) 00299 res = StorageBaseImpl_Flush(This->parentStorage); 00300 00301 TRACE("<-- S_OK, written %u\n", *pcbWritten); 00302 return res; 00303 } 00304 00305 /*** 00306 * This method is part of the IStream interface. 00307 * 00308 * It will move the current stream pointer according to the parameters 00309 * given. 00310 * 00311 * See the documentation of IStream for more info. 00312 */ 00313 static HRESULT WINAPI StgStreamImpl_Seek( 00314 IStream* iface, 00315 LARGE_INTEGER dlibMove, /* [in] */ 00316 DWORD dwOrigin, /* [in] */ 00317 ULARGE_INTEGER* plibNewPosition) /* [out] */ 00318 { 00319 StgStreamImpl* const This=(StgStreamImpl*)iface; 00320 00321 ULARGE_INTEGER newPosition; 00322 DirEntry currentEntry; 00323 HRESULT hr; 00324 00325 TRACE("(%p, %d, %d, %p)\n", 00326 iface, dlibMove.u.LowPart, dwOrigin, plibNewPosition); 00327 00328 /* 00329 * fail if the stream has no parent (as does windows) 00330 */ 00331 00332 if (!This->parentStorage) 00333 { 00334 WARN("storage reverted\n"); 00335 return STG_E_REVERTED; 00336 } 00337 00338 /* 00339 * The caller is allowed to pass in NULL as the new position return value. 00340 * If it happens, we assign it to a dynamic variable to avoid special cases 00341 * in the code below. 00342 */ 00343 if (plibNewPosition == 0) 00344 { 00345 plibNewPosition = &newPosition; 00346 } 00347 00348 /* 00349 * The file pointer is moved depending on the given "function" 00350 * parameter. 00351 */ 00352 switch (dwOrigin) 00353 { 00354 case STREAM_SEEK_SET: 00355 plibNewPosition->u.HighPart = 0; 00356 plibNewPosition->u.LowPart = 0; 00357 break; 00358 case STREAM_SEEK_CUR: 00359 *plibNewPosition = This->currentPosition; 00360 break; 00361 case STREAM_SEEK_END: 00362 hr = StorageBaseImpl_ReadDirEntry(This->parentStorage, This->dirEntry, ¤tEntry); 00363 if (FAILED(hr)) return hr; 00364 *plibNewPosition = currentEntry.size; 00365 break; 00366 default: 00367 WARN("invalid dwOrigin %d\n", dwOrigin); 00368 return STG_E_INVALIDFUNCTION; 00369 } 00370 00371 plibNewPosition->QuadPart += dlibMove.QuadPart; 00372 00373 /* 00374 * tell the caller what we calculated 00375 */ 00376 This->currentPosition = *plibNewPosition; 00377 00378 return S_OK; 00379 } 00380 00381 /*** 00382 * This method is part of the IStream interface. 00383 * 00384 * It will change the size of a stream. 00385 * 00386 * See the documentation of IStream for more info. 00387 */ 00388 static HRESULT WINAPI StgStreamImpl_SetSize( 00389 IStream* iface, 00390 ULARGE_INTEGER libNewSize) /* [in] */ 00391 { 00392 StgStreamImpl* const This=(StgStreamImpl*)iface; 00393 00394 HRESULT hr; 00395 00396 TRACE("(%p, %d)\n", iface, libNewSize.u.LowPart); 00397 00398 if(!This->parentStorage) 00399 { 00400 WARN("storage reverted\n"); 00401 return STG_E_REVERTED; 00402 } 00403 00404 /* 00405 * As documented. 00406 */ 00407 if (libNewSize.u.HighPart != 0) 00408 { 00409 WARN("invalid value for libNewSize.u.HighPart %d\n", libNewSize.u.HighPart); 00410 return STG_E_INVALIDFUNCTION; 00411 } 00412 00413 /* 00414 * Do we have permission? 00415 */ 00416 if (!(This->grfMode & (STGM_WRITE | STGM_READWRITE))) 00417 { 00418 WARN("access denied\n"); 00419 return STG_E_ACCESSDENIED; 00420 } 00421 00422 hr = StorageBaseImpl_StreamSetSize(This->parentStorage, This->dirEntry, libNewSize); 00423 00424 if (SUCCEEDED(hr)) 00425 hr = StorageBaseImpl_Flush(This->parentStorage); 00426 00427 return hr; 00428 } 00429 00430 /*** 00431 * This method is part of the IStream interface. 00432 * 00433 * It will copy the 'cb' Bytes to 'pstm' IStream. 00434 * 00435 * See the documentation of IStream for more info. 00436 */ 00437 static HRESULT WINAPI StgStreamImpl_CopyTo( 00438 IStream* iface, 00439 IStream* pstm, /* [unique][in] */ 00440 ULARGE_INTEGER cb, /* [in] */ 00441 ULARGE_INTEGER* pcbRead, /* [out] */ 00442 ULARGE_INTEGER* pcbWritten) /* [out] */ 00443 { 00444 StgStreamImpl* const This=(StgStreamImpl*)iface; 00445 HRESULT hr = S_OK; 00446 BYTE tmpBuffer[128]; 00447 ULONG bytesRead, bytesWritten, copySize; 00448 ULARGE_INTEGER totalBytesRead; 00449 ULARGE_INTEGER totalBytesWritten; 00450 00451 TRACE("(%p, %p, %d, %p, %p)\n", 00452 iface, pstm, cb.u.LowPart, pcbRead, pcbWritten); 00453 00454 /* 00455 * Sanity check 00456 */ 00457 00458 if (!This->parentStorage) 00459 { 00460 WARN("storage reverted\n"); 00461 return STG_E_REVERTED; 00462 } 00463 00464 if ( pstm == 0 ) 00465 return STG_E_INVALIDPOINTER; 00466 00467 totalBytesRead.QuadPart = 0; 00468 totalBytesWritten.QuadPart = 0; 00469 00470 while ( cb.QuadPart > 0 ) 00471 { 00472 if ( cb.QuadPart >= sizeof(tmpBuffer) ) 00473 copySize = sizeof(tmpBuffer); 00474 else 00475 copySize = cb.u.LowPart; 00476 00477 IStream_Read(iface, tmpBuffer, copySize, &bytesRead); 00478 00479 totalBytesRead.QuadPart += bytesRead; 00480 00481 IStream_Write(pstm, tmpBuffer, bytesRead, &bytesWritten); 00482 00483 totalBytesWritten.QuadPart += bytesWritten; 00484 00485 /* 00486 * Check that read & write operations were successful 00487 */ 00488 if (bytesRead != bytesWritten) 00489 { 00490 hr = STG_E_MEDIUMFULL; 00491 WARN("medium full\n"); 00492 break; 00493 } 00494 00495 if (bytesRead!=copySize) 00496 cb.QuadPart = 0; 00497 else 00498 cb.QuadPart -= bytesRead; 00499 } 00500 00501 if (pcbRead) pcbRead->QuadPart = totalBytesRead.QuadPart; 00502 if (pcbWritten) pcbWritten->QuadPart = totalBytesWritten.QuadPart; 00503 00504 return hr; 00505 } 00506 00507 /*** 00508 * This method is part of the IStream interface. 00509 * 00510 * For streams contained in structured storages, this method 00511 * does nothing. This is what the documentation tells us. 00512 * 00513 * See the documentation of IStream for more info. 00514 */ 00515 static HRESULT WINAPI StgStreamImpl_Commit( 00516 IStream* iface, 00517 DWORD grfCommitFlags) /* [in] */ 00518 { 00519 StgStreamImpl* const This=(StgStreamImpl*)iface; 00520 00521 if (!This->parentStorage) 00522 { 00523 WARN("storage reverted\n"); 00524 return STG_E_REVERTED; 00525 } 00526 00527 return StorageBaseImpl_Flush(This->parentStorage); 00528 } 00529 00530 /*** 00531 * This method is part of the IStream interface. 00532 * 00533 * For streams contained in structured storages, this method 00534 * does nothing. This is what the documentation tells us. 00535 * 00536 * See the documentation of IStream for more info. 00537 */ 00538 static HRESULT WINAPI StgStreamImpl_Revert( 00539 IStream* iface) 00540 { 00541 return S_OK; 00542 } 00543 00544 static HRESULT WINAPI StgStreamImpl_LockRegion( 00545 IStream* iface, 00546 ULARGE_INTEGER libOffset, /* [in] */ 00547 ULARGE_INTEGER cb, /* [in] */ 00548 DWORD dwLockType) /* [in] */ 00549 { 00550 StgStreamImpl* const This=(StgStreamImpl*)iface; 00551 00552 if (!This->parentStorage) 00553 { 00554 WARN("storage reverted\n"); 00555 return STG_E_REVERTED; 00556 } 00557 00558 FIXME("not implemented!\n"); 00559 return E_NOTIMPL; 00560 } 00561 00562 static HRESULT WINAPI StgStreamImpl_UnlockRegion( 00563 IStream* iface, 00564 ULARGE_INTEGER libOffset, /* [in] */ 00565 ULARGE_INTEGER cb, /* [in] */ 00566 DWORD dwLockType) /* [in] */ 00567 { 00568 StgStreamImpl* const This=(StgStreamImpl*)iface; 00569 00570 if (!This->parentStorage) 00571 { 00572 WARN("storage reverted\n"); 00573 return STG_E_REVERTED; 00574 } 00575 00576 FIXME("not implemented!\n"); 00577 return E_NOTIMPL; 00578 } 00579 00580 /*** 00581 * This method is part of the IStream interface. 00582 * 00583 * This method returns information about the current 00584 * stream. 00585 * 00586 * See the documentation of IStream for more info. 00587 */ 00588 static HRESULT WINAPI StgStreamImpl_Stat( 00589 IStream* iface, 00590 STATSTG* pstatstg, /* [out] */ 00591 DWORD grfStatFlag) /* [in] */ 00592 { 00593 StgStreamImpl* const This=(StgStreamImpl*)iface; 00594 00595 DirEntry currentEntry; 00596 HRESULT hr; 00597 00598 TRACE("%p %p %d\n", This, pstatstg, grfStatFlag); 00599 00600 /* 00601 * if stream has no parent, return STG_E_REVERTED 00602 */ 00603 00604 if (!This->parentStorage) 00605 { 00606 WARN("storage reverted\n"); 00607 return STG_E_REVERTED; 00608 } 00609 00610 /* 00611 * Read the information from the directory entry. 00612 */ 00613 hr = StorageBaseImpl_ReadDirEntry(This->parentStorage, 00614 This->dirEntry, 00615 ¤tEntry); 00616 00617 if (SUCCEEDED(hr)) 00618 { 00619 StorageUtl_CopyDirEntryToSTATSTG(This->parentStorage, 00620 pstatstg, 00621 ¤tEntry, 00622 grfStatFlag); 00623 00624 pstatstg->grfMode = This->grfMode; 00625 00626 /* In simple create mode cbSize is the current pos */ 00627 if((This->parentStorage->openFlags & STGM_SIMPLE) && This->parentStorage->create) 00628 pstatstg->cbSize = This->currentPosition; 00629 00630 return S_OK; 00631 } 00632 00633 WARN("failed to read entry\n"); 00634 return hr; 00635 } 00636 00637 /*** 00638 * This method is part of the IStream interface. 00639 * 00640 * This method returns a clone of the interface that allows for 00641 * another seek pointer 00642 * 00643 * See the documentation of IStream for more info. 00644 * 00645 * I am not totally sure what I am doing here but I presume that this 00646 * should be basically as simple as creating a new stream with the same 00647 * parent etc and positioning its seek cursor. 00648 */ 00649 static HRESULT WINAPI StgStreamImpl_Clone( 00650 IStream* iface, 00651 IStream** ppstm) /* [out] */ 00652 { 00653 StgStreamImpl* const This=(StgStreamImpl*)iface; 00654 HRESULT hres; 00655 StgStreamImpl* new_stream; 00656 LARGE_INTEGER seek_pos; 00657 00658 TRACE("%p %p\n", This, ppstm); 00659 00660 /* 00661 * Sanity check 00662 */ 00663 00664 if (!This->parentStorage) 00665 return STG_E_REVERTED; 00666 00667 if ( ppstm == 0 ) 00668 return STG_E_INVALIDPOINTER; 00669 00670 new_stream = StgStreamImpl_Construct (This->parentStorage, This->grfMode, This->dirEntry); 00671 00672 if (!new_stream) 00673 return STG_E_INSUFFICIENTMEMORY; /* Currently the only reason for new_stream=0 */ 00674 00675 *ppstm = (IStream*) new_stream; 00676 IStream_AddRef(*ppstm); 00677 00678 seek_pos.QuadPart = This->currentPosition.QuadPart; 00679 00680 hres=StgStreamImpl_Seek (*ppstm, seek_pos, STREAM_SEEK_SET, NULL); 00681 00682 assert (SUCCEEDED(hres)); 00683 00684 return S_OK; 00685 } 00686 00687 /* 00688 * Virtual function table for the StgStreamImpl class. 00689 */ 00690 static const IStreamVtbl StgStreamImpl_Vtbl = 00691 { 00692 StgStreamImpl_QueryInterface, 00693 StgStreamImpl_AddRef, 00694 StgStreamImpl_Release, 00695 StgStreamImpl_Read, 00696 StgStreamImpl_Write, 00697 StgStreamImpl_Seek, 00698 StgStreamImpl_SetSize, 00699 StgStreamImpl_CopyTo, 00700 StgStreamImpl_Commit, 00701 StgStreamImpl_Revert, 00702 StgStreamImpl_LockRegion, 00703 StgStreamImpl_UnlockRegion, 00704 StgStreamImpl_Stat, 00705 StgStreamImpl_Clone 00706 }; 00707 00708 /****************************************************************************** 00709 ** StgStreamImpl implementation 00710 */ 00711 00712 /*** 00713 * This is the constructor for the StgStreamImpl class. 00714 * 00715 * Params: 00716 * parentStorage - Pointer to the storage that contains the stream to open 00717 * dirEntry - Index of the directory entry that points to this stream. 00718 */ 00719 StgStreamImpl* StgStreamImpl_Construct( 00720 StorageBaseImpl* parentStorage, 00721 DWORD grfMode, 00722 DirRef dirEntry) 00723 { 00724 StgStreamImpl* newStream; 00725 00726 newStream = HeapAlloc(GetProcessHeap(), 0, sizeof(StgStreamImpl)); 00727 00728 if (newStream!=0) 00729 { 00730 /* 00731 * Set-up the virtual function table and reference count. 00732 */ 00733 newStream->lpVtbl = &StgStreamImpl_Vtbl; 00734 newStream->ref = 0; 00735 00736 newStream->parentStorage = parentStorage; 00737 00738 /* 00739 * We want to nail-down the reference to the storage in case the 00740 * stream out-lives the storage in the client application. 00741 * 00742 * -- IStorage_AddRef((IStorage*)newStream->parentStorage); 00743 * 00744 * No, don't do this. Some apps call IStorage_Release without 00745 * calling IStream_Release first. If we grab a reference the 00746 * file is not closed, and the app fails when it tries to 00747 * reopen the file (Easy-PC, for example) 00748 */ 00749 00750 newStream->grfMode = grfMode; 00751 newStream->dirEntry = dirEntry; 00752 00753 /* 00754 * Start the stream at the beginning. 00755 */ 00756 newStream->currentPosition.u.HighPart = 0; 00757 newStream->currentPosition.u.LowPart = 0; 00758 00759 /* add us to the storage's list of active streams */ 00760 StorageBaseImpl_AddStream(parentStorage, newStream); 00761 } 00762 00763 return newStream; 00764 } Generated on Thu May 24 2012 04:26:06 for ReactOS by
1.7.6.1
|