This is an incredible long shot, but here goes. We are making a utility for database backup, using SQLVDI API (Virtual Device Interface). The single backup was implemented without problems. But when we started to implement the striped backup (using virtual_device), we got an issue with IClientVirtualDeviceSet2::OpenDevice method.
Our virtual device set is failing to open all devices. Only the first device is returned and on the second one we are getting VD_E_PROTOCOL error. Any suggestions would be greatly appreciated.
Our C++ code.
public: Void ExecuteCommandEx(System::String^ command, array<Stream^>^ commandStreamListIn)
{
try
{
//Initialize COM
HRESULT hr = CoInitializeEx(NULL, COINIT_MULTITHREADED);
if (FAILED(hr))
{
throw gcnew ApplicationException(String::Format(gcnew CultureInfo("en-US"), "CoInitializeEx Failed HRESULT: {0}", hr));
}
//Get an interface to the virtual device set
IClientVirtualDeviceSet2* vds = NULL;
hr = CoCreateInstance(CLSID_MSSQL_ClientVirtualDeviceSet, NULL, CLSCTX_INPROC_SERVER, IID_IClientVirtualDeviceSet2, (void**)&vds);
if (FAILED(hr))
{
throw gcnew ApplicationException(String::Format(gcnew CultureInfo("en-US"), "Unable to get an interface to the virtual device set. Please check to make sure sqlvdi.dll is registered. HRESULT: {0}", hr));
}
//Configure the device configuration
VDConfig config = { 0 };
config.deviceCount = commandStreamListIn->Length; //The number of virtual devices to create
//Create a name for the device using a GUID
String^ DeviceName = Guid::NewGuid().ToString()->ToUpper(gcnew CultureInfo("en-US"));
WCHAR wVdsName[37] = { 0 };
Marshal::Copy(DeviceName->ToCharArray(), 0, (IntPtr)wVdsName, DeviceName->Length);
//Create the virtual device set
hr = vds->CreateEx(NULL, wVdsName, &config);
if (FAILED(hr))
{
throw gcnew ApplicationException(String::Format(gcnew CultureInfo("en-US"), "Unable to create and configure the virtual device set. HRESULT: {0}", hr));
}
//Format the command
List<String^>^ vdNames = gcnew List<String^>();
vdNames->Add(DeviceName);
for (int i = 0; i < commandStreamListIn->Length-1; i++)
{
String^ streamName = Guid::NewGuid().ToString()->ToUpper(gcnew CultureInfo("en-US"));
vdNames->Add(streamName);
}
command = String::Format(gcnew CultureInfo("en-US"), command, vdNames->ToArray());
//Create and execute a new thread to execute the command
Thread^ OdbcThread = gcnew Thread(gcnew ParameterizedThreadStart(this, &VdiDotNetEx::VdiEngineEx::ThreadFunc));
OdbcThread->Start(command);
//Configure the virtual device set
hr = vds->GetConfiguration(INFINITE, &config);
if (hr != NOERROR)
{
throw gcnew ApplicationException(String::Format(gcnew CultureInfo("en-US"), "GetConfiguration Failed. HRESULT: {0}", hr));
}
if (FAILED(hr))
{
switch (hr)
{
case VD_E_ABORT:
throw gcnew ApplicationException("GetConfiguration was aborted.");
break;
case VD_E_TIMEOUT:
throw gcnew ApplicationException("GetConfiguration timed out.");
break;
default:
throw gcnew ApplicationException(String::Format(gcnew CultureInfo("en-US"), "Un unknown exception was thrown during GetConfiguration. HRESULT: {0}", hr));
break;
};
}
int count = 0;
array<IClientVirtualDevice*>^ vDevices= gcnew array<IClientVirtualDevice*>(commandStreamListIn->Length);
//Open all devices on the device set
//VD_E_OPEN may be returned without problem. The client may call OpenDevice by means of a loop until this code is returned.
//If more than one device is configured(for example, n devices),
//the virtual device set will return n unique device interfaces.
//The first device has the same name as the virtual device set.
//Other devices are named as specified with the VIRTUAL_DEVICE clauses of the BACKUP / RESTORE statement.
while (hr!= VD_E_OPEN)
{
IClientVirtualDevice* vd = NULL;
hr = vds->OpenDevice(wVdsName, &vd);
switch(hr)
{
case VD_E_OPEN:
throw gcnew ApplicationException(String::Format(gcnew CultureInfo("en-US"), "OpenDevice Failed. VD_E_OPEN. HRESULT: {0}", hr));
break;
case VD_E_BUSY:
throw gcnew ApplicationException(String::Format(gcnew CultureInfo("en-US"), "OpenDevice Failed. VD_E_BUSY. HRESULT: {0}", hr));
break;
case VD_E_CLOSE:
throw gcnew ApplicationException(String::Format(gcnew CultureInfo("en-US"), "OpenDevice Failed. VD_E_CLOSE. HRESULT: {0}", hr));
break;
case VD_E_UNEXPECTED:
throw gcnew ApplicationException(String::Format(gcnew CultureInfo("en-US"), "OpenDevice Failed. VD_E_UNEXPECTED. HRESULT: {0}", hr));
break;
case VD_E_INVALID:
throw gcnew ApplicationException(String::Format(gcnew CultureInfo("en-US"), "OpenDevice Failed. VD_E_INVALID. HRESULT: {0}", hr));
break;
case VD_E_NOTOPEN:
throw gcnew ApplicationException(String::Format(gcnew CultureInfo("en-US"), "OpenDevice Failed. VD_E_NOTOPEN. HRESULT: {0}", hr));
break;
case VD_E_PROTOCOL:
throw gcnew ApplicationException(String::Format(gcnew CultureInfo("en-US"), "OpenDevice Failed. VD_E_PROTOCOL. HRESULT: {0}", hr));
break;
}
if (FAILED(hr))
{
throw gcnew ApplicationException(String::Format(gcnew CultureInfo("en-US"), "OpenDevice Failed. HRESULT: {0}", hr));
}
vDevices[count] = vd;
count++;
}
for (int i = 0; i < count; i++)
{
ExecuteDataTransfer(vDevices[i], commandStreamListIn[i]);
}
//Wait for the thread that issued the backup / restore command to SQL Server to complete.
OdbcThread->Join();
}
catch (Exception ^ex)
{
Debug::WriteLine("VDI: Exception=" + ex->Message->ToString());
Debug::WriteLine("VDI: Trace=" + ex->StackTrace);
Console::WriteLine(ex->Message);
LogException(ex);
throw gcnew ApplicationException(ex->Message);
}
}
The COM related code: (combaseapi.h)
#pragma region Application or OneCore Family
#if WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_APP | WINAPI_PARTITION_SYSTEM)
_Check_return_ WINOLEAPI
CoInitializeEx(
_In_opt_ LPVOID pvReserved,
_In_ DWORD dwCoInit
);
#endif // WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_APP | WINAPI_PARTITION_SYSTEM)
#pragma endregion
_Check_return_ WINOLEAPI
CoCreateInstance(
_In_ REFCLSID rclsid,
_In_opt_ LPUNKNOWN pUnkOuter,
_In_ DWORD dwClsContext,
_In_ REFIID riid,
_COM_Outptr_ _At_(*ppv, _Post_readable_size_(_Inexpressible_(varies))) LPVOID FAR * ppv
);
OpenDevice is not working according to the SQLVDI documentation. For example don't expect VD_E_OPEN as a result, just 0 is OK. Also pass not only the virtual device set name to this method, but GUID of every virtual_device from your command. Look at this adjusted code.
for(int i = 0; i < vdNames->Count; i++)
{
IClientVirtualDevice* vd = NULL;
WCHAR wDevName[37] = { 0 };
String^ vdName = vdNames[i];
Marshal::Copy(vdName->ToCharArray(), 0, (IntPtr)wDevName, vdName->Length);
hr = vds->OpenDevice(wDevName, &vd);
switch(hr)
{
case VD_E_BUSY:
DBGPRINTW(L"VDI: Exception 15\n");
throw gcnew ApplicationException(String::Format(gcnew CultureInfo("en-US"), "OpenDevice Failed. VD_E_BUSY. HRESULT: {0}", hr));
break;
case VD_E_CLOSE:
DBGPRINTW(L"VDI: Exception 14\n");
throw gcnew ApplicationException(String::Format(gcnew CultureInfo("en-US"), "OpenDevice Failed. VD_E_CLOSE. HRESULT: {0}", hr));
break;
case VD_E_UNEXPECTED:
DBGPRINTW(L"VDI: Exception 11\n");
throw gcnew ApplicationException(String::Format(gcnew CultureInfo("en-US"), "OpenDevice Failed. VD_E_UNEXPECTED. HRESULT: {0}", hr));
break;
case VD_E_INVALID:
DBGPRINTW(L"VDI: Exception 06\n");
throw gcnew ApplicationException(String::Format(gcnew CultureInfo("en-US"), "OpenDevice Failed. VD_E_INVALID. HRESULT: {0}", hr));
break;
case VD_E_NOTOPEN:
DBGPRINTW(L"VDI: Exception 02\n");
throw gcnew ApplicationException(String::Format(gcnew CultureInfo("en-US"), "OpenDevice Failed. VD_E_NOTOPEN. HRESULT: {0}", hr));
break;
case VD_E_PROTOCOL:
DBGPRINTW(L"VDI: Exception 12\n");
throw gcnew ApplicationException(String::Format(gcnew CultureInfo("en-US"), "OpenDevice Failed. VD_E_PROTOCOL. HRESULT: {0}", hr));
break;
}
if (FAILED(hr))
{
DBGPRINTW(L"VDI: Exception 08\n");
throw gcnew ApplicationException(String::Format(gcnew CultureInfo("en-US"), "OpenDevice Failed. HRESULT: {0}", hr));
}
vDevices[i] = vd;
count++;
}
I was testing this injection technique (Reflective dll injection) and found that any try/catch on the code rise an unhandle windows error (KERNELBASE.dll, code error e06d7363) and the host process dies.
I was injecting a test dll with a basic function.
the work flow is:
print "starting..." and then dies.
My dll.
bool WINAPI DllMain(HINSTANCE hinstDLL, DWORD dwReason, LPVOID lpReserved)
{
bool bReturnValue = TRUE;
switch (dwReason)
{
case DLL_PROCESS_ATTACH:
function();
break;
}
return bReturnValue;
}
void function()
{
cout << "starting..." << std::endl;
try
{
throw std::exception();
}
catch (...)
{
cout << " exception... " << std::endl;
}
}
any technical explanation?
This is likely because the exception handling is not properly set up when you do the injection. See https://hackmag.com/uncategorized/exceptions-for-hardcore-users/.
What library are you using for injection? Does it support SEH? Or are you registering the handlers properly if you are doing it yourself?
I suspect however the issue is more complexe as I am having a similar issue with standard in-memory DLL loading with a lib (SimplePELoader) that supports SEH in my standard test case.
I noticed on the MSDN website that it used a catch all exception handler. I.e.:
try {
MSXML2::IXMLDOMDocumentPtr docPtr;
MSXML2::IXMLDOMNodePtr DOMNodePtr;
// init
TESTHR(CoInitialize(NULL));
TESTHR(docPtr.CreateInstance("Msxml2.DOMDocument.6.0"));
VARIANT vtTemp;
vtTemp.vt=VT_I2;
vtTemp.iVal = 1; //NODE_ELEMENT
// load a document
_variant_t varXml("c:\\Temp\\books.xml");
_variant_t varOut((bool)TRUE);
varOut = docPtr->load(varXml);
if ((bool)varOut == FALSE)
throw(0);
MessageBox(NULL, _bstr_t(docPtr->xml), _T("Original Document"), MB_OK);
DOMNodePtr = docPtr->createNode(vtTemp, "VIDEOS", "");
docPtr->documentElement->appendChild(DOMNodePtr);
MessageBox(NULL, _bstr_t(docPtr->xml), _T("New Document"), MB_OK);
} catch(...)
{
MessageBox(NULL, _T("Exception occurred"), _T("Error"), MB_OK);
}
Now my question is, does the function calls to the MSXML2::IXMLDOMDocumentPtr object throw any exceptions? I wasn't aware that a COM object could throw a C++ exception due to the COM boundary can interface with any language.
If it is just to catch the throw(0), then why not catch on an int? I know a little theory on COM but my knowledge is a little limited.
I created a really simple DirectX program to capture the screen. It works well on my machine, but fails on another machine in the following line with a segmentation fault (SIGSEV):
g_pd3dDevice->GetFrontBufferData(0, g_pSurface);
The following function is used to initialize DirectX:
HRESULT InitD3D(HWND hWnd)
{
D3DDISPLAYMODE ddm;
D3DPRESENT_PARAMETERS d3dpp;
if((g_pD3D=Direct3DCreate9(D3D_SDK_VERSION))==NULL)
{
ErrorMessage("Unable to Create Direct3D ");
return E_FAIL;
}
if(FAILED(g_pD3D->GetAdapterDisplayMode(D3DADAPTER_DEFAULT,&ddm)))
{
ErrorMessage("Unable to Get Adapter Display Mode");
return E_FAIL;
}
ZeroMemory(&d3dpp,sizeof(D3DPRESENT_PARAMETERS));
d3dpp.Windowed=WINDOW_MODE;
d3dpp.Flags=D3DPRESENTFLAG_LOCKABLE_BACKBUFFER;
d3dpp.BackBufferFormat=ddm.Format;
d3dpp.BackBufferHeight=nDisplayHeight=gScreenRect.bottom =ddm.Height;
d3dpp.BackBufferWidth=nDisplayWidth=gScreenRect.right =ddm.Width;
d3dpp.MultiSampleType=D3DMULTISAMPLE_NONE;
d3dpp.SwapEffect=D3DSWAPEFFECT_DISCARD;
d3dpp.hDeviceWindow=hWnd;
d3dpp.PresentationInterval=D3DPRESENT_INTERVAL_DEFAULT;
d3dpp.FullScreen_RefreshRateInHz=D3DPRESENT_RATE_DEFAULT;
if(FAILED(g_pD3D->CreateDevice(D3DADAPTER_DEFAULT,D3DDEVTYPE_REF,hWnd,D3DCREATE_SOFTWARE_VERTEXPROCESSING ,&d3dpp,&g_pd3dDevice)))
{
ErrorMessage("Unable to Create Device");
return E_FAIL;
}
if(FAILED(g_pd3dDevice->CreateOffscreenPlainSurface(ddm.Width, ddm.Height, D3DFMT_A8R8G8B8, D3DPOOL_SYSTEMMEM, &g_pSurface, NULL)))
{
ErrorMessage("Unable to Create Surface");
return E_FAIL;
}
return S_OK;
}
Any ideas why this would throw a segmentation fault?
Solution: The DirectX driver was not correctly installed.
I got this exception in my program:
Unhandled exception at 0x0051cce0 in
JSONDataParsing.exe: 0xC0000005:
Access violation reading location
0x00000004
.
I tried catching the Exception, but of no use. I know where the problem occurs. But wanted to know how I can trap the exception. I used try, catch block around the code where exception occurs.
Is this an exception which can not be caught?
The catch statements are:
catch (bad_alloc&)
{
TCHAR msgbuf[MAX_PATH];
swprintf(msgbuf, L"bad_alloc \n");
OutputDebugString(msgbuf);
}
catch (bad_cast&)
{
TCHAR msgbuf[MAX_PATH];
swprintf(msgbuf, L"bad_cast \n");
OutputDebugString(msgbuf);
}
catch (bad_exception&)
{
TCHAR msgbuf[MAX_PATH];
swprintf(msgbuf, L"babad_exceptiond_alloc \n");
OutputDebugString(msgbuf);
}
catch (bad_typeid&)
{
TCHAR msgbuf[MAX_PATH];
swprintf(msgbuf, L"bad_alloc \n");
OutputDebugString(msgbuf);
}
catch( CMemoryException* e )
{
TCHAR msgbuf[MAX_PATH];
swprintf(msgbuf, L"CMemoryException \n");
OutputDebugString(msgbuf);
// Handle the out-of-memory exception here.
}
catch( CFileException* e )
{
TCHAR msgbuf[MAX_PATH];
swprintf(msgbuf, L"CFileException \n");
OutputDebugString(msgbuf);
// Handle the file exceptions here.
}
catch( CException* e )
{
TCHAR msgbuf[MAX_PATH];
swprintf(msgbuf, L"CException \n");
OutputDebugString(msgbuf);
// Handle the exception here.
// "e" contains information about the exception.
e->Delete();
}
You can only catch such exception with a special try-catch handler:
try
{
// code that triggers such an exception. for example:
int * a = NULL;
*a = 0;
}
catch (...)
{
// now that exception is handled here
}
But generally, it is bad practice to do this. Instead you should not get such an exception but check your parameters and variables.
See here for more details:
http://members.cox.net/doug_web/eh.htm
This type of low level exception can be caught using __try -except statement, but you should fix the cause of it rather than reporting it. In VS, while debugging hit CTRL+ALT+E and check all exceptions there, then continue to run your app until the exception occurs. The prompt will stop on the offending line.
See
http://msdn.microsoft.com/en-us/library/s58ftw19.aspx
for details.