1 // Written by Christopher E. Miller
2 // See the included license.txt for copyright and license details.
3 
4 
5 module dfl.internal.com;
6 
7 private import dfl.internal.winapi, dfl.internal.wincom, dfl.internal.dlib;
8 
9 
10 version(DFL_TANGO_SEEK_COMPAT) {
11 }
12 else {
13    version = DFL_TANGO_NO_SEEK_COMPAT;
14 }
15 
16 
17 // Importing dfl.application here causes the compiler to crash.
18 //import dfl.application;
19 private extern(C) {
20    size_t C_refCountInc(void* p);
21    size_t C_refCountDec(void* p);
22 }
23 
24 
25 // Won't be killed by GC if not referenced in D and the refcount is > 0.
26 class DflComObject: ComObject { // package
27    extern(Windows):
28 
29       override ULONG AddRef() {
30       //cprintf("AddRef `%.*s`\n", cast(int)toString().length, toString().ptr);
31       return C_refCountInc(cast(void*)this);
32    }
33 
34    override ULONG Release() {
35       //cprintf("Release `%.*s`\n", cast(int)toString().length, toString().ptr);
36       return C_refCountDec(cast(void*)this);
37    }
38 }
39 
40 
41 class DStreamToIStream: DflComObject, dfl.internal.wincom.IStream {
42    this(DStream sourceDStream) {
43       this.stm = sourceDStream;
44    }
45 
46 
47    extern(Windows):
48 
49       override HRESULT QueryInterface(IID* riid, void** ppv) {
50       if(*riid == _IID_IStream) {
51          *ppv = cast(void*)cast(dfl.internal.wincom.IStream)this;
52          AddRef();
53          return S_OK;
54       } else if(*riid == _IID_ISequentialStream) {
55          *ppv = cast(void*)cast(dfl.internal.wincom.ISequentialStream)this;
56          AddRef();
57          return S_OK;
58       } else if(*riid == _IID_IUnknown) {
59          *ppv = cast(void*)cast(IUnknown)this;
60          AddRef();
61          return S_OK;
62       } else {
63          *ppv = null;
64          return E_NOINTERFACE;
65       }
66    }
67 
68 
69    HRESULT Read(void* pv, ULONG cb, ULONG* pcbRead) {
70       ULONG read;
71       HRESULT result = S_OK;
72 
73       try {
74          read = stm.readBlock(pv, cb);
75       } catch(DStreamException e) {
76          result = S_FALSE; // ?
77       }
78 
79       if(pcbRead) {
80          *pcbRead = read;
81       }
82       //if(!read)
83       // result = S_FALSE;
84       return result;
85    }
86 
87 
88    HRESULT Write(void* pv, ULONG cb, ULONG* pcbWritten) {
89       ULONG written;
90       HRESULT result = S_OK;
91 
92       try {
93          if(!stm.writeable) {
94             return E_NOTIMPL;
95          }
96          written = stm.writeBlock(pv, cb);
97       } catch(DStreamException e) {
98          result = S_FALSE; // ?
99       }
100 
101       if(pcbWritten) {
102          *pcbWritten = written;
103       }
104       //if(!written)
105       // result = S_FALSE;
106       return result;
107    }
108 
109 
110    version(DFL_TANGO_NO_SEEK_COMPAT) {
111    }
112    else {
113       long _fakepos = 0;
114    }
115 
116 
117    HRESULT Seek(LARGE_INTEGER dlibMove, DWORD dwOrigin, ULARGE_INTEGER* plibNewPosition) {
118       HRESULT result = S_OK;
119 
120       //cprintf("seek move=%u, origin=0x%x\n", cast(uint)dlibMove.QuadPart, dwOrigin);
121 
122       try {
123          if(!stm.seekable)
124             //return S_FALSE; // ?
125          {
126             return E_NOTIMPL;   // ?
127          }
128 
129          ulong pos;
130          switch(dwOrigin) {
131             case STREAM_SEEK_SET:
132                pos = stm.seekSet(dlibMove.QuadPart);
133                if(plibNewPosition) {
134                   plibNewPosition.QuadPart = pos;
135                }
136                break;
137 
138             case STREAM_SEEK_CUR:
139                pos = stm.seekCur(dlibMove.QuadPart);
140                if(plibNewPosition) {
141                   plibNewPosition.QuadPart = pos;
142                }
143                break;
144 
145             case STREAM_SEEK_END:
146                pos = stm.seekEnd(dlibMove.QuadPart);
147                if(plibNewPosition) {
148                   plibNewPosition.QuadPart = pos;
149                }
150                break;
151 
152             default:
153                result = STG_E_INVALIDFUNCTION;
154          }
155       } catch(DStreamException e) {
156          result = S_FALSE; // ?
157       }
158 
159       return result;
160    }
161 
162 
163    HRESULT SetSize(ULARGE_INTEGER libNewSize) {
164       return E_NOTIMPL;
165    }
166 
167 
168    HRESULT CopyTo(IStream pstm, ULARGE_INTEGER cb, ULARGE_INTEGER* pcbRead, ULARGE_INTEGER* pcbWritten) {
169       // TODO: implement.
170       return E_NOTIMPL;
171    }
172 
173 
174    HRESULT Commit(DWORD grfCommitFlags) {
175       // Ignore -grfCommitFlags- and just flush the stream..
176       //stm.flush();
177       stm.flush();
178       return S_OK; // ?
179    }
180 
181 
182    HRESULT Revert() {
183       return E_NOTIMPL; // ? S_FALSE ?
184    }
185 
186 
187    HRESULT LockRegion(ULARGE_INTEGER libOffset, ULARGE_INTEGER cb, DWORD dwLockType) {
188       return E_NOTIMPL;
189    }
190 
191 
192    HRESULT UnlockRegion(ULARGE_INTEGER libOffset, ULARGE_INTEGER cb, DWORD dwLockType) {
193       return E_NOTIMPL;
194    }
195 
196 
197    HRESULT Stat(STATSTG* pstatstg, DWORD grfStatFlag) {
198       return E_NOTIMPL; // ?
199    }
200 
201 
202    HRESULT Clone(IStream* ppstm) {
203       // Cloned stream needs its own seek position.
204       return E_NOTIMPL; // ?
205    }
206 
207 
208    extern(D):
209 
210     private:
211       DStream stm;
212    }
213 
214    class MemoryIStream: DflComObject, dfl.internal.wincom.IStream {
215    this(void[] memory) {
216       this.mem = memory;
217    }
218 
219 
220    extern(Windows):
221 
222       override HRESULT QueryInterface(IID* riid, void** ppv) {
223       if(*riid == _IID_IStream) {
224          *ppv = cast(void*)cast(dfl.internal.wincom.IStream)this;
225          AddRef();
226          return S_OK;
227       } else if(*riid == _IID_ISequentialStream) {
228          *ppv = cast(void*)cast(dfl.internal.wincom.ISequentialStream)this;
229          AddRef();
230          return S_OK;
231       } else if(*riid == _IID_IUnknown) {
232          *ppv = cast(void*)cast(IUnknown)this;
233          AddRef();
234          return S_OK;
235       } else {
236          *ppv = null;
237          return E_NOINTERFACE;
238       }
239    }
240 
241 
242    HRESULT Read(void* pv, ULONG cb, ULONG* pcbRead) {
243       // Shouldn't happen unless the mem changes, which doesn't happen yet.
244       if(seekpos > mem.length) {
245          return S_FALSE;   // ?
246       }
247 
248       size_t count = mem.length - seekpos;
249       if(count > cb) {
250          count = cb;
251       }
252 
253       pv[0 .. count] = mem[seekpos .. seekpos + count];
254       seekpos += count;
255 
256       if(pcbRead) {
257          *pcbRead = count;
258       }
259       return S_OK;
260    }
261 
262 
263    HRESULT Write(void* pv, ULONG cb, ULONG* pcbWritten) {
264       //return STG_E_ACCESSDENIED;
265       return E_NOTIMPL;
266    }
267 
268 
269    HRESULT Seek(LARGE_INTEGER dlibMove, DWORD dwOrigin, ULARGE_INTEGER* plibNewPosition) {
270       //cprintf("seek move=%u, origin=0x%x\n", cast(uint)dlibMove.QuadPart, dwOrigin);
271 
272       auto toPos = cast(long)dlibMove.QuadPart;
273       switch(dwOrigin) {
274          case STREAM_SEEK_SET:
275             break;
276 
277          case STREAM_SEEK_CUR:
278             toPos = cast(long)seekpos + toPos;
279             break;
280 
281          case STREAM_SEEK_END:
282             toPos = cast(long)mem.length - toPos;
283             break;
284 
285          default:
286             return STG_E_INVALIDFUNCTION;
287       }
288 
289       if(withinbounds(toPos)) {
290          seekpos = cast(size_t)toPos;
291          if(plibNewPosition) {
292             plibNewPosition.QuadPart = seekpos;
293          }
294          return S_OK;
295       } else {
296          return 0x80030005; //STG_E_ACCESSDENIED; // Seeking past end needs write access.
297       }
298    }
299 
300 
301    HRESULT SetSize(ULARGE_INTEGER libNewSize) {
302       return E_NOTIMPL;
303    }
304 
305 
306    HRESULT CopyTo(IStream pstm, ULARGE_INTEGER cb, ULARGE_INTEGER* pcbRead, ULARGE_INTEGER* pcbWritten) {
307       // TODO: implement.
308       return E_NOTIMPL;
309    }
310 
311 
312    HRESULT Commit(DWORD grfCommitFlags) {
313       return S_OK; // ?
314    }
315 
316 
317    HRESULT Revert() {
318       return E_NOTIMPL; // ? S_FALSE ?
319    }
320 
321 
322    HRESULT LockRegion(ULARGE_INTEGER libOffset, ULARGE_INTEGER cb, DWORD dwLockType) {
323       return E_NOTIMPL;
324    }
325 
326 
327    HRESULT UnlockRegion(ULARGE_INTEGER libOffset, ULARGE_INTEGER cb, DWORD dwLockType) {
328       return E_NOTIMPL;
329    }
330 
331 
332    HRESULT Stat(STATSTG* pstatstg, DWORD grfStatFlag) {
333       return E_NOTIMPL; // ?
334    }
335 
336 
337    HRESULT Clone(IStream* ppstm) {
338       // Cloned stream needs its own seek position.
339       return E_NOTIMPL; // ?
340    }
341 
342 
343    extern(D):
344 
345     private:
346       void[] mem;
347       size_t seekpos = 0;
348 
349 
350       bool withinbounds(long pos) {
351       if(pos < seekpos.min || pos > seekpos.max) {
352          return false;
353       }
354       // Note: it IS within bounds if it's AT the end, it just can't read there.
355       return cast(size_t)pos <= mem.length;
356    }
357 }
358