oleaut32: Use a saner calling convention for the marshaller asm thunks.
[wine] / dlls / ole32 / filelockbytes.c
1 /******************************************************************************
2  *
3  * File-based ILockBytes implementation
4  *
5  * Copyright 1999 Thuy Nguyen
6  * Copyright 2010 Vincent Povirk for CodeWeavers
7  *
8  * This library is free software; you can redistribute it and/or
9  * modify it under the terms of the GNU Lesser General Public
10  * License as published by the Free Software Foundation; either
11  * version 2.1 of the License, or (at your option) any later version.
12  *
13  * This library is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
16  * Lesser General Public License for more details.
17  *
18  * You should have received a copy of the GNU Lesser General Public
19  * License along with this library; if not, write to the Free Software
20  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
21  */
22
23 #include <assert.h>
24 #include <stdlib.h>
25 #include <stdarg.h>
26 #include <stdio.h>
27 #include <string.h>
28 #include <limits.h>
29
30 #define COBJMACROS
31 #define NONAMELESSUNION
32 #define NONAMELESSSTRUCT
33
34 #include "windef.h"
35 #include "winbase.h"
36 #include "winuser.h"
37 #include "winerror.h"
38 #include "objbase.h"
39 #include "ole2.h"
40
41 #include "storage32.h"
42
43 #include "wine/debug.h"
44 #include "wine/unicode.h"
45
46 WINE_DEFAULT_DEBUG_CHANNEL(storage);
47
48 typedef struct FileLockBytesImpl
49 {
50     ILockBytes ILockBytes_iface;
51     LONG ref;
52     ULARGE_INTEGER filesize;
53     HANDLE hfile;
54     DWORD flProtect;
55     LPWSTR pwcsName;
56 } FileLockBytesImpl;
57
58 static const ILockBytesVtbl FileLockBytesImpl_Vtbl;
59
60 static inline FileLockBytesImpl *impl_from_ILockBytes(ILockBytes *iface)
61 {
62     return CONTAINING_RECORD(iface, FileLockBytesImpl, ILockBytes_iface);
63 }
64
65 /***********************************************************
66  * Prototypes for private methods
67  */
68
69 /* Note that this evaluates a and b multiple times, so don't
70  * pass expressions with side effects. */
71 #define ROUND_UP(a, b) ((((a) + (b) - 1)/(b))*(b))
72
73 /****************************************************************************
74  *      GetProtectMode
75  *
76  * This function will return a protection mode flag for a file-mapping object
77  * from the open flags of a file.
78  */
79 static DWORD GetProtectMode(DWORD openFlags)
80 {
81     switch(STGM_ACCESS_MODE(openFlags))
82     {
83     case STGM_WRITE:
84     case STGM_READWRITE:
85         return PAGE_READWRITE;
86     }
87     return PAGE_READONLY;
88 }
89
90 /******************************************************************************
91  *      FileLockBytesImpl_Construct
92  *
93  * Initialize a big block object supported by a file.
94  */
95 HRESULT FileLockBytesImpl_Construct(HANDLE hFile, DWORD openFlags, LPCWSTR pwcsName, ILockBytes **pLockBytes)
96 {
97   FileLockBytesImpl *This;
98   WCHAR fullpath[MAX_PATH];
99
100   if (hFile == INVALID_HANDLE_VALUE)
101     return E_FAIL;
102
103   This = HeapAlloc(GetProcessHeap(), 0, sizeof(FileLockBytesImpl));
104
105   if (!This)
106     return E_OUTOFMEMORY;
107
108   This->ILockBytes_iface.lpVtbl = &FileLockBytesImpl_Vtbl;
109   This->ref = 1;
110   This->hfile = hFile;
111   This->filesize.u.LowPart = GetFileSize(This->hfile,
112                                          &This->filesize.u.HighPart);
113   This->flProtect = GetProtectMode(openFlags);
114
115   if(pwcsName) {
116     if (!GetFullPathNameW(pwcsName, MAX_PATH, fullpath, NULL))
117     {
118       lstrcpynW(fullpath, pwcsName, MAX_PATH);
119     }
120     This->pwcsName = HeapAlloc(GetProcessHeap(), 0,
121                               (lstrlenW(fullpath)+1)*sizeof(WCHAR));
122     if (!This->pwcsName)
123     {
124        HeapFree(GetProcessHeap(), 0, This);
125        return E_OUTOFMEMORY;
126     }
127     strcpyW(This->pwcsName, fullpath);
128   }
129   else
130     This->pwcsName = NULL;
131
132   TRACE("file len %u\n", This->filesize.u.LowPart);
133
134   *pLockBytes = &This->ILockBytes_iface;
135
136   return S_OK;
137 }
138
139 /* ILockByte Interfaces */
140
141 static HRESULT WINAPI FileLockBytesImpl_QueryInterface(ILockBytes *iface, REFIID riid,
142     void **ppvObject)
143 {
144     if (IsEqualIID(riid, &IID_IUnknown) || IsEqualIID(riid, &IID_ILockBytes))
145         *ppvObject = iface;
146     else
147     {
148         *ppvObject = NULL;
149         return E_NOINTERFACE;
150     }
151
152     IUnknown_AddRef((IUnknown*)*ppvObject);
153
154     return S_OK;
155 }
156
157 static ULONG WINAPI FileLockBytesImpl_AddRef(ILockBytes *iface)
158 {
159     FileLockBytesImpl* This = impl_from_ILockBytes(iface);
160     return InterlockedIncrement(&This->ref);
161 }
162
163 static ULONG WINAPI FileLockBytesImpl_Release(ILockBytes *iface)
164 {
165     FileLockBytesImpl* This = impl_from_ILockBytes(iface);
166     ULONG ref;
167
168     ref = InterlockedDecrement(&This->ref);
169
170     if (ref == 0)
171     {
172         CloseHandle(This->hfile);
173         HeapFree(GetProcessHeap(), 0, This->pwcsName);
174         HeapFree(GetProcessHeap(), 0, This);
175     }
176
177     return ref;
178 }
179
180 /******************************************************************************
181  * This method is part of the ILockBytes interface.
182  *
183  * It reads a block of information from the byte array at the specified
184  * offset.
185  *
186  * See the documentation of ILockBytes for more info.
187  */
188 static HRESULT WINAPI FileLockBytesImpl_ReadAt(
189       ILockBytes* iface,
190       ULARGE_INTEGER ulOffset,  /* [in] */
191       void*          pv,        /* [length_is][size_is][out] */
192       ULONG          cb,        /* [in] */
193       ULONG*         pcbRead)   /* [out] */
194 {
195     FileLockBytesImpl* This = impl_from_ILockBytes(iface);
196     ULONG bytes_left = cb;
197     LPBYTE readPtr = pv;
198     BOOL ret;
199     LARGE_INTEGER offset;
200     ULONG cbRead;
201
202     TRACE("(%p)-> %i %p %i %p\n",This, ulOffset.u.LowPart, pv, cb, pcbRead);
203
204     /* verify a sane environment */
205     if (!This) return E_FAIL;
206
207     if (pcbRead)
208         *pcbRead = 0;
209
210     offset.QuadPart = ulOffset.QuadPart;
211
212     ret = SetFilePointerEx(This->hfile, offset, NULL, FILE_BEGIN);
213
214     if (!ret)
215         return STG_E_READFAULT;
216
217     while (bytes_left)
218     {
219         ret = ReadFile(This->hfile, readPtr, bytes_left, &cbRead, NULL);
220
221         if (!ret || cbRead == 0)
222             return STG_E_READFAULT;
223
224         if (pcbRead)
225             *pcbRead += cbRead;
226
227         bytes_left -= cbRead;
228         readPtr += cbRead;
229     }
230
231     TRACE("finished\n");
232     return S_OK;
233 }
234
235 /******************************************************************************
236  * This method is part of the ILockBytes interface.
237  *
238  * It writes the specified bytes at the specified offset.
239  * position. If the file is too small, it will be resized.
240  *
241  * See the documentation of ILockBytes for more info.
242  */
243 static HRESULT WINAPI FileLockBytesImpl_WriteAt(
244       ILockBytes* iface,
245       ULARGE_INTEGER ulOffset,    /* [in] */
246       const void*    pv,          /* [size_is][in] */
247       ULONG          cb,          /* [in] */
248       ULONG*         pcbWritten)  /* [out] */
249 {
250     FileLockBytesImpl* This = impl_from_ILockBytes(iface);
251     ULONG size_needed = ulOffset.u.LowPart + cb;
252     ULONG bytes_left = cb;
253     const BYTE *writePtr = pv;
254     BOOL ret;
255     LARGE_INTEGER offset;
256     ULONG cbWritten;
257
258     TRACE("(%p)-> %i %p %i %p\n",This, ulOffset.u.LowPart, pv, cb, pcbWritten);
259
260     /* verify a sane environment */
261     if (!This) return E_FAIL;
262
263     if (This->flProtect != PAGE_READWRITE)
264         return STG_E_ACCESSDENIED;
265
266     if (pcbWritten)
267         *pcbWritten = 0;
268
269     if (size_needed > This->filesize.u.LowPart)
270     {
271         ULARGE_INTEGER newSize;
272         newSize.u.HighPart = 0;
273         newSize.u.LowPart = size_needed;
274         ILockBytes_SetSize(iface, newSize);
275     }
276
277     offset.QuadPart = ulOffset.QuadPart;
278
279     ret = SetFilePointerEx(This->hfile, offset, NULL, FILE_BEGIN);
280
281     if (!ret)
282         return STG_E_READFAULT;
283
284     while (bytes_left)
285     {
286         ret = WriteFile(This->hfile, writePtr, bytes_left, &cbWritten, NULL);
287
288         if (!ret)
289             return STG_E_READFAULT;
290
291         if (pcbWritten)
292             *pcbWritten += cbWritten;
293
294         bytes_left -= cbWritten;
295         writePtr += cbWritten;
296     }
297
298     TRACE("finished\n");
299     return S_OK;
300 }
301
302 static HRESULT WINAPI FileLockBytesImpl_Flush(ILockBytes* iface)
303 {
304     return S_OK;
305 }
306
307 /******************************************************************************
308  *      ILockBytes_SetSize
309  *
310  * Sets the size of the file.
311  *
312  */
313 static HRESULT WINAPI FileLockBytesImpl_SetSize(ILockBytes* iface, ULARGE_INTEGER newSize)
314 {
315     FileLockBytesImpl* This = impl_from_ILockBytes(iface);
316     HRESULT hr = S_OK;
317     LARGE_INTEGER newpos;
318
319     if (This->filesize.u.LowPart == newSize.u.LowPart)
320         return hr;
321
322     TRACE("from %u to %u\n", This->filesize.u.LowPart, newSize.u.LowPart);
323
324     newpos.QuadPart = newSize.QuadPart;
325     if (SetFilePointerEx(This->hfile, newpos, NULL, FILE_BEGIN))
326     {
327         SetEndOfFile(This->hfile);
328     }
329
330     This->filesize = newSize;
331     return hr;
332 }
333
334 static HRESULT WINAPI FileLockBytesImpl_LockRegion(ILockBytes* iface,
335     ULARGE_INTEGER libOffset, ULARGE_INTEGER cb, DWORD dwLockType)
336 {
337     FIXME("stub\n");
338     return E_NOTIMPL;
339 }
340
341 static HRESULT WINAPI FileLockBytesImpl_UnlockRegion(ILockBytes* iface,
342     ULARGE_INTEGER libOffset, ULARGE_INTEGER cb, DWORD dwLockType)
343 {
344     FIXME("stub\n");
345     return E_NOTIMPL;
346 }
347
348 static HRESULT WINAPI FileLockBytesImpl_Stat(ILockBytes* iface,
349     STATSTG *pstatstg, DWORD grfStatFlag)
350 {
351     FileLockBytesImpl* This = impl_from_ILockBytes(iface);
352
353     if (!(STATFLAG_NONAME & grfStatFlag) && This->pwcsName)
354     {
355         pstatstg->pwcsName =
356           CoTaskMemAlloc((lstrlenW(This->pwcsName)+1)*sizeof(WCHAR));
357
358         strcpyW(pstatstg->pwcsName, This->pwcsName);
359     }
360     else
361         pstatstg->pwcsName = NULL;
362
363     pstatstg->type = STGTY_LOCKBYTES;
364     pstatstg->cbSize = This->filesize;
365     /* FIXME: If the implementation is exported, we'll need to set other fields. */
366
367     return S_OK;
368 }
369
370 static const ILockBytesVtbl FileLockBytesImpl_Vtbl = {
371     FileLockBytesImpl_QueryInterface,
372     FileLockBytesImpl_AddRef,
373     FileLockBytesImpl_Release,
374     FileLockBytesImpl_ReadAt,
375     FileLockBytesImpl_WriteAt,
376     FileLockBytesImpl_Flush,
377     FileLockBytesImpl_SetSize,
378     FileLockBytesImpl_LockRegion,
379     FileLockBytesImpl_UnlockRegion,
380     FileLockBytesImpl_Stat
381 };