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