3 * Implemented using the documentation of the LAOLA project at
4 * <URL:http://wwwwbs.cs.tu-berlin.de/~schwartz/pmh/index.html>
5 * (Thanks to Martin Schwartz <schwartz@cs.tu-berlin.de>)
7 * Copyright 1998 Marcus Meissner
16 #include <ddk/ntddk.h>
17 #include <ole32/ole32.h>
19 #include <storage32.h>
23 struct storage_header {
24 BYTE magic[8]; /* 00: magic */
25 BYTE unknown1[36]; /* 08: unknown */
26 DWORD num_of_bbd_blocks;/* 2C: length of big datablocks */
27 DWORD root_startblock;/* 30: root storage first big block */
28 DWORD unknown2[2]; /* 34: unknown */
29 DWORD sbd_startblock; /* 3C: small block depot first big block */
30 DWORD unknown3[3]; /* 40: unknown */
31 DWORD bbd_list[109]; /* 4C: big data block list (up to end of sector)*/
33 struct storage_pps_entry {
34 WCHAR pps_rawname[32];/* 00: \0 terminated widechar name */
35 WORD pps_sizeofname; /* 40: namelength in bytes */
36 BYTE pps_type; /* 42: flags, 1 storage/dir, 2 stream, 5 root */
37 BYTE pps_unknown0; /* 43: unknown */
38 DWORD pps_prev; /* 44: previous pps */
39 DWORD pps_next; /* 48: next pps */
40 DWORD pps_dir; /* 4C: directory pps */
41 GUID pps_guid; /* 50: class ID */
42 DWORD pps_unknown1; /* 60: unknown */
43 FILETIME pps_ft1; /* 64: filetime1 */
44 FILETIME pps_ft2; /* 70: filetime2 */
45 DWORD pps_sb; /* 74: data startblock */
46 DWORD pps_size; /* 78: datalength. (<0x1000)?small:big blocks*/
47 DWORD pps_unknown2; /* 7C: unknown */
50 #define STORAGE_CHAINENTRY_FAT 0xfffffffd
51 #define STORAGE_CHAINENTRY_ENDOFCHAIN 0xfffffffe
52 #define STORAGE_CHAINENTRY_FREE 0xffffffff
55 //static const BYTE STORAGE_magic[8] ={0xd0,0xcf,0x11,0xe0,0xa1,0xb1,0x1a,0xe1};
56 //static const BYTE STORAGE_notmagic[8]={0x0e,0x11,0xfc,0x0d,0xd0,0xcf,0x11,0xe0};
57 //static const BYTE STORAGE_oldmagic[8]={0xd0,0xcf,0x11,0xe0,0x0e,0x11,0xfc,0x0d};
62 #define SMALLBLOCKS_PER_BIGBLOCK (BIGSIZE/SMALLSIZE)
64 #define READ_HEADER assert(STORAGE_get_big_block(hf,-1,(LPBYTE)&sth));assert(!memcmp(STORAGE_magic,sth.magic,sizeof(STORAGE_magic)));
65 static ICOM_VTABLE(IStorage16) stvt16;
66 static ICOM_VTABLE(IStorage16) *segstvt16 = NULL;
67 static ICOM_VTABLE(IStream16) strvt16;
68 static ICOM_VTABLE(IStream16) *segstrvt16 = NULL;
70 /*ULONG WINAPI IStorage16_AddRef(LPSTORAGE16 this);*/
71 static void _create_istorage16(LPSTORAGE16 *stg);
72 static void _create_istream16(LPSTREAM16 *str);
77 /******************************************************************************
78 * STORAGE_get_big_block [Internal]
80 * Reading OLE compound storage
83 STORAGE_get_big_block(HFILE hf,int n,BYTE *block) {
85 if (-1==_llseek(hf,(n+1)*BIGSIZE,SEEK_SET)) {
86 Print(MID_TRACE, (" seek failed (%ld)\n",GetLastError()));
89 assert((n+1)*BIGSIZE==_llseek(hf,0,SEEK_CUR));
90 if (BIGSIZE!=_lread(hf,block,BIGSIZE)) {
91 Print(MID_TRACE, ("(block size %d): read didn't read (%ld)\n",n,GetLastError()));
98 /******************************************************************************
99 * STORAGE_put_big_block [INTERNAL]
102 STORAGE_put_big_block(HFILE hf,int n,BYTE *block) {
104 if (-1==_llseek(hf,(n+1)*BIGSIZE,SEEK_SET)) {
105 Print(MID_TRACE, (" seek failed (%ld)\n",GetLastError()));
108 assert((n+1)*BIGSIZE==_llseek(hf,0,SEEK_CUR));
109 if (BIGSIZE!=_lwrite(hf,block,BIGSIZE)) {
110 Print(MID_TRACE, (" write failed (%ld)\n",GetLastError()));
116 /******************************************************************************
117 * STORAGE_get_next_big_blocknr [INTERNAL]
120 STORAGE_get_next_big_blocknr(HFILE hf,int blocknr) {
121 INT bbs[BIGSIZE/sizeof(INT)];
122 struct storage_header sth;
126 assert(blocknr>>7<sth.num_of_bbd_blocks);
127 if (sth.bbd_list[blocknr>>7]==0xffffffff)
129 if (!STORAGE_get_big_block(hf,sth.bbd_list[blocknr>>7],(LPBYTE)bbs))
131 assert(bbs[blocknr&0x7f]!=STORAGE_CHAINENTRY_FREE);
132 return bbs[blocknr&0x7f];
135 /******************************************************************************
136 * STORAGE_get_nth_next_big_blocknr [INTERNAL]
139 STORAGE_get_nth_next_big_blocknr(HFILE hf,int blocknr,int nr) {
140 INT bbs[BIGSIZE/sizeof(INT)];
142 struct storage_header sth;
148 assert((blocknr>>7)<sth.num_of_bbd_blocks);
149 assert(sth.bbd_list[blocknr>>7]!=0xffffffff);
151 /* simple caching... */
152 if (lastblock!=sth.bbd_list[blocknr>>7]) {
153 assert(STORAGE_get_big_block(hf,sth.bbd_list[blocknr>>7],(LPBYTE)bbs));
154 lastblock = sth.bbd_list[blocknr>>7];
156 blocknr = bbs[blocknr&0x7f];
161 /******************************************************************************
162 * STORAGE_get_root_pps_entry [Internal]
165 STORAGE_get_root_pps_entry(HFILE hf,struct storage_pps_entry *pstde) {
168 struct storage_pps_entry *stde=(struct storage_pps_entry*)block;
169 struct storage_header sth;
172 blocknr = sth.root_startblock;
174 assert(STORAGE_get_big_block(hf,blocknr,block));
176 if (!stde[i].pps_sizeofname)
178 if (stde[i].pps_type==5) {
183 blocknr=STORAGE_get_next_big_blocknr(hf,blocknr);
188 /******************************************************************************
189 * STORAGE_get_small_block [INTERNAL]
192 STORAGE_get_small_block(HFILE hf,int blocknr,BYTE *sblock) {
195 struct storage_pps_entry root;
198 assert(STORAGE_get_root_pps_entry(hf,&root));
199 bigblocknr = STORAGE_get_nth_next_big_blocknr(hf,root.pps_sb,blocknr/SMALLBLOCKS_PER_BIGBLOCK);
200 assert(bigblocknr>=0);
201 assert(STORAGE_get_big_block(hf,bigblocknr,block));
203 memcpy(sblock,((LPBYTE)block)+SMALLSIZE*(blocknr&(SMALLBLOCKS_PER_BIGBLOCK-1)),SMALLSIZE);
207 /******************************************************************************
208 * STORAGE_put_small_block [INTERNAL]
211 STORAGE_put_small_block(HFILE hf,int blocknr,BYTE *sblock) {
214 struct storage_pps_entry root;
218 assert(STORAGE_get_root_pps_entry(hf,&root));
219 bigblocknr = STORAGE_get_nth_next_big_blocknr(hf,root.pps_sb,blocknr/SMALLBLOCKS_PER_BIGBLOCK);
220 assert(bigblocknr>=0);
221 assert(STORAGE_get_big_block(hf,bigblocknr,block));
223 memcpy(((LPBYTE)block)+SMALLSIZE*(blocknr&(SMALLBLOCKS_PER_BIGBLOCK-1)),sblock,SMALLSIZE);
224 assert(STORAGE_put_big_block(hf,bigblocknr,block));
228 /******************************************************************************
229 * STORAGE_get_next_small_blocknr [INTERNAL]
232 STORAGE_get_next_small_blocknr(HFILE hf,int blocknr) {
234 LPINT sbd = (LPINT)block;
236 struct storage_header sth;
240 bigblocknr = STORAGE_get_nth_next_big_blocknr(hf,sth.sbd_startblock,blocknr/128);
241 assert(bigblocknr>=0);
242 assert(STORAGE_get_big_block(hf,bigblocknr,block));
243 assert(sbd[blocknr & 127]!=STORAGE_CHAINENTRY_FREE);
244 return sbd[blocknr & (128-1)];
247 /******************************************************************************
248 * STORAGE_get_nth_next_small_blocknr [INTERNAL]
251 STORAGE_get_nth_next_small_blocknr(HFILE hf,int blocknr,int nr) {
254 LPINT sbd = (LPINT)block;
255 struct storage_header sth;
260 while ((nr--) && (blocknr>=0)) {
261 if (lastblocknr/128!=blocknr/128) {
263 bigblocknr = STORAGE_get_nth_next_big_blocknr(hf,sth.sbd_startblock,blocknr/128);
264 assert(bigblocknr>=0);
265 assert(STORAGE_get_big_block(hf,bigblocknr,block));
266 lastblocknr = blocknr;
268 assert(lastblocknr>=0);
270 blocknr=sbd[blocknr & (128-1)];
271 assert(blocknr!=STORAGE_CHAINENTRY_FREE);
276 /******************************************************************************
277 * STORAGE_get_pps_entry [INTERNAL]
280 STORAGE_get_pps_entry(HFILE hf,int n,struct storage_pps_entry *pstde) {
283 struct storage_pps_entry *stde = (struct storage_pps_entry*)(((LPBYTE)block)+128*(n&3));
284 struct storage_header sth;
287 /* we have 4 pps entries per big block */
288 blocknr = STORAGE_get_nth_next_big_blocknr(hf,sth.root_startblock,n/4);
290 assert(STORAGE_get_big_block(hf,blocknr,block));
296 /******************************************************************************
297 * STORAGE_put_pps_entry [Internal]
300 STORAGE_put_pps_entry(HFILE hf,int n,struct storage_pps_entry *pstde) {
303 struct storage_pps_entry *stde = (struct storage_pps_entry*)(((LPBYTE)block)+128*(n&3));
304 struct storage_header sth;
308 /* we have 4 pps entries per big block */
309 blocknr = STORAGE_get_nth_next_big_blocknr(hf,sth.root_startblock,n/4);
311 assert(STORAGE_get_big_block(hf,blocknr,block));
313 assert(STORAGE_put_big_block(hf,blocknr,block));
317 /******************************************************************************
318 * STORAGE_look_for_named_pps [Internal]
321 STORAGE_look_for_named_pps(HFILE hf,int n,LPOLESTR name) {
322 struct storage_pps_entry stde;
327 if (1!=STORAGE_get_pps_entry(hf,n,&stde))
330 if (!lstrcmpW(name,stde.pps_rawname))
332 if (stde.pps_prev != -1) {
333 ret=STORAGE_look_for_named_pps(hf,stde.pps_prev,name);
337 if (stde.pps_next != -1) {
338 ret=STORAGE_look_for_named_pps(hf,stde.pps_next,name);
345 /******************************************************************************
346 * STORAGE_dump_pps_entry [Internal]
352 STORAGE_dump_pps_entry(struct storage_pps_entry *stde) {
356 WideCharToMultiByte( CP_ACP, 0, stde->pps_rawname, -1, name, sizeof(name), NULL, NULL);
357 if (!stde->pps_sizeofname)
359 Print(MAX_TRACE, ("name: %s\n",name));
360 Print(MAX_TRACE, ("type: %d\n",stde->pps_type));
361 Print(MAX_TRACE, ("prev pps: %ld\n",stde->pps_prev));
362 Print(MAX_TRACE, ("next pps: %ld\n",stde->pps_next));
363 Print(MAX_TRACE, ("dir pps: %ld\n",stde->pps_dir));
364 Print(MAX_TRACE, ("guid: %s\n",PRINT_GUID(&(stde->pps_guid))));
365 if (stde->pps_type !=2) {
368 RtlTimeToSecondsSince1970(&(stde->pps_ft1),&dw);
370 Print(MAX_TRACE, ("ts1: %s\n",ctime(&t)));
371 RtlTimeToSecondsSince1970(&(stde->pps_ft2),&dw);
373 Print(MAX_TRACE, ("ts2: %s\n",ctime(&t)));
375 Print(MAX_TRACE, ("startblock: %ld\n",stde->pps_sb));
376 Print(MAX_TRACE, ("size: %ld\n",stde->pps_size));
380 /******************************************************************************
381 * STORAGE_init_storage [INTERNAL]
384 STORAGE_init_storage(HFILE hf) {
387 struct storage_header *sth;
388 struct storage_pps_entry *stde;
390 assert(-1!=_llseek(hf,0,SEEK_SET));
391 /* block -1 is the storage header */
392 sth = (struct storage_header*)block;
393 memcpy(sth->magic,STORAGE_magic,8);
394 memset(sth->unknown1,0,sizeof(sth->unknown1));
395 memset(sth->unknown2,0,sizeof(sth->unknown2));
396 memset(sth->unknown3,0,sizeof(sth->unknown3));
397 sth->num_of_bbd_blocks = 1;
398 sth->root_startblock = 1;
399 sth->sbd_startblock = 0xffffffff;
400 memset(sth->bbd_list,0xff,sizeof(sth->bbd_list));
401 sth->bbd_list[0] = 0;
402 assert(BIGSIZE==_lwrite(hf,block,BIGSIZE));
403 /* block 0 is the big block directory */
405 memset(block,0xff,sizeof(block)); /* mark all blocks as free */
406 bbs[0]=STORAGE_CHAINENTRY_ENDOFCHAIN; /* for this block */
407 bbs[1]=STORAGE_CHAINENTRY_ENDOFCHAIN; /* for directory entry */
408 assert(BIGSIZE==_lwrite(hf,block,BIGSIZE));
409 /* block 1 is the root directory entry */
410 memset(block,0x00,sizeof(block));
411 stde = (struct storage_pps_entry*)block;
412 MultiByteToWideChar( CP_ACP, 0, "RootEntry", -1, stde->pps_rawname,
413 sizeof(stde->pps_rawname)/sizeof(WCHAR));
414 stde->pps_sizeofname = (lstrlenW(stde->pps_rawname)+1) * sizeof(WCHAR);
419 stde->pps_sb = 0xffffffff;
421 assert(BIGSIZE==_lwrite(hf,block,BIGSIZE));
425 /******************************************************************************
426 * STORAGE_set_big_chain [Internal]
429 STORAGE_set_big_chain(HFILE hf,int blocknr,INT type) {
431 LPINT bbd = (LPINT)block;
432 int nextblocknr,bigblocknr;
433 struct storage_header sth;
436 assert(blocknr!=type);
438 bigblocknr = sth.bbd_list[blocknr/128];
439 assert(bigblocknr>=0);
440 assert(STORAGE_get_big_block(hf,bigblocknr,block));
442 nextblocknr = bbd[blocknr&(128-1)];
443 bbd[blocknr&(128-1)] = type;
446 assert(STORAGE_put_big_block(hf,bigblocknr,block));
447 type = STORAGE_CHAINENTRY_FREE;
448 blocknr = nextblocknr;
453 /******************************************************************************
454 * STORAGE_set_small_chain [Internal]
457 STORAGE_set_small_chain(HFILE hf,int blocknr,INT type) {
459 LPINT sbd = (LPINT)block;
460 int lastblocknr,nextsmallblocknr,bigblocknr;
461 struct storage_header sth;
465 assert(blocknr!=type);
466 lastblocknr=-129;bigblocknr=-2;
468 /* cache block ... */
469 if (lastblocknr/128!=blocknr/128) {
470 bigblocknr = STORAGE_get_nth_next_big_blocknr(hf,sth.sbd_startblock,blocknr/128);
471 assert(bigblocknr>=0);
472 assert(STORAGE_get_big_block(hf,bigblocknr,block));
474 lastblocknr = blocknr;
475 nextsmallblocknr = sbd[blocknr&(128-1)];
476 sbd[blocknr&(128-1)] = type;
477 assert(STORAGE_put_big_block(hf,bigblocknr,block));
480 type = STORAGE_CHAINENTRY_FREE;
481 blocknr = nextsmallblocknr;
486 /******************************************************************************
487 * STORAGE_get_free_big_blocknr [Internal]
490 STORAGE_get_free_big_blocknr(HFILE hf) {
492 LPINT sbd = (LPINT)block;
493 int lastbigblocknr,i,curblock,bigblocknr;
494 struct storage_header sth;
499 bigblocknr = sth.bbd_list[curblock];
500 while (curblock<sth.num_of_bbd_blocks) {
501 assert(bigblocknr>=0);
502 assert(STORAGE_get_big_block(hf,bigblocknr,block));
504 if (sbd[i]==STORAGE_CHAINENTRY_FREE) {
505 sbd[i] = STORAGE_CHAINENTRY_ENDOFCHAIN;
506 assert(STORAGE_put_big_block(hf,bigblocknr,block));
507 memset(block,0x42,sizeof(block));
508 assert(STORAGE_put_big_block(hf,i+curblock*128,block));
509 return i+curblock*128;
511 lastbigblocknr = bigblocknr;
512 bigblocknr = sth.bbd_list[++curblock];
514 bigblocknr = curblock*128;
515 /* since we have marked all blocks from 0 up to curblock*128-1
516 * the next free one is curblock*128, where we happily put our
517 * next large block depot.
519 memset(block,0xff,sizeof(block));
520 /* mark the block allocated and returned by this function */
521 sbd[1] = STORAGE_CHAINENTRY_ENDOFCHAIN;
522 assert(STORAGE_put_big_block(hf,bigblocknr,block));
524 /* if we had a bbd block already (mostlikely) we need
525 * to link the new one into the chain
527 if (lastbigblocknr!=-1)
528 assert(STORAGE_set_big_chain(hf,lastbigblocknr,bigblocknr));
529 sth.bbd_list[curblock]=bigblocknr;
530 sth.num_of_bbd_blocks++;
531 assert(sth.num_of_bbd_blocks==curblock+1);
532 assert(STORAGE_put_big_block(hf,-1,(LPBYTE)&sth));
534 /* Set the end of the chain for the bigblockdepots */
535 assert(STORAGE_set_big_chain(hf,bigblocknr,STORAGE_CHAINENTRY_ENDOFCHAIN));
536 /* add 1, for the first entry is used for the additional big block
537 * depot. (means we already used bigblocknr) */
538 memset(block,0x42,sizeof(block));
539 /* allocate this block (filled with 0x42) */
540 assert(STORAGE_put_big_block(hf,bigblocknr+1,block));
545 /******************************************************************************
546 * STORAGE_get_free_small_blocknr [Internal]
549 STORAGE_get_free_small_blocknr(HFILE hf) {
551 LPINT sbd = (LPINT)block;
552 int lastbigblocknr,newblocknr,i,curblock,bigblocknr;
553 struct storage_pps_entry root;
554 struct storage_header sth;
557 bigblocknr = sth.sbd_startblock;
561 while (bigblocknr>=0) {
562 if (!STORAGE_get_big_block(hf,bigblocknr,block))
565 if (sbd[i]==STORAGE_CHAINENTRY_FREE) {
566 sbd[i]=STORAGE_CHAINENTRY_ENDOFCHAIN;
567 newblocknr = i+curblock*128;
572 lastbigblocknr = bigblocknr;
573 bigblocknr = STORAGE_get_next_big_blocknr(hf,bigblocknr);
576 if (newblocknr==-1) {
577 bigblocknr = STORAGE_get_free_big_blocknr(hf);
581 memset(block,0xff,sizeof(block));
582 sbd[0]=STORAGE_CHAINENTRY_ENDOFCHAIN;
583 if (!STORAGE_put_big_block(hf,bigblocknr,block))
585 if (lastbigblocknr==-1) {
586 sth.sbd_startblock = bigblocknr;
587 if (!STORAGE_put_big_block(hf,-1,(LPBYTE)&sth)) /* need to write it */
590 if (!STORAGE_set_big_chain(hf,lastbigblocknr,bigblocknr))
593 if (!STORAGE_set_big_chain(hf,bigblocknr,STORAGE_CHAINENTRY_ENDOFCHAIN))
595 newblocknr = curblock*128;
597 /* allocate enough big blocks for storing the allocated small block */
598 if (!STORAGE_get_root_pps_entry(hf,&root))
603 lastbigblocknr = STORAGE_get_nth_next_big_blocknr(hf,root.pps_sb,(root.pps_size-1)/BIGSIZE);
604 while (root.pps_size < (newblocknr*SMALLSIZE+SMALLSIZE-1)) {
605 /* we need to allocate more stuff */
606 bigblocknr = STORAGE_get_free_big_blocknr(hf);
610 if (root.pps_sb==-1) {
611 root.pps_sb = bigblocknr;
612 root.pps_size += BIGSIZE;
614 if (!STORAGE_set_big_chain(hf,lastbigblocknr,bigblocknr))
616 root.pps_size += BIGSIZE;
618 lastbigblocknr = bigblocknr;
620 if (!STORAGE_set_big_chain(hf,lastbigblocknr,STORAGE_CHAINENTRY_ENDOFCHAIN))
622 if (!STORAGE_put_pps_entry(hf,0,&root))
627 /******************************************************************************
628 * STORAGE_get_free_pps_entry [Internal]
631 STORAGE_get_free_pps_entry(HFILE hf) {
632 int blocknr,i,curblock,lastblocknr;
634 struct storage_pps_entry *stde = (struct storage_pps_entry*)block;
635 struct storage_header sth;
638 blocknr = sth.root_startblock;
642 if (!STORAGE_get_big_block(hf,blocknr,block))
645 if (stde[i].pps_sizeofname==0) /* free */
647 lastblocknr = blocknr;
648 blocknr = STORAGE_get_next_big_blocknr(hf,blocknr);
651 assert(blocknr==STORAGE_CHAINENTRY_ENDOFCHAIN);
652 blocknr = STORAGE_get_free_big_blocknr(hf);
653 /* sth invalidated */
657 if (!STORAGE_set_big_chain(hf,lastblocknr,blocknr))
659 if (!STORAGE_set_big_chain(hf,blocknr,STORAGE_CHAINENTRY_ENDOFCHAIN))
661 memset(block,0,sizeof(block));
662 STORAGE_put_big_block(hf,blocknr,block);
666 /* --- IStream32 implementation */
670 /* IUnknown fields */
671 ICOM_VFIELD(IStream);
673 /* IStream32 fields */
674 struct storage_pps_entry stde;
677 ULARGE_INTEGER offset;
680 /*****************************************************************************
681 * IStream32_QueryInterface [VTABLE]
683 HRESULT WINAPI IStream_fnQueryInterface(
684 IStream* iface,REFIID refiid,LPVOID *obj
686 ICOM_THIS(IStream32Impl,iface);
688 Print(MAX_TRACE, ("(%p)->(%s,%p)\n",This,debugstr_guid(refiid),obj));
689 if (!memcmp(&IID_IUnknown,refiid,sizeof(IID_IUnknown))) {
693 return OLE_E_ENUM_NOMORE;
697 /******************************************************************************
698 * IStream32_AddRef [VTABLE]
700 ULONG WINAPI IStream_fnAddRef(IStream* iface) {
701 ICOM_THIS(IStream32Impl,iface);
702 return ++(This->ref);
705 /******************************************************************************
706 * IStream32_Release [VTABLE]
708 ULONG WINAPI IStream_fnRelease(IStream* iface) {
710 ICOM_THIS(IStream32Impl,iface);
711 FlushFileBuffers(This->hf);
714 CloseHandle(This->hf);
726 /******************************************************************************
727 * Storage API functions
730 /******************************************************************************
731 * StgCreateDocFile16 [STORAGE.1]
733 HRESULT WINAPI StgCreateDocFile16(
734 LPCOLESTR16 pwcsName,DWORD grfMode,DWORD reserved,IStorage16 **ppstgOpen
740 /******************************************************************************
741 * StgIsStorageFile16 [STORAGE.5]
743 HRESULT WINAPI StgIsStorageFile16(LPCOLESTR16 fn) {
748 /******************************************************************************
749 * StgIsStorageFile [OLE32.146]
752 StgIsStorageFile(LPCOLESTR fn)
755 LPOLESTR16 xfn = HEAP_strdupWtoA(GetProcessHeap(),0,fn);
756 HRESULT ret = StgIsStorageFile16(xfn);
758 HeapFree(GetProcessHeap(),0,xfn);
767 /******************************************************************************
768 * StgOpenStorage16 [STORAGE.3]
770 HRESULT WINAPI StgOpenStorage16(
771 LPCOLESTR16 pwcsName,IStorage16 *pstgPriority,DWORD grfMode,
772 SNB16 snbExclude,DWORD reserved, IStorage16 **ppstgOpen