466 lines
24 KiB
C#
466 lines
24 KiB
C#
|
//------------------------------------------------------------------------------
|
||
|
// <copyright file="OleDbWrapper.cs" company="Microsoft">
|
||
|
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||
|
// </copyright>
|
||
|
// <owner current="true" primary="true">[....]</owner>
|
||
|
// <owner current="true" primary="false">[....]</owner>
|
||
|
//------------------------------------------------------------------------------
|
||
|
|
||
|
namespace System.Data.OleDb {
|
||
|
|
||
|
using System;
|
||
|
using System.Data.Common;
|
||
|
using System.Data.ProviderBase;
|
||
|
using System.Diagnostics;
|
||
|
using System.Runtime.CompilerServices;
|
||
|
using System.Runtime.ConstrainedExecution;
|
||
|
using System.Runtime.InteropServices;
|
||
|
|
||
|
// SafeHandle wrapper around 'DataLinks' object which pools the native OLE DB providers.
|
||
|
// expect 1 per app-domain
|
||
|
sealed internal class OleDbServicesWrapper : WrappedIUnknown {
|
||
|
|
||
|
// we expect to store IDataInitialize instance pointer in base.handle
|
||
|
|
||
|
// since we only have one DataLinks object, caching the delegate here is valid as long we
|
||
|
// maintain an AddRef which is also the lifetime of this class instance
|
||
|
private UnsafeNativeMethods.IDataInitializeGetDataSource DangerousIDataInitializeGetDataSource;
|
||
|
|
||
|
// DataLinks (the unknown parameter) is created via Activator.CreateInstance outside of the SafeHandle
|
||
|
internal OleDbServicesWrapper(object unknown) : base() {
|
||
|
if (null != unknown) {
|
||
|
RuntimeHelpers.PrepareConstrainedRegions();
|
||
|
try { } finally {
|
||
|
// store the QI result for IID_IDataInitialize
|
||
|
base.handle = Marshal.GetComInterfaceForObject(unknown, typeof(UnsafeNativeMethods.IDataInitialize)); //
|
||
|
}
|
||
|
// native COM rules are the QI result is the 'this' pointer
|
||
|
// the pointer stored at that location is the vtable
|
||
|
// since IDataInitialize is a public,shipped COM interface, its layout will not change (ever)
|
||
|
IntPtr vtable = Marshal.ReadIntPtr(base.handle, 0);
|
||
|
IntPtr method = Marshal.ReadIntPtr(vtable, 3 * IntPtr.Size); // GetDataSource is the 4'th vtable entry
|
||
|
DangerousIDataInitializeGetDataSource = (UnsafeNativeMethods.IDataInitializeGetDataSource)Marshal.GetDelegateForFunctionPointer(method, typeof(UnsafeNativeMethods.IDataInitializeGetDataSource));
|
||
|
}
|
||
|
}
|
||
|
|
||
|
internal void GetDataSource(OleDbConnectionString constr, ref DataSourceWrapper datasrcWrapper) {
|
||
|
OleDbHResult hr;
|
||
|
UnsafeNativeMethods.IDataInitializeGetDataSource GetDataSource = DangerousIDataInitializeGetDataSource;
|
||
|
bool mustRelease = false;
|
||
|
RuntimeHelpers.PrepareConstrainedRegions();
|
||
|
try {
|
||
|
DangerousAddRef(ref mustRelease);
|
||
|
|
||
|
// this is the string that DataLinks / OLE DB Services will use to create the provider
|
||
|
string connectionString = constr.ActualConnectionString;
|
||
|
|
||
|
// base.handle is the 'this' pointer for making the COM call to GetDataSource
|
||
|
// the datasrcWrapper will store the IID_IDBInitialize pointer
|
||
|
// call IDataInitiailze::GetDataSource via the delegate
|
||
|
hr = GetDataSource(base.handle, IntPtr.Zero, ODB.CLSCTX_ALL, connectionString, ref ODB.IID_IDBInitialize, ref datasrcWrapper);
|
||
|
}
|
||
|
finally {
|
||
|
if (mustRelease) {
|
||
|
DangerousRelease();
|
||
|
}
|
||
|
}
|
||
|
if (hr < 0) { // ignore infomsg
|
||
|
if (OleDbHResult.REGDB_E_CLASSNOTREG == hr) {
|
||
|
throw ODB.ProviderUnavailable(constr.Provider, null);
|
||
|
}
|
||
|
Exception e = OleDbConnection.ProcessResults(hr, null, null);
|
||
|
Debug.Assert(null != e, "CreateProviderError");
|
||
|
throw e;
|
||
|
}
|
||
|
else if (datasrcWrapper.IsInvalid) {
|
||
|
SafeNativeMethods.Wrapper.ClearErrorInfo();
|
||
|
throw ODB.ProviderUnavailable(constr.Provider, null);
|
||
|
}
|
||
|
Debug.Assert(!datasrcWrapper.IsInvalid, "bad DataSource");
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// SafeHandle wrapper around 'Data Source' object which represents the connection
|
||
|
// expect 1 per OleDbConnectionInternal
|
||
|
sealed internal class DataSourceWrapper : WrappedIUnknown {
|
||
|
|
||
|
// we expect to store IDBInitialize instance pointer in base.handle
|
||
|
|
||
|
// construct a DataSourceWrapper and used as a ref parameter to GetDataSource
|
||
|
internal DataSourceWrapper() : base() {
|
||
|
}
|
||
|
|
||
|
internal OleDbHResult InitializeAndCreateSession(OleDbConnectionString constr, ref SessionWrapper sessionWrapper) {
|
||
|
OleDbHResult hr;
|
||
|
bool mustRelease = false;
|
||
|
IntPtr idbCreateSession = IntPtr.Zero;
|
||
|
RuntimeHelpers.PrepareConstrainedRegions();
|
||
|
try {
|
||
|
DangerousAddRef(ref mustRelease);
|
||
|
|
||
|
// native COM rules are the QI result is the 'this' pointer
|
||
|
// the pointer stored at that location is the vtable
|
||
|
// since IUnknown is a public,shipped COM interface, its layout will not change (ever)
|
||
|
IntPtr vtable = Marshal.ReadIntPtr(base.handle, 0);
|
||
|
IntPtr method = Marshal.ReadIntPtr(vtable, 0);
|
||
|
|
||
|
// we cache the QueryInterface delegate to prevent recreating it on every call
|
||
|
UnsafeNativeMethods.IUnknownQueryInterface QueryInterface = constr.DangerousDataSourceIUnknownQueryInterface;
|
||
|
|
||
|
// since the delegate lifetime is longer than the original instance used to create it
|
||
|
// we double check before each usage to verify the delegates function pointer
|
||
|
if ((null == QueryInterface) || (method != Marshal.GetFunctionPointerForDelegate(QueryInterface))) {
|
||
|
QueryInterface = (UnsafeNativeMethods.IUnknownQueryInterface)Marshal.GetDelegateForFunctionPointer(method, typeof(UnsafeNativeMethods.IUnknownQueryInterface));
|
||
|
constr.DangerousDataSourceIUnknownQueryInterface = QueryInterface;
|
||
|
}
|
||
|
|
||
|
// native COM rules are the QI result is the 'this' pointer
|
||
|
// the pointer stored at that location is the vtable
|
||
|
// since IDBInitialize is a public,shipped COM interface, its layout will not change (ever)
|
||
|
vtable = Marshal.ReadIntPtr(base.handle, 0);
|
||
|
method = Marshal.ReadIntPtr(vtable, 3 * IntPtr.Size); // Initialize is the 4'th vtable entry
|
||
|
|
||
|
// we cache the Initialize delegate to prevent recreating it on every call
|
||
|
UnsafeNativeMethods.IDBInitializeInitialize Initialize = constr.DangerousIDBInitializeInitialize;
|
||
|
|
||
|
// since the delegate lifetime is longer than the original instance used to create it
|
||
|
// we double check before each usage to verify the delegates function pointer
|
||
|
if ((null == Initialize) || (method != Marshal.GetFunctionPointerForDelegate(Initialize))) {
|
||
|
Initialize = (UnsafeNativeMethods.IDBInitializeInitialize)Marshal.GetDelegateForFunctionPointer(method, typeof(UnsafeNativeMethods.IDBInitializeInitialize));
|
||
|
constr.DangerousIDBInitializeInitialize = Initialize;
|
||
|
}
|
||
|
|
||
|
// call IDBInitialize::Initialize via the delegate
|
||
|
hr = Initialize(base.handle);
|
||
|
|
||
|
// we don't ever expect DB_E_ALREADYINITIALIZED, but since we checked in V1.0 - its propagated along
|
||
|
if ((0 <= hr) || (OleDbHResult.DB_E_ALREADYINITIALIZED == hr)) {
|
||
|
|
||
|
// call IUnknown::QueryInterface via the delegate
|
||
|
hr = (OleDbHResult)QueryInterface(base.handle, ref ODB.IID_IDBCreateSession, ref idbCreateSession);
|
||
|
if ((0 <= hr) && (IntPtr.Zero != idbCreateSession)) {
|
||
|
|
||
|
// native COM rules are the QI result is the 'this' pointer
|
||
|
// the pointer stored at that location is the vtable
|
||
|
// since IDBCreateSession is a public,shipped COM interface, its layout will not change (ever)
|
||
|
vtable = Marshal.ReadIntPtr(idbCreateSession, 0);
|
||
|
method = Marshal.ReadIntPtr(vtable, 3 * IntPtr.Size); // CreateSession is the 4'th vtable entry
|
||
|
|
||
|
UnsafeNativeMethods.IDBCreateSessionCreateSession CreateSession = constr.DangerousIDBCreateSessionCreateSession;
|
||
|
|
||
|
// since the delegate lifetime is longer than the original instance used to create it
|
||
|
// we double check before each usage to verify the delegates function pointer
|
||
|
if ((null == CreateSession) || (method != Marshal.GetFunctionPointerForDelegate(CreateSession))) {
|
||
|
CreateSession = (UnsafeNativeMethods.IDBCreateSessionCreateSession)Marshal.GetDelegateForFunctionPointer(method, typeof(UnsafeNativeMethods.IDBCreateSessionCreateSession));
|
||
|
constr.DangerousIDBCreateSessionCreateSession = CreateSession;
|
||
|
}
|
||
|
|
||
|
// if I have a delegate for CreateCommand directly ask for IDBCreateCommand
|
||
|
if (null != constr.DangerousIDBCreateCommandCreateCommand) {
|
||
|
// call IDBCreateSession::CreateSession via the delegate directly for IDBCreateCommand
|
||
|
hr = CreateSession(idbCreateSession, IntPtr.Zero, ref ODB.IID_IDBCreateCommand, ref sessionWrapper);
|
||
|
if ((0 <= hr) && !sessionWrapper.IsInvalid) {
|
||
|
// double check the cached delegate is correct
|
||
|
sessionWrapper.VerifyIDBCreateCommand(constr);
|
||
|
}
|
||
|
}
|
||
|
else {
|
||
|
// otherwise ask for IUnknown (it may be first time usage or IDBCreateCommand not supported)
|
||
|
hr = CreateSession(idbCreateSession, IntPtr.Zero, ref ODB.IID_IUnknown, ref sessionWrapper);
|
||
|
if ((0 <= hr) && !sessionWrapper.IsInvalid) {
|
||
|
// and check support for IDBCreateCommand and create delegate for CreateCommand
|
||
|
sessionWrapper.QueryInterfaceIDBCreateCommand(constr);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
finally {
|
||
|
if (IntPtr.Zero != idbCreateSession) {
|
||
|
// release the QI for IDBCreateSession
|
||
|
Marshal.Release(idbCreateSession);
|
||
|
}
|
||
|
if (mustRelease) {
|
||
|
// release the AddRef on DataLinks
|
||
|
DangerousRelease();
|
||
|
}
|
||
|
}
|
||
|
return hr;
|
||
|
}
|
||
|
|
||
|
internal IDBInfoWrapper IDBInfo(OleDbConnectionInternal connection) {
|
||
|
Bid.Trace("<oledb.IUnknown.QueryInterface|API|OLEDB|datasource> %d#, IDBInfo\n", connection.ObjectID);
|
||
|
return new IDBInfoWrapper(ComWrapper());
|
||
|
}
|
||
|
internal IDBPropertiesWrapper IDBProperties(OleDbConnectionInternal connection) {
|
||
|
Bid.Trace("<oledb.IUnknown.QueryInterface|API|OLEDB|datasource> %d#, IDBProperties\n", connection.ObjectID);
|
||
|
return new IDBPropertiesWrapper(ComWrapper());
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
// SafeHandle wrapper around 'Session' object which represents the session on the connection
|
||
|
// expect 1 per OleDbConnectionInternal
|
||
|
sealed internal class SessionWrapper : WrappedIUnknown {
|
||
|
|
||
|
// base.handle will either reference the IUnknown interface or IDBCreateCommand interface
|
||
|
// if OleDbConnectionString.DangerousIDBCreateCommandCreateCommand exists
|
||
|
// the CreateSession call will ask directly for the optional IDBCreateCommand
|
||
|
// otherwise it is the first call or known that IDBCreateCommand isn't supported
|
||
|
|
||
|
// we cache the DangerousIDBCreateCommandCreateCommand (expecting base.handle to be IDBCreateCommand)
|
||
|
// since we maintain an AddRef on IDBCreateCommand it is safe to use the delegate without rechecking its function pointer
|
||
|
private UnsafeNativeMethods.IDBCreateCommandCreateCommand DangerousIDBCreateCommandCreateCommand;
|
||
|
|
||
|
internal SessionWrapper() : base() {
|
||
|
}
|
||
|
|
||
|
// if OleDbConnectionString.DangerousIDBCreateCommandCreateCommand does not exist
|
||
|
// this method will be called to query for IDBCreateCommand (and cache that interface pointer)
|
||
|
// or it will be known that IDBCreateCommand is not supported
|
||
|
internal void QueryInterfaceIDBCreateCommand(OleDbConnectionString constr) {
|
||
|
// DangerousAddRef/DangerousRelease are not neccessary here in the current implementation
|
||
|
// only used from within OleDbConnectionInternal.ctor->DataSourceWrapper.InitializeAndCreateSession
|
||
|
|
||
|
// caching the fact if we have queried for IDBCreateCommand or not
|
||
|
// the command object is not supported by all providers, they would use IOpenRowset
|
||
|
// SQLBU:446413, SQLHotfix:1138 -- added extra if condition
|
||
|
// If constr.HaveQueriedForCreateCommand is false, this is the first time through this method and we need to set up the cache for sure.
|
||
|
// If two threads try to set the cache at the same time, everything should be okay. There can be multiple delegates that point to the same unmanaged function.
|
||
|
// If constr.HaveQueriedForCreateCommand is true, we have already tried to query for IDBCreateCommand on a previous call to this method, but based on that alone,
|
||
|
// we don't know if another thread set the flag, or if the provider really doesn't support commands.
|
||
|
// If constr.HaveQueriedForCreateCommand is true and constr.DangerousIDBCreateCommandCreateCommand is not null, that means that another thread has set it after we
|
||
|
// determined we needed to call QueryInterfaceIDBCreateCommand -- otherwise we would have called VerifyIDBCreateCommand instead
|
||
|
// In that case, we still need to set our local DangerousIDBCreateCommandCreateCommand, so we want to go through the if block even though the cache has been set on constr already
|
||
|
if (!constr.HaveQueriedForCreateCommand || (null != constr.DangerousIDBCreateCommandCreateCommand)) {
|
||
|
IntPtr idbCreateCommand = IntPtr.Zero;
|
||
|
RuntimeHelpers.PrepareConstrainedRegions();
|
||
|
try {
|
||
|
// native COM rules are the QI result is the 'this' pointer
|
||
|
// the pointer stored at that location is the vtable
|
||
|
// since IUnknown is a public,shipped COM interface, its layout will not change (ever)
|
||
|
IntPtr vtable = Marshal.ReadIntPtr(base.handle, 0);
|
||
|
IntPtr method = Marshal.ReadIntPtr(vtable, 0);
|
||
|
UnsafeNativeMethods.IUnknownQueryInterface QueryInterface = (UnsafeNativeMethods.IUnknownQueryInterface)Marshal.GetDelegateForFunctionPointer(method, typeof(UnsafeNativeMethods.IUnknownQueryInterface));
|
||
|
|
||
|
int hresult = QueryInterface(base.handle, ref ODB.IID_IDBCreateCommand, ref idbCreateCommand); //
|
||
|
if ((0 <= hresult) && (IntPtr.Zero != idbCreateCommand)) {
|
||
|
vtable = Marshal.ReadIntPtr(idbCreateCommand, 0);
|
||
|
method = Marshal.ReadIntPtr(vtable, 3 * IntPtr.Size);
|
||
|
|
||
|
DangerousIDBCreateCommandCreateCommand = (UnsafeNativeMethods.IDBCreateCommandCreateCommand)Marshal.GetDelegateForFunctionPointer(method, typeof(UnsafeNativeMethods.IDBCreateCommandCreateCommand));
|
||
|
constr.DangerousIDBCreateCommandCreateCommand = DangerousIDBCreateCommandCreateCommand;
|
||
|
}
|
||
|
|
||
|
// caching the fact that we have queried for IDBCreateCommand
|
||
|
constr.HaveQueriedForCreateCommand = true;
|
||
|
}
|
||
|
finally {
|
||
|
if (IntPtr.Zero != idbCreateCommand) {
|
||
|
IntPtr ptr = base.handle;
|
||
|
base.handle = idbCreateCommand;
|
||
|
Marshal.Release(ptr);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
//else if constr.HaveQueriedForCreateCommand is true and constr.DangerousIDBCreateCommandCreateCommand is still null, it means that this provider doesn't support commands
|
||
|
}
|
||
|
|
||
|
internal void VerifyIDBCreateCommand(OleDbConnectionString constr) {
|
||
|
// DangerousAddRef/DangerousRelease are not neccessary here in the current implementation
|
||
|
// only used from within OleDbConnectionInternal.ctor->DataSourceWrapper.InitializeAndCreateSession
|
||
|
|
||
|
Debug.Assert(constr.HaveQueriedForCreateCommand, "expected HaveQueriedForCreateCommand");
|
||
|
Debug.Assert(null != constr.DangerousIDBCreateCommandCreateCommand, "expected DangerousIDBCreateCommandCreateCommand");
|
||
|
|
||
|
// native COM rules are the QI result is the 'this' pointer
|
||
|
// the pointer stored at that location is the vtable
|
||
|
// since IDBCreateCommand is a public,shipped COM interface, its layout will not change (ever)
|
||
|
IntPtr vtable = Marshal.ReadIntPtr(base.handle, 0);
|
||
|
IntPtr method = Marshal.ReadIntPtr(vtable, 3 * IntPtr.Size);
|
||
|
|
||
|
// obtain the cached delegate to be cached on this instance
|
||
|
UnsafeNativeMethods.IDBCreateCommandCreateCommand CreateCommand = constr.DangerousIDBCreateCommandCreateCommand;
|
||
|
|
||
|
// since the delegate lifetime is longer than the original instance used to create it
|
||
|
// we double check before each usage to verify the delegates function pointer
|
||
|
if ((null == CreateCommand) || (method != Marshal.GetFunctionPointerForDelegate(CreateCommand))) {
|
||
|
CreateCommand = (UnsafeNativeMethods.IDBCreateCommandCreateCommand)Marshal.GetDelegateForFunctionPointer(method, typeof(UnsafeNativeMethods.IDBCreateCommandCreateCommand));
|
||
|
constr.DangerousIDBCreateCommandCreateCommand = CreateCommand;
|
||
|
}
|
||
|
// since this instance can be used to create multiple commands
|
||
|
// cache it on the class so that the function pointer doesn't have to be validated every time
|
||
|
DangerousIDBCreateCommandCreateCommand = CreateCommand;
|
||
|
}
|
||
|
|
||
|
internal OleDbHResult CreateCommand(ref object icommandText) {
|
||
|
// if (null == CreateCommand), the IDBCreateCommand isn't supported - aka E_NOINTERFACE
|
||
|
OleDbHResult hr = OleDbHResult.E_NOINTERFACE;
|
||
|
UnsafeNativeMethods.IDBCreateCommandCreateCommand CreateCommand = DangerousIDBCreateCommandCreateCommand;
|
||
|
if (null != CreateCommand) {
|
||
|
bool mustRelease = false;
|
||
|
RuntimeHelpers.PrepareConstrainedRegions();
|
||
|
try {
|
||
|
DangerousAddRef(ref mustRelease);
|
||
|
|
||
|
// call IDBCreateCommand::CreateCommand via the delegate directly for IDBCreateCommand
|
||
|
hr = CreateCommand(base.handle, IntPtr.Zero, ref ODB.IID_ICommandText, ref icommandText);
|
||
|
}
|
||
|
finally {
|
||
|
if (mustRelease) {
|
||
|
DangerousRelease();
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
return hr;
|
||
|
}
|
||
|
|
||
|
internal IDBSchemaRowsetWrapper IDBSchemaRowset(OleDbConnectionInternal connection) {
|
||
|
Bid.Trace("<oledb.IUnknown.QueryInterface|API|OLEDB|session> %d#, IDBSchemaRowset\n", connection.ObjectID);
|
||
|
return new IDBSchemaRowsetWrapper(ComWrapper());
|
||
|
}
|
||
|
|
||
|
internal IOpenRowsetWrapper IOpenRowset(OleDbConnectionInternal connection) {
|
||
|
Bid.Trace("<oledb.IUnknown.QueryInterface|API|OLEDB|session> %d#, IOpenRowset\n", connection.ObjectID);
|
||
|
return new IOpenRowsetWrapper(ComWrapper());
|
||
|
}
|
||
|
|
||
|
internal ITransactionJoinWrapper ITransactionJoin(OleDbConnectionInternal connection) {
|
||
|
Bid.Trace("<oledb.IUnknown.QueryInterface|API|OLEDB|session> %d#, ITransactionJoin\n", connection.ObjectID);
|
||
|
return new ITransactionJoinWrapper(ComWrapper());
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// unable to use generics bacause (unknown as T) doesn't compile
|
||
|
internal struct IDBInfoWrapper : IDisposable {
|
||
|
// _unknown must be tracked because the _value may not exist,
|
||
|
// yet _unknown must still be released
|
||
|
private object _unknown;
|
||
|
private UnsafeNativeMethods.IDBInfo _value;
|
||
|
|
||
|
internal IDBInfoWrapper(object unknown) {
|
||
|
_unknown = unknown;
|
||
|
_value = (unknown as UnsafeNativeMethods.IDBInfo);
|
||
|
}
|
||
|
|
||
|
internal UnsafeNativeMethods.IDBInfo Value {
|
||
|
get {
|
||
|
return _value;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
public void Dispose() {
|
||
|
object unknown = _unknown;
|
||
|
_unknown = null;
|
||
|
_value = null;
|
||
|
if (null != unknown) {
|
||
|
Marshal.ReleaseComObject(unknown);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
internal struct IDBPropertiesWrapper : IDisposable {
|
||
|
private object _unknown;
|
||
|
private UnsafeNativeMethods.IDBProperties _value;
|
||
|
|
||
|
internal IDBPropertiesWrapper(object unknown) {
|
||
|
_unknown = unknown;
|
||
|
_value = (unknown as UnsafeNativeMethods.IDBProperties);
|
||
|
Debug.Assert(null != _value, "null IDBProperties");
|
||
|
}
|
||
|
|
||
|
internal UnsafeNativeMethods.IDBProperties Value {
|
||
|
get {
|
||
|
Debug.Assert(null != _value, "null IDBProperties");
|
||
|
return _value;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
public void Dispose() {
|
||
|
object unknown = _unknown;
|
||
|
_unknown = null;
|
||
|
_value = null;
|
||
|
if (null != unknown) {
|
||
|
Marshal.ReleaseComObject(unknown);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
internal struct IDBSchemaRowsetWrapper : IDisposable {
|
||
|
private object _unknown;
|
||
|
private UnsafeNativeMethods.IDBSchemaRowset _value;
|
||
|
|
||
|
internal IDBSchemaRowsetWrapper(object unknown) {
|
||
|
_unknown = unknown;
|
||
|
_value = (unknown as UnsafeNativeMethods.IDBSchemaRowset);
|
||
|
}
|
||
|
|
||
|
internal UnsafeNativeMethods.IDBSchemaRowset Value {
|
||
|
get {
|
||
|
return _value;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
public void Dispose() {
|
||
|
object unknown = _unknown;
|
||
|
_unknown = null;
|
||
|
_value = null;
|
||
|
if (null != unknown) {
|
||
|
Marshal.ReleaseComObject(unknown);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
internal struct IOpenRowsetWrapper : IDisposable {
|
||
|
private object _unknown;
|
||
|
private UnsafeNativeMethods.IOpenRowset _value;
|
||
|
|
||
|
internal IOpenRowsetWrapper(object unknown) {
|
||
|
_unknown = unknown;
|
||
|
_value = (unknown as UnsafeNativeMethods.IOpenRowset);
|
||
|
Debug.Assert(null != _value, "null IOpenRowsetWrapper");
|
||
|
}
|
||
|
|
||
|
internal UnsafeNativeMethods.IOpenRowset Value {
|
||
|
get {
|
||
|
Debug.Assert(null != _value, "null IDBProperties");
|
||
|
return _value;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
public void Dispose() {
|
||
|
object unknown = _unknown;
|
||
|
_unknown = null;
|
||
|
_value = null;
|
||
|
if (null != unknown) {
|
||
|
Marshal.ReleaseComObject(unknown);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
internal struct ITransactionJoinWrapper : IDisposable {
|
||
|
private object _unknown;
|
||
|
private NativeMethods.ITransactionJoin _value;
|
||
|
|
||
|
internal ITransactionJoinWrapper(object unknown) {
|
||
|
_unknown = unknown;
|
||
|
_value = (unknown as NativeMethods.ITransactionJoin);
|
||
|
}
|
||
|
|
||
|
internal NativeMethods.ITransactionJoin Value {
|
||
|
get {
|
||
|
return _value;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
public void Dispose() {
|
||
|
object unknown = _unknown;
|
||
|
_unknown = null;
|
||
|
_value = null;
|
||
|
if (null != unknown) {
|
||
|
Marshal.ReleaseComObject(unknown);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|