mirror of https://github.com/dahall/Vanara.git
Added Vanara.PInvoke.ODBC32 package and supporting unit test
parent
35a544b5c7
commit
94fdd98d31
|
@ -0,0 +1,7 @@
|
|||
namespace Vanara.PInvoke;
|
||||
|
||||
/// <summary>Items from the Odbc32.dll.</summary>
|
||||
public static partial class Odbc32
|
||||
{
|
||||
private const string Lib_Odbc32 = "Odbc32.dll";
|
||||
}
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,269 @@
|
|||
#pragma warning disable CS1591 // Missing XML comment for publicly visible type or member
|
||||
|
||||
namespace Vanara.PInvoke;
|
||||
|
||||
public static partial class Odbc32
|
||||
{
|
||||
public const int SQL_NO_TOTAL = -4;
|
||||
public const int SQL_NTS = -3;
|
||||
public const nint SQL_NULL_DATA = -1;
|
||||
private const int SQL_MAX_NUMERIC_LEN = 16;
|
||||
|
||||
[PInvokeData("sqltypes.h")]
|
||||
public enum SQLINTERVAL
|
||||
{
|
||||
SQL_IS_YEAR = 1,
|
||||
SQL_IS_MONTH = 2,
|
||||
SQL_IS_DAY = 3,
|
||||
SQL_IS_HOUR = 4,
|
||||
SQL_IS_MINUTE = 5,
|
||||
SQL_IS_SECOND = 6,
|
||||
SQL_IS_YEAR_TO_MONTH = 7,
|
||||
SQL_IS_DAY_TO_HOUR = 8,
|
||||
SQL_IS_DAY_TO_MINUTE = 9,
|
||||
SQL_IS_DAY_TO_SECOND = 10,
|
||||
SQL_IS_HOUR_TO_MINUTE = 11,
|
||||
SQL_IS_HOUR_TO_SECOND = 12,
|
||||
SQL_IS_MINUTE_TO_SECOND = 13
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Each function in ODBC returns a code, known as its return code, which indicates the overall success or failure of the function.
|
||||
/// Program logic is generally based on return codes.
|
||||
/// </summary>
|
||||
[PInvokeData("sqltypes.h")]
|
||||
[Serializable]
|
||||
public enum SQLRETURN : short
|
||||
{
|
||||
/// <summary>
|
||||
/// Function completed successfully. The application calls SQLGetDiagField to retrieve additional information from the header record.
|
||||
/// </summary>
|
||||
SQL_SUCCESS = 0,
|
||||
|
||||
/// <summary>
|
||||
/// Function completed successfully, possibly with a nonfatal error (warning). The application calls SQLGetDiagRec or SQLGetDiagField
|
||||
/// to retrieve additional information.
|
||||
/// </summary>
|
||||
SQL_SUCCESS_WITH_INFO = 1,
|
||||
|
||||
/// <summary>
|
||||
/// Function failed. The application calls SQLGetDiagRec or SQLGetDiagField to retrieve additional information. The contents of any
|
||||
/// output arguments to the function are undefined.
|
||||
/// </summary>
|
||||
SQL_ERROR = -1,
|
||||
|
||||
/// <summary>
|
||||
/// Function failed due to an invalid environment, connection, statement, or descriptor handle. This indicates a programming error.
|
||||
/// No additional information is available from SQLGetDiagRec or SQLGetDiagField. This code is returned only when the handle is a
|
||||
/// null pointer or is the wrong type, such as when a statement handle is passed for an argument that requires a connection handle.
|
||||
/// </summary>
|
||||
SQL_INVALID_HANDLE = -2,
|
||||
|
||||
/// <summary>
|
||||
/// No more data was available. The application calls SQLGetDiagRec or SQLGetDiagField to retrieve additional information. One or
|
||||
/// more driver-defined status records in class 02xxx may be returned. Note: In ODBC 2.x, this return code was named SQL_NO_DATA_FOUND.
|
||||
/// </summary>
|
||||
SQL_NO_DATA = 100,
|
||||
|
||||
/// <summary>
|
||||
/// A function that was started asynchronously is still executing. The application calls SQLGetDiagRec or SQLGetDiagField to retrieve
|
||||
/// additional information, if any.
|
||||
/// </summary>
|
||||
SQL_STILL_EXECUTING = 2,
|
||||
|
||||
/// <summary>
|
||||
/// More data is needed, such as when parameter data is sent at execution time or additional connection information is required. The
|
||||
/// application calls SQLGetDiagRec or SQLGetDiagField to retrieve additional information, if any.
|
||||
/// </summary>
|
||||
SQL_NEED_DATA = 99,
|
||||
|
||||
/// <summary>
|
||||
/// More data is available. The application calls SQLParamData to retrieve the data. Note: In ODBC 2.x, this return code was named
|
||||
/// </summary>
|
||||
SQL_PARAM_DATA_AVAILABLE = 101,
|
||||
}
|
||||
|
||||
public static Exception? GetException(this SQLRETURN ret, ISQLHANDLE? h = null) => ret switch
|
||||
{
|
||||
SQLRETURN.SQL_SUCCESS => null,
|
||||
_ => Odbc32Exception.CreateException($"ODBC call failed with return code {ret}", ret, h),
|
||||
};
|
||||
|
||||
public static bool SQL_SUCCEEDED(this SQLRETURN ret) => (((short)ret) & (~1)) == 0;
|
||||
|
||||
public static void ThrowIfFailed(this SQLRETURN ret, ISQLHANDLE? h = null)
|
||||
{
|
||||
if (!SQL_SUCCEEDED(ret))
|
||||
throw ret.GetException(h)!;
|
||||
}
|
||||
|
||||
[PInvokeData("sqltypes.h")]
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
public struct DATE_STRUCT
|
||||
{
|
||||
public short year;
|
||||
public ushort month;
|
||||
public ushort day;
|
||||
|
||||
public static implicit operator DateTime(DATE_STRUCT d) => new(d.year, d.month, d.day);
|
||||
|
||||
public static implicit operator DATE_STRUCT(DateTime d) => new() { year = (short)d.Year, month = (ushort)d.Month, day = (ushort)d.Day };
|
||||
}
|
||||
|
||||
[PInvokeData("sqltypes.h")]
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
public struct SQL_DAY_SECOND
|
||||
{
|
||||
public uint day;
|
||||
public uint hour;
|
||||
public uint minute;
|
||||
public uint second;
|
||||
public uint fraction;
|
||||
}
|
||||
|
||||
[PInvokeData("sqltypes.h")]
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
public struct SQL_INTERVAL_STRUCT
|
||||
{
|
||||
public SQLINTERVAL interval_type;
|
||||
public short interval_sign;
|
||||
public INTVAL intval;
|
||||
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
public struct INTVAL
|
||||
{
|
||||
public SQL_YEAR_MONTH year_month;
|
||||
public SQL_DAY_SECOND day_second;
|
||||
}
|
||||
}
|
||||
|
||||
[PInvokeData("sqltypes.h")]
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
public struct SQL_NUMERIC_STRUCT
|
||||
{
|
||||
public byte precision;
|
||||
public sbyte scale;
|
||||
public byte sign;
|
||||
|
||||
[MarshalAs(UnmanagedType.ByValArray, SizeConst = SQL_MAX_NUMERIC_LEN)]
|
||||
public byte[] val;
|
||||
|
||||
public static SQL_NUMERIC_STRUCT FromDecimal(decimal d, byte precision)
|
||||
{
|
||||
SafeCoTaskMemHandle ret = SafeCoTaskMemHandle.CreateFromStructure<SQL_NUMERIC_STRUCT>();
|
||||
|
||||
int[] parts = decimal.GetBits(d);
|
||||
byte[] bits = BitConverter.GetBytes(parts[3]);
|
||||
|
||||
ret.Write(precision, false, 0); //Bits 0-7 precision
|
||||
ret.Write(bits[2], false, 1); //Bits 16-23 scale
|
||||
ret.Write((byte)(0 == bits[3] ? 1 : 0), false, 2); //Bit 31 - sign(isnegative)
|
||||
ret.Write(parts[0], false, 3);
|
||||
ret.Write(parts[1], false, 7);
|
||||
ret.Write(parts[2], false, 11);
|
||||
|
||||
return ret.ToStructure<SQL_NUMERIC_STRUCT>();
|
||||
}
|
||||
|
||||
public static implicit operator decimal(SQL_NUMERIC_STRUCT ns)
|
||||
{
|
||||
SafeCoTaskMemStruct<SQL_NUMERIC_STRUCT> mem = ns;
|
||||
byte[] bits = mem.GetBytes();
|
||||
|
||||
int[] buffer = new int[4];
|
||||
buffer[3] = bits[2] << 16; // scale
|
||||
if (0 == bits[3])
|
||||
buffer[3] |= unchecked((int)0x80000000); //sign
|
||||
buffer[0] = BitConverter.ToInt32(bits, 4); // low
|
||||
buffer[1] = BitConverter.ToInt32(bits, 8); // mid
|
||||
buffer[2] = BitConverter.ToInt32(bits, 12); // high
|
||||
return 0 != BitConverter.ToInt32(bits, 16) ? throw new OverflowException() : new decimal(buffer);
|
||||
}
|
||||
}
|
||||
|
||||
[PInvokeData("sqltypes.h")]
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
public struct SQL_YEAR_MONTH
|
||||
{
|
||||
public uint year;
|
||||
public uint month;
|
||||
}
|
||||
|
||||
[PInvokeData("sqltypes.h")]
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
public struct TIME_STRUCT
|
||||
{
|
||||
public ushort hour;
|
||||
public ushort minute;
|
||||
public ushort second;
|
||||
|
||||
public static implicit operator TimeSpan(TIME_STRUCT t) => new(t.hour, t.minute, t.second);
|
||||
|
||||
public static implicit operator TIME_STRUCT(TimeSpan t) => new() { hour = (ushort)t.Hours, minute = (ushort)t.Minutes, second = (ushort)t.Seconds };
|
||||
}
|
||||
|
||||
[PInvokeData("sqltypes.h")]
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
public struct TIMESTAMP_STRUCT
|
||||
{
|
||||
public short year;
|
||||
public ushort month;
|
||||
public ushort day;
|
||||
public ushort hour;
|
||||
public ushort minute;
|
||||
public ushort second;
|
||||
public uint fraction;
|
||||
|
||||
public static implicit operator DateTime(TIMESTAMP_STRUCT t) => new DateTime(t.year, t.month, t.day, t.hour, t.minute, t.second, DateTimeKind.Unspecified) + TimeSpan.FromTicks(t.fraction / 100);
|
||||
|
||||
public static implicit operator TIMESTAMP_STRUCT(DateTime t) => new() { year = (short)t.Year, month = (ushort)t.Month, day = (ushort)t.Day, hour = (ushort)t.Hour, minute = (ushort)t.Minute, second = (ushort)t.Second, fraction = (uint)(t.Ticks % TimeSpan.TicksPerSecond) * 100 };
|
||||
}
|
||||
|
||||
public class Odbc32Exception(string message) : System.Data.Common.DbException(message)
|
||||
{
|
||||
public IReadOnlyCollection<Odbc32Error> Errors { get; private set; } = [];
|
||||
|
||||
public static Odbc32Exception CreateException(string message, SQLRETURN ret, ISQLHANDLE? h = null)
|
||||
{
|
||||
var errs = Odbc32Error.GetDiagErrors(h, ret);
|
||||
StringBuilder sb = new(message);
|
||||
if (sb.Length > 0) sb.AppendLine();
|
||||
foreach (var e in errs)
|
||||
sb.AppendLine($"State: {e.SqlState},\tErr: {e.hResult},\tMessage: {e.Message}");
|
||||
return new(sb.ToString()) { Errors = errs };
|
||||
}
|
||||
|
||||
public class Odbc32Error(string msg, string state, int err)
|
||||
{
|
||||
public int hResult { get; } = err;
|
||||
public string Message { get; } = msg;
|
||||
public string SqlState { get; } = state;
|
||||
|
||||
internal static List<Odbc32Error> GetDiagErrors(ISQLHANDLE? h, SQLRETURN ret)
|
||||
{
|
||||
List<Odbc32Error> errors = [];
|
||||
if (ret != SQLRETURN.SQL_SUCCESS && h is not null)
|
||||
{
|
||||
short i = 0;
|
||||
StringBuilder sbState = new(5), sbMsg = new(1024);
|
||||
bool more = true;
|
||||
while (more)
|
||||
{
|
||||
++i;
|
||||
var rc = SQLGetDiagRec(h.GetHandleVal(), h.DangerousGetHandle(), i, sbState, out var err, sbMsg, (short)sbMsg.Capacity, out var req);
|
||||
if (rc == SQLRETURN.SQL_SUCCESS_WITH_INFO && sbMsg.Capacity - 1 < req)
|
||||
{
|
||||
sbMsg.Capacity = req + 1;
|
||||
rc = SQLGetDiagRec(h.GetHandleVal(), h.DangerousGetHandle(), i, sbState, out err, sbMsg, (short)sbMsg.Capacity, out _);
|
||||
}
|
||||
if (more = SQL_SUCCEEDED(rc))
|
||||
errors.Add(new Odbc32Error(sbMsg.ToString(), sbState.ToString(), err));
|
||||
}
|
||||
}
|
||||
return errors;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* enumerations for DATETIME_INTERVAL_SUBCODE values for interval data ref types these values are from SQL-ref 92 */
|
||||
}
|
|
@ -0,0 +1,23 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<ProjectExtensions>
|
||||
<SupportedDlls>odbc32.dll</SupportedDlls>
|
||||
</ProjectExtensions>
|
||||
<PropertyGroup>
|
||||
<Description>PInvoke API (methods, structures and constants) imported from Windows Odbc32.dll.</Description>
|
||||
<AssemblyName>Vanara.PInvoke.Odbc32</AssemblyName>
|
||||
<AssemblyTitle>$(AssemblyName)</AssemblyTitle>
|
||||
<PackageId>$(AssemblyName)</PackageId>
|
||||
<PackageTags>pinvoke;vanara;net-extensions;interop;odbc32;odbc</PackageTags>
|
||||
<PackageReleaseNotes />
|
||||
<PackageReadmeFile>pkgreadme.md</PackageReadmeFile>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<Compile Remove="OleDB.cs" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\..\Core\Vanara.Core.csproj" />
|
||||
<ProjectReference Include="..\Shared\Vanara.PInvoke.Shared.csproj" />
|
||||
</ItemGroup>
|
||||
</Project>
|
|
@ -0,0 +1,9 @@
|
|||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<PropertyGroup>
|
||||
<AssemblyName>UnitTest.PInvoke.Odbc32</AssemblyName>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\..\..\PInvoke\Kernel32\Vanara.PInvoke.Kernel32.csproj" />
|
||||
<ProjectReference Include="..\..\..\PInvoke\Odbc32\Vanara.PInvoke.Odbc32.csproj" />
|
||||
</ItemGroup>
|
||||
</Project>
|
|
@ -0,0 +1,214 @@
|
|||
using NUnit.Framework;
|
||||
using NUnit.Framework.Internal;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Runtime.InteropServices;
|
||||
using static Vanara.PInvoke.Odbc32;
|
||||
|
||||
namespace Vanara.PInvoke.Tests;
|
||||
[TestFixture]
|
||||
public class Odbc32Tests
|
||||
{
|
||||
private const int DISPLAY_MAX = 50;
|
||||
private const short NULL_SIZE = 6;
|
||||
private const short DISPLAY_FORMAT_EXTRA = 3;
|
||||
private const short gHeight = 80;
|
||||
|
||||
[OneTimeSetUp]
|
||||
public void _Setup()
|
||||
{
|
||||
}
|
||||
|
||||
[OneTimeTearDown]
|
||||
public void _TearDown()
|
||||
{
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Test()
|
||||
{
|
||||
using var hEnv = SQLAllocHandle<SafeSQLHENV>();
|
||||
SQLSetEnvAttr(hEnv, SQL_ATTR.SQL_ATTR_ODBC_VERSION, (IntPtr)SQL_OV.SQL_OV_ODBC3, 0).ThrowIfFailed(hEnv);
|
||||
using var hDbc = SQLAllocHandle<SafeSQLHDBC>(hEnv);
|
||||
SQLDriverConnect(hDbc, GetDesktopWindow(), "", SQL_NTS, default, 0, out _, SQL_DRIVER.SQL_DRIVER_COMPLETE).ThrowIfFailed(hDbc);
|
||||
try
|
||||
{
|
||||
using var hStmt = SQLAllocHandle<SafeSQLHSTMT>(hDbc);
|
||||
SQLExec(hStmt, "SELECT * FROM Filters");
|
||||
}
|
||||
finally
|
||||
{
|
||||
SQLDisconnect(hDbc);
|
||||
}
|
||||
}
|
||||
|
||||
static private void SQLExec(SQLHSTMT hStmt, string cmd)
|
||||
{
|
||||
SQLExecDirect(hStmt, cmd).ThrowIfFailed(hStmt);
|
||||
try
|
||||
{
|
||||
SQLNumResultCols(hStmt, out var sNumResults).ThrowIfFailed(hStmt);
|
||||
if (sNumResults > 0)
|
||||
{
|
||||
DisplayResults(hStmt, sNumResults);
|
||||
}
|
||||
else
|
||||
{
|
||||
SQLRowCount(hStmt, out var rows).ThrowIfFailed(hStmt);
|
||||
TestContext.WriteLine($"{rows} row{(rows == 1 ? "" : "s")} affected");
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
SQLFreeStmt(hStmt, SQL_STMT.SQL_CLOSE).ThrowIfFailed(hStmt);
|
||||
}
|
||||
}
|
||||
|
||||
/************************************************************************
|
||||
/* DisplayResults: display results of a select query
|
||||
/*
|
||||
/* Parameters:
|
||||
/* hStmt ODBC statement handle
|
||||
/* cCols Count of columns
|
||||
/************************************************************************/
|
||||
static void DisplayResults(SQLHSTMT hStmt, short cCols)
|
||||
{
|
||||
// Allocate memory for each column
|
||||
AllocateBindings(hStmt, cCols, out var pBindings, out var cDisplaySize);
|
||||
|
||||
// Set the display mode and write the titles
|
||||
DisplayTitles(hStmt, (short)(cDisplaySize + 1), pBindings);
|
||||
|
||||
// Fetch and display the data
|
||||
|
||||
bool fNoData = false;
|
||||
int iCount = 0;
|
||||
do
|
||||
{
|
||||
// Fetch a row
|
||||
|
||||
if (iCount++ >= gHeight - 2)
|
||||
{
|
||||
iCount = 1;
|
||||
DisplayTitles(hStmt, (short)(cDisplaySize + 1), pBindings);
|
||||
}
|
||||
|
||||
SQLRETURN RetCode = SQLFetch(hStmt);
|
||||
if (RetCode == SQLRETURN.SQL_NO_DATA)
|
||||
{
|
||||
fNoData = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Display the data. Ignore truncations
|
||||
|
||||
foreach (var pThisBinding in pBindings)
|
||||
{
|
||||
TestContext.Write($"| {(pThisBinding.indPtr == SQL_NULL_DATA ? "<null>" : pThisBinding.wszBuffer!.ToString()!).PadRight(pThisBinding.cDisplaySize)}");
|
||||
}
|
||||
TestContext.WriteLine("|");
|
||||
}
|
||||
} while (!fNoData);
|
||||
}
|
||||
|
||||
/************************************************************************
|
||||
/* AllocateBindings: Get column information and allocate bindings
|
||||
/* for each column.
|
||||
/*
|
||||
/* Parameters:
|
||||
/* hStmt Statement handle
|
||||
/* cCols Number of columns in the result set
|
||||
/* *lppBinding Binding pointer (returned)
|
||||
/* lpDisplay Display size of one line
|
||||
/************************************************************************/
|
||||
static void AllocateBindings(SQLHSTMT hStmt, short cCols, out IList<BINDING> ppBinding, out short pDisplay)
|
||||
{
|
||||
ppBinding = [];
|
||||
pDisplay = 0;
|
||||
|
||||
for (ushort iCol = 1; iCol <= cCols; iCol++)
|
||||
{
|
||||
BINDING pThisBinding = new();
|
||||
ppBinding.Add(pThisBinding);
|
||||
|
||||
// Figure out the display length of the column (we will
|
||||
// bind to byte since we are only displaying data, in general
|
||||
// you should bind to the appropriate C type if you are going
|
||||
// to manipulate data since it is much faster...)
|
||||
|
||||
//SQLColAttribute(hStmt, iCol, SQL_DESC.SQL_DESC_DISPLAY_SIZE, default, 0, out _, out var cchDisplay).ThrowIfFailed(hStmt);
|
||||
var cchDisplay = SQLColAttribute<int>(hStmt, iCol, SQL_DESC.SQL_DESC_DISPLAY_SIZE);
|
||||
|
||||
// Figure out if this is a character or numeric column; this is
|
||||
// used to determine if we want to display the data left- or right-
|
||||
// aligned.
|
||||
|
||||
// SQL_DESC_CONCISE_TYPE maps to the 1.x SQL_COLUMN_TYPE.
|
||||
// This is what you must use if you want to work
|
||||
// against a 2.x driver.
|
||||
|
||||
//SQLColAttribute(hStmt, iCol, SQL_DESC.SQL_DESC_CONCISE_TYPE, default, 0, out _, out var ssType).ThrowIfFailed(hStmt);
|
||||
var ssType = SQLColAttribute<SQL_TYPE>(hStmt, iCol, SQL_DESC.SQL_DESC_CONCISE_TYPE);
|
||||
|
||||
pThisBinding.fChar = ssType is SQL_TYPE.SQL_CHAR or SQL_TYPE.SQL_VARCHAR or SQL_TYPE.SQL_LONGVARCHAR;
|
||||
|
||||
// Arbitrary limit on display size
|
||||
if (cchDisplay > DISPLAY_MAX)
|
||||
cchDisplay = DISPLAY_MAX;
|
||||
|
||||
// Allocate a buffer big enough to hold the text representation
|
||||
// of the data. Add one character for the null terminator
|
||||
|
||||
pThisBinding.wszBuffer = new(cchDisplay + 1);
|
||||
|
||||
// Map this buffer to the driver's buffer. At Fetch time,
|
||||
// the driver will fill in this data. Note that the size is
|
||||
// count of bytes (for Unicode). All ODBC functions that take
|
||||
// SQLPOINTER use count of bytes; all functions that take only
|
||||
// strings use count of characters.
|
||||
|
||||
SQLBindCol(hStmt, iCol, SQL_C_TCHAR(), pThisBinding.wszBuffer, pThisBinding.wszBuffer.Size, ref pThisBinding.indPtr).ThrowIfFailed(hStmt);
|
||||
|
||||
// Now set the display size that we will use to display
|
||||
// the data. Figure out the length of the column name
|
||||
|
||||
pThisBinding.colName = SQLColAttribute<string>(hStmt, iCol, SQL_DESC.SQL_DESC_NAME);
|
||||
|
||||
pThisBinding.cDisplaySize = (short)Math.Max(cchDisplay, pThisBinding.colName.Length);
|
||||
if (pThisBinding.cDisplaySize < NULL_SIZE)
|
||||
pThisBinding.cDisplaySize = NULL_SIZE;
|
||||
|
||||
pDisplay += (short)(pThisBinding.cDisplaySize + DISPLAY_FORMAT_EXTRA);
|
||||
}
|
||||
}
|
||||
|
||||
/************************************************************************
|
||||
/* DisplayTitles: print the titles of all the columns and set the
|
||||
/* shell window's width
|
||||
/*
|
||||
/* Parameters:
|
||||
/* hStmt Statement handle
|
||||
/* cDisplaySize Total display size
|
||||
/* pBinding list of binding information
|
||||
/************************************************************************/
|
||||
static void DisplayTitles(SQLHSTMT hStmt, short cDisplaySize, IList<BINDING> pBindings)
|
||||
{
|
||||
foreach (var pBinding in pBindings)
|
||||
{
|
||||
TestContext.Write($"| {(pBinding.colName ?? "").PadRight(pBinding.cDisplaySize)}");
|
||||
}
|
||||
TestContext.WriteLine("|");
|
||||
}
|
||||
|
||||
public class BINDING
|
||||
{
|
||||
public short cDisplaySize; /* size to display */
|
||||
public string? colName; /* name of column */
|
||||
public SafeLPTSTR? wszBuffer; /* display buffer */
|
||||
public nint indPtr; /* size or null */
|
||||
public bool fChar; /* character col? */
|
||||
}
|
||||
|
||||
[DllImport("user32.dll", SetLastError = true)]
|
||||
private static extern HWND GetDesktopWindow();
|
||||
}
|
41
Vanara.sln
41
Vanara.sln
|
@ -433,6 +433,10 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "WebSocket", "UnitTests\PInv
|
|||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "IMAPI", "UnitTests\PInvoke\IMAPI\IMAPI.csproj", "{55E8ADA4-D27A-42B9-8BFD-8313B2DEF6DB}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Vanara.PInvoke.Odbc32", "PInvoke\Odbc32\Vanara.PInvoke.Odbc32.csproj", "{ED1BF632-4ACD-4B6B-9F27-C6DE86E00D37}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Odbc32", "UnitTests\PInvoke\Odbc32\Odbc32.csproj", "{94724746-9D66-45E5-8EF1-419E29C6DF12}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|Any CPU = Debug|Any CPU
|
||||
|
@ -3790,6 +3794,41 @@ Global
|
|||
{55E8ADA4-D27A-42B9-8BFD-8313B2DEF6DB}.Release|x64.Build.0 = Release|x64
|
||||
{55E8ADA4-D27A-42B9-8BFD-8313B2DEF6DB}.Release|x86.ActiveCfg = Release|x86
|
||||
{55E8ADA4-D27A-42B9-8BFD-8313B2DEF6DB}.Release|x86.Build.0 = Release|x86
|
||||
{ED1BF632-4ACD-4B6B-9F27-C6DE86E00D37}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{ED1BF632-4ACD-4B6B-9F27-C6DE86E00D37}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{ED1BF632-4ACD-4B6B-9F27-C6DE86E00D37}.Debug|x64.ActiveCfg = Debug|x64
|
||||
{ED1BF632-4ACD-4B6B-9F27-C6DE86E00D37}.Debug|x64.Build.0 = Debug|x64
|
||||
{ED1BF632-4ACD-4B6B-9F27-C6DE86E00D37}.Debug|x86.ActiveCfg = Debug|x86
|
||||
{ED1BF632-4ACD-4B6B-9F27-C6DE86E00D37}.Debug|x86.Build.0 = Debug|x86
|
||||
{ED1BF632-4ACD-4B6B-9F27-C6DE86E00D37}.DebugNoTests|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{ED1BF632-4ACD-4B6B-9F27-C6DE86E00D37}.DebugNoTests|Any CPU.Build.0 = Debug|Any CPU
|
||||
{ED1BF632-4ACD-4B6B-9F27-C6DE86E00D37}.DebugNoTests|x64.ActiveCfg = Debug|x64
|
||||
{ED1BF632-4ACD-4B6B-9F27-C6DE86E00D37}.DebugNoTests|x64.Build.0 = Debug|x64
|
||||
{ED1BF632-4ACD-4B6B-9F27-C6DE86E00D37}.DebugNoTests|x86.ActiveCfg = Debug|x86
|
||||
{ED1BF632-4ACD-4B6B-9F27-C6DE86E00D37}.DebugNoTests|x86.Build.0 = Debug|x86
|
||||
{ED1BF632-4ACD-4B6B-9F27-C6DE86E00D37}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{ED1BF632-4ACD-4B6B-9F27-C6DE86E00D37}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{ED1BF632-4ACD-4B6B-9F27-C6DE86E00D37}.Release|x64.ActiveCfg = Release|x64
|
||||
{ED1BF632-4ACD-4B6B-9F27-C6DE86E00D37}.Release|x64.Build.0 = Release|x64
|
||||
{ED1BF632-4ACD-4B6B-9F27-C6DE86E00D37}.Release|x86.ActiveCfg = Release|x86
|
||||
{ED1BF632-4ACD-4B6B-9F27-C6DE86E00D37}.Release|x86.Build.0 = Release|x86
|
||||
{94724746-9D66-45E5-8EF1-419E29C6DF12}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{94724746-9D66-45E5-8EF1-419E29C6DF12}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{94724746-9D66-45E5-8EF1-419E29C6DF12}.Debug|x64.ActiveCfg = Debug|x64
|
||||
{94724746-9D66-45E5-8EF1-419E29C6DF12}.Debug|x64.Build.0 = Debug|x64
|
||||
{94724746-9D66-45E5-8EF1-419E29C6DF12}.Debug|x86.ActiveCfg = Debug|x86
|
||||
{94724746-9D66-45E5-8EF1-419E29C6DF12}.Debug|x86.Build.0 = Debug|x86
|
||||
{94724746-9D66-45E5-8EF1-419E29C6DF12}.DebugNoTests|Any CPU.ActiveCfg = DebugNoTests|Any CPU
|
||||
{94724746-9D66-45E5-8EF1-419E29C6DF12}.DebugNoTests|Any CPU.Build.0 = DebugNoTests|Any CPU
|
||||
{94724746-9D66-45E5-8EF1-419E29C6DF12}.DebugNoTests|x64.ActiveCfg = DebugNoTests|x64
|
||||
{94724746-9D66-45E5-8EF1-419E29C6DF12}.DebugNoTests|x64.Build.0 = DebugNoTests|x64
|
||||
{94724746-9D66-45E5-8EF1-419E29C6DF12}.DebugNoTests|x86.ActiveCfg = DebugNoTests|x86
|
||||
{94724746-9D66-45E5-8EF1-419E29C6DF12}.DebugNoTests|x86.Build.0 = DebugNoTests|x86
|
||||
{94724746-9D66-45E5-8EF1-419E29C6DF12}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{94724746-9D66-45E5-8EF1-419E29C6DF12}.Release|x64.ActiveCfg = Release|x64
|
||||
{94724746-9D66-45E5-8EF1-419E29C6DF12}.Release|x64.Build.0 = Release|x64
|
||||
{94724746-9D66-45E5-8EF1-419E29C6DF12}.Release|x86.ActiveCfg = Release|x86
|
||||
{94724746-9D66-45E5-8EF1-419E29C6DF12}.Release|x86.Build.0 = Release|x86
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
|
@ -3984,6 +4023,8 @@ Global
|
|||
{2727B06D-1E73-4108-AFDE-39C1A6F99806} = {3EC6B40D-71D3-4E59-A0E0-544EC605FE11}
|
||||
{AEA2913A-5E67-4C33-823F-CE06295DB35A} = {385CAD2D-0A5E-4F80-927B-D5499D126B90}
|
||||
{55E8ADA4-D27A-42B9-8BFD-8313B2DEF6DB} = {385CAD2D-0A5E-4F80-927B-D5499D126B90}
|
||||
{ED1BF632-4ACD-4B6B-9F27-C6DE86E00D37} = {212ABBD0-B724-4CFA-9D6D-E3891547FA90}
|
||||
{94724746-9D66-45E5-8EF1-419E29C6DF12} = {385CAD2D-0A5E-4F80-927B-D5499D126B90}
|
||||
EndGlobalSection
|
||||
GlobalSection(ExtensibilityGlobals) = postSolution
|
||||
SolutionGuid = {543FAC75-2AF1-4EF1-9609-B242B63FEED4}
|
||||
|
|
Loading…
Reference in New Issue