Skip to content

Commit 8b893b4

Browse files
authored
Merge pull request #1256 from mikeblome/mb-fix
fixes previous commit
2 parents cef4cdc + 97e6a67 commit 8b893b4

File tree

3 files changed

+326
-10
lines changed

3 files changed

+326
-10
lines changed
Lines changed: 24 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
---
22
title: "Microsoft-Specific Modifiers | Microsoft Docs"
33
ms.custom: ""
4-
ms.date: "11/04/2016"
4+
ms.date: "08/16/2018"
55
ms.technology: ["cpp-language"]
66
ms.topic: "language-reference"
77
dev_langs: ["C++"]
@@ -13,10 +13,29 @@ ms.workload: ["cplusplus"]
1313
# Microsoft-Specific Modifiers
1414
This section describes Microsoft-specific extensions to C++ in the following areas:
1515

16-
- [Based addressing](../cpp/based-addressing.md), the practice of using a pointer as a base from which other pointers can be offset
16+
- [Based addressing](based-addressing.md), the practice of using a pointer as a base from which other pointers can be offset
1717

18-
- [Function calling conventions](../cpp/calling-conventions.md)
18+
- [Function calling conventions](calling-conventions.md)
1919

20-
- Extended storage-class attributes declared with the [__declspec](../cpp/declspec.md) keyword
20+
- Extended storage-class attributes declared with the [__declspec](declspec.md) keyword
2121

22-
- The [__w64](../cpp/w64.md) keyword
22+
- The [__w64](w64.md) keyword
23+
24+
### Microsoft-Specific Keywords
25+
26+
Many of the Microsoft-specific keywords can be used to modify declarators to form derived types. For more information about declarators, see [Declarators](overview-of-declarators.md).
27+
28+
|Keyword|Meaning|Used to Form Derived Types?|
29+
|-------------|-------------|---------------------------------|
30+
|[__based](based-grammar.md)|The name that follows declares a 32-bit offset to the 32-bit base contained in the declaration.|Yes|
31+
|[__cdecl](cdecl.md)|The name that follows uses the C naming and calling conventions.|Yes|
32+
|[__declspec](declspec.md)|The name that follows specifies a Microsoft-specific storage-class attribute.|No|
33+
|[__fastcall](fastcall.md)|The name that follows declares a function that uses registers, when available, instead of the stack for argument passing.|Yes|
34+
|[__restrict](extension-restrict.md)|Similar to __declspec([restrict](restrict.md)), but for use on variables.|No|
35+
|[__stdcall](stdcall.md)|The name that follows specifies a function that observes the standard calling convention.|Yes|
36+
|[__w64](w64.md)|Marks a data type as being larger on a 64-bit compiler.|No|
37+
|[__unaligned](unaligned.md)|Specifies that a pointer to a type or other data is not aligned..|No|
38+
|[__vectorcall](vectorcall.md)|The name that follows declares a function that uses registers, including SSE registers, when available, instead of the stack for argument passing.|Yes|
39+
40+
## See Also
41+
[C++ Language Reference](cpp-language-reference.md)

docs/cpp/specifiers.md

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -22,9 +22,11 @@ This topic describes the *decl-specifiers* (declaration specifiers) component of
2222

2323
*function-specifier*
2424

25-
[friend](../cpp/friend-cpp.md)
26-
27-
[typedef]( [typedef](http://msdn.microsod) `(` *extended-decl-modifier-seq* `)`
25+
[friend](friend-cpp.md)
26+
27+
[typedef](aliases-and-typedefs-cpp.md) `(` *extended-decl-modifier-seq* `)`
28+
29+
[__declspec](declspec.md) `(` *extended-decl-modifier-seq* `)`
2830

2931
## Remarks
3032
The *decl-specifiers* part of a declaration is the longest sequence of *decl-specifiers* that can be taken to mean a type name, not including the pointer or reference modifiers. The remainder of the declaration is the *declarator*, which includes the name introduced.
@@ -44,4 +46,4 @@ This topic describes the *decl-specifiers* (declaration specifiers) component of
4446
> Because a name can be redeclared, its interpretation is subject to the most recent declaration in the current scope. Redeclaration can affect how names are interpreted by the compiler, especially **typedef** names.
4547
4648
## See also
47-
[Declarations and Definitions](declarations-and-definitions-cpp.md)
49+
[Declarations and Definitions](declarations-and-definitions-cpp.md)

docs/data/oledb/creating-an-updatable-provider.md

Lines changed: 296 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
---
22
title: "Creating an Updatable Provider | Microsoft Docs"
33
ms.custom: ""
4-
ms.date: "11/04/2016"
4+
ms.date: "08/16/2018"
55
ms.technology: ["cpp-data"]
66
ms.topic: "reference"
77
dev_langs: ["C++"]
@@ -134,3 +134,298 @@ Visual C++ supports updatable providers or providers that can update (write to)
134134
## <a name="vchowwritingtothedatasource"></a> Writing to the Data Source
135135
To read from the data source, call the `Execute` function. To write to the data source, call the `FlushData` function. (In a general sense, flush means to save modifications you make to a table or index to disk.)
136136

137+
```cpp
138+
139+
FlushData(HROW, HACCESSOR);
140+
141+
```
142+
143+
The row handle (HROW) and accessor handle (HACCESSOR) arguments allow you to specify the region to write. Typically, you write a single data field at a time.
144+
145+
The `FlushData` method writes data in the format in which it was originally stored. If you do not override this function, your provider will function correctly but changes will not be flushed to the data store.
146+
147+
### When to Flush
148+
The provider templates call FlushData whenever data needs to be written to the data store; this usually (but not always) occurs as a result of calls to the following functions:
149+
150+
- `IRowsetChange::DeleteRows`
151+
152+
- `IRowsetChange::SetData`
153+
154+
- `IRowsetChange::InsertRows` (if there is new data to insert in the row)
155+
156+
- `IRowsetUpdate::Update`
157+
158+
### How It Works
159+
160+
The consumer makes a call that requires a flush (such as Update) and this call is passed to the provider, which always does the following:
161+
162+
- Calls `SetDBStatus` whenever you have a status value bound.
163+
164+
- Checks column flags.
165+
166+
- Calls `IsUpdateAllowed`.
167+
168+
These three steps help provide security. Then the provider calls `FlushData`.
169+
170+
### How to Implement FlushData
171+
172+
To implement `FlushData`, you need to take into account several issues:
173+
174+
Making sure that the data store can handle changes.
175+
176+
Handling NULL values.
177+
178+
### Handling default values.
179+
180+
To implement your own FlushData method, you need to:
181+
182+
- Go to your rowset class.
183+
184+
- In the rowset class put the declaration of:
185+
186+
```cpp
187+
HRESULT FlushData(HROW, HACCESSOR)
188+
{
189+
// Insert your implementation here and return an HRESULT.
190+
}
191+
```
192+
193+
- Provide an implementation of `FlushData`.
194+
195+
A good implementation of FlushData stores only the rows and columns that are actually updated. You can use the HROW and HACCESSOR parameters to determine the current row and column being stored for optimization.
196+
197+
Typically, the biggest challenge is working with your own native data store. If possible, try to:
198+
199+
- Keep the method of writing to your data store as simple as possible.
200+
201+
- Handle NULL values (optional but advised).
202+
203+
- Handle default values (optional but advised).
204+
205+
The best thing to do is to have actual specified values in your data store for NULL and default values. It is best if you can extrapolate this data. If not, you are advised not to allow NULL and default values.
206+
207+
The following example shows how `FlushData` is implemented in the RUpdateRowset class in the UpdatePV sample (see Rowset.h in the sample code):
208+
209+
```cpp
210+
///////////////////////////////////////////////////////////////////////////
211+
// class RUpdateRowset (in rowset.h)
212+
...
213+
HRESULT FlushData(HROW, HACCESSOR)
214+
{
215+
ATLTRACE2(atlTraceDBProvider, 0, "RUpdateRowset::FlushData\n");
216+
217+
USES_CONVERSION;
218+
enum {
219+
sizeOfString = 256,
220+
sizeOfFileName = MAX_PATH
221+
};
222+
FILE* pFile = NULL;
223+
TCHAR szString[sizeOfString];
224+
TCHAR szFile[sizeOfFileName];
225+
errcode err = 0;
226+
227+
ObjectLock lock(this);
228+
229+
// From a filename, passed in as a command text,
230+
// scan the file placing data in the data array.
231+
if (m_strCommandText == (BSTR)NULL)
232+
{
233+
ATLTRACE( "RRowsetUpdate::FlushData -- "
234+
"No filename specified\n");
235+
return E_FAIL;
236+
}
237+
238+
// Open the file
239+
_tcscpy_s(szFile, sizeOfFileName, OLE2T(m_strCommandText));
240+
if ((szFile[0] == _T('\0')) ||
241+
((err = _tfopen_s(&pFile, &szFile[0], _T("w"))) != 0))
242+
{
243+
ATLTRACE("RUpdateRowset::FlushData -- Could not open file\n");
244+
return DB_E_NOTABLE;
245+
}
246+
247+
// Iterate through the row data and store it.
248+
for (long l=0; l<m_rgRowData.GetSize(); l++)
249+
{
250+
CAgentMan am = m_rgRowData[l];
251+
252+
_putw((int)am.dwFixed, pFile);
253+
254+
if (_tcscmp(&am.szCommand[0], _T("")) != 0)
255+
_stprintf_s(&szString[0], _T("%s\n"), am.szCommand);
256+
else
257+
_stprintf_s(&szString[0], _T("%s\n"), _T("NULL"));
258+
_fputts(szString, pFile);
259+
260+
if (_tcscmp(&am.szText[0], _T("")) != 0)
261+
_stprintf_s(&szString[0], _T("%s\n"), am.szText);
262+
else
263+
_stprintf_s(&szString[0], _T("%s\n"), _T("NULL"));
264+
_fputts(szString, pFile);
265+
266+
if (_tcscmp(&am.szCommand2[0], _T("")) != 0)
267+
_stprintf_s(&szString[0], _T("%s\n"), am.szCommand2);
268+
else
269+
_stprintf_s(&szString[0], _T("%s\n"), _T("NULL"));
270+
_fputts(szString, pFile);
271+
272+
if (_tcscmp(&am.szText2[0], _T("")) != 0)
273+
_stprintf_s(&szString[0], _T("%s\n"), am.szText2);
274+
else
275+
_stprintf_s(&szString[0], _T("%s\n"), _T("NULL"));
276+
_fputts(szString, pFile);
277+
}
278+
279+
if (fflush(pFile) == EOF || fclose(pFile) == EOF)
280+
{
281+
ATLTRACE("RRowsetUpdate::FlushData -- "
282+
"Couldn't flush or close file\n");
283+
}
284+
285+
return S_OK;
286+
}
287+
```
288+
289+
### Handling Changes
290+
291+
For your provider to handle changes, you first need to make sure your data store (such as a text file or video file) has facilities that enable you to make changes on it. If it does not, you should create that code separately from the provider project.
292+
293+
### Handling NULL Data
294+
295+
It is possible that an end user will send NULL data. When you write NULL values to fields in the data source, there can be potential problems. Imagine an order-taking application that accepts values for city and postal code; it could accept either or both values, but not neither, because in that case delivery would be impossible. You therefore have to restrict certain combinations of NULL values in fields that make sense for your application.
296+
297+
As the provider developer, you have to consider how you will store that data, how you will read that data from the data store, and how you specify that to the user. Specifically, you must consider how to change the data status of rowset data in the data source (for example, DataStatus = NULL). You decide what value to return when a consumer accesses a field containing a NULL value.
298+
299+
Look at the code in the UpdatePV sample; it illustrates how a provider can handle NULL data. In UpdatePV, the provider stores NULL data by writing the string "NULL" in the data store. When it reads NULL data from the data store, it sees that string and then empties the buffer, creating a NULL string. It also has an override of `IRowsetImpl::GetDBStatus` in which it returns DBSTATUS_S_ISNULL if that data value is empty.
300+
301+
### Marking Nullable Columns
302+
If you also implement schema rowsets (see `IDBSchemaRowsetImpl`), your implementation should specify in the DBSCHEMA_COLUMNS rowset (usually marked in your provider by CxxxSchemaColSchemaRowset) that the column is nullable.
303+
304+
You also need to specify that all nullable columns contain the DBCOLUMNFLAGS_ISNULLABLE value in your version of the `GetColumnInfo`.
305+
306+
In the OLE DB templates implementation, if you fail to mark columns as nullable, the provider assumes that they must contain a value and will not allow the consumer to send it null values.
307+
308+
The following example shows how the `CommonGetColInfo` function is implemented in CUpdateCommand (see UpProvRS.cpp) in UpdatePV. Note how the columns have this DBCOLUMNFLAGS_ISNULLABLE for nullable columns.
309+
310+
```cpp
311+
/////////////////////////////////////////////////////////////////////////////
312+
// CUpdateCommand (in UpProvRS.cpp)
313+
314+
ATLCOLUMNINFO* CommonGetColInfo(IUnknown* pPropsUnk, ULONG* pcCols, bool bBookmark)
315+
{
316+
static ATLCOLUMNINFO _rgColumns[6];
317+
ULONG ulCols = 0;
318+
319+
if (bBookmark)
320+
{
321+
ADD_COLUMN_ENTRY_EX(ulCols, OLESTR("Bookmark"), 0,
322+
sizeof(DWORD), DBTYPE_BYTES,
323+
0, 0, GUID_NULL, CAgentMan, dwBookmark,
324+
DBCOLUMNFLAGS_ISBOOKMARK)
325+
ulCols++;
326+
}
327+
328+
// Next set the other columns up.
329+
// Add a fixed length entry for OLE DB conformance testing purposes
330+
ADD_COLUMN_ENTRY_EX(ulCols, OLESTR("Fixed"), 1, 4, DBTYPE_UI4,
331+
10, 255, GUID_NULL, CAgentMan, dwFixed,
332+
DBCOLUMNFLAGS_WRITE |
333+
DBCOLUMNFLAGS_ISFIXEDLENGTH)
334+
ulCols++;
335+
336+
ADD_COLUMN_ENTRY_EX(ulCols, OLESTR("Command"), 2, 16, DBTYPE_STR,
337+
255, 255, GUID_NULL, CAgentMan, szCommand,
338+
DBCOLUMNFLAGS_WRITE | DBCOLUMNFLAGS_ISNULLABLE)
339+
ulCols++;
340+
ADD_COLUMN_ENTRY_EX(ulCols, OLESTR("Text"), 3, 16, DBTYPE_STR,
341+
255, 255, GUID_NULL, CAgentMan, szText,
342+
DBCOLUMNFLAGS_WRITE | DBCOLUMNFLAGS_ISNULLABLE)
343+
ulCols++;
344+
345+
ADD_COLUMN_ENTRY_EX(ulCols, OLESTR("Command2"), 4, 16, DBTYPE_STR,
346+
255, 255, GUID_NULL, CAgentMan, szCommand2,
347+
DBCOLUMNFLAGS_WRITE | DBCOLUMNFLAGS_ISNULLABLE)
348+
ulCols++;
349+
ADD_COLUMN_ENTRY_EX(ulCols, OLESTR("Text2"), 5, 16, DBTYPE_STR,
350+
255, 255, GUID_NULL, CAgentMan, szText2,
351+
DBCOLUMNFLAGS_WRITE | DBCOLUMNFLAGS_ISNULLABLE)
352+
ulCols++;
353+
354+
if (pcCols != NULL)
355+
{
356+
*pcCols = ulCols;
357+
}
358+
359+
return _rgColumns;
360+
}
361+
362+
```
363+
364+
### Default Values
365+
366+
As with NULL data, you have the responsibility to deal with changing default values.
367+
368+
The default of FlushData and Execute is to return S_OK. Therefore, if you do not override this function, the changes appear to succeed (S_OK will be returned), but they will not be transmitted to the data store.
369+
370+
In the UpdatePV sample (in Rowset.h), the `SetDBStatus` method handles default values as follows:
371+
372+
```cpp
373+
virtual HRESULT SetDBStatus(DBSTATUS* pdbStatus, CSimpleRow* pRow,
374+
ATLCOLUMNINFO* pColInfo)
375+
{
376+
ATLASSERT(pRow != NULL && pColInfo != NULL && pdbStatus != NULL);
377+
378+
void* pData = NULL;
379+
char* pDefaultData = NULL;
380+
DWORD* pFixedData = NULL;
381+
382+
switch (*pdbStatus)
383+
{
384+
case DBSTATUS_S_DEFAULT:
385+
pData = (void*)&m_rgRowData[pRow->m_iRowset];
386+
if (pColInfo->wType == DBTYPE_STR)
387+
{
388+
pDefaultData = (char*)pData + pColInfo->cbOffset;
389+
strcpy_s(pDefaultData, "Default");
390+
}
391+
else
392+
{
393+
pFixedData = (DWORD*)((BYTE*)pData +
394+
pColInfo->cbOffset);
395+
*pFixedData = 0;
396+
return S_OK;
397+
}
398+
break;
399+
case DBSTATUS_S_ISNULL:
400+
default:
401+
break;
402+
}
403+
return S_OK;
404+
}
405+
```
406+
407+
### Column Flags
408+
409+
If you support default values on your columns, you need to set it using metadata in the \<provider class\>SchemaRowset class. Set `m_bColumnHasDefault` = VARIANT_TRUE.
410+
411+
You also have the responsibility to set the column flags, which are specified using the DBCOLUMNFLAGS enumerated type. The column flags describe column characteristics.
412+
413+
For example, in the `CUpdateSessionColSchemaRowset` class in UpdatePV (in Session.h), the first column is set up this way:
414+
415+
```cpp
416+
// Set up column 1
417+
trData[0].m_ulOrdinalPosition = 1;
418+
trData[0].m_bIsNullable = VARIANT_FALSE;
419+
trData[0].m_bColumnHasDefault = VARIANT_TRUE;
420+
trData[0].m_nDataType = DBTYPE_UI4;
421+
trData[0].m_nNumericPrecision = 10;
422+
trData[0].m_ulColumnFlags = DBCOLUMNFLAGS_WRITE |
423+
DBCOLUMNFLAGS_ISFIXEDLENGTH;
424+
lstrcpyW(trData[0].m_szColumnDefault, OLESTR("0"));
425+
m_rgRowData.Add(trData[0]);
426+
```
427+
428+
This code specifies, among other things, that the column supports a default value of 0, that it be writeable, and that all data in the column have the same length. If you want the data in a column to have variable length, you would not set this flag.
429+
430+
## See Also
431+
[Creating an OLE DB Provider](creating-an-ole-db-provider.md)

0 commit comments

Comments
 (0)