mirror of
https://github.com/Microsoft/sql-server-samples.git
synced 2025-12-08 14:58:54 +00:00
Added original v_backup files
This commit is contained in:
File diff suppressed because it is too large
Load Diff
@@ -1,11 +1,22 @@
|
||||
|
||||
|
||||
/* this ALWAYS GENERATED file contains the IIDs and CLSIDs */
|
||||
|
||||
/* link this file in with the server and any clients */
|
||||
|
||||
|
||||
/* File created by MIDL compiler version 7.00.0408 */
|
||||
/* File created by MIDL compiler version 7.00.0408 */
|
||||
/* at Tue Sep 28 18:18:04 2004
|
||||
*/
|
||||
|
||||
/* Compiler settings for vdi.idl:
|
||||
Oicf, W1, Zp8, env=Win32 (32b run)
|
||||
protocol : dce , ms_ext, c_ext, robust
|
||||
error checks: allocation ref bounds_check enum stub_data
|
||||
VC __declspec() decoration level:
|
||||
__declspec(uuid()), __declspec(selectany), __declspec(novtable)
|
||||
DECLSPEC_UUID(), MIDL_INTERFACE()
|
||||
*/
|
||||
//@@MIDL_FILE_HEADING( )
|
||||
|
||||
#if !defined(_M_IA64) && !defined(_M_AMD64)
|
||||
|
||||
@@ -15,7 +26,7 @@
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C"{
|
||||
#endif
|
||||
#endif
|
||||
|
||||
|
||||
#include <rpc.h>
|
||||
@@ -99,8 +110,8 @@ MIDL_DEFINE_GUID(IID, IID_IServerVirtualDeviceSet2,0xAECBD0D6,0x24C6,0x11d3,0x85
|
||||
/* Compiler settings for vdi.idl:
|
||||
Oicf, W1, Zp8, env=Win64 (32b run,appending)
|
||||
protocol : dce , ms_ext, c_ext, robust
|
||||
error checks: allocation ref bounds_check enum stub_data
|
||||
VC __declspec() decoration level:
|
||||
error checks: allocation ref bounds_check enum stub_data
|
||||
VC __declspec() decoration level:
|
||||
__declspec(uuid()), __declspec(selectany), __declspec(novtable)
|
||||
DECLSPEC_UUID(), MIDL_INTERFACE()
|
||||
*/
|
||||
@@ -114,7 +125,7 @@ MIDL_DEFINE_GUID(IID, IID_IServerVirtualDeviceSet2,0xAECBD0D6,0x24C6,0x11d3,0x85
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C"{
|
||||
#endif
|
||||
#endif
|
||||
|
||||
|
||||
#include <rpc.h>
|
||||
@@ -0,0 +1,648 @@
|
||||
/***********************************************************************
|
||||
Copyright (c) Microsoft Corporation
|
||||
All Rights Reserved.
|
||||
***********************************************************************/
|
||||
// This source code is an intended supplement to the Microsoft SQL
|
||||
// Server online references and related electronic documentation.
|
||||
//
|
||||
// This sample is for instructional purposes only.
|
||||
// Code contained herein is not intended to be used "as is" in real applications.
|
||||
//
|
||||
// mprocess.cpp :
|
||||
//
|
||||
// Test & demonstrate the use of multiple streams where each stream is
|
||||
// handled by a secondary process.
|
||||
//
|
||||
// This is a sample program used to demonstrate the Virtual Device Interface
|
||||
// feature of Microsoft SQL Server.
|
||||
//
|
||||
// The program will backup or restore the 'pubs' sample database.
|
||||
//
|
||||
// The program requires two command line parameters.
|
||||
// 1)
|
||||
// One of:
|
||||
// b perform a backup
|
||||
// r perform a restore
|
||||
//
|
||||
// s Act as a secondary client (used internally only)
|
||||
//
|
||||
// 2)
|
||||
// If b or r is given, then a second parm gives the number of streams to use,
|
||||
// (1-32).
|
||||
// The secondary processes are invoked automatically, and the second parm is the
|
||||
// stream id (0..31), the third parm the VDSName.
|
||||
//
|
||||
|
||||
#define _WIN32_DCOM
|
||||
|
||||
#include <objbase.h> // for 'CoInitialize()'
|
||||
#include <stdio.h> // for file operations
|
||||
#include <ctype.h> // for toupper ()
|
||||
#include <windows.h>
|
||||
|
||||
#include "vdi.h" // interface declaration
|
||||
#include "vdierror.h" // error constants
|
||||
#include "vdiguid.h" // define the GUIDs
|
||||
|
||||
void LogError (
|
||||
LPSTR location, // must always be provided
|
||||
LPSTR description, // NULL is acceptable
|
||||
DWORD errCode); // windows status code
|
||||
|
||||
int performTransfer (
|
||||
IClientVirtualDevice* vd,
|
||||
int backup,
|
||||
int streamId);
|
||||
|
||||
HANDLE execSQL (int doBackup, int nStreams);
|
||||
|
||||
int
|
||||
runSecondary (int streamId, IClientVirtualDeviceSet *vds);
|
||||
|
||||
int
|
||||
startSecondaries(
|
||||
IClientVirtualDeviceSet *vds,
|
||||
HANDLE hSQLProcess, // handle to process dealing with the SQL
|
||||
int nStreams, // number of i/o streams
|
||||
char* pgmName); // the name of this program
|
||||
|
||||
// Using a GUID for the VDS Name is a good way to assure uniqueness.
|
||||
//
|
||||
WCHAR wVdsName [100];
|
||||
|
||||
|
||||
//
|
||||
// main function
|
||||
//
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
HRESULT hr;
|
||||
IClientVirtualDeviceSet* vds = NULL ;
|
||||
VDConfig config;
|
||||
int badParm=TRUE;
|
||||
int doBackup;
|
||||
HANDLE hProcess;
|
||||
int termCode = -1;
|
||||
int nStreams=1;
|
||||
int isSecondary = FALSE;
|
||||
|
||||
// Check the input parm
|
||||
//
|
||||
if (argc >= 3)
|
||||
{
|
||||
sscanf (argv[2], "%d", &nStreams);
|
||||
switch (toupper(argv[1][0]))
|
||||
{
|
||||
case 'B':
|
||||
doBackup = TRUE;
|
||||
badParm = FALSE;
|
||||
break;
|
||||
|
||||
case 'R':
|
||||
doBackup = FALSE;
|
||||
badParm = FALSE;
|
||||
break;
|
||||
|
||||
case 'S':
|
||||
doBackup = FALSE; // we don't know or care
|
||||
badParm = FALSE;
|
||||
isSecondary = TRUE;
|
||||
// nStreams is the streamid!
|
||||
swprintf (wVdsName, L"%hs", argv[3]);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (badParm)
|
||||
{
|
||||
printf ("useage: mprocess {B|R} <nStreams>\n"
|
||||
"Demonstrate a multistream Backup or Restore using the Virtual Device Interface\n");
|
||||
exit (1);
|
||||
}
|
||||
|
||||
if (isSecondary)
|
||||
{
|
||||
printf("Secondary pid %d working on stream %d\n", GetCurrentProcessId (), nStreams);
|
||||
}
|
||||
else
|
||||
{
|
||||
// 1..32 streams.
|
||||
//
|
||||
if (nStreams < 1)
|
||||
nStreams = 1;
|
||||
else if (nStreams > 32)
|
||||
nStreams = 32;
|
||||
|
||||
printf ("Performing a %s using %d virtual device(s).\n",
|
||||
(doBackup) ? "BACKUP" : "RESTORE", nStreams);
|
||||
}
|
||||
|
||||
// Initialize COM Library
|
||||
// Note: _WIN32_DCOM must be defined during the compile.
|
||||
//
|
||||
hr = CoInitializeEx (NULL, COINIT_MULTITHREADED);
|
||||
|
||||
if (!SUCCEEDED (hr))
|
||||
{
|
||||
printf ("Coinit fails: x%X\n", hr);
|
||||
exit (1);
|
||||
}
|
||||
|
||||
|
||||
// Get an interface to the device set.
|
||||
// Notice how we use a single IID for both the class and interface
|
||||
// identifiers.
|
||||
//
|
||||
hr = CoCreateInstance (
|
||||
IID_IClientVirtualDeviceSet,
|
||||
NULL,
|
||||
CLSCTX_INPROC_SERVER,
|
||||
IID_IClientVirtualDeviceSet,
|
||||
(void**)&vds);
|
||||
|
||||
if (!SUCCEEDED (hr))
|
||||
{
|
||||
// This failure might happen if the DLL was not registered.
|
||||
//
|
||||
printf ("Could not create component: x%X\n", hr);
|
||||
printf ("Check registration of SQLVDI.DLL and value of IID\n");
|
||||
goto exit;
|
||||
}
|
||||
|
||||
// Perform secondary processing, if this is a
|
||||
// secondary process.
|
||||
//
|
||||
if (isSecondary)
|
||||
{
|
||||
termCode = runSecondary (nStreams, vds);
|
||||
|
||||
goto exit;
|
||||
}
|
||||
|
||||
// The following logic is executed by the primary process.
|
||||
//
|
||||
|
||||
// Setup the VDI configuration we want to use.
|
||||
// This program doesn't use any fancy features, so the
|
||||
// only field to setup is the deviceCount.
|
||||
//
|
||||
// The server will treat the virtual device just like a pipe:
|
||||
// I/O will be strictly sequential with only the basic commands.
|
||||
//
|
||||
memset (&config, 0, sizeof(config));
|
||||
|
||||
config.deviceCount = nStreams;
|
||||
|
||||
// Create a GUID to use for a unique virtual device name
|
||||
//
|
||||
GUID vdsId;
|
||||
CoCreateGuid (&vdsId);
|
||||
StringFromGUID2 (vdsId, wVdsName, 49);
|
||||
|
||||
// Create the virtual device set
|
||||
//
|
||||
hr = vds->Create (wVdsName, &config);
|
||||
if (!SUCCEEDED (hr))
|
||||
{
|
||||
printf ("VDS::Create fails: x%X", hr);
|
||||
goto exit;
|
||||
}
|
||||
|
||||
// Send the SQL command, via isql in a subprocess.
|
||||
//
|
||||
printf("\nSending the SQL...\n");
|
||||
|
||||
hProcess = execSQL (doBackup, nStreams);
|
||||
if (hProcess == NULL)
|
||||
{
|
||||
printf ("execSQL failed.\n");
|
||||
goto shutdown;
|
||||
}
|
||||
|
||||
|
||||
// Wait for the server to connect, completing the configuration.
|
||||
// Notice that we wait a maximum of 15 seconds.
|
||||
//
|
||||
printf("\nWaiting for SQL to complete configuration...\n");
|
||||
|
||||
hr = vds->GetConfiguration (15000, &config);
|
||||
if (!SUCCEEDED (hr))
|
||||
{
|
||||
printf ("VDS::Getconfig fails: x%X\n", hr);
|
||||
goto shutdown;
|
||||
}
|
||||
|
||||
// Handle the virtual devices in secondary processes.
|
||||
//
|
||||
printf ("\nSpawning secondary processes...\n");
|
||||
termCode = startSecondaries (vds, hProcess, nStreams, argv[0]);
|
||||
|
||||
shutdown:
|
||||
|
||||
// Close the set
|
||||
//
|
||||
vds->Close ();
|
||||
|
||||
// COM reference counting: Release the interface.
|
||||
//
|
||||
vds->Release () ;
|
||||
|
||||
exit:
|
||||
|
||||
// Uninitialize COM Library
|
||||
//
|
||||
CoUninitialize () ;
|
||||
|
||||
return termCode;
|
||||
}
|
||||
|
||||
//
|
||||
// Execute a basic backup/restore, by starting 'osql' in a subprocess.
|
||||
//
|
||||
// Returns:
|
||||
// NULL : failed to start isql
|
||||
// else : process handle
|
||||
//
|
||||
HANDLE execSQL (int doBackup, int nStreams)
|
||||
{
|
||||
char cmd[5000];
|
||||
char extend[100];
|
||||
PROCESS_INFORMATION pi;
|
||||
STARTUPINFO si;
|
||||
int ix;
|
||||
|
||||
// Build the SQL, submitting it via 'isql'
|
||||
// If you want to use Windows NT Authentication, please do not use the -U or -P options.
|
||||
sprintf (cmd,
|
||||
"osql -E -b -Q\"%s DATABASE PUBS %s VIRTUAL_DEVICE='%ls'",
|
||||
(doBackup) ? "BACKUP" : "RESTORE",
|
||||
(doBackup) ? "TO" : "FROM",
|
||||
wVdsName);
|
||||
|
||||
for (ix=1; ix<nStreams; ix++)
|
||||
{
|
||||
sprintf (extend, ", VIRTUAL_DEVICE='%ls%d'", wVdsName, ix);
|
||||
strcat (cmd, extend);
|
||||
}
|
||||
|
||||
strcat (cmd, "\"");
|
||||
|
||||
printf ("Submitting SQL:\n%s\n\n", cmd);
|
||||
|
||||
// use my process for startup info
|
||||
//
|
||||
GetStartupInfo (&si);
|
||||
|
||||
if (!CreateProcess (NULL, cmd, NULL, NULL,
|
||||
TRUE, // inherit handles (stdin/stdout)
|
||||
0, // creation flags,
|
||||
NULL, NULL,
|
||||
&si, // startup info
|
||||
&pi)) // out: process info
|
||||
{
|
||||
LogError ("startSecondary", "CreateProcess", GetLastError ());
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// Return the process handle
|
||||
//
|
||||
return (pi.hProcess);
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------
|
||||
// Invoke the secondary processes, and wait for all children
|
||||
// to complete.
|
||||
//
|
||||
// Returns: 0 if no errors were detected.
|
||||
//
|
||||
//
|
||||
int
|
||||
startSecondaries(
|
||||
IClientVirtualDeviceSet *vds,
|
||||
HANDLE hSQLProcess, // handle to process dealing with the SQL
|
||||
int nStreams, // number of i/o streams
|
||||
char* pgmName) // the name of this program
|
||||
{
|
||||
int ix,nActive;
|
||||
HANDLE children[33]; // 32 is maximum number of streams.
|
||||
// plus one for the isql process.
|
||||
DWORD waitStatus, exitCode;
|
||||
char cmd[200];
|
||||
PROCESS_INFORMATION pi;
|
||||
STARTUPINFO si;
|
||||
|
||||
// use my process for startup info
|
||||
//
|
||||
GetStartupInfo (&si);
|
||||
|
||||
for (ix=0; ix<nStreams; ix++)
|
||||
{
|
||||
sprintf (cmd, "%s s %d %ls", pgmName, ix, wVdsName);
|
||||
|
||||
if (!CreateProcess (NULL, cmd, NULL, NULL,
|
||||
TRUE, // inherit handles (just stdin/stdout I hope!)
|
||||
0, // creation flags,
|
||||
NULL, NULL,
|
||||
&si, // startup info
|
||||
&pi)) // out: process info
|
||||
{
|
||||
printf ("Error starting %s\n", cmd);
|
||||
LogError ("startSecondary", "CreateProcess", GetLastError ());
|
||||
goto errorExit;
|
||||
}
|
||||
// keep the process handle
|
||||
children[ix] = pi.hProcess;
|
||||
}
|
||||
|
||||
// Add the isql process into the array
|
||||
//
|
||||
children[nStreams] = hSQLProcess;
|
||||
nActive = nStreams+1;
|
||||
|
||||
// Wait for all to finish.
|
||||
// Max wait is one minute for this tiny test.
|
||||
//
|
||||
printf ("All children are now running.\n"
|
||||
"Waiting for their completion...\n");
|
||||
|
||||
// Notice how this differs from the threaded model in mthread.cpp.
|
||||
// In the multiprocess model, the primary client (running this code)
|
||||
// is responsible for detecting abnormal termination of the
|
||||
// secondary clients.
|
||||
// A simple "wait-for-all" approach may wait indefinitely if only
|
||||
// one of the secondaries was to abnormally terminate.
|
||||
//
|
||||
do
|
||||
{
|
||||
// Wait for any completion
|
||||
//
|
||||
waitStatus = WaitForMultipleObjects (nActive, children,
|
||||
FALSE, INFINITE);
|
||||
|
||||
if (waitStatus >= WAIT_OBJECT_0 &&
|
||||
waitStatus < WAIT_OBJECT_0+nActive)
|
||||
{
|
||||
// One of the children completed.
|
||||
// Determine which one.
|
||||
//
|
||||
ix = waitStatus - WAIT_OBJECT_0;
|
||||
|
||||
// Check its completion code
|
||||
//
|
||||
if (!GetExitCodeProcess (children[ix], &exitCode))
|
||||
{
|
||||
LogError ("startSecondary", "GetExitCode", GetLastError ());
|
||||
goto errorExit;
|
||||
}
|
||||
|
||||
if (exitCode != 0)
|
||||
{
|
||||
printf ("A child exitted with code %d\n", exitCode);
|
||||
goto errorExit;
|
||||
}
|
||||
|
||||
// It is good programming practice to close handles when
|
||||
// finished with them.
|
||||
// Since this sample simply terminates the process for error
|
||||
// handling, we don't need to do it, as handles are automatically
|
||||
// closed as part of process termination.
|
||||
//
|
||||
CloseHandle (children[ix]);
|
||||
|
||||
// Remove the handle for this child
|
||||
//
|
||||
memmove (&children[ix], &children[ix+1],
|
||||
sizeof (HANDLE) * (nActive-ix-1));
|
||||
|
||||
nActive--;
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
printf("Unexpected wait code: %d\n", waitStatus);
|
||||
goto errorExit;
|
||||
}
|
||||
|
||||
} while (nActive > 0);
|
||||
|
||||
printf ("All children completed successfully\n");
|
||||
|
||||
return 0;
|
||||
|
||||
errorExit:
|
||||
// Handle all problems in a trivial fashion:
|
||||
// SignalAbort() will cause all processes using the virtual device set
|
||||
// to terminate processing.
|
||||
// Thus, we don't bother waiting for any children to terminate.
|
||||
//
|
||||
vds->SignalAbort ();
|
||||
return -1;
|
||||
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------
|
||||
// Perform secondary client processing
|
||||
// Return 0 if no errors detected, else nonzero.
|
||||
//
|
||||
int
|
||||
runSecondary (int streamId, IClientVirtualDeviceSet *vds)
|
||||
{
|
||||
HRESULT hr;
|
||||
WCHAR devName[100];
|
||||
IClientVirtualDevice* vd;
|
||||
VDConfig config;
|
||||
int termCode;
|
||||
|
||||
// Open the device
|
||||
//
|
||||
if (streamId == 0)
|
||||
{
|
||||
// The first device has the same name as the set.
|
||||
//
|
||||
wcscpy (devName, wVdsName);
|
||||
}
|
||||
else
|
||||
{
|
||||
// For this example, we've simply appended a number
|
||||
// for additional devices. You are free to name them
|
||||
// as you wish.
|
||||
//
|
||||
swprintf (devName, L"%ls%d", wVdsName, streamId);
|
||||
}
|
||||
|
||||
// Open the virtual device set in this secondary process.
|
||||
//
|
||||
hr = vds->OpenInSecondary (wVdsName);
|
||||
if (!SUCCEEDED (hr))
|
||||
{
|
||||
printf ("VD::Open(%ls) fails: x%X", devName, hr);
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Open the device assigned to this process.
|
||||
//
|
||||
hr = vds->OpenDevice (devName, &vd);
|
||||
if (!SUCCEEDED (hr))
|
||||
{
|
||||
printf ("OpenDevice fails on %ls: x%X", devName, hr);
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Grab the config to figure out data direction
|
||||
//
|
||||
hr = vds->GetConfiguration (INFINITE, &config);
|
||||
if (!SUCCEEDED (hr))
|
||||
{
|
||||
printf ("VDS::Getconfig fails: x%X\n", hr);
|
||||
termCode = -1;
|
||||
goto errExit;
|
||||
}
|
||||
|
||||
printf ("\nPerforming data transfer...\n");
|
||||
|
||||
termCode = performTransfer (vd,
|
||||
(config.features&VDF_WriteMedia), streamId);
|
||||
|
||||
errExit:
|
||||
|
||||
// If errors were detected, force an abort.
|
||||
//
|
||||
if (termCode != 0)
|
||||
{
|
||||
vds->SignalAbort ();
|
||||
}
|
||||
|
||||
vds->Close ();
|
||||
|
||||
return termCode;
|
||||
}
|
||||
|
||||
|
||||
// This routine reads commands from the server until a 'Close' status is received.
|
||||
// It simply synchronously reads or writes a file on the root of the current drive.
|
||||
//
|
||||
// Returns 0, if no errors are detected, else non-zero.
|
||||
//
|
||||
int performTransfer (
|
||||
IClientVirtualDevice* vd,
|
||||
int backup,
|
||||
int streamId)
|
||||
{
|
||||
FILE * fh;
|
||||
char fname[80];
|
||||
VDC_Command * cmd;
|
||||
DWORD completionCode;
|
||||
DWORD bytesTransferred;
|
||||
HRESULT hr;
|
||||
int termCode = -1;
|
||||
|
||||
sprintf (fname, "multi.%d.dmp", streamId);
|
||||
|
||||
fh = fopen (fname, (backup)? "wb" : "rb");
|
||||
if (fh == NULL )
|
||||
{
|
||||
printf ("Failed to open: %s\n", fname);
|
||||
return -1;
|
||||
}
|
||||
|
||||
while (SUCCEEDED (hr=vd->GetCommand (INFINITE, &cmd)))
|
||||
{
|
||||
bytesTransferred = 0;
|
||||
switch (cmd->commandCode)
|
||||
{
|
||||
case VDC_Read:
|
||||
bytesTransferred = fread (cmd->buffer, 1, cmd->size, fh);
|
||||
if (bytesTransferred == cmd->size)
|
||||
completionCode = ERROR_SUCCESS;
|
||||
else
|
||||
// assume failure is eof
|
||||
completionCode = ERROR_HANDLE_EOF;
|
||||
|
||||
break;
|
||||
|
||||
case VDC_Write:
|
||||
bytesTransferred = fwrite (cmd->buffer, 1, cmd->size, fh);
|
||||
if (bytesTransferred == cmd->size )
|
||||
{
|
||||
completionCode = ERROR_SUCCESS;
|
||||
}
|
||||
else
|
||||
// assume failure is disk full
|
||||
completionCode = ERROR_DISK_FULL;
|
||||
break;
|
||||
|
||||
case VDC_Flush:
|
||||
fflush (fh);
|
||||
completionCode = ERROR_SUCCESS;
|
||||
break;
|
||||
|
||||
case VDC_ClearError:
|
||||
completionCode = ERROR_SUCCESS;
|
||||
break;
|
||||
|
||||
default:
|
||||
// If command is unknown...
|
||||
completionCode = ERROR_NOT_SUPPORTED;
|
||||
}
|
||||
|
||||
|
||||
hr = vd->CompleteCommand (cmd, completionCode, bytesTransferred, 0);
|
||||
if (!SUCCEEDED (hr))
|
||||
{
|
||||
printf ("Completion Failed: x%X\n", hr);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (hr != VD_E_CLOSE)
|
||||
{
|
||||
printf ("Unexpected termination: x%X\n", hr);
|
||||
}
|
||||
else
|
||||
{
|
||||
// As far as the data transfer is concerned, no
|
||||
// errors occurred. The code which issues the SQL
|
||||
// must determine if the backup/restore was
|
||||
// really successful.
|
||||
//
|
||||
printf ("Successfully completed data transfer.\n");
|
||||
termCode = 0;
|
||||
}
|
||||
|
||||
fclose (fh);
|
||||
|
||||
return termCode;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
//
|
||||
// A simple error logger.
|
||||
//
|
||||
void LogError (
|
||||
LPSTR location, // must always be provided
|
||||
LPSTR description, // NULL is acceptable
|
||||
DWORD errCode) // windows status code
|
||||
{
|
||||
LPVOID lpMsgBuf;
|
||||
|
||||
printf (
|
||||
"Error at %s: %s StatusCode: %X\n",
|
||||
location,
|
||||
(description==NULL)?"":description,
|
||||
errCode);
|
||||
|
||||
// Attempt to explain the code
|
||||
//
|
||||
if (errCode != 0 && FormatMessage(
|
||||
FORMAT_MESSAGE_ALLOCATE_BUFFER |
|
||||
FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
|
||||
NULL,
|
||||
errCode,
|
||||
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // Default language
|
||||
(LPTSTR) &lpMsgBuf, 0, NULL ) )// Process any inserts in lpMsgBuf.
|
||||
{
|
||||
printf ("Explanation: %s\n", lpMsgBuf);
|
||||
LocalFree( lpMsgBuf );
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,100 @@
|
||||
# Microsoft Developer Studio Project File - Name="mprocess" - Package Owner=<4>
|
||||
# Microsoft Developer Studio Generated Build File, Format Version 6.00
|
||||
# ** DO NOT EDIT **
|
||||
|
||||
# TARGTYPE "Win32 (x86) Console Application" 0x0103
|
||||
|
||||
CFG=mprocess - Win32 Debug
|
||||
!MESSAGE This is not a valid makefile. To build this project using NMAKE,
|
||||
!MESSAGE use the Export Makefile command and run
|
||||
!MESSAGE
|
||||
!MESSAGE NMAKE /f "mprocess.mak".
|
||||
!MESSAGE
|
||||
!MESSAGE You can specify a configuration when running NMAKE
|
||||
!MESSAGE by defining the macro CFG on the command line. For example:
|
||||
!MESSAGE
|
||||
!MESSAGE NMAKE /f "mprocess.mak" CFG="mprocess - Win32 Debug"
|
||||
!MESSAGE
|
||||
!MESSAGE Possible choices for configuration are:
|
||||
!MESSAGE
|
||||
!MESSAGE "mprocess - Win32 Release" (based on "Win32 (x86) Console Application")
|
||||
!MESSAGE "mprocess - Win32 Debug" (based on "Win32 (x86) Console Application")
|
||||
!MESSAGE
|
||||
|
||||
# Begin Project
|
||||
# PROP AllowPerConfigDependencies 0
|
||||
# PROP Scc_ProjName ""
|
||||
# PROP Scc_LocalPath ""
|
||||
CPP=cl.exe
|
||||
RSC=rc.exe
|
||||
|
||||
!IF "$(CFG)" == "mprocess - Win32 Release"
|
||||
|
||||
# PROP BASE Use_MFC 0
|
||||
# PROP BASE Use_Debug_Libraries 0
|
||||
# PROP BASE Output_Dir "Release"
|
||||
# PROP BASE Intermediate_Dir "Release"
|
||||
# PROP BASE Target_Dir ""
|
||||
# PROP Use_MFC 0
|
||||
# PROP Use_Debug_Libraries 0
|
||||
# PROP Output_Dir "Release"
|
||||
# PROP Intermediate_Dir "Release"
|
||||
# PROP Target_Dir ""
|
||||
# ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /c
|
||||
# ADD CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /c
|
||||
# ADD BASE RSC /l 0x409 /d "NDEBUG"
|
||||
# ADD RSC /l 0x409 /d "NDEBUG"
|
||||
BSC32=bscmake.exe
|
||||
# ADD BASE BSC32 /nologo
|
||||
# ADD BSC32 /nologo
|
||||
LINK32=link.exe
|
||||
# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /machine:I386
|
||||
# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /machine:I386
|
||||
|
||||
!ELSEIF "$(CFG)" == "mprocess - Win32 Debug"
|
||||
|
||||
# PROP BASE Use_MFC 0
|
||||
# PROP BASE Use_Debug_Libraries 1
|
||||
# PROP BASE Output_Dir "Debug"
|
||||
# PROP BASE Intermediate_Dir "Debug"
|
||||
# PROP BASE Target_Dir ""
|
||||
# PROP Use_MFC 0
|
||||
# PROP Use_Debug_Libraries 1
|
||||
# PROP Output_Dir "Debug"
|
||||
# PROP Intermediate_Dir "Debug"
|
||||
# PROP Target_Dir ""
|
||||
# ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /GZ /c
|
||||
# ADD CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /GZ /c
|
||||
# ADD BASE RSC /l 0x409 /d "_DEBUG"
|
||||
# ADD RSC /l 0x409 /d "_DEBUG"
|
||||
BSC32=bscmake.exe
|
||||
# ADD BASE BSC32 /nologo
|
||||
# ADD BSC32 /nologo
|
||||
LINK32=link.exe
|
||||
# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /debug /machine:I386 /pdbtype:sept
|
||||
# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /debug /machine:I386 /pdbtype:sept
|
||||
|
||||
!ENDIF
|
||||
|
||||
# Begin Target
|
||||
|
||||
# Name "mprocess - Win32 Release"
|
||||
# Name "mprocess - Win32 Debug"
|
||||
# Begin Group "Source Files"
|
||||
|
||||
# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat"
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=.\mprocess.cpp
|
||||
# End Source File
|
||||
# End Group
|
||||
# Begin Group "Header Files"
|
||||
|
||||
# PROP Default_Filter "h;hpp;hxx;hm;inl"
|
||||
# End Group
|
||||
# Begin Group "Resource Files"
|
||||
|
||||
# PROP Default_Filter "ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe"
|
||||
# End Group
|
||||
# End Target
|
||||
# End Project
|
||||
@@ -0,0 +1,29 @@
|
||||
Microsoft Developer Studio Workspace File, Format Version 6.00
|
||||
# WARNING: DO NOT EDIT OR DELETE THIS WORKSPACE FILE!
|
||||
|
||||
###############################################################################
|
||||
|
||||
Project: "mprocess"=.\mprocess.dsp - Package Owner=<4>
|
||||
|
||||
Package=<5>
|
||||
{{{
|
||||
}}}
|
||||
|
||||
Package=<4>
|
||||
{{{
|
||||
}}}
|
||||
|
||||
###############################################################################
|
||||
|
||||
Global:
|
||||
|
||||
Package=<5>
|
||||
{{{
|
||||
}}}
|
||||
|
||||
Package=<3>
|
||||
{{{
|
||||
}}}
|
||||
|
||||
###############################################################################
|
||||
|
||||
@@ -0,0 +1,600 @@
|
||||
/***********************************************************************
|
||||
Copyright (c) Microsoft Corporation
|
||||
All Rights Reserved.
|
||||
***********************************************************************/
|
||||
// This source code is an intended supplement to the Microsoft SQL
|
||||
// Server online references and related electronic documentation.
|
||||
//
|
||||
// This sample is for instructional purposes only.
|
||||
// Code contained herein is not intended to be used "as is" in real applications.
|
||||
//
|
||||
// mthread.cpp :
|
||||
//
|
||||
// Test & demonstrate the use of multiple streams from a single process.
|
||||
//
|
||||
// This is a sample program used to demonstrate the Virtual Device Interface
|
||||
// feature of Microsoft SQL Server.
|
||||
//
|
||||
// The program will backup or restore the 'pubs' sample database.
|
||||
//
|
||||
// The program requires two command line parameters.
|
||||
// 1)
|
||||
// One of:
|
||||
// b perform a backup
|
||||
// r perform a restore
|
||||
//
|
||||
// 2)
|
||||
// The number of streams to use, 1-32.
|
||||
//
|
||||
|
||||
#define _WIN32_DCOM
|
||||
|
||||
#include <objbase.h> // for 'CoInitialize()'
|
||||
#include <stdio.h> // for file operations
|
||||
#include <ctype.h> // for toupper ()
|
||||
#include <process.h> // for C library-safe _beginthreadex,_endthreadex
|
||||
#include <windows.h>
|
||||
|
||||
#include "vdi.h" // interface declaration
|
||||
#include "vdierror.h" // error constants
|
||||
#include "vdiguid.h" // define the GUIDs
|
||||
|
||||
void LogError (
|
||||
LPSTR location, // must always be provided
|
||||
LPSTR description, // NULL is acceptable
|
||||
DWORD errCode); // windows status code
|
||||
|
||||
int performTransfer (
|
||||
IClientVirtualDevice* vd,
|
||||
int backup,
|
||||
int streamId);
|
||||
|
||||
HANDLE execSQL (int doBackup, int nStreams);
|
||||
|
||||
int
|
||||
startSecondaries(
|
||||
IClientVirtualDeviceSet *vds,
|
||||
HANDLE hSQLProcess, // handle to process dealing with the SQL
|
||||
int nStreams); // number of i/o streams
|
||||
|
||||
unsigned __stdcall
|
||||
runSecondary (void *parms);
|
||||
|
||||
// Using a GUID for the VDS Name is a good way to assure uniqueness.
|
||||
//
|
||||
WCHAR wVdsName [50];
|
||||
|
||||
//
|
||||
// main function
|
||||
//
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
HRESULT hr;
|
||||
IClientVirtualDeviceSet* vds = NULL ;
|
||||
VDConfig config;
|
||||
int badParm=TRUE;
|
||||
int doBackup;
|
||||
HANDLE hProcess;
|
||||
int termCode = -1;
|
||||
int nStreams=1;
|
||||
|
||||
// Check the input parm
|
||||
//
|
||||
if (argc == 3)
|
||||
{
|
||||
sscanf (argv[2], "%d", &nStreams);
|
||||
switch (toupper(argv[1][0]))
|
||||
{
|
||||
case 'B':
|
||||
doBackup = TRUE;
|
||||
badParm = FALSE;
|
||||
break;
|
||||
|
||||
case 'R':
|
||||
doBackup = FALSE;
|
||||
badParm = FALSE;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (badParm)
|
||||
{
|
||||
printf ("useage: mprocess {B|R} <nStreams>\n"
|
||||
"Demonstrate a multistream Backup or Restore using the Virtual Device Interface\n");
|
||||
exit (1);
|
||||
}
|
||||
|
||||
// 1..32 streams.
|
||||
//
|
||||
if (nStreams < 1)
|
||||
nStreams = 1;
|
||||
else if (nStreams > 32)
|
||||
nStreams = 32;
|
||||
|
||||
printf ("Performing a %s using %d virtual device(s).\n",
|
||||
(doBackup) ? "BACKUP" : "RESTORE", nStreams);
|
||||
|
||||
// Initialize COM Library
|
||||
// Note: _WIN32_DCOM must be defined during the compile.
|
||||
//
|
||||
hr = CoInitializeEx (NULL, COINIT_MULTITHREADED);
|
||||
|
||||
if (!SUCCEEDED (hr))
|
||||
{
|
||||
printf ("Coinit fails: x%X\n", hr);
|
||||
exit (1);
|
||||
}
|
||||
|
||||
|
||||
// Get an interface to the device set.
|
||||
// Notice how we use a single IID for both the class and interface
|
||||
// identifiers.
|
||||
//
|
||||
hr = CoCreateInstance (
|
||||
IID_IClientVirtualDeviceSet,
|
||||
NULL,
|
||||
CLSCTX_INPROC_SERVER,
|
||||
IID_IClientVirtualDeviceSet,
|
||||
(void**)&vds);
|
||||
|
||||
if (!SUCCEEDED (hr))
|
||||
{
|
||||
// This failure might happen if the DLL was not registered.
|
||||
//
|
||||
printf ("Could not create component: x%X\n", hr);
|
||||
printf ("Check registration of SQLVDI.DLL and value of IID\n");
|
||||
goto exit;
|
||||
}
|
||||
|
||||
// Create a GUID to use for a unique virtual device name
|
||||
//
|
||||
GUID vdsId;
|
||||
CoCreateGuid (&vdsId);
|
||||
StringFromGUID2 (vdsId, wVdsName, 49);
|
||||
|
||||
// Setup the VDI configuration we want to use.
|
||||
// This program doesn't use any fancy features, so the
|
||||
// only field to setup is the deviceCount.
|
||||
//
|
||||
// The server will treat the virtual device just like a pipe:
|
||||
// I/O will be strictly sequential with only the basic commands.
|
||||
//
|
||||
memset (&config, 0, sizeof(config));
|
||||
|
||||
config.deviceCount = nStreams;
|
||||
|
||||
// Create the virtual device set
|
||||
//
|
||||
hr = vds->Create (wVdsName, &config);
|
||||
if (!SUCCEEDED (hr))
|
||||
{
|
||||
printf ("VDS::Create fails: x%X", hr);
|
||||
goto exit;
|
||||
}
|
||||
|
||||
// Send the SQL command, via isql in a subprocess.
|
||||
//
|
||||
printf("\nSending the SQL...\n");
|
||||
|
||||
hProcess = execSQL (doBackup, nStreams);
|
||||
if (hProcess == NULL)
|
||||
{
|
||||
printf ("execSQL failed.\n");
|
||||
goto shutdown;
|
||||
}
|
||||
|
||||
|
||||
// Wait for the server to connect, completing the configuration.
|
||||
// Notice that we wait a maximum of 15 seconds.
|
||||
//
|
||||
printf("\nWaiting for SQL to complete configuration...\n");
|
||||
|
||||
hr = vds->GetConfiguration (15000, &config);
|
||||
if (!SUCCEEDED (hr))
|
||||
{
|
||||
printf ("VDS::Getconfig fails: x%X\n", hr);
|
||||
goto shutdown;
|
||||
}
|
||||
|
||||
// Handle the virtual devices in secondary processes.
|
||||
//
|
||||
printf ("\nSpawning secondary threads..\n");
|
||||
termCode = startSecondaries (vds, hProcess, nStreams);
|
||||
|
||||
shutdown:
|
||||
|
||||
// Close the set
|
||||
//
|
||||
vds->Close ();
|
||||
|
||||
// COM reference counting: Release the interface.
|
||||
//
|
||||
vds->Release () ;
|
||||
|
||||
exit:
|
||||
|
||||
// Uninitialize COM Library
|
||||
//
|
||||
CoUninitialize () ;
|
||||
|
||||
return termCode;
|
||||
}
|
||||
|
||||
//
|
||||
// Execute a basic backup/restore, by starting 'isql' in a subprocess.
|
||||
//
|
||||
// Returns:
|
||||
// NULL : failed to start isql
|
||||
// else : process handle
|
||||
//
|
||||
HANDLE execSQL (int doBackup, int nStreams)
|
||||
{
|
||||
char cmd[5000];
|
||||
char extend[100];
|
||||
PROCESS_INFORMATION pi;
|
||||
STARTUPINFO si;
|
||||
int ix;
|
||||
|
||||
sprintf (cmd, "osql -E -b -Q\"%s DATABASE PUBS %s VIRTUAL_DEVICE='%ls'",
|
||||
(doBackup) ? "BACKUP" : "RESTORE",
|
||||
(doBackup) ? "TO" : "FROM",
|
||||
wVdsName);
|
||||
|
||||
for (ix=1; ix<nStreams; ix++)
|
||||
{
|
||||
sprintf (extend, ", VIRTUAL_DEVICE='%ls%d'", wVdsName, ix);
|
||||
strcat (cmd, extend);
|
||||
}
|
||||
|
||||
strcat (cmd, "\"");
|
||||
|
||||
printf ("Submitting SQL:\n%s\n\n", cmd);
|
||||
|
||||
// use my process for startup info
|
||||
//
|
||||
GetStartupInfo (&si);
|
||||
|
||||
if (!CreateProcess (NULL, cmd, NULL, NULL,
|
||||
TRUE, // inherit handles (stdin/stdout)
|
||||
0, // creation flags,
|
||||
NULL, NULL,
|
||||
&si, // startup info
|
||||
&pi)) // out: process info
|
||||
{
|
||||
LogError ("startSecondary", "CreateProcess", GetLastError ());
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// Return the process handle
|
||||
//
|
||||
return (pi.hProcess);
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------
|
||||
//
|
||||
// A parmameter block to use when spawning secondaries.
|
||||
//
|
||||
struct THREAD_PARMS {
|
||||
IClientVirtualDeviceSet *vds;
|
||||
int streamId;
|
||||
};
|
||||
|
||||
|
||||
//-----------------------------------------------------------
|
||||
// Invoke the secondary threads, and wait for all children
|
||||
// to complete.
|
||||
//
|
||||
// Returns: 0 if no errors were detected.
|
||||
//
|
||||
//
|
||||
int
|
||||
startSecondaries(
|
||||
IClientVirtualDeviceSet *vds,
|
||||
HANDLE hSQLProcess, // handle to process dealing with the SQL
|
||||
int nStreams) // number of i/o streams
|
||||
{
|
||||
THREAD_PARMS parms[32]; // each thread needs its own parm block
|
||||
int ix,nActive;
|
||||
HANDLE children[33]; // 32 is maximum number of streams.
|
||||
// plus one for the isql process.
|
||||
DWORD waitStatus, exitCode;
|
||||
unsigned threadId;
|
||||
|
||||
for (ix=0; ix<nStreams; ix++)
|
||||
{
|
||||
// All threads share the same virtual device set,
|
||||
// but must operate on different virtual devices.
|
||||
//
|
||||
parms[ix].vds = vds;
|
||||
parms[ix].streamId = ix;
|
||||
|
||||
children[ix] = (HANDLE)_beginthreadex (
|
||||
NULL, 0, runSecondary, (void*)&parms[ix], 0, &threadId);
|
||||
|
||||
if (children[ix] == NULL)
|
||||
{
|
||||
printf ("Failed to create thread. errno is %d\n", errno);
|
||||
goto errorExit;
|
||||
}
|
||||
|
||||
printf ("\nStarted thread %d\n", threadId);
|
||||
}
|
||||
|
||||
// Add the isql process into the array
|
||||
//
|
||||
children[nStreams] = hSQLProcess;
|
||||
nActive = nStreams+1;
|
||||
|
||||
// Wait for all to finish.
|
||||
// Max wait is one minute for this tiny test.
|
||||
//
|
||||
printf ("All children are now running.\n"
|
||||
"Waiting for their completion...\n");
|
||||
|
||||
waitStatus = WaitForMultipleObjects (nActive, children,
|
||||
TRUE, 60000);
|
||||
|
||||
if (waitStatus < WAIT_OBJECT_0 ||
|
||||
waitStatus >= WAIT_OBJECT_0+nActive)
|
||||
{
|
||||
LogError ("startSecondary", "WaitForMultiple", GetLastError ());
|
||||
printf("Unexpected wait code: %d\n", waitStatus);
|
||||
goto errorExit;
|
||||
}
|
||||
|
||||
// All of the children have completed.
|
||||
// Get the completion code from 'isql' to check for sucess.
|
||||
//
|
||||
if (!GetExitCodeProcess (hSQLProcess, &exitCode))
|
||||
{
|
||||
LogError ("startSecondary", "GetExitCode", GetLastError ());
|
||||
goto errorExit;
|
||||
}
|
||||
|
||||
if (exitCode != 0)
|
||||
{
|
||||
printf ("The SQL operation failed with code %d\n", exitCode);
|
||||
goto errorExit;
|
||||
}
|
||||
|
||||
printf ("The SQL operation was sucessful.\n");
|
||||
|
||||
// Be sure to close handles when finished with them.
|
||||
//
|
||||
// Notice that in our trivial error handling here we
|
||||
// don't bother closing them, since handles are
|
||||
// automatically closed as part of process termination.
|
||||
//
|
||||
for (ix = 0; ix < nActive; ix++)
|
||||
{
|
||||
CloseHandle (children[ix]);
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
errorExit:
|
||||
// Handle all problems in a trivial fashion:
|
||||
// SignalAbort() will cause all processes using the virtual device set
|
||||
// to terminate processing.
|
||||
//
|
||||
vds->SignalAbort ();
|
||||
|
||||
// However, since the threads are using the virtual device set allocated
|
||||
// by the main thread, we can't let the main thread close the set before
|
||||
// the threads are finished with it. There are two options:
|
||||
// 1) exit the process immediately.
|
||||
// 2) wait for the threads to terminate.
|
||||
//
|
||||
// Option 2 is "cleaner" but requires more code, so we just exit here:
|
||||
//
|
||||
ExitProcess ((unsigned)-1);
|
||||
|
||||
return -1; // ExitProcess doesn't return; this avoids a compiler error.
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------
|
||||
// Perform secondary client processing from within a thread.
|
||||
// Exits with 0 if no errors detected, else nonzero.
|
||||
// The caller must setup a THREAD_PARMS parameter block when
|
||||
// spawning the thread.
|
||||
//
|
||||
unsigned __stdcall
|
||||
runSecondary (void *parms)
|
||||
{
|
||||
HRESULT hr;
|
||||
WCHAR devName[100];
|
||||
IClientVirtualDevice* vd;
|
||||
VDConfig config;
|
||||
int termCode;
|
||||
|
||||
// Fetch the input parms
|
||||
//
|
||||
int streamId = ((THREAD_PARMS*)parms)->streamId;
|
||||
IClientVirtualDeviceSet * vds = ((THREAD_PARMS*)parms)->vds;
|
||||
|
||||
// Build the name of the device assigned to this thread.
|
||||
//
|
||||
if (streamId == 0)
|
||||
{
|
||||
// The first device has the same name as the set.
|
||||
//
|
||||
wcscpy (devName, wVdsName);
|
||||
}
|
||||
else
|
||||
{
|
||||
// For this example, we've simply appended a number
|
||||
// for additional devices. You are free to name them
|
||||
// as you wish.
|
||||
//
|
||||
swprintf (devName, L"%s%d", wVdsName, streamId);
|
||||
}
|
||||
|
||||
// Open the device assigned to this thread.
|
||||
//
|
||||
hr = vds->OpenDevice (devName, &vd);
|
||||
if (!SUCCEEDED (hr))
|
||||
{
|
||||
printf ("OpenDevice fails on %ls: x%X", devName, hr);
|
||||
termCode = -1;
|
||||
goto errExit;
|
||||
}
|
||||
|
||||
// Grab the config to figure out data direction
|
||||
//
|
||||
hr = vds->GetConfiguration (INFINITE, &config);
|
||||
if (!SUCCEEDED (hr))
|
||||
{
|
||||
printf ("VDS::Getconfig fails: x%X\n", hr);
|
||||
termCode = -1;
|
||||
goto errExit;
|
||||
}
|
||||
|
||||
printf ("\nPerforming data transfer...\n");
|
||||
|
||||
termCode = performTransfer (vd,
|
||||
(config.features&VDF_WriteMedia), streamId);
|
||||
|
||||
errExit:
|
||||
|
||||
// If errors were detected, force an abort.
|
||||
//
|
||||
if (termCode != 0)
|
||||
{
|
||||
vds->SignalAbort ();
|
||||
}
|
||||
|
||||
return ((unsigned)termCode);
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
// This routine reads commands from the server until a 'Close' status is received.
|
||||
// It simply synchronously reads or writes a file on the root of the current drive.
|
||||
//
|
||||
// Returns 0, if no errors are detected, else non-zero.
|
||||
//
|
||||
int performTransfer (
|
||||
IClientVirtualDevice* vd,
|
||||
int backup,
|
||||
int streamId)
|
||||
{
|
||||
FILE * fh;
|
||||
char fname[80];
|
||||
VDC_Command * cmd;
|
||||
DWORD completionCode;
|
||||
DWORD bytesTransferred;
|
||||
HRESULT hr;
|
||||
int termCode = -1;
|
||||
|
||||
sprintf (fname, "multi.%d.dmp", streamId);
|
||||
|
||||
fh = fopen (fname, (backup)? "wb" : "rb");
|
||||
if (fh == NULL )
|
||||
{
|
||||
printf ("Failed to open: %s\n", fname);
|
||||
return -1;
|
||||
}
|
||||
|
||||
while (SUCCEEDED (hr=vd->GetCommand (INFINITE, &cmd)))
|
||||
{
|
||||
bytesTransferred = 0;
|
||||
switch (cmd->commandCode)
|
||||
{
|
||||
case VDC_Read:
|
||||
bytesTransferred = fread (cmd->buffer, 1, cmd->size, fh);
|
||||
if (bytesTransferred == cmd->size)
|
||||
completionCode = ERROR_SUCCESS;
|
||||
else
|
||||
// assume failure is eof
|
||||
completionCode = ERROR_HANDLE_EOF;
|
||||
|
||||
break;
|
||||
|
||||
case VDC_Write:
|
||||
bytesTransferred = fwrite (cmd->buffer, 1, cmd->size, fh);
|
||||
if (bytesTransferred == cmd->size )
|
||||
{
|
||||
completionCode = ERROR_SUCCESS;
|
||||
}
|
||||
else
|
||||
// assume failure is disk full
|
||||
completionCode = ERROR_DISK_FULL;
|
||||
break;
|
||||
|
||||
case VDC_Flush:
|
||||
fflush (fh);
|
||||
completionCode = ERROR_SUCCESS;
|
||||
break;
|
||||
|
||||
case VDC_ClearError:
|
||||
completionCode = ERROR_SUCCESS;
|
||||
break;
|
||||
|
||||
default:
|
||||
// If command is unknown...
|
||||
completionCode = ERROR_NOT_SUPPORTED;
|
||||
}
|
||||
|
||||
|
||||
hr = vd->CompleteCommand (cmd, completionCode, bytesTransferred, 0);
|
||||
if (!SUCCEEDED (hr))
|
||||
{
|
||||
printf ("Completion Failed: x%X\n", hr);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (hr != VD_E_CLOSE)
|
||||
{
|
||||
printf ("Unexpected termination: x%X\n", hr);
|
||||
}
|
||||
else
|
||||
{
|
||||
// As far as the data transfer is concerned, no
|
||||
// errors occurred. The code which issues the SQL
|
||||
// must determine if the backup/restore was
|
||||
// really successful.
|
||||
//
|
||||
printf ("Successfully completed data transfer.\n");
|
||||
termCode = 0;
|
||||
}
|
||||
|
||||
fclose (fh);
|
||||
|
||||
return termCode;
|
||||
}
|
||||
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
//
|
||||
// A simple error logger.
|
||||
//
|
||||
void LogError (
|
||||
LPSTR location, // must always be provided
|
||||
LPSTR description, // NULL is acceptable
|
||||
DWORD errCode) // windows status code
|
||||
{
|
||||
LPVOID lpMsgBuf;
|
||||
|
||||
printf (
|
||||
"Error at %s: %s StatusCode: %X\n",
|
||||
location,
|
||||
(description==NULL)?"":description,
|
||||
errCode);
|
||||
|
||||
// Attempt to explain the code
|
||||
//
|
||||
if (errCode != 0 && FormatMessage(
|
||||
FORMAT_MESSAGE_ALLOCATE_BUFFER |
|
||||
FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
|
||||
NULL,
|
||||
errCode,
|
||||
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // Default language
|
||||
(LPTSTR) &lpMsgBuf, 0, NULL ) )// Process any inserts in lpMsgBuf.
|
||||
{
|
||||
printf ("Explanation: %s\n", lpMsgBuf);
|
||||
LocalFree( lpMsgBuf );
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,100 @@
|
||||
# Microsoft Developer Studio Project File - Name="mthread" - Package Owner=<4>
|
||||
# Microsoft Developer Studio Generated Build File, Format Version 6.00
|
||||
# ** DO NOT EDIT **
|
||||
|
||||
# TARGTYPE "Win32 (x86) Console Application" 0x0103
|
||||
|
||||
CFG=mthread - Win32 Debug
|
||||
!MESSAGE This is not a valid makefile. To build this project using NMAKE,
|
||||
!MESSAGE use the Export Makefile command and run
|
||||
!MESSAGE
|
||||
!MESSAGE NMAKE /f "mthread.mak".
|
||||
!MESSAGE
|
||||
!MESSAGE You can specify a configuration when running NMAKE
|
||||
!MESSAGE by defining the macro CFG on the command line. For example:
|
||||
!MESSAGE
|
||||
!MESSAGE NMAKE /f "mthread.mak" CFG="mthread - Win32 Debug"
|
||||
!MESSAGE
|
||||
!MESSAGE Possible choices for configuration are:
|
||||
!MESSAGE
|
||||
!MESSAGE "mthread - Win32 Release" (based on "Win32 (x86) Console Application")
|
||||
!MESSAGE "mthread - Win32 Debug" (based on "Win32 (x86) Console Application")
|
||||
!MESSAGE
|
||||
|
||||
# Begin Project
|
||||
# PROP AllowPerConfigDependencies 0
|
||||
# PROP Scc_ProjName ""
|
||||
# PROP Scc_LocalPath ""
|
||||
CPP=cl.exe
|
||||
RSC=rc.exe
|
||||
|
||||
!IF "$(CFG)" == "mthread - Win32 Release"
|
||||
|
||||
# PROP BASE Use_MFC 0
|
||||
# PROP BASE Use_Debug_Libraries 0
|
||||
# PROP BASE Output_Dir "Release"
|
||||
# PROP BASE Intermediate_Dir "Release"
|
||||
# PROP BASE Target_Dir ""
|
||||
# PROP Use_MFC 0
|
||||
# PROP Use_Debug_Libraries 0
|
||||
# PROP Output_Dir "Release"
|
||||
# PROP Intermediate_Dir "Release"
|
||||
# PROP Target_Dir ""
|
||||
# ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /c
|
||||
# ADD CPP /nologo /MT /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /c
|
||||
# ADD BASE RSC /l 0x409 /d "NDEBUG"
|
||||
# ADD RSC /l 0x409 /d "NDEBUG"
|
||||
BSC32=bscmake.exe
|
||||
# ADD BASE BSC32 /nologo
|
||||
# ADD BSC32 /nologo
|
||||
LINK32=link.exe
|
||||
# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /machine:I386
|
||||
# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /machine:I386
|
||||
|
||||
!ELSEIF "$(CFG)" == "mthread - Win32 Debug"
|
||||
|
||||
# PROP BASE Use_MFC 0
|
||||
# PROP BASE Use_Debug_Libraries 1
|
||||
# PROP BASE Output_Dir "Debug"
|
||||
# PROP BASE Intermediate_Dir "Debug"
|
||||
# PROP BASE Target_Dir ""
|
||||
# PROP Use_MFC 0
|
||||
# PROP Use_Debug_Libraries 1
|
||||
# PROP Output_Dir "Debug"
|
||||
# PROP Intermediate_Dir "Debug"
|
||||
# PROP Target_Dir ""
|
||||
# ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /GZ /c
|
||||
# ADD CPP /nologo /MTd /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /GZ /c
|
||||
# ADD BASE RSC /l 0x409 /d "_DEBUG"
|
||||
# ADD RSC /l 0x409 /d "_DEBUG"
|
||||
BSC32=bscmake.exe
|
||||
# ADD BASE BSC32 /nologo
|
||||
# ADD BSC32 /nologo
|
||||
LINK32=link.exe
|
||||
# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /debug /machine:I386 /pdbtype:sept
|
||||
# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /debug /machine:I386 /pdbtype:sept
|
||||
|
||||
!ENDIF
|
||||
|
||||
# Begin Target
|
||||
|
||||
# Name "mthread - Win32 Release"
|
||||
# Name "mthread - Win32 Debug"
|
||||
# Begin Group "Source Files"
|
||||
|
||||
# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat"
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=.\mthread.cpp
|
||||
# End Source File
|
||||
# End Group
|
||||
# Begin Group "Header Files"
|
||||
|
||||
# PROP Default_Filter "h;hpp;hxx;hm;inl"
|
||||
# End Group
|
||||
# Begin Group "Resource Files"
|
||||
|
||||
# PROP Default_Filter "ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe"
|
||||
# End Group
|
||||
# End Target
|
||||
# End Project
|
||||
@@ -0,0 +1,29 @@
|
||||
Microsoft Developer Studio Workspace File, Format Version 6.00
|
||||
# WARNING: DO NOT EDIT OR DELETE THIS WORKSPACE FILE!
|
||||
|
||||
###############################################################################
|
||||
|
||||
Project: "mthread"=.\mthread.dsp - Package Owner=<4>
|
||||
|
||||
Package=<5>
|
||||
{{{
|
||||
}}}
|
||||
|
||||
Package=<4>
|
||||
{{{
|
||||
}}}
|
||||
|
||||
###############################################################################
|
||||
|
||||
Global:
|
||||
|
||||
Package=<5>
|
||||
{{{
|
||||
}}}
|
||||
|
||||
Package=<3>
|
||||
{{{
|
||||
}}}
|
||||
|
||||
###############################################################################
|
||||
|
||||
@@ -0,0 +1,659 @@
|
||||
/***********************************************************************
|
||||
Copyright (c) Microsoft Corporation
|
||||
All Rights Reserved.
|
||||
***********************************************************************/
|
||||
// This source code is an intended supplement to the Microsoft SQL
|
||||
// Server online references and related electronic documentation.
|
||||
//
|
||||
// This sample is for instructional purposes only.
|
||||
// Code contained herein is not intended to be used "as is" in real applications.
|
||||
//
|
||||
// osimple.cpp
|
||||
//
|
||||
// This sample extends the "simple.cpp" sample to use an ODBC connection.
|
||||
// This is intended only as an introduction to ODBC; not as a comprehensive tutorial.
|
||||
//
|
||||
// This is a sample program used to demonstrate the Virtual Device Interface
|
||||
// feature of Microsoft SQL Server together with an ODBC connection.
|
||||
//
|
||||
// The program will backup or restore the 'pubs' sample database from
|
||||
// the default instance of sql server.
|
||||
//
|
||||
// The program accepts a single command line parameter.
|
||||
// One of:
|
||||
// b perform a backup
|
||||
// r perform a restore
|
||||
//
|
||||
|
||||
|
||||
// To gain access to free threaded COM, you'll need to define _WIN32_DCOM
|
||||
// before the system headers, either in the source (as in this example),
|
||||
// or when invoking the compiler (by using /D "_WIN32_DCOM")
|
||||
//
|
||||
// This sample uses Windows NT Authentication, other than mixed mode security,
|
||||
// to establish connections. If you want to use mixed mode security, please
|
||||
// set Trusted_Connection to no in the SQLDriverConnect command.
|
||||
// Make necessary change to the server name in the SQLDriverConnect command.
|
||||
|
||||
#define _WIN32_DCOM
|
||||
|
||||
#include <objbase.h> // for 'CoInitialize()'
|
||||
#include <stdio.h> // for file operations
|
||||
#include <ctype.h> // for toupper ()
|
||||
#include <process.h> // for C library-safe _beginthreadex,_endthreadex
|
||||
|
||||
#include "vdi.h" // interface declaration
|
||||
#include "vdierror.h" // error constants
|
||||
|
||||
#include "vdiguid.h" // define the interface identifiers.
|
||||
// IMPORTANT: vdiguid.h can only be included in one source file.
|
||||
//
|
||||
|
||||
#include <windows.h>
|
||||
#include "sql.h"
|
||||
#include "sqlext.h"
|
||||
#include "odbcss.h"
|
||||
|
||||
void performTransfer (
|
||||
IClientVirtualDevice* vd,
|
||||
int backup );
|
||||
|
||||
HANDLE execSQL (int doBackup);
|
||||
int checkSQL (HANDLE);
|
||||
|
||||
// Using a GUID for the VDS Name is a good way to assure uniqueness.
|
||||
//
|
||||
WCHAR wVdsName [50];
|
||||
|
||||
//------------------------------------------------------------
|
||||
//
|
||||
// Mainline
|
||||
//
|
||||
int main (int argc, char *argv[])
|
||||
{
|
||||
HRESULT hr;
|
||||
IClientVirtualDeviceSet2* vds = NULL ;
|
||||
IClientVirtualDevice* vd=NULL;
|
||||
|
||||
VDConfig config;
|
||||
int badParm=TRUE;
|
||||
int doBackup;
|
||||
HANDLE hThread = NULL;
|
||||
|
||||
// Check the input parm
|
||||
//
|
||||
if (argc == 2)
|
||||
{
|
||||
if (toupper(argv[1][0]) == 'B')
|
||||
{
|
||||
doBackup = TRUE;
|
||||
badParm = FALSE;
|
||||
}
|
||||
else if (toupper(argv[1][0]) == 'R')
|
||||
{
|
||||
doBackup = FALSE;
|
||||
badParm = FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
if (badParm)
|
||||
{
|
||||
printf ("useage: osimple {B|R}\n"
|
||||
"Demonstrate a Backup or Restore using the Virtual Device Interface & ODBC\n");
|
||||
exit (1);
|
||||
}
|
||||
|
||||
printf ("Performing a %s using a virtual device.\n",
|
||||
(doBackup) ? "BACKUP" : "RESTORE");
|
||||
|
||||
// Initialize COM Library
|
||||
// Note: _WIN32_DCOM must be defined during the compile.
|
||||
//
|
||||
hr = CoInitializeEx (NULL, COINIT_MULTITHREADED);
|
||||
|
||||
if (!SUCCEEDED (hr))
|
||||
{
|
||||
printf ("Coinit fails: x%X\n", hr);
|
||||
exit (1);
|
||||
}
|
||||
|
||||
// Get an interface to the device set.
|
||||
// Notice how we use a single IID for both the class and interface
|
||||
// identifiers.
|
||||
//
|
||||
hr = CoCreateInstance (
|
||||
CLSID_MSSQL_ClientVirtualDeviceSet,
|
||||
NULL,
|
||||
CLSCTX_INPROC_SERVER,
|
||||
IID_IClientVirtualDeviceSet2,
|
||||
(void**)&vds);
|
||||
|
||||
if (!SUCCEEDED (hr))
|
||||
{
|
||||
// This failure might happen if the DLL was not registered,
|
||||
// or if the application is using the wrong interface id (IID).
|
||||
//
|
||||
printf ("Could not create component: x%X\n", hr);
|
||||
printf ("Check registration of SQLVDI.DLL and value of IID\n");
|
||||
goto exit;
|
||||
}
|
||||
|
||||
// Setup the VDI configuration we want to use.
|
||||
// This program doesn't use any fancy features, so the
|
||||
// only field to setup is the deviceCount.
|
||||
//
|
||||
// The server will treat the virtual device just like a pipe:
|
||||
// I/O will be strictly sequential with only the basic commands.
|
||||
//
|
||||
memset (&config, 0, sizeof(config));
|
||||
config.deviceCount = 1;
|
||||
|
||||
// Create a GUID to use for a unique virtual device name
|
||||
//
|
||||
GUID vdsId;
|
||||
CoCreateGuid (&vdsId);
|
||||
StringFromGUID2 (vdsId, wVdsName, 49);
|
||||
|
||||
// Create the virtual device set
|
||||
//
|
||||
hr = vds->CreateEx (NULL, wVdsName, &config);
|
||||
if (!SUCCEEDED (hr))
|
||||
{
|
||||
printf ("VDS::Create fails: x%X", hr);
|
||||
goto exit;
|
||||
}
|
||||
|
||||
// Send the SQL command, by starting a thread to handle the ODBC
|
||||
//
|
||||
printf("\nSending the SQL...\n");
|
||||
|
||||
hThread = execSQL (doBackup);
|
||||
if (hThread == NULL)
|
||||
{
|
||||
printf ("execSQL failed.\n");
|
||||
goto shutdown;
|
||||
}
|
||||
|
||||
// Wait for the server to connect, completing the configuration.
|
||||
//
|
||||
printf ("\nWaiting for SQLServer to respond...\n");
|
||||
|
||||
while (!SUCCEEDED (hr=vds->GetConfiguration (1000, &config)))
|
||||
{
|
||||
if (hr == VD_E_TIMEOUT)
|
||||
{
|
||||
// Check on the SQL thread
|
||||
//
|
||||
DWORD rc = WaitForSingleObject (hThread, 1000);
|
||||
if (rc == WAIT_OBJECT_0)
|
||||
{
|
||||
printf ("SQL command failed before VD transfer\n");
|
||||
goto shutdown;
|
||||
}
|
||||
if (rc == WAIT_TIMEOUT)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
printf ("Check on SQL failed: %d\n", rc);
|
||||
goto shutdown;
|
||||
}
|
||||
|
||||
printf ("VDS::Getconfig fails: x%X\n", hr);
|
||||
goto shutdown;
|
||||
}
|
||||
|
||||
// Open the single device in the set.
|
||||
//
|
||||
hr = vds->OpenDevice (wVdsName, &vd);
|
||||
if (!SUCCEEDED(hr))
|
||||
{
|
||||
printf ("VDS::OpenDevice fails: x%X\n", hr);
|
||||
goto shutdown;
|
||||
}
|
||||
|
||||
printf ("\nPerforming data transfer...\n");
|
||||
|
||||
performTransfer (vd, doBackup);
|
||||
|
||||
|
||||
shutdown:
|
||||
|
||||
// Close the set
|
||||
//
|
||||
vds->Close ();
|
||||
|
||||
// Obtain the SQL completion information
|
||||
//
|
||||
if (hThread != NULL)
|
||||
{
|
||||
if (checkSQL (hThread))
|
||||
printf("\nThe SQL command executed successfully.\n");
|
||||
else
|
||||
printf("\nThe SQL command failed.\n");
|
||||
|
||||
CloseHandle (hThread);
|
||||
}
|
||||
|
||||
// COM reference counting: Release the interface.
|
||||
//
|
||||
vds->Release () ;
|
||||
|
||||
exit:
|
||||
// Uninitialize COM Library
|
||||
//
|
||||
CoUninitialize () ;
|
||||
|
||||
return 0 ;
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
// ODBC Message processing routine.
|
||||
// This is SQLServer specific, to show native message IDs, sev, state.
|
||||
//
|
||||
// The routine will watch for message 3014 in order to detect
|
||||
// a successful backup/restore operation. This is useful for
|
||||
// operations like RESTORE which can sometimes recover from
|
||||
// errors (error messages will be followed by the 3014 success message).
|
||||
//
|
||||
void ProcessMessages (
|
||||
SQLSMALLINT handle_type, // ODBC handle type
|
||||
SQLHANDLE handle, // ODBC handle
|
||||
int ConnInd, // TRUE if sucessful connection made
|
||||
int* pBackupSuccess) // Set TRUE if a 3014 message is seen.
|
||||
{
|
||||
RETCODE plm_retcode = SQL_SUCCESS;
|
||||
UCHAR plm_szSqlState[SQL_SQLSTATE_SIZE + 1];
|
||||
UCHAR plm_szErrorMsg[SQL_MAX_MESSAGE_LENGTH + 1];
|
||||
SDWORD plm_pfNativeError = 0L;
|
||||
SWORD plm_pcbErrorMsg = 0;
|
||||
SQLSMALLINT plm_cRecNmbr = 1;
|
||||
SDWORD plm_SS_MsgState = 0, plm_SS_Severity = 0;
|
||||
SQLINTEGER plm_Rownumber = 0;
|
||||
USHORT plm_SS_Line;
|
||||
SQLSMALLINT plm_cbSS_Procname, plm_cbSS_Srvname;
|
||||
SQLCHAR plm_SS_Procname[MAXNAME], plm_SS_Srvname[MAXNAME];
|
||||
|
||||
while (plm_retcode != SQL_NO_DATA_FOUND)
|
||||
{
|
||||
plm_retcode = SQLGetDiagRec(handle_type, handle,
|
||||
plm_cRecNmbr, plm_szSqlState, &plm_pfNativeError,
|
||||
plm_szErrorMsg, SQL_MAX_MESSAGE_LENGTH, &plm_pcbErrorMsg);
|
||||
|
||||
// Note that if the application has not yet made a
|
||||
// successful connection, the SQLGetDiagField
|
||||
// information has not yet been cached by ODBC
|
||||
// Driver Manager and these calls to SQLGetDiagField
|
||||
// will fail.
|
||||
//
|
||||
if (plm_retcode != SQL_NO_DATA_FOUND)
|
||||
{
|
||||
if (ConnInd)
|
||||
{
|
||||
plm_retcode = SQLGetDiagField(
|
||||
handle_type, handle, plm_cRecNmbr,
|
||||
SQL_DIAG_ROW_NUMBER, &plm_Rownumber,
|
||||
SQL_IS_INTEGER,
|
||||
NULL);
|
||||
|
||||
plm_retcode = SQLGetDiagField(
|
||||
handle_type, handle, plm_cRecNmbr,
|
||||
SQL_DIAG_SS_LINE, &plm_SS_Line,
|
||||
SQL_IS_INTEGER,
|
||||
NULL);
|
||||
|
||||
plm_retcode = SQLGetDiagField(
|
||||
handle_type, handle, plm_cRecNmbr,
|
||||
SQL_DIAG_SS_MSGSTATE, &plm_SS_MsgState,
|
||||
SQL_IS_INTEGER,
|
||||
NULL);
|
||||
|
||||
plm_retcode = SQLGetDiagField(
|
||||
handle_type, handle, plm_cRecNmbr,
|
||||
SQL_DIAG_SS_SEVERITY, &plm_SS_Severity,
|
||||
SQL_IS_INTEGER,
|
||||
NULL);
|
||||
|
||||
plm_retcode = SQLGetDiagField(
|
||||
handle_type, handle, plm_cRecNmbr,
|
||||
SQL_DIAG_SS_PROCNAME, &plm_SS_Procname,
|
||||
sizeof(plm_SS_Procname),
|
||||
&plm_cbSS_Procname);
|
||||
|
||||
plm_retcode = SQLGetDiagField(
|
||||
handle_type, handle, plm_cRecNmbr,
|
||||
SQL_DIAG_SS_SRVNAME, &plm_SS_Srvname,
|
||||
sizeof(plm_SS_Srvname),
|
||||
&plm_cbSS_Srvname);
|
||||
|
||||
printf ("Msg %d, SevLevel %d, State %d, SQLState %s\n",
|
||||
plm_pfNativeError,
|
||||
plm_SS_Severity,
|
||||
plm_SS_MsgState,
|
||||
plm_szSqlState);
|
||||
}
|
||||
|
||||
printf ("%s\n", plm_szErrorMsg);
|
||||
|
||||
if (pBackupSuccess && plm_pfNativeError == 3014)
|
||||
{
|
||||
*pBackupSuccess = TRUE;
|
||||
}
|
||||
}
|
||||
|
||||
plm_cRecNmbr++; //Increment to next diagnostic record.
|
||||
} // End while.
|
||||
}
|
||||
|
||||
|
||||
|
||||
//------------------------------------------------------------------
|
||||
// The mainline of the ODBC thread.
|
||||
//
|
||||
// Returns TRUE if a successful backup/restore is performed.
|
||||
//
|
||||
unsigned __stdcall
|
||||
SQLRoutine (void *parms)
|
||||
{
|
||||
int doBackup = (int)parms;
|
||||
|
||||
char sqlCommand [1024]; // way more space than we'll need.
|
||||
int successDetected = FALSE;
|
||||
|
||||
// ODBC handles
|
||||
//
|
||||
SQLHENV henv = NULL;
|
||||
SQLHDBC hdbc = NULL;
|
||||
SQLHSTMT hstmt = NULL;
|
||||
|
||||
|
||||
sprintf (sqlCommand, "%s DATABASE PUBS %s VIRTUAL_DEVICE='%ls'",
|
||||
(doBackup) ? "BACKUP" : "RESTORE",
|
||||
(doBackup) ? "TO" : "FROM",
|
||||
wVdsName);
|
||||
|
||||
int sentSQL = FALSE;
|
||||
int rc;
|
||||
|
||||
#define MAX_CONN_OUT 1024
|
||||
SQLCHAR szOutConn[MAX_CONN_OUT];
|
||||
SQLSMALLINT cbOutConn;
|
||||
|
||||
// Initialize the ODBC environment.
|
||||
//
|
||||
if (SQLAllocHandle (SQL_HANDLE_ENV, NULL, &henv) == SQL_ERROR)
|
||||
goto exit;
|
||||
|
||||
// This is an ODBC v3 application
|
||||
//
|
||||
SQLSetEnvAttr (henv, SQL_ATTR_ODBC_VERSION, (void*) SQL_OV_ODBC3, SQL_IS_INTEGER);
|
||||
|
||||
// Allocate a connection handle
|
||||
//
|
||||
if (SQLAllocHandle (SQL_HANDLE_DBC, henv, &hdbc) == SQL_ERROR)
|
||||
{
|
||||
printf ("AllocHandle on DBC failed.");
|
||||
goto exit;
|
||||
}
|
||||
|
||||
// Connect to the server using Trusted connection.
|
||||
// Trusted connection uses integrated NT security.
|
||||
// If you want to use mixed-mode Authentication, please set Trusted_Connection to no.
|
||||
rc = SQLDriverConnect(
|
||||
hdbc,
|
||||
NULL, // no diaglogs please
|
||||
(SQLCHAR*) "DRIVER={SQL Server};Trusted_Connection=yes;SERVER=(local)",
|
||||
SQL_NTS,
|
||||
szOutConn,
|
||||
MAX_CONN_OUT,
|
||||
&cbOutConn,
|
||||
SQL_DRIVER_NOPROMPT);
|
||||
|
||||
if (rc == SQL_ERROR)
|
||||
{
|
||||
SQLCHAR szSqlState[20];
|
||||
SQLINTEGER ssErr;
|
||||
SQLCHAR szErrorMsg [MAX_CONN_OUT];
|
||||
SQLSMALLINT cbErrorMsg;
|
||||
|
||||
printf ("Connect fails\n");
|
||||
|
||||
rc = SQLError (
|
||||
henv, hdbc, SQL_NULL_HSTMT,
|
||||
szSqlState,
|
||||
&ssErr,
|
||||
szErrorMsg,
|
||||
MAX_CONN_OUT,
|
||||
&cbErrorMsg);
|
||||
|
||||
printf ("msg=%s\n", szErrorMsg);
|
||||
|
||||
goto exit;
|
||||
}
|
||||
|
||||
// Get a statement handle
|
||||
//
|
||||
if (SQLAllocHandle(SQL_HANDLE_STMT, hdbc, &hstmt) == SQL_ERROR)
|
||||
{
|
||||
printf ("Failed to get statement handle\n");
|
||||
|
||||
ProcessMessages (SQL_HANDLE_DBC, hdbc, TRUE, NULL);
|
||||
goto exit;
|
||||
}
|
||||
|
||||
// Execute the SQL
|
||||
//
|
||||
printf ("Executing %s\n", sqlCommand);
|
||||
|
||||
rc = SQLExecDirect (hstmt, (SQLCHAR*)sqlCommand, SQL_NTS);
|
||||
|
||||
// Extract all the resulting messages
|
||||
//
|
||||
|
||||
SQLSMALLINT numResultCols;
|
||||
while (1)
|
||||
{
|
||||
switch (rc)
|
||||
{
|
||||
case SQL_ERROR:
|
||||
successDetected = FALSE;
|
||||
ProcessMessages (SQL_HANDLE_STMT, hstmt, TRUE, &successDetected);
|
||||
if (!successDetected)
|
||||
{
|
||||
printf ("Errors resulted in failure of the command\n");
|
||||
goto exit;
|
||||
}
|
||||
printf ("Errors were encountered but the command was able to recover and successfully complete.\n");
|
||||
break;
|
||||
|
||||
case SQL_SUCCESS_WITH_INFO:
|
||||
ProcessMessages (SQL_HANDLE_STMT, hstmt, TRUE, NULL);
|
||||
// fall through
|
||||
|
||||
case SQL_SUCCESS:
|
||||
successDetected = TRUE;
|
||||
|
||||
numResultCols = 0;
|
||||
SQLNumResultCols (hstmt, &numResultCols);
|
||||
if (numResultCols > 0)
|
||||
{
|
||||
printf ("A result set with %d columns was produced\n",
|
||||
(int)numResultCols);
|
||||
}
|
||||
break;
|
||||
|
||||
case SQL_NO_DATA:
|
||||
// All results have been processed. We are done.
|
||||
//
|
||||
goto exit;
|
||||
|
||||
case SQL_NEED_DATA:
|
||||
case SQL_INVALID_HANDLE:
|
||||
case SQL_STILL_EXECUTING:
|
||||
default:
|
||||
successDetected = FALSE;
|
||||
printf ("Unexpected SQLExec result %d\n", rc);
|
||||
goto exit;
|
||||
}
|
||||
rc = SQLMoreResults (hstmt);
|
||||
}
|
||||
|
||||
exit:
|
||||
// Release the ODBC resources.
|
||||
//
|
||||
if (hstmt != NULL)
|
||||
{
|
||||
SQLFreeHandle(SQL_HANDLE_STMT, hstmt);
|
||||
hstmt = NULL;
|
||||
}
|
||||
|
||||
if (hdbc != NULL)
|
||||
{
|
||||
SQLDisconnect(hdbc);
|
||||
SQLFreeHandle(SQL_HANDLE_DBC, hdbc);
|
||||
hdbc = NULL;
|
||||
}
|
||||
|
||||
if (henv != NULL)
|
||||
{
|
||||
SQLFreeHandle(SQL_HANDLE_ENV, henv);
|
||||
henv = NULL;
|
||||
}
|
||||
|
||||
return successDetected;
|
||||
}
|
||||
|
||||
|
||||
//------------------------------------------------------------
|
||||
//
|
||||
// execSQL: Send the SQL to the server via ODBC.
|
||||
//
|
||||
// Return the thread handle (NULL on error).
|
||||
//
|
||||
HANDLE execSQL (int doBackup)
|
||||
{
|
||||
unsigned int threadId;
|
||||
HANDLE hThread;
|
||||
|
||||
hThread = (HANDLE)_beginthreadex (
|
||||
NULL, 0, SQLRoutine, (void*)doBackup, 0, &threadId);
|
||||
if (hThread == NULL)
|
||||
{
|
||||
printf ("Failed to create thread. errno is %d\n", errno);
|
||||
}
|
||||
return hThread;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------
|
||||
//
|
||||
// checkSQL: Wait for the T-SQL to complete,
|
||||
// returns TRUE if statement successfully executed.
|
||||
//
|
||||
int checkSQL (HANDLE hThread)
|
||||
{
|
||||
if (hThread == NULL)
|
||||
return FALSE;
|
||||
|
||||
DWORD rc = WaitForSingleObject (hThread, INFINITE);
|
||||
if (rc != WAIT_OBJECT_0)
|
||||
{
|
||||
printf ("checkSQL failed: %d\n", rc);
|
||||
return FALSE;
|
||||
}
|
||||
if (!GetExitCodeThread (hThread, &rc))
|
||||
{
|
||||
printf ("failed to get exit code: %d\n", GetLastError ());
|
||||
return FALSE;
|
||||
}
|
||||
return rc == TRUE;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
//----------------------------------------------------------------------------------
|
||||
// VDI data transfer handler.
|
||||
//
|
||||
// This routine reads commands from the server until a 'Close' status is received.
|
||||
// It simply reads or writes a file 'superbak.dmp' in the current directory.
|
||||
//
|
||||
void performTransfer (
|
||||
IClientVirtualDevice* vd,
|
||||
int backup )
|
||||
{
|
||||
FILE * fh;
|
||||
char* fname = "superbak.dmp";
|
||||
VDC_Command * cmd;
|
||||
DWORD completionCode;
|
||||
DWORD bytesTransferred;
|
||||
HRESULT hr;
|
||||
|
||||
fh = fopen (fname, (backup)? "wb" : "rb");
|
||||
if (fh == NULL )
|
||||
{
|
||||
printf ("Failed to open: %s\n", fname);
|
||||
return;
|
||||
}
|
||||
|
||||
while (SUCCEEDED (hr=vd->GetCommand (INFINITE, &cmd)))
|
||||
{
|
||||
bytesTransferred = 0;
|
||||
switch (cmd->commandCode)
|
||||
{
|
||||
case VDC_Read:
|
||||
bytesTransferred = fread (cmd->buffer, 1, cmd->size, fh);
|
||||
if (bytesTransferred == cmd->size)
|
||||
completionCode = ERROR_SUCCESS;
|
||||
else
|
||||
// assume failure is eof
|
||||
completionCode = ERROR_HANDLE_EOF;
|
||||
break;
|
||||
|
||||
case VDC_Write:
|
||||
bytesTransferred = fwrite (cmd->buffer, 1, cmd->size, fh);
|
||||
if (bytesTransferred == cmd->size )
|
||||
{
|
||||
completionCode = ERROR_SUCCESS;
|
||||
}
|
||||
else
|
||||
// assume failure is disk full
|
||||
completionCode = ERROR_DISK_FULL;
|
||||
break;
|
||||
|
||||
case VDC_Flush:
|
||||
fflush (fh);
|
||||
completionCode = ERROR_SUCCESS;
|
||||
break;
|
||||
|
||||
case VDC_ClearError:
|
||||
completionCode = ERROR_SUCCESS;
|
||||
break;
|
||||
|
||||
default:
|
||||
// If command is unknown...
|
||||
completionCode = ERROR_NOT_SUPPORTED;
|
||||
}
|
||||
|
||||
hr = vd->CompleteCommand (cmd, completionCode, bytesTransferred, 0);
|
||||
if (!SUCCEEDED (hr))
|
||||
{
|
||||
printf ("Completion Failed: x%X\n", hr);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (hr != VD_E_CLOSE)
|
||||
{
|
||||
printf ("Unexpected termination: x%X\n", hr);
|
||||
}
|
||||
else
|
||||
{
|
||||
// As far as the data transfer is concerned, no
|
||||
// errors occurred. The code which issues the SQL
|
||||
// must determine if the backup/restore was
|
||||
// really successful.
|
||||
//
|
||||
printf ("Successfully completed data transfer.\n");
|
||||
}
|
||||
|
||||
fclose (fh);
|
||||
}
|
||||
@@ -0,0 +1,100 @@
|
||||
# Microsoft Developer Studio Project File - Name="osimple" - Package Owner=<4>
|
||||
# Microsoft Developer Studio Generated Build File, Format Version 6.00
|
||||
# ** DO NOT EDIT **
|
||||
|
||||
# TARGTYPE "Win32 (x86) Console Application" 0x0103
|
||||
|
||||
CFG=osimple - Win32 Debug
|
||||
!MESSAGE This is not a valid makefile. To build this project using NMAKE,
|
||||
!MESSAGE use the Export Makefile command and run
|
||||
!MESSAGE
|
||||
!MESSAGE NMAKE /f "osimple.mak".
|
||||
!MESSAGE
|
||||
!MESSAGE You can specify a configuration when running NMAKE
|
||||
!MESSAGE by defining the macro CFG on the command line. For example:
|
||||
!MESSAGE
|
||||
!MESSAGE NMAKE /f "osimple.mak" CFG="osimple - Win32 Debug"
|
||||
!MESSAGE
|
||||
!MESSAGE Possible choices for configuration are:
|
||||
!MESSAGE
|
||||
!MESSAGE "osimple - Win32 Release" (based on "Win32 (x86) Console Application")
|
||||
!MESSAGE "osimple - Win32 Debug" (based on "Win32 (x86) Console Application")
|
||||
!MESSAGE
|
||||
|
||||
# Begin Project
|
||||
# PROP AllowPerConfigDependencies 0
|
||||
# PROP Scc_ProjName ""
|
||||
# PROP Scc_LocalPath ""
|
||||
CPP=cl.exe
|
||||
RSC=rc.exe
|
||||
|
||||
!IF "$(CFG)" == "osimple - Win32 Release"
|
||||
|
||||
# PROP BASE Use_MFC 0
|
||||
# PROP BASE Use_Debug_Libraries 0
|
||||
# PROP BASE Output_Dir "Release"
|
||||
# PROP BASE Intermediate_Dir "Release"
|
||||
# PROP BASE Target_Dir ""
|
||||
# PROP Use_MFC 0
|
||||
# PROP Use_Debug_Libraries 0
|
||||
# PROP Output_Dir "Release"
|
||||
# PROP Intermediate_Dir "Release"
|
||||
# PROP Target_Dir ""
|
||||
# ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /c
|
||||
# ADD CPP /nologo /MT /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /c
|
||||
# ADD BASE RSC /l 0x409 /d "NDEBUG"
|
||||
# ADD RSC /l 0x409 /d "NDEBUG"
|
||||
BSC32=bscmake.exe
|
||||
# ADD BASE BSC32 /nologo
|
||||
# ADD BSC32 /nologo
|
||||
LINK32=link.exe
|
||||
# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /machine:I386
|
||||
# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /machine:I386
|
||||
|
||||
!ELSEIF "$(CFG)" == "osimple - Win32 Debug"
|
||||
|
||||
# PROP BASE Use_MFC 0
|
||||
# PROP BASE Use_Debug_Libraries 1
|
||||
# PROP BASE Output_Dir "Debug"
|
||||
# PROP BASE Intermediate_Dir "Debug"
|
||||
# PROP BASE Target_Dir ""
|
||||
# PROP Use_MFC 0
|
||||
# PROP Use_Debug_Libraries 1
|
||||
# PROP Output_Dir "Debug"
|
||||
# PROP Intermediate_Dir "Debug"
|
||||
# PROP Target_Dir ""
|
||||
# ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /GZ /c
|
||||
# ADD CPP /nologo /MTd /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /GZ /c
|
||||
# ADD BASE RSC /l 0x409 /d "_DEBUG"
|
||||
# ADD RSC /l 0x409 /d "_DEBUG"
|
||||
BSC32=bscmake.exe
|
||||
# ADD BASE BSC32 /nologo
|
||||
# ADD BSC32 /nologo
|
||||
LINK32=link.exe
|
||||
# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /debug /machine:I386 /pdbtype:sept
|
||||
# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /debug /machine:I386 /pdbtype:sept
|
||||
|
||||
!ENDIF
|
||||
|
||||
# Begin Target
|
||||
|
||||
# Name "osimple - Win32 Release"
|
||||
# Name "osimple - Win32 Debug"
|
||||
# Begin Group "Source Files"
|
||||
|
||||
# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat"
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=.\osimple.cpp
|
||||
# End Source File
|
||||
# End Group
|
||||
# Begin Group "Header Files"
|
||||
|
||||
# PROP Default_Filter "h;hpp;hxx;hm;inl"
|
||||
# End Group
|
||||
# Begin Group "Resource Files"
|
||||
|
||||
# PROP Default_Filter "ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe"
|
||||
# End Group
|
||||
# End Target
|
||||
# End Project
|
||||
@@ -0,0 +1,29 @@
|
||||
Microsoft Developer Studio Workspace File, Format Version 6.00
|
||||
# WARNING: DO NOT EDIT OR DELETE THIS WORKSPACE FILE!
|
||||
|
||||
###############################################################################
|
||||
|
||||
Project: "osimple"=.\osimple.dsp - Package Owner=<4>
|
||||
|
||||
Package=<5>
|
||||
{{{
|
||||
}}}
|
||||
|
||||
Package=<4>
|
||||
{{{
|
||||
}}}
|
||||
|
||||
###############################################################################
|
||||
|
||||
Global:
|
||||
|
||||
Package=<5>
|
||||
{{{
|
||||
}}}
|
||||
|
||||
Package=<3>
|
||||
{{{
|
||||
}}}
|
||||
|
||||
###############################################################################
|
||||
|
||||
@@ -0,0 +1,348 @@
|
||||
/***********************************************************************
|
||||
Copyright (c) Microsoft Corporation
|
||||
All Rights Reserved.
|
||||
***********************************************************************/
|
||||
// This source code is an intended supplement to the Microsoft SQL
|
||||
// Server online references and related electronic documentation.
|
||||
//
|
||||
// This sample is for instructional purposes only.
|
||||
// Code contained herein is not intended to be used "as is" in real applications.
|
||||
//
|
||||
// simple.cpp
|
||||
//
|
||||
// This is a sample program used to demonstrate the Virtual Device Interface
|
||||
// feature of Microsoft SQL Server.
|
||||
//
|
||||
// The program will backup or restore the 'pubs' sample database.
|
||||
//
|
||||
// The program accepts a single command line parameter.
|
||||
// One of:
|
||||
// b perform a backup
|
||||
// r perform a restore
|
||||
//
|
||||
|
||||
|
||||
// To gain access to free threaded COM, you'll need to define _WIN32_DCOM
|
||||
// before the system headers, either in the source (as in this example),
|
||||
// or when invoking the compiler (by using /D "_WIN32_DCOM")
|
||||
//
|
||||
#define _WIN32_DCOM
|
||||
|
||||
#include <objbase.h> // for 'CoInitialize()'
|
||||
#include <stdio.h> // for file operations
|
||||
#include <ctype.h> // for toupper ()
|
||||
#include <process.h> // for spawn ()
|
||||
|
||||
#include "vdi.h" // interface declaration
|
||||
#include "vdierror.h" // error constants
|
||||
|
||||
#include "vdiguid.h" // define the interface identifiers.
|
||||
// IMPORTANT: vdiguid.h can only be included in one source file.
|
||||
//
|
||||
|
||||
|
||||
void performTransfer (
|
||||
IClientVirtualDevice* vd,
|
||||
int backup );
|
||||
|
||||
int execSQL (int doBackup);
|
||||
|
||||
// Using a GUID for the VDS Name is a good way to assure uniqueness.
|
||||
//
|
||||
WCHAR wVdsName [50];
|
||||
|
||||
|
||||
//
|
||||
// main function
|
||||
//
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
HRESULT hr;
|
||||
IClientVirtualDeviceSet2* vds = NULL ;
|
||||
IClientVirtualDevice* vd=NULL;
|
||||
|
||||
VDConfig config;
|
||||
int badParm=TRUE;
|
||||
int doBackup;
|
||||
int hProcess;
|
||||
int termCode;
|
||||
|
||||
// Check the input parm
|
||||
//
|
||||
if (argc == 2)
|
||||
{
|
||||
if (toupper(argv[1][0]) == 'B')
|
||||
{
|
||||
doBackup = TRUE;
|
||||
badParm = FALSE;
|
||||
}
|
||||
else if (toupper(argv[1][0]) == 'R')
|
||||
{
|
||||
doBackup = FALSE;
|
||||
badParm = FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
if (badParm)
|
||||
{
|
||||
printf ("useage: simple {B|R}\n"
|
||||
"Demonstrate a Backup or Restore using the Virtual Device Interface\n");
|
||||
exit (1);
|
||||
}
|
||||
|
||||
// Initialize COM Library
|
||||
// Note: _WIN32_DCOM must be defined during the compile.
|
||||
//
|
||||
hr = CoInitializeEx (NULL, COINIT_MULTITHREADED);
|
||||
|
||||
if (!SUCCEEDED (hr))
|
||||
{
|
||||
printf ("Coinit fails: x%X\n", hr);
|
||||
exit (1);
|
||||
}
|
||||
|
||||
// Get an interface to the device set.
|
||||
// Changes from SQLServer v7.0:
|
||||
// - we've introduced a symbol for the class ID.
|
||||
// - We've added a new "multi-instance-aware" interface.
|
||||
//
|
||||
hr = CoCreateInstance (
|
||||
CLSID_MSSQL_ClientVirtualDeviceSet,
|
||||
NULL,
|
||||
CLSCTX_INPROC_SERVER,
|
||||
IID_IClientVirtualDeviceSet2,
|
||||
(void**)&vds);
|
||||
|
||||
if (!SUCCEEDED (hr))
|
||||
{
|
||||
// This failure might happen if the DLL was not registered,
|
||||
// or if the application is using the wrong interface id (IID).
|
||||
//
|
||||
printf ("Could not create component: x%X\n", hr);
|
||||
printf ("Check registration of SQLVDI.DLL and value of IID\n");
|
||||
goto exit;
|
||||
}
|
||||
|
||||
// Setup the VDI configuration we want to use.
|
||||
// This program doesn't use any fancy features, so the
|
||||
// only field to setup is the deviceCount.
|
||||
//
|
||||
// The server will treat the virtual device just like a pipe:
|
||||
// I/O will be strictly sequential with only the basic commands.
|
||||
//
|
||||
memset (&config, 0, sizeof(config));
|
||||
config.deviceCount = 1;
|
||||
|
||||
// Create a GUID to use for a unique virtual device name
|
||||
//
|
||||
GUID vdsId;
|
||||
CoCreateGuid (&vdsId);
|
||||
StringFromGUID2 (vdsId, wVdsName, 49);
|
||||
|
||||
// Create the virtual device set
|
||||
// for use by the default instance.
|
||||
// Change the first parameter to identify a named instance.
|
||||
//
|
||||
hr = vds->CreateEx (NULL, wVdsName, &config);
|
||||
if (!SUCCEEDED (hr))
|
||||
{
|
||||
printf ("VDS::Create fails: x%X", hr);
|
||||
goto exit;
|
||||
}
|
||||
|
||||
// Send the SQL command, by starting 'isql' in a subprocess.
|
||||
//
|
||||
printf("\nSending the SQL...\n");
|
||||
|
||||
hProcess = execSQL (doBackup);
|
||||
if (hProcess == -1)
|
||||
{
|
||||
printf ("execSQL failed.\n");
|
||||
goto shutdown;
|
||||
}
|
||||
|
||||
// Wait for the server to connect, completing the configuration.
|
||||
//
|
||||
hr = vds->GetConfiguration (10000, &config);
|
||||
if (!SUCCEEDED (hr))
|
||||
{
|
||||
printf ("VDS::Getconfig fails: x%X\n", hr);
|
||||
if (hr == VD_E_TIMEOUT)
|
||||
{
|
||||
printf ("Timed out. Was Microsoft SQLServer running?\n");
|
||||
}
|
||||
goto shutdown;
|
||||
}
|
||||
|
||||
// Open the single device in the set.
|
||||
//
|
||||
hr = vds->OpenDevice (wVdsName, &vd);
|
||||
if (!SUCCEEDED(hr))
|
||||
{
|
||||
printf ("VDS::OpenDevice fails: x%X\n", hr);
|
||||
goto shutdown;
|
||||
}
|
||||
|
||||
printf ("\nPerforming data transfer...\n");
|
||||
|
||||
performTransfer (vd, doBackup);
|
||||
|
||||
|
||||
shutdown:
|
||||
|
||||
// Close the set
|
||||
//
|
||||
vds->Close ();
|
||||
|
||||
// Obtain the SQL completion information, by waiting for isql to exit.
|
||||
//
|
||||
if (hProcess == _cwait( &termCode, hProcess, NULL))
|
||||
{
|
||||
if (termCode == 0)
|
||||
printf("\nThe SQL command executed successfully.\n");
|
||||
else
|
||||
printf("\nThe SQL command failed.\n");
|
||||
}
|
||||
else
|
||||
{
|
||||
printf ("cwait failed: %d\n", errno);
|
||||
}
|
||||
|
||||
// COM reference counting: Release the interface.
|
||||
//
|
||||
// Rather than releasing it, the application has the
|
||||
// option to 'Create' another set, reusing the interface
|
||||
// that it currently has. Of course that applies to
|
||||
// a real application, not this simple sample!
|
||||
//
|
||||
vds->Release () ;
|
||||
|
||||
exit:
|
||||
|
||||
// Uninitialize COM Library
|
||||
//
|
||||
CoUninitialize () ;
|
||||
|
||||
return 0 ;
|
||||
}
|
||||
|
||||
//
|
||||
// Execute a basic backup/restore, by spawning a process to execute 'isql'.
|
||||
//
|
||||
// Returns:
|
||||
// -1 : failed to spawn
|
||||
// else : a "process handle"
|
||||
//
|
||||
int execSQL (int doBackup)
|
||||
{
|
||||
char sqlCommand [1024]; // plenty of space for our purpose
|
||||
|
||||
sprintf (sqlCommand, "-Q\"%s DATABASE PUBS %s VIRTUAL_DEVICE='%ls'\"",
|
||||
(doBackup) ? "BACKUP" : "RESTORE",
|
||||
(doBackup) ? "TO" : "FROM",
|
||||
wVdsName);
|
||||
int rc;
|
||||
|
||||
printf ("spawning osql to execute: %s\n", sqlCommand);
|
||||
|
||||
// Spawn off the osql utility to execute the SQL.
|
||||
// Notice the '-b' which causes an error to set a non-zero
|
||||
// exit code on error.
|
||||
//
|
||||
rc = _spawnlp( _P_NOWAIT, "osql", "osql", "-E", "-b",
|
||||
sqlCommand, NULL);
|
||||
|
||||
if (rc == -1)
|
||||
{
|
||||
printf ("Spawn failed with error: %d\n", errno);
|
||||
}
|
||||
|
||||
return (rc);
|
||||
}
|
||||
|
||||
// This routine reads commands from the server until a 'Close' status is received.
|
||||
// It simply reads or writes a file 'superbak.dmp' in the current directory.
|
||||
//
|
||||
void performTransfer (
|
||||
IClientVirtualDevice* vd,
|
||||
int backup )
|
||||
{
|
||||
FILE * fh;
|
||||
char* fname = "superbak.dmp";
|
||||
VDC_Command * cmd;
|
||||
DWORD completionCode;
|
||||
DWORD bytesTransferred;
|
||||
HRESULT hr;
|
||||
|
||||
fh = fopen (fname, (backup)? "wb" : "rb");
|
||||
if (fh == NULL )
|
||||
{
|
||||
printf ("Failed to open: %s\n", fname);
|
||||
return;
|
||||
}
|
||||
|
||||
while (SUCCEEDED (hr=vd->GetCommand (INFINITE, &cmd)))
|
||||
{
|
||||
bytesTransferred = 0;
|
||||
switch (cmd->commandCode)
|
||||
{
|
||||
case VDC_Read:
|
||||
bytesTransferred = fread (cmd->buffer, 1, cmd->size, fh);
|
||||
if (bytesTransferred == cmd->size)
|
||||
completionCode = ERROR_SUCCESS;
|
||||
else
|
||||
// assume failure is eof
|
||||
completionCode = ERROR_HANDLE_EOF;
|
||||
break;
|
||||
|
||||
case VDC_Write:
|
||||
bytesTransferred = fwrite (cmd->buffer, 1, cmd->size, fh);
|
||||
if (bytesTransferred == cmd->size )
|
||||
{
|
||||
completionCode = ERROR_SUCCESS;
|
||||
}
|
||||
else
|
||||
// assume failure is disk full
|
||||
completionCode = ERROR_DISK_FULL;
|
||||
break;
|
||||
|
||||
case VDC_Flush:
|
||||
fflush (fh);
|
||||
completionCode = ERROR_SUCCESS;
|
||||
break;
|
||||
|
||||
case VDC_ClearError:
|
||||
completionCode = ERROR_SUCCESS;
|
||||
break;
|
||||
|
||||
default:
|
||||
// If command is unknown...
|
||||
completionCode = ERROR_NOT_SUPPORTED;
|
||||
}
|
||||
|
||||
hr = vd->CompleteCommand (cmd, completionCode, bytesTransferred, 0);
|
||||
if (!SUCCEEDED (hr))
|
||||
{
|
||||
printf ("Completion Failed: x%X\n", hr);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (hr != VD_E_CLOSE)
|
||||
{
|
||||
printf ("Unexpected termination: x%X\n", hr);
|
||||
}
|
||||
else
|
||||
{
|
||||
// As far as the data transfer is concerned, no
|
||||
// errors occurred. The code which issues the SQL
|
||||
// must determine if the backup/restore was
|
||||
// really successful.
|
||||
//
|
||||
printf ("Successfully completed data transfer.\n");
|
||||
}
|
||||
|
||||
fclose (fh);
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,100 @@
|
||||
# Microsoft Developer Studio Project File - Name="simple" - Package Owner=<4>
|
||||
# Microsoft Developer Studio Generated Build File, Format Version 6.00
|
||||
# ** DO NOT EDIT **
|
||||
|
||||
# TARGTYPE "Win32 (x86) Console Application" 0x0103
|
||||
|
||||
CFG=simple - Win32 Debug
|
||||
!MESSAGE This is not a valid makefile. To build this project using NMAKE,
|
||||
!MESSAGE use the Export Makefile command and run
|
||||
!MESSAGE
|
||||
!MESSAGE NMAKE /f "simple.mak".
|
||||
!MESSAGE
|
||||
!MESSAGE You can specify a configuration when running NMAKE
|
||||
!MESSAGE by defining the macro CFG on the command line. For example:
|
||||
!MESSAGE
|
||||
!MESSAGE NMAKE /f "simple.mak" CFG="simple - Win32 Debug"
|
||||
!MESSAGE
|
||||
!MESSAGE Possible choices for configuration are:
|
||||
!MESSAGE
|
||||
!MESSAGE "simple - Win32 Release" (based on "Win32 (x86) Console Application")
|
||||
!MESSAGE "simple - Win32 Debug" (based on "Win32 (x86) Console Application")
|
||||
!MESSAGE
|
||||
|
||||
# Begin Project
|
||||
# PROP AllowPerConfigDependencies 0
|
||||
# PROP Scc_ProjName ""
|
||||
# PROP Scc_LocalPath ""
|
||||
CPP=cl.exe
|
||||
RSC=rc.exe
|
||||
|
||||
!IF "$(CFG)" == "simple - Win32 Release"
|
||||
|
||||
# PROP BASE Use_MFC 0
|
||||
# PROP BASE Use_Debug_Libraries 0
|
||||
# PROP BASE Output_Dir "Release"
|
||||
# PROP BASE Intermediate_Dir "Release"
|
||||
# PROP BASE Target_Dir ""
|
||||
# PROP Use_MFC 0
|
||||
# PROP Use_Debug_Libraries 0
|
||||
# PROP Output_Dir "Release"
|
||||
# PROP Intermediate_Dir "Release"
|
||||
# PROP Target_Dir ""
|
||||
# ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /c
|
||||
# ADD CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /c
|
||||
# ADD BASE RSC /l 0x409 /d "NDEBUG"
|
||||
# ADD RSC /l 0x409 /d "NDEBUG"
|
||||
BSC32=bscmake.exe
|
||||
# ADD BASE BSC32 /nologo
|
||||
# ADD BSC32 /nologo
|
||||
LINK32=link.exe
|
||||
# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /machine:I386
|
||||
# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /machine:I386
|
||||
|
||||
!ELSEIF "$(CFG)" == "simple - Win32 Debug"
|
||||
|
||||
# PROP BASE Use_MFC 0
|
||||
# PROP BASE Use_Debug_Libraries 1
|
||||
# PROP BASE Output_Dir "Debug"
|
||||
# PROP BASE Intermediate_Dir "Debug"
|
||||
# PROP BASE Target_Dir ""
|
||||
# PROP Use_MFC 0
|
||||
# PROP Use_Debug_Libraries 1
|
||||
# PROP Output_Dir "Debug"
|
||||
# PROP Intermediate_Dir "Debug"
|
||||
# PROP Target_Dir ""
|
||||
# ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /GZ /c
|
||||
# ADD CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /GZ /c
|
||||
# ADD BASE RSC /l 0x409 /d "_DEBUG"
|
||||
# ADD RSC /l 0x409 /d "_DEBUG"
|
||||
BSC32=bscmake.exe
|
||||
# ADD BASE BSC32 /nologo
|
||||
# ADD BSC32 /nologo
|
||||
LINK32=link.exe
|
||||
# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /debug /machine:I386 /pdbtype:sept
|
||||
# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /debug /machine:I386 /pdbtype:sept
|
||||
|
||||
!ENDIF
|
||||
|
||||
# Begin Target
|
||||
|
||||
# Name "simple - Win32 Release"
|
||||
# Name "simple - Win32 Debug"
|
||||
# Begin Group "Source Files"
|
||||
|
||||
# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat"
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=.\simple.cpp
|
||||
# End Source File
|
||||
# End Group
|
||||
# Begin Group "Header Files"
|
||||
|
||||
# PROP Default_Filter "h;hpp;hxx;hm;inl"
|
||||
# End Group
|
||||
# Begin Group "Resource Files"
|
||||
|
||||
# PROP Default_Filter "ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe"
|
||||
# End Group
|
||||
# End Target
|
||||
# End Project
|
||||
@@ -0,0 +1,29 @@
|
||||
Microsoft Developer Studio Workspace File, Format Version 6.00
|
||||
# WARNING: DO NOT EDIT OR DELETE THIS WORKSPACE FILE!
|
||||
|
||||
###############################################################################
|
||||
|
||||
Project: "simple"=.\simple.dsp - Package Owner=<4>
|
||||
|
||||
Package=<5>
|
||||
{{{
|
||||
}}}
|
||||
|
||||
Package=<4>
|
||||
{{{
|
||||
}}}
|
||||
|
||||
###############################################################################
|
||||
|
||||
Global:
|
||||
|
||||
Package=<5>
|
||||
{{{
|
||||
}}}
|
||||
|
||||
Package=<3>
|
||||
{{{
|
||||
}}}
|
||||
|
||||
###############################################################################
|
||||
|
||||
@@ -0,0 +1,828 @@
|
||||
/***********************************************************************
|
||||
Copyright (c) Microsoft Corporation
|
||||
All Rights Reserved.
|
||||
***********************************************************************/
|
||||
// This source code is an intended supplement to the Microsoft SQL
|
||||
// Server online references and related electronic documentation.
|
||||
//
|
||||
// This sample is for instructional purposes only.
|
||||
// Code contained herein is not intended to be used "as is" in real applications.
|
||||
//
|
||||
// snapshot.cpp
|
||||
//
|
||||
// This sample extends the "osimple.cpp" sample to demonstrate BACKUP WITH SNAPSHOT.
|
||||
// It is not fully functional. The ability to take/mount snapshots must be provided.
|
||||
//
|
||||
// This is a sample program used to demonstrate the Virtual Device Interface
|
||||
// feature of Microsoft SQL Server together with an ODBC connection.
|
||||
//
|
||||
// The program will request backup or restore of a single database
|
||||
// on some instance of sql server.
|
||||
//
|
||||
// The program accepts input:
|
||||
// {b | r} <databaseName> [<instanceName>]
|
||||
// b -> backup
|
||||
// r -> restore
|
||||
//
|
||||
|
||||
|
||||
// To gain access to free threaded COM, you'll need to define _WIN32_DCOM
|
||||
// before the system headers, either in the source (as in this example),
|
||||
// or when invoking the compiler (by using /D "_WIN32_DCOM")
|
||||
//
|
||||
// This sample uses Windows NT Authentication, other than mixed mode security,
|
||||
// to establish connections. If you want to use mixed mode security, please
|
||||
// set Trusted_Connection to no in the SQLDriverConnect command.
|
||||
// Make necessary change to the server name in the SQLDriverConnect command.
|
||||
//
|
||||
#define _WIN32_DCOM
|
||||
|
||||
#include <objbase.h> // for 'CoInitialize()'
|
||||
#include <stdio.h> // for file operations
|
||||
#include <ctype.h> // for toupper ()
|
||||
#include <process.h> // for C library-safe _beginthreadex,_endthreadex
|
||||
|
||||
#include "vdi.h" // interface declaration
|
||||
#include "vdierror.h" // error constants
|
||||
|
||||
#include "vdiguid.h" // define the interface identifiers.
|
||||
// IMPORTANT: vdiguid.h can only be included in one source file.
|
||||
//
|
||||
|
||||
#include <windows.h>
|
||||
#include "sql.h"
|
||||
#include "sqlext.h"
|
||||
#include "odbcss.h"
|
||||
|
||||
void performTransfer (
|
||||
IClientVirtualDeviceSet2* vds,
|
||||
IClientVirtualDevice* vd,
|
||||
int backup );
|
||||
|
||||
HANDLE execSQL (int doBackup, char* pInstanceName, char* pDbName, WCHAR* pVdsName);
|
||||
int checkSQL (HANDLE);
|
||||
|
||||
BOOL
|
||||
ynPrompt (char *str);
|
||||
|
||||
//------------------------------------------------------------
|
||||
//
|
||||
// Mainline
|
||||
//
|
||||
int __cdecl
|
||||
main (int argc, char *argv[])
|
||||
{
|
||||
HRESULT hr;
|
||||
IClientVirtualDeviceSet2* vds = NULL ;
|
||||
IClientVirtualDevice* vd=NULL;
|
||||
|
||||
VDConfig config;
|
||||
int badParm=TRUE;
|
||||
int doBackup;
|
||||
HANDLE hThread = NULL;
|
||||
char* pDbName = NULL;
|
||||
char* pInstanceName = NULL;
|
||||
|
||||
// Check the input parm
|
||||
//
|
||||
if (argc >= 3)
|
||||
{
|
||||
if (toupper(argv[1][0]) == 'B')
|
||||
{
|
||||
doBackup = TRUE;
|
||||
badParm = FALSE;
|
||||
}
|
||||
else if (toupper(argv[1][0]) == 'R')
|
||||
{
|
||||
doBackup = FALSE;
|
||||
badParm = FALSE;
|
||||
}
|
||||
|
||||
pDbName = argv[2];
|
||||
|
||||
if (argc == 4)
|
||||
{
|
||||
pInstanceName = argv[3];
|
||||
}
|
||||
}
|
||||
|
||||
if (badParm)
|
||||
{
|
||||
printf ("useage: snapshot {B|R} <databaseName> [<instanceName>]\n"
|
||||
"Demonstrate a Backup or Restore WITH SNAPSHOT\n");
|
||||
printf ("\n\n** NOTE **\n The ability to take or mount snapshots must be implemented\n"
|
||||
"before this sample is truely functional.\n");
|
||||
exit (1);
|
||||
}
|
||||
|
||||
printf ("Performing a %s of %s on %s using a VIRTUAL_DEVICE.\n",
|
||||
(doBackup) ? "BACKUP" : "RESTORE",
|
||||
pDbName,
|
||||
(pInstanceName) ? pInstanceName : "Default");
|
||||
|
||||
// Initialize COM Library
|
||||
// Note: _WIN32_DCOM must be defined during the compile.
|
||||
//
|
||||
hr = CoInitializeEx (NULL, COINIT_MULTITHREADED);
|
||||
|
||||
if (!SUCCEEDED (hr))
|
||||
{
|
||||
printf ("Coinit fails: x%X\n", hr);
|
||||
exit (1);
|
||||
}
|
||||
|
||||
// Get an interface to the device set.
|
||||
// Notice how we use a single IID for both the class and interface
|
||||
// identifiers.
|
||||
//
|
||||
hr = CoCreateInstance (
|
||||
CLSID_MSSQL_ClientVirtualDeviceSet,
|
||||
NULL,
|
||||
CLSCTX_INPROC_SERVER,
|
||||
IID_IClientVirtualDeviceSet2,
|
||||
(void**)&vds);
|
||||
|
||||
if (!SUCCEEDED (hr))
|
||||
{
|
||||
// This failure might happen if the DLL was not registered,
|
||||
// or if the application is using the wrong interface id (IID).
|
||||
//
|
||||
printf ("Could not create component: x%X\n", hr);
|
||||
printf ("Check registration of SQLVDI.DLL and value of IID\n");
|
||||
goto exit;
|
||||
}
|
||||
|
||||
// Setup the VDI configuration we want to use.
|
||||
// This program doesn't use any fancy features, so the
|
||||
// only field to setup is the deviceCount.
|
||||
//
|
||||
// The server will treat the virtual device just like a pipe:
|
||||
// I/O will be strictly sequential with only the basic commands.
|
||||
//
|
||||
memset (&config, 0, sizeof(config));
|
||||
config.deviceCount = 1;
|
||||
config.features = VDF_SnapshotPrepare;
|
||||
|
||||
// Create a GUID to use for a unique virtual device name
|
||||
//
|
||||
GUID vdsId;
|
||||
WCHAR wVdsName [50];
|
||||
CoCreateGuid (&vdsId);
|
||||
StringFromGUID2 (vdsId, wVdsName, 49);
|
||||
|
||||
// Create the virtual device set
|
||||
// Notice that we only support unicode interfaces
|
||||
//
|
||||
WCHAR wInstanceName [128];
|
||||
int rc = 0;
|
||||
if (pInstanceName)
|
||||
{
|
||||
rc = MultiByteToWideChar (CP_ACP, 0,
|
||||
pInstanceName, strlen (pInstanceName),
|
||||
wInstanceName, 127);
|
||||
}
|
||||
wInstanceName [rc] = 0;
|
||||
|
||||
hr = vds->CreateEx (wInstanceName, wVdsName, &config);
|
||||
if (!SUCCEEDED (hr))
|
||||
{
|
||||
printf ("VDS::Create fails: x%X", hr);
|
||||
goto exit;
|
||||
}
|
||||
|
||||
// Send the SQL command, by starting a thread to handle the ODBC
|
||||
//
|
||||
printf("\nSending the SQL...\n");
|
||||
|
||||
hThread = execSQL (doBackup, pInstanceName, pDbName, wVdsName);
|
||||
if (hThread == NULL)
|
||||
{
|
||||
printf ("execSQL failed.\n");
|
||||
goto shutdown;
|
||||
}
|
||||
|
||||
// Wait for the server to connect, completing the configuration.
|
||||
//
|
||||
printf ("\nWaiting for SQLServer to respond...\n");
|
||||
|
||||
while (!SUCCEEDED (hr=vds->GetConfiguration (1000, &config)))
|
||||
{
|
||||
if (hr == VD_E_TIMEOUT)
|
||||
{
|
||||
// Check on the SQL thread
|
||||
//
|
||||
DWORD rc = WaitForSingleObject (hThread, 1000);
|
||||
if (rc == WAIT_OBJECT_0)
|
||||
{
|
||||
printf ("SQL command failed before VD transfer\n");
|
||||
goto shutdown;
|
||||
}
|
||||
if (rc == WAIT_TIMEOUT)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
printf ("Check on SQL failed: %d\n", rc);
|
||||
goto shutdown;
|
||||
}
|
||||
|
||||
printf ("VDS::Getconfig fails: x%X\n", hr);
|
||||
goto shutdown;
|
||||
}
|
||||
|
||||
// Open the single device in the set.
|
||||
//
|
||||
hr = vds->OpenDevice (wVdsName, &vd);
|
||||
if (!SUCCEEDED(hr))
|
||||
{
|
||||
printf ("VDS::OpenDevice fails: x%X\n", hr);
|
||||
goto shutdown;
|
||||
}
|
||||
|
||||
printf ("\nPerforming data transfer...\n");
|
||||
|
||||
performTransfer (vds, vd, doBackup);
|
||||
|
||||
|
||||
shutdown:
|
||||
|
||||
// Close the set
|
||||
//
|
||||
vds->Close ();
|
||||
|
||||
// Obtain the SQL completion information
|
||||
//
|
||||
if (hThread != NULL)
|
||||
{
|
||||
if (checkSQL (hThread))
|
||||
printf("\nThe SQL command executed successfully.\n");
|
||||
else
|
||||
printf("\nThe SQL command failed.\n");
|
||||
|
||||
CloseHandle (hThread);
|
||||
}
|
||||
|
||||
// COM reference counting: Release the interface.
|
||||
//
|
||||
vds->Release () ;
|
||||
|
||||
exit:
|
||||
// Uninitialize COM Library
|
||||
//
|
||||
CoUninitialize () ;
|
||||
|
||||
return 0 ;
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
// ODBC Message processing routine.
|
||||
// This is SQLServer specific, to show native message IDs, sev, state.
|
||||
//
|
||||
// The routine will watch for message 3014 in order to detect
|
||||
// a successful backup/restore operation. This is useful for
|
||||
// operations like RESTORE which can sometimes recover from
|
||||
// errors (error messages will be followed by the 3014 success message).
|
||||
//
|
||||
void ProcessMessages (
|
||||
SQLSMALLINT handle_type, // ODBC handle type
|
||||
SQLHANDLE handle, // ODBC handle
|
||||
int ConnInd, // TRUE if sucessful connection made
|
||||
int* pBackupSuccess) // Set TRUE if a 3014 message is seen.
|
||||
{
|
||||
RETCODE plm_retcode = SQL_SUCCESS;
|
||||
UCHAR plm_szSqlState[SQL_SQLSTATE_SIZE + 1];
|
||||
UCHAR plm_szErrorMsg[SQL_MAX_MESSAGE_LENGTH + 1];
|
||||
SDWORD plm_pfNativeError = 0L;
|
||||
SWORD plm_pcbErrorMsg = 0;
|
||||
SQLSMALLINT plm_cRecNmbr = 1;
|
||||
SDWORD plm_SS_MsgState = 0, plm_SS_Severity = 0;
|
||||
SQLINTEGER plm_Rownumber = 0;
|
||||
USHORT plm_SS_Line;
|
||||
SQLSMALLINT plm_cbSS_Procname, plm_cbSS_Srvname;
|
||||
SQLCHAR plm_SS_Procname[MAXNAME], plm_SS_Srvname[MAXNAME];
|
||||
|
||||
while (plm_retcode != SQL_NO_DATA_FOUND)
|
||||
{
|
||||
plm_retcode = SQLGetDiagRec(handle_type, handle,
|
||||
plm_cRecNmbr, plm_szSqlState, &plm_pfNativeError,
|
||||
plm_szErrorMsg, SQL_MAX_MESSAGE_LENGTH, &plm_pcbErrorMsg);
|
||||
|
||||
// Note that if the application has not yet made a
|
||||
// successful connection, the SQLGetDiagField
|
||||
// information has not yet been cached by ODBC
|
||||
// Driver Manager and these calls to SQLGetDiagField
|
||||
// will fail.
|
||||
//
|
||||
if (plm_retcode != SQL_NO_DATA_FOUND)
|
||||
{
|
||||
if (ConnInd)
|
||||
{
|
||||
plm_retcode = SQLGetDiagField(
|
||||
handle_type, handle, plm_cRecNmbr,
|
||||
SQL_DIAG_ROW_NUMBER, &plm_Rownumber,
|
||||
SQL_IS_INTEGER,
|
||||
NULL);
|
||||
|
||||
plm_retcode = SQLGetDiagField(
|
||||
handle_type, handle, plm_cRecNmbr,
|
||||
SQL_DIAG_SS_LINE, &plm_SS_Line,
|
||||
SQL_IS_INTEGER,
|
||||
NULL);
|
||||
|
||||
plm_retcode = SQLGetDiagField(
|
||||
handle_type, handle, plm_cRecNmbr,
|
||||
SQL_DIAG_SS_MSGSTATE, &plm_SS_MsgState,
|
||||
SQL_IS_INTEGER,
|
||||
NULL);
|
||||
|
||||
plm_retcode = SQLGetDiagField(
|
||||
handle_type, handle, plm_cRecNmbr,
|
||||
SQL_DIAG_SS_SEVERITY, &plm_SS_Severity,
|
||||
SQL_IS_INTEGER,
|
||||
NULL);
|
||||
|
||||
plm_retcode = SQLGetDiagField(
|
||||
handle_type, handle, plm_cRecNmbr,
|
||||
SQL_DIAG_SS_PROCNAME, &plm_SS_Procname,
|
||||
sizeof(plm_SS_Procname),
|
||||
&plm_cbSS_Procname);
|
||||
|
||||
plm_retcode = SQLGetDiagField(
|
||||
handle_type, handle, plm_cRecNmbr,
|
||||
SQL_DIAG_SS_SRVNAME, &plm_SS_Srvname,
|
||||
sizeof(plm_SS_Srvname),
|
||||
&plm_cbSS_Srvname);
|
||||
|
||||
printf ("Msg %d, SevLevel %d, State %d, SQLState %s\n",
|
||||
plm_pfNativeError,
|
||||
plm_SS_Severity,
|
||||
plm_SS_MsgState,
|
||||
plm_szSqlState);
|
||||
}
|
||||
|
||||
printf ("%s\n", plm_szErrorMsg);
|
||||
|
||||
if (pBackupSuccess && plm_pfNativeError == 3014)
|
||||
{
|
||||
*pBackupSuccess = TRUE;
|
||||
}
|
||||
}
|
||||
|
||||
plm_cRecNmbr++; //Increment to next diagnostic record.
|
||||
} // End while.
|
||||
}
|
||||
|
||||
|
||||
|
||||
//------------------------------------------------------------------
|
||||
// The mainline of the ODBC thread.
|
||||
//
|
||||
// Returns TRUE if a successful backup/restore is performed.
|
||||
//
|
||||
struct PARMS
|
||||
{
|
||||
int doBackup;
|
||||
char* pInstanceName;
|
||||
char* pDbName;
|
||||
WCHAR* pVdsName;
|
||||
};
|
||||
|
||||
unsigned __stdcall
|
||||
SQLRoutine (void* input)
|
||||
{
|
||||
PARMS* parms = (PARMS*)input;
|
||||
|
||||
SQLCHAR* pSQLText; // the command being executed.
|
||||
int successDetected = FALSE;
|
||||
|
||||
// ODBC handles
|
||||
//
|
||||
SQLHENV henv = NULL;
|
||||
SQLHDBC hdbc = NULL;
|
||||
SQLHSTMT hstmt = NULL;
|
||||
|
||||
char sqlCommand [1024];
|
||||
|
||||
int sentSQL = FALSE;
|
||||
int rc;
|
||||
|
||||
#define MAX_CONN_OUT 1024
|
||||
SQLCHAR szOutConn[MAX_CONN_OUT];
|
||||
SQLSMALLINT cbOutConn;
|
||||
|
||||
// Convert the VDSNAME for use by the non-unicode interfaces we're using here.
|
||||
//
|
||||
char aVdsName [50];
|
||||
int aSize;
|
||||
aSize = WideCharToMultiByte (
|
||||
CP_ACP, 0,
|
||||
parms->pVdsName,
|
||||
-1, // null terminated, so calculate
|
||||
aVdsName, 49,
|
||||
NULL, NULL ); // don't worry about fancy conversions
|
||||
|
||||
|
||||
// Generate the command to execute
|
||||
//
|
||||
sprintf (sqlCommand, "%s DATABASE [%s] %s VIRTUAL_DEVICE='%s' WITH SNAPSHOT",
|
||||
parms->doBackup ? "BACKUP" : "RESTORE",
|
||||
parms->pDbName,
|
||||
parms->doBackup ? "TO" : "FROM",
|
||||
aVdsName);
|
||||
|
||||
// Initialize the ODBC environment.
|
||||
//
|
||||
if (SQLAllocHandle (SQL_HANDLE_ENV, NULL, &henv) == SQL_ERROR)
|
||||
goto exit;
|
||||
|
||||
// This is an ODBC v3 application
|
||||
//
|
||||
SQLSetEnvAttr (henv, SQL_ATTR_ODBC_VERSION, (void*) SQL_OV_ODBC3, SQL_IS_INTEGER);
|
||||
|
||||
// Allocate a connection handle
|
||||
//
|
||||
if (SQLAllocHandle (SQL_HANDLE_DBC, henv, &hdbc) == SQL_ERROR)
|
||||
{
|
||||
printf ("AllocHandle on DBC failed.");
|
||||
goto exit;
|
||||
}
|
||||
|
||||
char connectString [200];
|
||||
strcpy (connectString, "DRIVER={SQL Server};Trusted_Connection=yes;SERVER=.");
|
||||
if (parms->pInstanceName)
|
||||
{
|
||||
sprintf (connectString+strlen(connectString), "\\%s", parms->pInstanceName);
|
||||
}
|
||||
|
||||
printf ("\n\nConnecting with: %s\n", connectString);
|
||||
|
||||
// Connect to the server using Trusted connection.
|
||||
// Trusted connection uses integrated NT security.
|
||||
// If you want to use mixed-mode Authentication, please set Trusted_Connection to no.
|
||||
rc = SQLDriverConnect(
|
||||
hdbc,
|
||||
NULL, // no diaglogs please
|
||||
(SQLCHAR*) connectString,
|
||||
SQL_NTS,
|
||||
szOutConn,
|
||||
MAX_CONN_OUT,
|
||||
&cbOutConn,
|
||||
SQL_DRIVER_NOPROMPT);
|
||||
|
||||
if (rc == SQL_ERROR)
|
||||
{
|
||||
SQLCHAR szSqlState[20];
|
||||
SQLINTEGER ssErr;
|
||||
SQLCHAR szErrorMsg [MAX_CONN_OUT];
|
||||
SQLSMALLINT cbErrorMsg;
|
||||
|
||||
printf ("Connect fails\n");
|
||||
|
||||
rc = SQLError (
|
||||
henv, hdbc, SQL_NULL_HSTMT,
|
||||
szSqlState,
|
||||
&ssErr,
|
||||
szErrorMsg,
|
||||
MAX_CONN_OUT,
|
||||
&cbErrorMsg);
|
||||
|
||||
printf ("msg=%s\n", szErrorMsg);
|
||||
|
||||
goto exit;
|
||||
}
|
||||
|
||||
// Get a statement handle
|
||||
//
|
||||
if (SQLAllocHandle(SQL_HANDLE_STMT, hdbc, &hstmt) == SQL_ERROR)
|
||||
{
|
||||
printf ("Failed to get statement handle\n");
|
||||
|
||||
ProcessMessages (SQL_HANDLE_DBC, hdbc, TRUE, NULL);
|
||||
goto exit;
|
||||
}
|
||||
|
||||
// Execute the SQL
|
||||
//
|
||||
printf ("\n\nExecuting SQL: %s\n", sqlCommand);
|
||||
|
||||
pSQLText = (SQLCHAR*)sqlCommand;
|
||||
|
||||
rc = SQLExecDirect (hstmt, pSQLText, SQL_NTS);
|
||||
|
||||
// Extract all the resulting messages
|
||||
//
|
||||
|
||||
SQLSMALLINT numResultCols;
|
||||
while (1)
|
||||
{
|
||||
switch (rc)
|
||||
{
|
||||
case SQL_ERROR:
|
||||
successDetected = FALSE;
|
||||
ProcessMessages (SQL_HANDLE_STMT, hstmt, TRUE, &successDetected);
|
||||
if (!successDetected)
|
||||
{
|
||||
printf ("Errors resulted in failure of the command\n");
|
||||
goto exit;
|
||||
}
|
||||
printf ("Errors were encountered but the command was able to recover and successfully complete.\n");
|
||||
break;
|
||||
|
||||
case SQL_SUCCESS_WITH_INFO:
|
||||
ProcessMessages (SQL_HANDLE_STMT, hstmt, TRUE, NULL);
|
||||
// fall through
|
||||
|
||||
case SQL_SUCCESS:
|
||||
successDetected = TRUE;
|
||||
|
||||
numResultCols = 0;
|
||||
SQLNumResultCols (hstmt, &numResultCols);
|
||||
if (numResultCols > 0)
|
||||
{
|
||||
printf ("A result set with %d columns was produced\n",
|
||||
(int)numResultCols);
|
||||
}
|
||||
break;
|
||||
|
||||
case SQL_NO_DATA:
|
||||
// All results have been processed. We are done.
|
||||
//
|
||||
goto exit;
|
||||
|
||||
case SQL_NEED_DATA:
|
||||
case SQL_INVALID_HANDLE:
|
||||
case SQL_STILL_EXECUTING:
|
||||
default:
|
||||
successDetected = FALSE;
|
||||
printf ("Unexpected SQLExec result %d\n", rc);
|
||||
goto exit;
|
||||
}
|
||||
rc = SQLMoreResults (hstmt);
|
||||
}
|
||||
|
||||
exit:
|
||||
// Release the ODBC resources.
|
||||
//
|
||||
if (hstmt != NULL)
|
||||
{
|
||||
SQLFreeHandle(SQL_HANDLE_STMT, hstmt);
|
||||
hstmt = NULL;
|
||||
}
|
||||
|
||||
if (hdbc != NULL)
|
||||
{
|
||||
SQLDisconnect(hdbc);
|
||||
SQLFreeHandle(SQL_HANDLE_DBC, hdbc);
|
||||
hdbc = NULL;
|
||||
}
|
||||
|
||||
if (henv != NULL)
|
||||
{
|
||||
SQLFreeHandle(SQL_HANDLE_ENV, henv);
|
||||
henv = NULL;
|
||||
}
|
||||
|
||||
return successDetected;
|
||||
}
|
||||
|
||||
|
||||
//------------------------------------------------------------
|
||||
//
|
||||
// execSQL: Send the SQL to the server via ODBC.
|
||||
//
|
||||
// Return the thread handle (NULL on error).
|
||||
//
|
||||
HANDLE execSQL (int doBackup, char* pInstanceName, char* pDbName, WCHAR* pVDName)
|
||||
{
|
||||
unsigned int threadId;
|
||||
HANDLE hThread;
|
||||
static PARMS parms; // yucky, but only a single command is used....
|
||||
|
||||
parms.doBackup = doBackup;
|
||||
parms.pDbName = pDbName;
|
||||
parms.pInstanceName = pInstanceName;
|
||||
parms.pVdsName = pVDName;
|
||||
|
||||
hThread = (HANDLE)_beginthreadex (
|
||||
NULL, 0, SQLRoutine, (void*)&parms, 0, &threadId);
|
||||
if (hThread == NULL)
|
||||
{
|
||||
printf ("Failed to create thread. errno is %d\n", errno);
|
||||
}
|
||||
return hThread;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------
|
||||
//
|
||||
// checkSQL: Wait for the T-SQL to complete,
|
||||
// returns TRUE if statement successfully executed.
|
||||
//
|
||||
int checkSQL (HANDLE hThread)
|
||||
{
|
||||
if (hThread == NULL)
|
||||
return FALSE;
|
||||
|
||||
DWORD rc = WaitForSingleObject (hThread, INFINITE);
|
||||
if (rc != WAIT_OBJECT_0)
|
||||
{
|
||||
printf ("checkSQL failed: %d\n", rc);
|
||||
return FALSE;
|
||||
}
|
||||
if (!GetExitCodeThread (hThread, &rc))
|
||||
{
|
||||
printf ("failed to get exit code: %d\n", GetLastError ());
|
||||
return FALSE;
|
||||
}
|
||||
return rc == TRUE;
|
||||
}
|
||||
|
||||
|
||||
//----------------------------------------------------------------------------------
|
||||
// Ask the user a "yes/no" question.
|
||||
// Return TRUE if he answers "yes"
|
||||
//
|
||||
BOOL
|
||||
ynPrompt (char *str)
|
||||
{
|
||||
char line[256];
|
||||
|
||||
printf ("\n\n%s", str);
|
||||
line [0] = 0;
|
||||
gets (line);
|
||||
printf ("\n\n");
|
||||
return (line [0] == 'y' || line [0] == 'Y');
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------------
|
||||
// VDI data transfer handler.
|
||||
//
|
||||
// This routine reads commands from the server until a 'Close' status is received.
|
||||
// It simply reads or writes a file 'superbak.dmp' in the current directory.
|
||||
//
|
||||
void performTransfer (
|
||||
IClientVirtualDeviceSet2* iVds,
|
||||
IClientVirtualDevice* vd,
|
||||
int backup )
|
||||
{
|
||||
FILE * fh;
|
||||
char* fname = "snapshot.dmp";
|
||||
VDC_Command * cmd;
|
||||
DWORD completionCode;
|
||||
DWORD bytesTransferred;
|
||||
HRESULT hr;
|
||||
|
||||
fh = fopen (fname, (backup)? "wb" : "rb");
|
||||
if (fh == NULL )
|
||||
{
|
||||
printf ("Failed to open: %s\n", fname);
|
||||
return;
|
||||
}
|
||||
|
||||
while (SUCCEEDED (hr=vd->GetCommand (INFINITE, &cmd)))
|
||||
{
|
||||
bytesTransferred = 0;
|
||||
switch (cmd->commandCode)
|
||||
{
|
||||
case VDC_Read:
|
||||
bytesTransferred = fread (cmd->buffer, 1, cmd->size, fh);
|
||||
if (bytesTransferred == cmd->size)
|
||||
completionCode = ERROR_SUCCESS;
|
||||
else
|
||||
// assume failure is eof
|
||||
completionCode = ERROR_HANDLE_EOF;
|
||||
break;
|
||||
|
||||
case VDC_Write:
|
||||
bytesTransferred = fwrite (cmd->buffer, 1, cmd->size, fh);
|
||||
if (bytesTransferred == cmd->size )
|
||||
{
|
||||
completionCode = ERROR_SUCCESS;
|
||||
}
|
||||
else
|
||||
// assume failure is disk full
|
||||
completionCode = ERROR_DISK_FULL;
|
||||
break;
|
||||
|
||||
case VDC_Flush:
|
||||
fflush (fh);
|
||||
completionCode = ERROR_SUCCESS;
|
||||
break;
|
||||
|
||||
case VDC_ClearError:
|
||||
completionCode = ERROR_SUCCESS;
|
||||
break;
|
||||
|
||||
case VDC_PrepareToFreeze:
|
||||
printf ("\n*** SQL Server is prepared to freeze the database now ***\n");
|
||||
printf ("At this point the application can perform any final coordination activities.\n");
|
||||
|
||||
while (!ynPrompt ("Are you ready to freeze the database?"))
|
||||
{
|
||||
if (ynPrompt ("Do you want to abort?"))
|
||||
{
|
||||
iVds->SignalAbort ();
|
||||
return;
|
||||
}
|
||||
}
|
||||
// Acknowledging this command results in a freeze of database writes.
|
||||
// The server will then issue VDC_Write commands to record metadata
|
||||
// about the frozen state.
|
||||
// Then the VDC_Snapshot is issued.
|
||||
//
|
||||
completionCode = ERROR_SUCCESS;
|
||||
break;
|
||||
|
||||
case VDC_Snapshot:
|
||||
// At this point the metadata is complete, so the
|
||||
// output stream can be closed. Thus it is possible
|
||||
// to include the metadata with the data of the snapshot.
|
||||
//
|
||||
fclose (fh);
|
||||
fh = NULL;
|
||||
|
||||
printf ("\n*** Make the snapshot now ***\n");
|
||||
|
||||
while (!ynPrompt ("Did you complete the snapshot?"))
|
||||
{
|
||||
if (ynPrompt ("Do you want to abort?"))
|
||||
{
|
||||
iVds->SignalAbort ();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// For clarity, we "unroll" the loop logic
|
||||
// in the following block of code so you can
|
||||
// easily see the sequence of operations.
|
||||
//
|
||||
|
||||
// Tell SQLServer that the snapshot is done.
|
||||
//
|
||||
completionCode = ERROR_SUCCESS;
|
||||
hr = vd->CompleteCommand (cmd, completionCode, bytesTransferred, 0);
|
||||
if (!SUCCEEDED (hr))
|
||||
{
|
||||
printf ("Completion Failed: x%X\n", hr);
|
||||
return;
|
||||
}
|
||||
|
||||
// The only valid command will be a "close" request.
|
||||
//
|
||||
hr = vd->GetCommand (INFINITE, &cmd);
|
||||
if (hr != VD_E_CLOSE)
|
||||
{
|
||||
printf ("Unexpected snapshot termination: x%X\n", hr);
|
||||
}
|
||||
else
|
||||
{
|
||||
printf ("SQLServer is aware that the snapshot is successful.\n");
|
||||
}
|
||||
return;
|
||||
|
||||
case VDC_MountSnapshot:
|
||||
printf ("\n*** Mount the snapshot now ***\n");
|
||||
|
||||
while (!ynPrompt ("Did you complete the snapshot?"))
|
||||
{
|
||||
if (ynPrompt ("Do you want to abort?"))
|
||||
{
|
||||
iVds->SignalAbort ();
|
||||
return;
|
||||
}
|
||||
}
|
||||
completionCode = ERROR_SUCCESS;
|
||||
break;
|
||||
|
||||
default:
|
||||
// If command is unknown...
|
||||
completionCode = ERROR_NOT_SUPPORTED;
|
||||
}
|
||||
|
||||
hr = vd->CompleteCommand (cmd, completionCode, bytesTransferred, 0);
|
||||
if (!SUCCEEDED (hr))
|
||||
{
|
||||
printf ("Completion Failed: x%X\n", hr);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (hr != VD_E_CLOSE)
|
||||
{
|
||||
printf ("Unexpected termination: x%X\n", hr);
|
||||
}
|
||||
else
|
||||
{
|
||||
// As far as the data transfer is concerned, no
|
||||
// errors occurred. The code which issues the SQL
|
||||
// must determine if the backup/restore was
|
||||
// really successful.
|
||||
//
|
||||
printf ("Successfully completed data transfer.\n");
|
||||
}
|
||||
|
||||
if (fh != NULL)
|
||||
{
|
||||
fclose (fh);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,100 @@
|
||||
# Microsoft Developer Studio Project File - Name="snapshot" - Package Owner=<4>
|
||||
# Microsoft Developer Studio Generated Build File, Format Version 6.00
|
||||
# ** DO NOT EDIT **
|
||||
|
||||
# TARGTYPE "Win32 (x86) Console Application" 0x0103
|
||||
|
||||
CFG=snapshot - Win32 Debug
|
||||
!MESSAGE This is not a valid makefile. To build this project using NMAKE,
|
||||
!MESSAGE use the Export Makefile command and run
|
||||
!MESSAGE
|
||||
!MESSAGE NMAKE /f "snapshot.mak".
|
||||
!MESSAGE
|
||||
!MESSAGE You can specify a configuration when running NMAKE
|
||||
!MESSAGE by defining the macro CFG on the command line. For example:
|
||||
!MESSAGE
|
||||
!MESSAGE NMAKE /f "snapshot.mak" CFG="snapshot - Win32 Debug"
|
||||
!MESSAGE
|
||||
!MESSAGE Possible choices for configuration are:
|
||||
!MESSAGE
|
||||
!MESSAGE "snapshot - Win32 Release" (based on "Win32 (x86) Console Application")
|
||||
!MESSAGE "snapshot - Win32 Debug" (based on "Win32 (x86) Console Application")
|
||||
!MESSAGE
|
||||
|
||||
# Begin Project
|
||||
# PROP AllowPerConfigDependencies 0
|
||||
# PROP Scc_ProjName ""
|
||||
# PROP Scc_LocalPath ""
|
||||
CPP=cl.exe
|
||||
RSC=rc.exe
|
||||
|
||||
!IF "$(CFG)" == "snapshot - Win32 Release"
|
||||
|
||||
# PROP BASE Use_MFC 0
|
||||
# PROP BASE Use_Debug_Libraries 0
|
||||
# PROP BASE Output_Dir "Release"
|
||||
# PROP BASE Intermediate_Dir "Release"
|
||||
# PROP BASE Target_Dir ""
|
||||
# PROP Use_MFC 0
|
||||
# PROP Use_Debug_Libraries 0
|
||||
# PROP Output_Dir "Release"
|
||||
# PROP Intermediate_Dir "Release"
|
||||
# PROP Target_Dir ""
|
||||
# ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /c
|
||||
# ADD CPP /nologo /MT /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /c
|
||||
# ADD BASE RSC /l 0x409 /d "NDEBUG"
|
||||
# ADD RSC /l 0x409 /d "NDEBUG"
|
||||
BSC32=bscmake.exe
|
||||
# ADD BASE BSC32 /nologo
|
||||
# ADD BSC32 /nologo
|
||||
LINK32=link.exe
|
||||
# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /machine:I386
|
||||
# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /machine:I386
|
||||
|
||||
!ELSEIF "$(CFG)" == "snapshot - Win32 Debug"
|
||||
|
||||
# PROP BASE Use_MFC 0
|
||||
# PROP BASE Use_Debug_Libraries 1
|
||||
# PROP BASE Output_Dir "Debug"
|
||||
# PROP BASE Intermediate_Dir "Debug"
|
||||
# PROP BASE Target_Dir ""
|
||||
# PROP Use_MFC 0
|
||||
# PROP Use_Debug_Libraries 1
|
||||
# PROP Output_Dir "Debug"
|
||||
# PROP Intermediate_Dir "Debug"
|
||||
# PROP Target_Dir ""
|
||||
# ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /GZ /c
|
||||
# ADD CPP /nologo /MTd /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /GZ /c
|
||||
# ADD BASE RSC /l 0x409 /d "_DEBUG"
|
||||
# ADD RSC /l 0x409 /d "_DEBUG"
|
||||
BSC32=bscmake.exe
|
||||
# ADD BASE BSC32 /nologo
|
||||
# ADD BSC32 /nologo
|
||||
LINK32=link.exe
|
||||
# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /debug /machine:I386 /pdbtype:sept
|
||||
# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /debug /machine:I386 /pdbtype:sept
|
||||
|
||||
!ENDIF
|
||||
|
||||
# Begin Target
|
||||
|
||||
# Name "snapshot - Win32 Release"
|
||||
# Name "snapshot - Win32 Debug"
|
||||
# Begin Group "Source Files"
|
||||
|
||||
# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat"
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=.\snapshot.cpp
|
||||
# End Source File
|
||||
# End Group
|
||||
# Begin Group "Header Files"
|
||||
|
||||
# PROP Default_Filter "h;hpp;hxx;hm;inl"
|
||||
# End Group
|
||||
# Begin Group "Resource Files"
|
||||
|
||||
# PROP Default_Filter "ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe"
|
||||
# End Group
|
||||
# End Target
|
||||
# End Project
|
||||
@@ -0,0 +1,29 @@
|
||||
Microsoft Developer Studio Workspace File, Format Version 6.00
|
||||
# WARNING: DO NOT EDIT OR DELETE THIS WORKSPACE FILE!
|
||||
|
||||
###############################################################################
|
||||
|
||||
Project: "snapshot"=.\snapshot.dsp - Package Owner=<4>
|
||||
|
||||
Package=<5>
|
||||
{{{
|
||||
}}}
|
||||
|
||||
Package=<4>
|
||||
{{{
|
||||
}}}
|
||||
|
||||
###############################################################################
|
||||
|
||||
Global:
|
||||
|
||||
Package=<5>
|
||||
{{{
|
||||
}}}
|
||||
|
||||
Package=<3>
|
||||
{{{
|
||||
}}}
|
||||
|
||||
###############################################################################
|
||||
|
||||
Reference in New Issue
Block a user