Multiclient pipe connection? - c++

Is it possible to create a multiclient pipe? With one server and multiple clients? From the official documentation I have read that " A pipe server could use a single pipe instance to connect with multiple pipe clients by connecting to and disconnecting from each client in sequence, but performance would be poor" ( https://msdn.microsoft.com/en-us/library/windows/desktop/aa365594(v=vs.85).aspx ). Is this behaviour standard (can be done using some flags or something like that) or I have to implement this behaviour by myself? I have written a test with multiply clients, but when I trying to connect by the second client, I got error STATUS_PIPE_NOT_AVAILABLE.
There is my code, it quite big, but functions test_multiple_client and test_multiple_client2 is the same
void test_mutiple_client( PVOID arg )
{
OBJECT_ATTRIBUTES oa;
UNICODE_STRING us;
IO_STATUS_BLOCK iosb;
HANDLE thread = 0, event = 0, client = 0;
NTSTATUS r;
CLIENT_ID id;
LARGE_INTEGER timeout;
ULONG i;
us.Buffer = pipename;
us.Length = sizeof pipename - 2;
us.MaximumLength = us.Length;
oa.Length = sizeof oa;
oa.RootDirectory = 0;
oa.ObjectName = &us;
oa.Attributes = OBJ_CASE_INSENSITIVE;
oa.SecurityDescriptor = 0;
oa.SecurityQualityOfService = 0;
r = NtCreateEvent( &event, EVENT_ALL_ACCESS, NULL, NotificationEvent, 0 );
ok(r == STATUS_SUCCESS, "return wrong (%08lx)\n", r);
r = NtOpenFile( &client, GENERIC_READ | GENERIC_WRITE, &oa, &iosb, FILE_SHARE_READ|FILE_SHARE_WRITE, 0 );
ok(r == STATUS_SUCCESS, "return wrong %08lx\n", r);
dprintf("mc: client1 pipe created\n");
int thread_id = __sync_add_and_fetch(&g__clientsCounter, 1);
while (g__clientsCounter != 2);
dprintf("thread %d stated\n", thread_id);
r = NtReadFile( client, event, 0, 0, &iosb, &i, sizeof i, 0, 0 );
if (r == STATUS_PENDING)
r = NtWaitForSingleObject( event, TRUE, 0 );
ok (r == STATUS_SUCCESS, "read %ld returned %08lx\n", i, r);
ok (i == 13, "lol?????");
r = NtClose( client );
ok( r == STATUS_SUCCESS, "return wrong %08lx\n", r);
}
void test_mutiple_client2( PVOID arg )
{
OBJECT_ATTRIBUTES oa;
UNICODE_STRING us;
IO_STATUS_BLOCK iosb;
HANDLE thread = 0, event = 0, client = 0;
NTSTATUS r;
CLIENT_ID id;
LARGE_INTEGER timeout;
ULONG i;
us.Buffer = pipename;
us.Length = sizeof pipename - 2;
us.MaximumLength = us.Length;
oa.Length = sizeof oa;
oa.RootDirectory = 0;
oa.ObjectName = &us;
oa.Attributes = OBJ_CASE_INSENSITIVE;
oa.SecurityDescriptor = 0;
oa.SecurityQualityOfService = 0;
r = NtCreateEvent( &event, EVENT_ALL_ACCESS, NULL, NotificationEvent, 0 );
ok(r == STATUS_SUCCESS, "return wrong (%08lx)\n", r);
r = NtOpenFile( &client, GENERIC_READ | GENERIC_WRITE, &oa, &iosb, FILE_SHARE_READ|FILE_SHARE_WRITE, 0 );
ok(r == STATUS_SUCCESS, "return wrong %08lx\n", r);
dprintf("mc: client1 pipe created\n");
int thread_id = __sync_add_and_fetch(&g__clientsCounter, 1);
while (g__clientsCounter != 2);
dprintf("thread %d stated\n", thread_id);
r = NtReadFile( client, event, 0, 0, &iosb, &i, sizeof i, 0, 0 );
if (r == STATUS_PENDING)
r = NtWaitForSingleObject( event, TRUE, 0 );
ok (r == STATUS_SUCCESS, "read %ld returned %08lx\n", i, r);
ok (i == 13, "lol?????");
r = NtClose( client );
ok( r == STATUS_SUCCESS, "return wrong %08lx\n", r);
}
void test_multiple_connections( )
{
OBJECT_ATTRIBUTES oa;
UNICODE_STRING us;
IO_STATUS_BLOCK iosb;
HANDLE pipe = 0, thread = 0, event = 0;
NTSTATUS r;
CLIENT_ID id;
LARGE_INTEGER timeout;
ULONG i;
us.Buffer = pipename;
us.Length = sizeof pipename - 2;
us.MaximumLength = us.Length;
oa.Length = sizeof oa;
oa.RootDirectory = 0;
oa.ObjectName = &us;
oa.Attributes = OBJ_CASE_INSENSITIVE;
oa.SecurityDescriptor = 0;
oa.SecurityQualityOfService = 0;
timeout.QuadPart = -10000LL;
r = NtCreateNamedPipeFile( &pipe, GENERIC_READ|GENERIC_WRITE|SYNCHRONIZE,
&oa, &iosb, FILE_SHARE_READ|FILE_SHARE_WRITE, FILE_OPEN_IF, 0, TRUE,
TRUE, FALSE, /*Unlimited*/ -1, 0, 0, &timeout );
ok( r == STATUS_SUCCESS, "return wrong %08lx\n", r);
dprintf("mc: server pipe created\n");
r = RtlCreateUserThread( NtCurrentProcess(), NULL, FALSE,
NULL, 0, 0, &test_mutiple_client, NULL, &thread, &id );
ok( r == STATUS_SUCCESS, "failed to create thread\n" );
r = RtlCreateUserThread( NtCurrentProcess(), NULL, FALSE,
NULL, 0, 0, &test_mutiple_client2, NULL, &thread, &id );
ok( r == STATUS_SUCCESS, "failed to create thread\n" );
r = NtCreateEvent( &event, EVENT_ALL_ACCESS, NULL, NotificationEvent, 0 );
ok(r == STATUS_SUCCESS, "return wrong (%08lx)\n", r);
r = NtFsControlFile( pipe, event, 0, 0, &iosb, FSCTL_PIPE_LISTEN, 0, 0, 0, 0 );
if (r == STATUS_PENDING) {
dprintf("mc: pending\n");
r = NtWaitForSingleObject( event, TRUE, 0 );
}
ok( r == STATUS_SUCCESS, "failed to listen %08lx\n", r );
dprintf("mc: server pipe listen\n");
i = 13;
while (g__clientsCounter != 2);
dprintf("server started\n");
r = NtWriteFile( pipe, event, 0, 0, &iosb, &i, sizeof i, 0, 0 );
if (r == STATUS_PENDING)
r = NtWaitForSingleObject( event, TRUE, 0 );
ok (r == STATUS_SUCCESS, "write %ld returned %08lx\n", i, r);
dprintf("server write data\n");
r = NtClose( pipe );
ok( r == STATUS_SUCCESS, "return wrong %08lx\n", r);
}
The output is
mc: server pipe created
mc: pending
mc: server pipe listen
mc: client1 pipe created
478: return wrong c00000ac
mc: client1 pipe created
thread 2 stated
488: read 2013057864 returned c0000008
489: lol?????492: return wrong c0000008
server started
thread 1 stated
server write data
4 failed, 38 passed
I also have seen an answer of stack ( Number of Clients that can connect to a Named Pipe ) where mentioned that windows pipes can hold up to 256 clients

Related

odbc binding to datetime column fails on Linux but works on Windows

Our C++ code on Windows uses ODBC to talk to SQL Server.
We are in the process of porting to Linux and
getting this ODBC error for a datetime Stored Procedure argument.
Datetime field overflow. Fractional second precision exceeds the scale specified in the parameter binding.
Googling that error says to set scale to 3 on the ODBC bind.
We do that and it works on Windows but not Linux.
Here is a semi small code sample that reproduces the problem.
The sample is 330 lines of C++ and 100 lines of SQL.
(The original is 1000 times bigger.)
It seems to be related to a blob SP argument.
datetime before the blob work, and those after the blob fail.
I tried making a 3 column test table with (datetime, blob, datetime)
but the problem did not happen.
On Windows this test inserts 820 rows while
on Linux it inserts 700 because 120 fail.
Windows has "ODBC Driver 17 for SQL Server".
Linux is Ubuntu 20.04, unixODBC 2.3.7, ODBC 17.7
Create a database name bgb_test2.
Create a DSN named bgb8_sql2016 pointing to DB server.
Add SQL user 'bgb' with password 'Abc-123'.
Or change the code as you like.
SQL code
USE [bgb_test2]
GO
/****** Object: StoredProcedure [dbo].[sp_TTemporalAccounts_ins] Script Date: 5/28/2021 11:46:19 AM ******/
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
CREATE TABLE [dbo].[TTemporalAccounts](
[acctId] [int] NOT NULL,
[rowClassifier] [char](16) NULL,
[grpClassifier] [char](16) NULL,
[LAD] [datetime] NULL,
[LAPD] [datetime] NULL,
[NAD] [datetime] NULL,
[AccountBlob] [text] NULL,
[StatementDate] [datetime] NULL,
[postBalance] [money] NULL,
[Balance] [money] NULL,
[CountDe] [int] NULL,
[bucketMTD1] [money] NULL,
[bucketMTD2] [money] NULL,
[bucketMTD3] [money] NULL,
[bucketCTD1] [money] NULL,
[bucketCTD2] [money] NULL,
[bucketCTD3] [money] NULL,
[parent01ATID] [int] NULL,
[parent01AID] [int] NULL,
[parent02ATID] [int] NULL,
[parent02AID] [int] NULL,
[parent03ATID] [int] NULL,
[parent03AID] [int] NULL,
[VisaInterestRate] [money] NULL,
[VisaLateCharge] [money] NULL,
[VisaParam1] [money] NULL,
[VisaParam2] [money] NULL,
[VisaParam3] [money] NULL,
[VisaParam4] [money] NULL,
[VisaParam5] [money] NULL,
[VisaParam6] [money] NULL,
[VisaParam7] [money] NULL,
[VisaParam8] [money] NULL,
[tpyNAD] [datetime] NULL,
[tpyLAD] [datetime] NULL,
[tpyBlob] [text] NULL,
[param_sig] [int] NULL,
[param_state] [varchar](171) NULL,
[param_lcd] [datetime] NULL,
[param_fvt] [datetime] NULL,
[_paramlvt] [datetime] NULL,
CONSTRAINT [csPk_TTemporalAccounts] PRIMARY KEY CLUSTERED
(
[acctId] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY] TEXTIMAGE_ON [PRIMARY]
GO
CREATE PROCEDURE [dbo].[sp_TTemporalAccounts_ins] (
#acctId int, #rowClassifier char (16), #grpClassifier char (16),
#LAD datetime, #LAPD datetime, #NAD datetime,
#AccountBlob text, #StatementDate datetime, #postBalance money,
#Balance money, #CountDe int,
#bucketMTD1 money, #bucketMTD2 money, #bucketMTD3 money,
#bucketCTD1 money, #bucketCTD2 money, #bucketCTD3 money,
#parent01ATID int, #parent01AID int,
#parent02ATID int, #parent02AID int,
#parent03ATID int, #parent03AID int,
#VisaInterestRate money, #VisaLateCharge money,
#VisaParam1 money, #VisaParam2 money, #VisaParam3 money,
#VisaParam4 money, #VisaParam5 money, #VisaParam6 money,
#VisaParam7 money, #VisaParam8 money,
#tpyNAD datetime, #tpyLAD datetime, #tpyBlob text,
#param_sig int, #param_state varchar (171),
#param_lcd datetime, #param_fvt datetime, #_paramlvt datetime
) AS SET NOCOUNT ON; SET ANSI_NULLS ON; SET ANSI_PADDING ON; SET ANSI_WARNINGS ON; SET ARITHABORT ON; SET QUOTED_IDENTIFIER ON; SET CONCAT_NULL_YIELDS_NULL ON; SET NUMERIC_ROUNDABORT OFF;
INSERT INTO TTemporalAccounts ( [acctId], [rowClassifier], [grpClassifier], [LAD], [LAPD], [NAD],
[AccountBlob], [StatementDate], [postBalance], [Balance], [CountDe], [bucketMTD1], [bucketMTD2], [bucketMTD3],
[bucketCTD1], [bucketCTD2], [bucketCTD3], [parent01ATID], [parent01AID], [parent02ATID], [parent02AID],
[parent03ATID], [parent03AID], [VisaInterestRate], [VisaLateCharge],
[VisaParam1], [VisaParam2], [VisaParam3], [VisaParam4], [VisaParam5], [VisaParam6], [VisaParam7], [VisaParam8],
[tpyNAD], [tpyLAD], [tpyBlob],
[param_sig], [param_state], [param_lcd], [param_fvt], [_paramlvt])
VALUES ( #acctId, #rowClassifier, #grpClassifier, #LAD, #LAPD, #NAD, #AccountBlob, #StatementDate,
#postBalance, #Balance, #CountDe, #bucketMTD1, #bucketMTD2, #bucketMTD3, #bucketCTD1, #bucketCTD2, #bucketCTD3,
#parent01ATID, #parent01AID, #parent02ATID, #parent02AID, #parent03ATID, #parent03AID,
#VisaInterestRate, #VisaLateCharge,
#VisaParam1, #VisaParam2, #VisaParam3, #VisaParam4, #VisaParam5, #VisaParam6, #VisaParam7, #VisaParam8,
#tpyNAD, #tpyLAD, #tpyBlob,
#param_sig, #param_state, #param_lcd, #param_fvt, #_paramlvt)
GO
C++ code
#ifdef WIN32
#ifdef _DEBUG
#define _ITERATOR_DEBUG_LEVEL 1
#endif
#define WIN32_LEAN_AND_MEAN
#include <Windows.h>
#include <sal.h>
#endif
#include <string.h>
#include <sql.h>
#include <sqlext.h>
#include <stdio.h>
#include <string>
// Binding to datetime column is failing on Linux
RETCODE
BBCheckForInfo(SQLSMALLINT HandleType, SQLHANDLE h, RETCODE rc, const char* api, const char* name = 0)
{
SQLCHAR SqlState[6], Msg[SQL_MAX_MESSAGE_LENGTH];
SQLINTEGER NativeError;
SQLSMALLINT i, MsgLen;
SQLRETURN rc2;
long cpu_time = 0;
long elapsed_time = 0;
// Support growing these if needed
SQLCHAR* pMsg = Msg;
size_t MsgAlloc = sizeof(Msg);
if (name == 0) name = "";
// Print any errors or warnings.
if (rc != SQL_SUCCESS) {
// Get the status records.
i = 1;
while ((rc2 = SQLGetDiagRec(HandleType, h, i, SqlState, &NativeError, pMsg, MsgAlloc, &MsgLen)) == SQL_SUCCESS || rc2 == SQL_SUCCESS_WITH_INFO) {
if (rc2 == SQL_SUCCESS_WITH_INFO) {
printf("Msg[] too small, need %d bytes (have %d)\n", MsgLen, MsgAlloc);
if (pMsg != Msg) delete[] pMsg;
MsgAlloc = MsgLen + 100;
pMsg = new SQLCHAR[MsgAlloc + 10];
// Do not increment i so we retry getting the last error message
continue;
}
i++;
printf("Query %s API %s() Name %s State %s Native %d Msg %s\n",
name,
api,
name,
SqlState, NativeError, Msg);
} // while
if (rc2 != SQL_NO_DATA) {
printf("SQLGetDiagRec(%s) name %s returned %d\n", api, name, rc2);
}
} // if
if (pMsg != Msg) delete[] pMsg;
pMsg = 0;
if (rc == SQL_SUCCESS_WITH_INFO) rc = SQL_SUCCESS;
return rc;
}
enum class CI_type {
CI_int,
CI_char16,
CI_datetime,
CI_text,
CI_money,
CI_varchar171,
// CI_end
};
#define N_COL 41
struct ColInfo {
const char* name;
enum CI_type type;
} g_ColInfoArray[N_COL] = {
"acctId", CI_type::CI_int,
"rowClassifier", CI_type::CI_char16,
"grpClassifier", CI_type::CI_char16,
"LAD", CI_type::CI_datetime,
"LAPD", CI_type::CI_datetime,
"NAD", CI_type::CI_datetime,
"AccountBlob", CI_type::CI_text,
"StatementDate", CI_type::CI_datetime,
"postBalance", CI_type::CI_money,
"Balance", CI_type::CI_money,
"CountDe", CI_type::CI_int,
"bucketMTD1", CI_type::CI_money,
"bucketMTD2", CI_type::CI_money,
"bucketMTD3", CI_type::CI_money,
"bucketCTD1", CI_type::CI_money,
"bucketCTD2", CI_type::CI_money,
"bucketCTD3", CI_type::CI_money,
"parent01ATID", CI_type::CI_int,
"parent01AID", CI_type::CI_int,
"parent02ATID", CI_type::CI_int,
"parent02AID", CI_type::CI_int,
"parent03ATID", CI_type::CI_int,
"parent03AID", CI_type::CI_int,
"VisaInterestRate", CI_type::CI_money,
"VisaLateCharge", CI_type::CI_money,
"VisaParam1", CI_type::CI_money,
"VisaParam2", CI_type::CI_money,
"VisaParam3", CI_type::CI_money,
"VisaParam4", CI_type::CI_money,
"VisaParam5", CI_type::CI_money,
"VisaParam6", CI_type::CI_money,
"VisaParam7", CI_type::CI_money,
"VisaParam8", CI_type::CI_money,
"tpyNAD", CI_type::CI_datetime,
"tpyLAD", CI_type::CI_datetime,
"tpyBlob", CI_type::CI_text,
"param_sig", CI_type::CI_int,
"param_state", CI_type::CI_varchar171,
"param_lcd", CI_type::CI_datetime,
"param_fvt", CI_type::CI_datetime,
"_paramlvt", CI_type::CI_datetime,
// { 0, CI_type::CI_end }
};
void
main()
{
std::string ConStr;
ConStr += ";DSN=bgb8_sql2016"; // change
ConStr += ";DATABASE=bgb_test2";
ConStr += ";UID=bgb;PWD=Abc-123"; // change
ConStr += ";APP=DBB";
// const char* computername = dbbGetenv("COMPUTERNAME");
ConStr += ";WSID=host";
// ConStr += computername;
ConStr += ";AutoTranslate=no";
SQLHENV hEnv;
int rc;
rc = SQLAllocEnv(&hEnv);
rc = SQLSetEnvAttr(hEnv, SQL_ATTR_ODBC_VERSION, (void*)SQL_OV_ODBC3, 0);
rc = BBCheckForInfo(SQL_HANDLE_ENV, hEnv, rc, "SQLSetEnvAttr", "SQL_ATTR_ODBC_VERSION");
SQLHDBC hDbc;
rc = SQLAllocConnect(hEnv, &hDbc);
rc = BBCheckForInfo(SQL_HANDLE_ENV, hEnv, rc, "SQLAllocConnect()");
// rc = SQLSetConnectAttr(hDbc, SQL_COPT_SS_BCP, (void*)SQL_BCP_ON, SQL_IS_INTEGER);
// BBCheckForInfo(SQL_HANDLE_DBC, hDbc, rc, "SQLSetConnectAttr", "SQL_COPT_SS_BCP");
short outlen = 0;
UCHAR outbuf[2048];
rc = SQLDriverConnect(hDbc, NULL, (UCHAR*)ConStr.c_str(), ConStr.length(), outbuf, sizeof(outbuf), &outlen, SQL_DRIVER_NOPROMPT);
rc = BBCheckForInfo(SQL_HANDLE_DBC, hDbc, rc, "SQLDriverConnect()");
SQLHSTMT hstmt;
SQLAllocStmt(hDbc, &hstmt);
rc = BBCheckForInfo(SQL_HANDLE_DBC, hDbc, rc, "SQLAllocStmt()");
int skey = 0;
for (int rows = 0; rows < 20; rows++) {
// for x in 0 .. 2^N_COL-1
// (c in x) means set col, else NULL
// Inserting 2^41 rows may hit practical limitations (BOOM)
for (int nn = 0; nn < N_COL; nn++) {
void* mem[N_COL];
for (int c = 0; c < N_COL; c++) {
// Set PK and column nn to values, rest are NULL
SQLHSTMT StatementHandle = hstmt;
SQLUSMALLINT ParameterNumber = c + 1;
SQLSMALLINT InputOutputType = SQL_PARAM_INPUT;
SQLSMALLINT ValueType = -1;
SQLSMALLINT ParameterType = -1;
SQLULEN ColumnSize = 0;
SQLSMALLINT DecimalDigits = 0;
SQLPOINTER ParameterValuePtr = 0;
SQLLEN BufferLength = 0;
SQLLEN* StrLen_or_IndPtr = 0;
switch (g_ColInfoArray[c].type) {
case CI_type::CI_int:
ValueType = SQL_C_DEFAULT;
ParameterType = SQL_INTEGER;
break;
case CI_type::CI_char16:
ValueType = SQL_C_CHAR;
ParameterType = SQL_CHAR;
ColumnSize = 16;
break;
case CI_type::CI_datetime:
ValueType = SQL_C_TYPE_TIMESTAMP;
ParameterType = SQL_TYPE_TIMESTAMP;
ColumnSize = 23;
DecimalDigits = 3;
break;
case CI_type::CI_text:
ValueType = SQL_C_CHAR;
ParameterType = SQL_LONGVARCHAR;
ColumnSize = 100;
break;
case CI_type::CI_money:
ValueType = SQL_C_CHAR;
ParameterType = SQL_VARCHAR;
break;
case CI_type::CI_varchar171:
ValueType = SQL_C_CHAR;
ParameterType = SQL_CHAR;
ColumnSize = 171;
break;
}
// (c == 0) is Primary Key
int value = 5;
if (c == 0) value = skey++;
if (c == 0 || c == nn) {
switch (g_ColInfoArray[c].type) {
case CI_type::CI_int:
{
SQLINTEGER* p = new SQLINTEGER;
mem[c] = p;
*p = value;
}
break;
case CI_type::CI_char16:
{
BufferLength = 16;
char* p = new char[BufferLength + 1];
mem[c] = p;
strcpy(p, "five");
}
break;
case CI_type::CI_datetime:
{
SQL_TIMESTAMP_STRUCT* p = new SQL_TIMESTAMP_STRUCT;
mem[c] = p;
p->year = 2021;
p->month = 5;
p->day = 27;
p->hour = 20;
p->minute = 21;
p->second = 0;
p->fraction = 0;
}
break;
case CI_type::CI_text:
{
BufferLength = 100;
char* p = new char[BufferLength + 1];
mem[c] = p;
strcpy(p, "five");
}
break;
case CI_type::CI_money:
{
BufferLength = 10;
char* p = new char[BufferLength + 1];
mem[c] = p;
strcpy(p, "555.55");
}
break;
case CI_type::CI_varchar171:
{
BufferLength = 171;
char* p = new char[BufferLength + 1];
mem[c] = p;
strcpy(p, "five");
}
break;
}
ParameterValuePtr = mem[c];
}
else {
SQLLEN* p = new SQLLEN;
mem[c] = p;
*p = SQL_NULL_DATA;
StrLen_or_IndPtr = p;
}
rc = SQLBindParameter(StatementHandle, ParameterNumber, InputOutputType, ValueType, ParameterType, ColumnSize, DecimalDigits, ParameterValuePtr, BufferLength, StrLen_or_IndPtr);
rc = BBCheckForInfo(SQL_HANDLE_STMT, hstmt, rc, "SQLBindParameter");
} // for
rc = SQLExecDirect(hstmt, (SQLCHAR*)"{call [sp_TTemporalAccounts_ins](?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)}", SQL_NTS);
rc = BBCheckForInfo(SQL_HANDLE_STMT, hstmt, rc, "SQLExecDirect()");
for (int c = 0; c < N_COL; c++) {
delete mem[c];
}
} // for
} // for
#if 0
// Table has (int, datetime, text, datetime)
// Did not fail like expected
SQLINTEGER null = SQL_NULL_DATA;
// rc = SQLBindParameter(hstmt, 1, SQL_PARAM_INPUT, SQL_C_TYPE_TIMESTAMP, SQL_TYPE_TIMESTAMP, 23, 3, 0, 0, &null);
// rc = BBCheckForInfo(SQL_HANDLE_STMT, hstmt, rc, "SQLBindParameter()");
// rc = SQLExecDirect(hstmt, (SQLCHAR*)"{call [sp_TTemporalAccounts_ins](?)}", SQL_NTS);
// rc = BBCheckForInfo(SQL_HANDLE_STMT, hstmt, rc, "SQLExecDirect()");
// SQLcancel
SQL_TIMESTAMP_STRUCT ts;
ts.year = 2021;
ts.month = 5;
ts.day = 27;
ts.hour = 20;
ts.minute = 21;
ts.second = 0;
ts.fraction = 0;
rc = SQLBindParameter(hstmt, 1, SQL_PARAM_INPUT, SQL_C_TYPE_TIMESTAMP, SQL_TYPE_TIMESTAMP, 23, 3, &ts, 0, 0);
rc = BBCheckForInfo(SQL_HANDLE_STMT, hstmt, rc, "SQLBindParameter()");
rc = SQLBindParameter(hstmt, 2, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_LONGVARCHAR, 23, 3, 0, 0, &null);
rc = BBCheckForInfo(SQL_HANDLE_STMT, hstmt, rc, "SQLBindParameter()");
rc = SQLBindParameter(hstmt, 3, SQL_PARAM_INPUT, SQL_C_TYPE_TIMESTAMP, SQL_TYPE_TIMESTAMP, 23, 3, 0, 0, &null);
rc = BBCheckForInfo(SQL_HANDLE_STMT, hstmt, rc, "SQLBindParameter()");
rc = SQLExecDirect(hstmt, (SQLCHAR*)"{call [sp_TTemporalAccounts_ins](?,?,?)}", SQL_NTS);
rc = BBCheckForInfo(SQL_HANDLE_STMT, hstmt, rc, "SQLExecDirect()");
rc = SQLBindParameter(hstmt, 1, SQL_PARAM_INPUT, SQL_C_TYPE_TIMESTAMP, SQL_TYPE_TIMESTAMP, 23, 3, 0, 0, &null);
rc = BBCheckForInfo(SQL_HANDLE_STMT, hstmt, rc, "SQLBindParameter()");
rc = SQLBindParameter(hstmt, 2, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_LONGVARCHAR, 23, 3, 0, 0, &null);
rc = BBCheckForInfo(SQL_HANDLE_STMT, hstmt, rc, "SQLBindParameter()");
rc = SQLBindParameter(hstmt, 3, SQL_PARAM_INPUT, SQL_C_TYPE_TIMESTAMP, SQL_TYPE_TIMESTAMP, 23, 3, &ts, 0, 0);
rc = BBCheckForInfo(SQL_HANDLE_STMT, hstmt, rc, "SQLBindParameter()");
rc = SQLExecDirect(hstmt, (SQLCHAR*)"{call [sp_TTemporalAccounts_ins](?,?,?)}", SQL_NTS);
rc = BBCheckForInfo(SQL_HANDLE_STMT, hstmt, rc, "SQLExecDirect()");
// Bindings remain in effect until the application calls SQLBindParameter again,
// calls SQLFreeStmt with the SQL_RESET_PARAMS option,
// or calls SQLSetDescField to set the SQL_DESC_COUNT header field of the APD to 0
#endif
}

How can I fix 22 invalid argument error(22)?

I don't know why keep occurring [22] invalid argument in the process.
the source code like this
int MainLoop()
{
struct timeval timeout;
fd_set event;
int n, maxfd, newfd, rc, err_cnt = 0;
CSocket* pSocket;
int chktime, currtime;
char buff[MAX_PACKET_SIZE];
int MaxPosition, CurrPosition = 0;
WmBoard *pWmBoard;
MaxPosition = pCWmBoard->GetMaxPosition();
g_CAgent.Create(g_CsPort, g_CsAddr);
chktime = GetTime();
while(g_Running) {
timeout.tv_sec = 1; /* Second */
timeout.tv_usec = 0; /* micro Second */
pSocket = NULL;
maxfd = g_SockList.GetEventMask(&event);
n = select(maxfd, &event, (fd_set *)0,(fd_set *)0,
(struct timeval *) &timeout);
if(n > 0) {
pSocket = g_SockList.GetEventSock(&event);
if(pSocket != NULL)
{
newfd = pSocket->Accept();
if(newfd > 0)
{
err_cnt = 0;
pWmBoard = GetMinClientCH(MaxPosition);
if (pWmBoard != NULL)
{
rc = SendFD(pWmBoard->PipeFd, (void *)" ", 1, newfd);
if (rc <= 0)
{
char szTmp[128];
sprintf(szTmp,"FDSend Error=[%d]errno=[%d]",
newfd,errno);
g_CAgent.SendMessage("!S001", g_PgmName, szTmp);
g_Log.Write("[%s:%d][E] %s",
g_PgmName, g_Pid, szTmp);
/*
if(errno == EBADF) g_Running = FALSE;
*/
#if 0
if(pWmBoard -> ProcessID > 1)
kill(pWmBoard->ProcessID, SIGTERM);
#endif
pWmBoard->PipeFd = -1;
}
}
else
{
g_Log.Write("[%s:%d][E] client is full. value=%d",
g_PgmName, g_Pid, g_CurrCHPos);
g_Running = FALSE;
}
close(newfd);
}
else
{
g_Log.Write("[%s:%d][E] accept error. errno = [%d]",
g_PgmName, g_Pid, errno);
switch(errno)
{
case EMFILE : break; /* Too many open files */
case ENOENT : /* No such file or directory */
case EAGAIN : /* Try again */
case EINVAL : /* Invalid argument */
case ENOMSG : ; /* No message of desired type */
default :
ResetSocket();
}
}
}
else
g_Log.Write("[%s:%d] GetEventSock is NULL", g_PgmName, g_Pid);
}
else if (n == 0) {
currtime = GetTime();
if( currtime == 0 && chktime != currtime)
{ // ¸ÅÀÏ ÁöÁ¤ÇÑ ½Ã°£ (24½Ã)
g_Log.ReOpen();
g_MaxUser = 0;
ClearTotalTR();
}
chktime = currtime;
}
if (getppid() <= 1){
g_Running = FALSE;
}
if(WhatTime()) SendWmBoardInfo();
}
g_CAgent.Close();
return FALSE;
}
We call the process as WmCL and the WmCL send data to WmCH for connection.
and I got log using strace command the result same as below.
select(19, [16 17 18], NULL, NULL, {1, 0}) = 1 (in [17], left {0, 914250})
accept(17, {sa_family=AF_INET, sin_port=htons(38610), sin_addr=inet_addr("114.122.207.70")}, [16]) = 20
sendmsg(9, {msg_name(0)=NULL, msg_iov(1)=[{" ", 1}], msg_controllen=24, {cmsg_len=20, cmsg_level=SOL_SOCKET, cmsg_type=SCM_RIGHTS, {20}}, msg_flags=MSG_OOB|MSG_DONTROUTE|MSG_CTRUNC|0x10}, 0) = 1
close(20) = 0
getppid() = 17099
select(19, [16 17 18], NULL, NULL, {1, 0}) = 0 (Timeout)
stat("/etc/localtime", {st_mode=S_IFREG|0644, st_size=344, ...}) = 0
getppid() = 17099
select(19, [16 17 18], NULL, NULL, {1, 0}) = 0 (Timeout)
stat("/etc/localtime", {st_mode=S_IFREG|0644, st_size=344, ...}) = 0
getppid() = 17099
select(19, [16 17 18], NULL, NULL, {1, 0}) = ? ERESTARTNOHAND (To be restarted if no handler)
--- SIGCHLD {si_signo=SIGCHLD, si_code=CLD_EXITED, si_pid=18976, si_status=0, si_utime=1173, si_stime=797} ---
wait4(-1, [{WIFEXITED(s) && WEXITSTATUS(s) == 0}], WNOHANG, NULL) = 18976
close(7) = 0
stat("/etc/localtime", {st_mode=S_IFREG|0644, st_size=344, ...}) = 0
sendto(19, "m20192.168.103.22 1028360WmCL "..., 161, 0, {sa_family=AF_INET, sin_port=htons(8499), sin_addr=inet_addr("192.168.201.17")}, 16) = 161
write(5, "[10:28:36-000002][WmCL:17236][E]"..., 63) = 63
socketpair(PF_LOCAL, SOCK_STREAM, 0, [7, 20]) = 0
clone(child_stack=0, flags=CLONE_CHILD_CLEARTID|CLONE_CHILD_SETTID|SIGCHLD, child_tidptr=0x7f287d4219f0) = 23210
close(20) = 0
as following the log, 'select' result is '?' but I don't know why the result is '?'....
Can you advise to me to fix this problem?

WSARecvFrom blocking

For some reason when I call WSARecvFrom the function does not return until something is received.
_socket = WSASocket(AF_INET, SOCK_DGRAM, IPPROTO_UDP, NULL, NULL, WS_OVERLAPPED);
...
sockaddr_in addr = ...
if(bind(_socket, (const sockaddr*)&addr, sizeof(addr)) != EXIT_SUCCESS) { ... }
...
HANDLE _handle = CreateIoCompletionPort((HANDLE)_socket, _ioHandle, NULL, NULL);
...
_recvOverlap->OVERLAP.hEvent = WSACreateEvent();
DWORD sz = 0, flag = 0;
return WSARecvFrom(_socket, _recvBuf, 1, &sz, &flag, (sockaddr*)_recvAddr, &_recvAddrSz, (LPWSAOVERLAPPED)_recvOverlap, NULL);
Change WS_OVERLAPPED (which as a value of 0) to WSA_FLAG_OVERLAPPED (which has a value of 1) instead. You are not creating an overlapped socket correctly, so _recvOverlap is being ignored by WSARecvFrom().

How do I find the port name for a bluetooth device with a specific device name?

How do I find the port name for a bluetooth device with a specific device name?
I have this code, which enumerates all bluetooth devices, but doesn't give me their port name:
HBLUETOOTH_DEVICE_FIND founded_device;
BLUETOOTH_DEVICE_INFO device_info;
device_info.dwSize = sizeof(device_info);
BLUETOOTH_DEVICE_SEARCH_PARAMS search_criteria;
search_criteria.dwSize = sizeof(BLUETOOTH_DEVICE_SEARCH_PARAMS);
search_criteria.fReturnAuthenticated = TRUE;
search_criteria.fReturnRemembered = FALSE;
search_criteria.fReturnConnected = FALSE;
search_criteria.fReturnUnknown = FALSE;
search_criteria.fIssueInquiry = FALSE;
search_criteria.cTimeoutMultiplier = 0;
founded_device = BluetoothFindFirstDevice(&search_criteria, &device_info);
if(founded_device == NULL)
return -1;
do {
wstring ws = device_info.szName;
cout << string(ws.begin(), ws.end()) << endl;
} while (BluetoothFindNextDevice(founded_device, &device_info));
And then I have this code, which enumerates all port names but doesn't give me the device name:
DWORD bytesNeeded = 0;
DWORD portCount = 0;
BOOL ret = EnumPorts(nullptr, 2, nullptr, 0, &bytesNeeded, &portCount);
BYTE *ports = new BYTE[bytesNeeded];
if(EnumPorts(nullptr, 2, (LPBYTE)ports, bytesNeeded, &bytesNeeded, &portCount))
{
PORT_INFO_2 *portInfo = (PORT_INFO_2*)ports;
for(DWORD i = 0; i < portCount; ++i)
cout << portInfo[i].pPortName << endl;
}
delete [] ports;
I need to automatically connect to a specific device when my app is started, so I need to either get the port name for the bluetooth device in the first piece of code so I can connect to it, or check each portname in the second piece of code to make sure it's the right device before connecting to it.
How do I do it?
I remember struggling with this in the past.
the only solution i found was to use sockets for communicating with the Bluetooth device using its address, then use the send() and recv() methods for communicating with the device.
// assuming you have the BT device address in blueToothDeviceAddr;
char blueToothDeviceAddr[18];
SOCKET sock;
SOCKADDR_BTH sa = { 0,0,0,0 };
int sa_len = sizeof(sa);
// initialize windows sockets
WORD wVersionRequested;
WSADATA wsaData;
wVersionRequested = MAKEWORD( 2, 0 );
if( WSAStartup( wVersionRequested, &wsaData ) != 0 )
{
ExitProcess(100);
}
// parse the specified Bluetooth address
if( SOCKET_ERROR == WSAStringToAddress( blueToothDeviceAddr, AF_BTH,
NULL, (LPSOCKADDR) &sa, &sa_len ) )
{
ExitProcess(101);
}
// query it for the right port
// create the socket
sock = socket(AF_BTH, SOCK_STREAM, BTHPROTO_RFCOMM);
if( SOCKET_ERROR == sock )
{
ExitProcess(102);
}
// fill in the rest of the SOCKADDR_BTH struct
GUID pService = (GUID)SerialPortServiceClass_UUID;
SOCKADDR_BTH outSA;
sa.port = SDPGetPort(blueToothDeviceAddr, (LPGUID) &pService,&outSA);
if( sa.port == 0 )
{
ExitProcess(103);
}
// in case you have a pass code you need to register for authetication callback
// look the web for this part
// connect to the device
if( SOCKET_ERROR == connect( sock, (LPSOCKADDR) &outSA, sa_len ) )
{
int lastError = GetLastError();
ExitProcess(105);
}
Under the key:
HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Enum\BTHENUM
you can find a subkey which has a list of keys containing the device address.
Under this last key, you can find a subkey named Device Parameters which finally has the PortName value.
The code is written in C++ with MFC libraries and is tested under Windows XP, 7 and 10. I hope it helps you !
// Returns the outgoing COM port of a bluetooth device given by address
int GetBluetoothCOM( CString sAddr )
{
int iPort = 0;
HKEY hKey_1;
DWORD KeyNdx_1 = 0;
DWORD MaxKeyLen_1;
char KeyNam_1[ MAX_PATH + 1 ];
LONG RetVal_1;
sAddr.MakeUpper();
sAddr.Replace( ":", "" );
sAddr.Replace( " ", "" );
// Enumerate keys under: HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Enum\BTHENUM
RegOpenKeyEx( HKEY_LOCAL_MACHINE, "SYSTEM\\CurrentControlSet\\Enum\\BTHENUM", NULL, KEY_READ | KEY_ENUMERATE_SUB_KEYS, &hKey_1 );
while( true )
{
MaxKeyLen_1 = MAX_PATH;
RetVal_1 = RegEnumKeyEx( hKey_1, KeyNdx_1, KeyNam_1, &MaxKeyLen_1, NULL, NULL, NULL, NULL );
if( RetVal_1 == ERROR_NO_MORE_ITEMS )
{
break;
}
if( RetVal_1 == ERROR_SUCCESS )
{
HKEY hKey_2;
DWORD KeyNdx_2 = 0;
DWORD MaxKeyLen_2;
char KeyNam_2[ MAX_PATH + 1 ];
LONG RetVal_2;
// Enumerate subkeys
RegOpenKeyEx( HKEY_LOCAL_MACHINE, "SYSTEM\\CurrentControlSet\\Enum\\BTHENUM\\" + CString( KeyNam_1 ), NULL, KEY_READ | KEY_ENUMERATE_SUB_KEYS, &hKey_2 );
while( true )
{
MaxKeyLen_2 = MAX_PATH;
RetVal_2 = RegEnumKeyEx( hKey_2, KeyNdx_2, KeyNam_2, &MaxKeyLen_2, NULL, NULL, NULL, NULL );
if( RetVal_2 == ERROR_NO_MORE_ITEMS )
{
break;
}
if( RetVal_2 == ERROR_SUCCESS )
{
// Find out if the key name contains &ADDRESS_
CString sKey = "SYSTEM\\CurrentControlSet\\Enum\\BTHENUM\\" + CString( KeyNam_1 ) + "\\" + CString( KeyNam_2 );
sKey.MakeUpper();
if( sKey.Find( "&" + sAddr + "_" ) != -1 )
{
HKEY hKey;
char szPort[ 100 + 1 ];
DWORD dwLen = 100;
// I find out the device
RegOpenKeyEx( HKEY_LOCAL_MACHINE, sKey + "\\Device Parameters", 0, KEY_READ, &hKey );
if( RegQueryValueEx( hKey, "PortName", NULL, NULL, ( LPBYTE ) &szPort, &dwLen ) == ERROR_SUCCESS )
{
szPort[ dwLen ] = 0;
CString sPort = CString( szPort );
sPort.MakeUpper();
if( sPort.Find( "COM" ) == -1 )
{
RegCloseKey( hKey );
continue;
}
sPort.Replace( "COM", "" );
sPort.Trim();
iPort = atoi( sPort.GetBuffer() );
if( iPort != 0 )
{
RegCloseKey( hKey );
break;
}
}
RegCloseKey( hKey );
}
}
++KeyNdx_2;
}
RegCloseKey( hKey_2 );
if( iPort != 0 )
{
break;
}
}
++KeyNdx_1;
};
RegCloseKey( hKey_1 );
return iPort;
}

Retrieve serial number from USB memory (Windows environment c++)

I would need to retrieve the serial number from my USB memory, namely the hard disk's serial number that the manufacturer assigns. For this reason I cannot use GetVolumeInformation() as suggested in some other threads. I would need to have the "unique" number
I kindly ask you if you can share an example in C++ and Windows environment (Visual c++)
Thanks!
You can check out this article:- http://oroboro.com/usb-serial-number/
#include <WinIOCtl.h>
#include <api/usbioctl.h>
#include <Setupapi.h>
DEFINE_GUID( GUID_DEVINTERFACE_USB_DISK,
0x53f56307L, 0xb6bf, 0x11d0, 0x94, 0xf2,
0x00, 0xa0, 0xc9, 0x1e, 0xfb, 0x8b );
void getDeviceInfo( int vol )
{
UsbDeviceInfo info;
// get the device handle
char devicePath[7] = "\\\\.\\#:";
devicePath[4] = (char)( vol + 'A' );
HANDLE deviceHandle = CreateFile( devicePath, 0,
FILE_SHARE_READ |
FILE_SHARE_WRITE, NULL,
OPEN_EXISTING, 0, NULL );
if ( deviceHandle == INVALID_HANDLE_VALUE )
return;
// to get the device number
DWORD volumeDeviceNumber = getDeviceNumber( deviceHandle );
CloseHandle( deviceHandle );
// Get device interface info set handle
// for all devices attached to the system
HDEVINFO hDevInfo = SetupDiGetClassDevs(
&GUID_DEVINTERFACE_USB_DISK, NULL, NULL,
DIGCF_PRESENT | DIGCF_DEVICEINTERFACE );
if ( hDevInfo == INVALID_HANDLE_VALUE )
return;
// Get a context structure for the device interface
// of a device information set.
BYTE Buf[1024];
PSP_DEVICE_INTERFACE_DETAIL_DATA pspdidd =
(PSP_DEVICE_INTERFACE_DETAIL_DATA)Buf;
SP_DEVICE_INTERFACE_DATA spdid;
SP_DEVINFO_DATA spdd;
spdid.cbSize = sizeof( spdid );
DWORD dwIndex = 0;
while ( true )
{
if ( ! SetupDiEnumDeviceInterfaces( hDevInfo, NULL,
&GUID_DEVINTERFACE_USB_DISK,
dwIndex, &spdid ))
break;
DWORD dwSize = 0;
SetupDiGetDeviceInterfaceDetail( hDevInfo, &spdid, NULL,
0, &dwSize, NULL );
if (( dwSize != 0 ) && ( dwSize <= sizeof( Buf )))
{
pspdidd->cbSize = sizeof( *pspdidd ); // 5 Bytes!
ZeroMemory((PVOID)&spdd, sizeof(spdd));
spdd.cbSize = sizeof(spdd);
long res = SetupDiGetDeviceInterfaceDetail(
hDevInfo, &spdid, pspdidd,
dwSize, &dwSize, &spdd );
if ( res )
{
HANDLE hDrive = CreateFile( pspdidd->DevicePath,0,
FILE_SHARE_READ | FILE_SHARE_WRITE,
NULL, OPEN_EXISTING, 0, NULL );
if ( hDrive != INVALID_HANDLE_VALUE )
{
DWORD usbDeviceNumber = getDeviceNumber( hDrive );
if ( usbDeviceNumber == volumeDeviceNumber )
{
fprintf( "%s", pspdidd->DevicePath );
}
}
CloseHandle( hDrive );
}
}
dwIndex++;
}
SetupDiDestroyDeviceInfoList(hDevInfo);
return;
}
You get the device number by calling DeviceIOControl() with the handle to your device:
DWORD getDeviceNumber( HANDLE deviceHandle )
{
STORAGE_DEVICE_NUMBER sdn;
sdn.DeviceNumber = -1;
DWORD dwBytesReturned = 0;
if ( !DeviceIoControl( deviceHandle,
IOCTL_STORAGE_GET_DEVICE_NUMBER,
NULL, 0, &sdn, sizeof( sdn ),
&dwBytesReturned, NULL ) )
{
// handle error - like a bad handle.
return U32_MAX;
}
return sdn.DeviceNumber;
}
Next here is a method to recognize if a volume is removable media (e.g. like a usb or firewire disk):
bool isRemovableMedia( s32 vol )
{
char rootPath[5] = "#:\\";
rootPath[0] = (char)( vol + 'A' );
char szDosDeviceName[MAX_PATH];
char dosDevicePath[3] = "#:";
// get the drive type
UINT DriveType = GetDriveType( rootPath );
if ( DriveType != DRIVE_REMOVABLE )
return false;
dosDevicePath[0] = (char)( vol + 'A' );
QueryDosDevice( dosDevicePath, szDosDeviceName, MAX_PATH );
if ( strstr( szDosDeviceName,"\\Floppy") != NULL )
{
// its a floppy
return false;
}
return true;
}