CreateProcessAsUser issue on Windows - winapi

I'm trying to get user token via LogonUser() WinAPI call and execute process via that user on windows by CreateProcessAsUser () call. The problem is that I get 1314 'A required privilege is not held by the client' error. I'm currently logged in via 'Pearl' and as you see I'm using my own credentials there. This used is in Administrators group, I also checked the Local Security Policy and this user has all necessary permissions (Act as part of operating system, Create token object and Log on as batch job. My code is:
#include <windows.h>
#include <stdio.h>
#include <userenv.h>
int enable_priv(HANDLE hToken, const char* name)
{
TOKEN_PRIVILEGES tp;
memset(&tp, 0, sizeof(tp));
if (LookupPrivilegeValue(NULL, name, &tp.Privileges[0].Luid) == 0)
{
fprintf(stderr, "Error: %ld\n", GetLastError());
return 0;
}
tp.PrivilegeCount = 1;
tp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
if (AdjustTokenPrivileges(hToken, FALSE, &tp, sizeof(TOKEN_PRIVILEGES), NULL, 0) == 0)
{
fprintf(stderr, "Error: %ld.\n", GetLastError());
return 0;
}
return 1;
}
int main(int argc, char **argv)
{
HANDLE hToken1 = NULL;
HANDLE dup = NULL;
STARTUPINFO si = {0};
PROCESS_INFORMATION pi = {0};
si.cb = sizeof(STARTUPINFO);
DWORD flags = CREATE_NO_WINDOW;
fprintf(stderr, "testlog starting...\n");
if (OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY | TOKEN_ADJUST_PRIVILEGES, &hToken1) == 0)
{
fprintf(stderr, "token failed code '%lu'.\n", GetLastError());
return -1;
}
enable_priv(hToken1, SE_TCB_NAME);
enable_priv(hToken1, SE_ASSIGNPRIMARYTOKEN_NAME);
enable_priv(hToken1, SE_INCREASE_QUOTA_NAME);
if (LogonUser("Pearl", NULL, "pass",
LOGON32_LOGON_INTERACTIVE,
LOGON32_PROVIDER_DEFAULT, &hToken1) == 0)
{
fprintf(stderr, " Interactive logon failed.\n");
return 0;
}
if (DuplicateTokenEx(hToken1, MAXIMUM_ALLOWED, NULL, SecurityDelegation,
TokenPrimary, &dup) == 0)
{
fprintf(stderr, "ERROR is '%lu'.\n", GetLastError());
return 0;
}
fprintf(stderr, "testlog executing...\n");
fflush(stderr);
fflush(stdout);
if (CreateProcessAsUser(dup, NULL, "C:\\Program Files (x86)\\folder1\\tazo.exe", NULL, NULL, TRUE,
flags, NULL, NULL, &si, &pi) == 0)
{
fprintf(stderr, "testlog Error is '%lu'\n", GetLastError());
}
}
Can anyone please help?

Related

Inject Dll and Create Thread When Launching Process with DEBUG_PROCESS on Windows

In the past, when not operating as a debugger, I have used this approach to inject a DLL and Create Thread In a Process which has worked well for me. Note: I need this to work on Windows XP 32-bit only (Although prefer methods that also work on latested OS):
#include <iostream>
#include <Windows.h>
#include <Psapi.h>
#include <pathcch.h>
#include "log.h"
#include <wchar.h>
#pragma comment(lib,"Pathcch.lib")
typedef void (WINAPI* PHookInit)();
HMODULE WINAPI GetRemoteModuleHandle(HANDLE hProcess, LPCWSTR lpModuleName)
{
HMODULE* ModuleArray = NULL;
DWORD ModuleArraySize = 100;
DWORD NumModules = 0;
WCHAR lpModuleNameCopy[MAX_PATH] = { 0 };
WCHAR ModuleNameBuffer[MAX_PATH] = { 0 };
if (lpModuleName == NULL) return NULL;
ModuleArray = new HMODULE[ModuleArraySize];
if (ModuleArray == NULL) return NULL;
if (!EnumProcessModulesEx(hProcess, ModuleArray,
ModuleArraySize * sizeof(HMODULE), &NumModules, LIST_MODULES_ALL))
{
DWORD dwResult = GetLastError();
LOG_E("Unable to get modules in process Error %i", dwResult);
}
else
{
NumModules /= sizeof(HMODULE);
if (NumModules > ModuleArraySize)
{
delete[] ModuleArray;
ModuleArray = NULL;
ModuleArray = new HMODULE[NumModules];
if (ModuleArray != NULL)
{
ModuleArraySize = NumModules;
if (EnumProcessModulesEx(
hProcess,
ModuleArray,
ModuleArraySize * sizeof(HMODULE),
&NumModules,
LIST_MODULES_ALL))
{
NumModules /= sizeof(HMODULE);
}
}
}
}
for (DWORD i = 0; i <= NumModules; ++i)
{
GetModuleBaseNameW(hProcess, ModuleArray[i],
ModuleNameBuffer, MAX_PATH);
LOG_I("Module = '%s'", ModuleNameBuffer);
if (_wcsicmp(ModuleNameBuffer, lpModuleName) == 0)
{
LOG_I("Target module found!");
HMODULE TempReturn = ModuleArray[i];
delete[] ModuleArray;
return TempReturn;
}
}
if (ModuleArray != NULL)
delete[] ModuleArray;
return NULL;
}
int wmain(HINSTANCE hInstance, HINSTANCE hPrevInstance,LPSTR lpCmdLine, INT nCmdShow)
{
LPWSTR* argv;
int argc;
argv = CommandLineToArgvW(GetCommandLineW(), &argc);
LOG_I(L"LaunchAndInject Started");
wchar_t CurrentProcessDirectory[MAX_PATH];
wchar_t TargetDllFilename[MAX_PATH];
#ifdef _WIN64
wchar_t TargetDllName[] = L"HookInit64.dll";
#else
wchar_t TargetDllName[] = L"HookInit32.dll";
#endif
char TargetFunctionName[] = "HookInit";
STARTUPINFO si;
PROCESS_INFORMATION pi;
DWORD dwTimeOut = 60000;
if (argc < 2)
{
LOG_E(L"No command line parameters specified.");
return 1;
}
wchar_t* cmd_pos = wcsstr(GetCommandLine(), argv[1]) - 1;
if (cmd_pos)
{
if (cmd_pos[0] != L'"')
{
cmd_pos += 1;
}
}
LOG_I(L"Command Line='%s'", cmd_pos);
DWORD dwResult = GetModuleFileNameW(NULL, CurrentProcessDirectory, MAX_PATH);
PathCchRemoveFileSpec(CurrentProcessDirectory, MAX_PATH);
PathCchCombine(TargetDllFilename, MAX_PATH, CurrentProcessDirectory, TargetDllName);
LOG_I(L"Current Directory='%s' Result='%i'", CurrentProcessDirectory, dwResult);
LOG_I(L"Target DLL='%s'", TargetDllFilename);
ZeroMemory(&si, sizeof(si));
si.cb = sizeof(si);
ZeroMemory(&pi, sizeof(pi));
// Start the child process.
if (!CreateProcess(NULL, // No module name (use command line)
cmd_pos, // Command line
NULL, // Process handle not inheritable
NULL, // Thread handle not inheritable
FALSE, // Set handle inheritance to FALSE
CREATE_SUSPENDED, // No creation flags
NULL, // Use parent's environment block
NULL, // Use parent's starting directory
&si,
&pi)
)
{
dwResult = GetLastError();
LOG_E(L"CreateProcess Failed with Error #%i", dwResult);
return 1;
}
LOG_I(L"Suspended Process created with PID '%i'", pi.dwProcessId);
LOG_I("Loading Target DLL");
// load DLL in this process first so we can calculate function offset
HMODULE hModuleTargetDll = LoadLibraryW(TargetDllFilename);
__int64 iTargetProcAddress = 0;
__int64 iTargetOffset = 0;
if (hModuleTargetDll != NULL)
{
iTargetProcAddress = (__int64)GetProcAddress(hModuleTargetDll, TargetFunctionName);
iTargetOffset = iTargetProcAddress - (__int64)hModuleTargetDll;
LOG_I("Function Target Offset = %i", iTargetOffset);
}
HMODULE hModuleKernel32 = GetModuleHandle(L"kernel32.dll");
LPVOID pLoadLibraryAddress = NULL;
if (hModuleKernel32 != NULL)
{
pLoadLibraryAddress = (LPVOID)GetProcAddress(hModuleKernel32, "LoadLibraryW");
}
else
{
LOG_E("Unable to get module handle for kernel32.dll");
}
if (pLoadLibraryAddress == NULL) {
dwResult = GetLastError();
LOG_E(L"ERROR: Unable to find LoadLibraryW in Kernel32.dll Error: %i", dwResult);
}
// allocate space for LoadLibrary arguments in target process
size_t iTargetDllSize = (wcslen(TargetDllFilename) + 1) * sizeof(wchar_t);
LPVOID pLoadLibraryArguments = (LPVOID)VirtualAllocEx(
pi.hProcess,
NULL,
iTargetDllSize,
MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE);
if (pLoadLibraryArguments == NULL) {
dwResult = GetLastError();
LOG_E(L"ERROR: Unable to allocate %i bytes in target process Error: %i",
iTargetDllSize,
dwResult);
}
else
{
if (!WriteProcessMemory(
pi.hProcess,
pLoadLibraryArguments,
TargetDllFilename,
iTargetDllSize,
NULL))
{
dwResult = GetLastError();
LOG_E("Unable to write bytes into target process address space. Error %i", dwResult);
}
else
{
LOG_I("LoadLibrary Arguments Successfully written to target process address space.");
HANDLE hThread = NULL;
if (pLoadLibraryAddress != NULL)
{
hThread = CreateRemoteThread(
pi.hProcess,
NULL,
0,
(LPTHREAD_START_ROUTINE)pLoadLibraryAddress,
pLoadLibraryArguments,
NULL,
NULL);
}
if (hThread == NULL) {
dwResult = GetLastError();
LOG_E("The remote thread calling LoadLibrary could not be created. Error %i", dwResult);
}
else {
LOG_I("Remote Thread for LoadLibrary successfully created.");
dwResult = WaitForSingleObject(hThread, dwTimeOut);
if (dwResult == WAIT_FAILED)
{
dwResult = GetLastError();
LOG_I("Remote Thread for LoadLibrary Failed Error %i", dwResult);
}
if (dwResult == WAIT_TIMEOUT)
{
LOG_E("Remote Thread for LoadLibrary in hung state");
}
HMODULE hInjected = GetRemoteModuleHandle(pi.hProcess, TargetDllName);
PHookInit pHookInit = NULL;
if (hInjected == NULL)
{
LOG_E("Unable to get module handle in target process");
}
else
{
pHookInit = (PHookInit)((__int64)hInjected + iTargetOffset);
}
if (pHookInit != NULL)
{
LOG_I("Running HookInit function!");
hThread = CreateRemoteThread(pi.hProcess, NULL, 0, (LPTHREAD_START_ROUTINE)pHookInit, NULL, NULL, NULL);
if (hThread == NULL)
{
dwResult = GetLastError();
LOG_E("The remote thread calling HookInit could not be created. Error %i", dwResult);
}
else
{
LOG_I("HookInit function started!");
dwResult = WaitForSingleObject(hThread, dwTimeOut);
if (dwResult == WAIT_FAILED)
{
dwResult = GetLastError();
LOG_I("Remote Thread for HookInit Failed Error %i", dwResult);
}
if (dwResult == WAIT_TIMEOUT)
{
LOG_E("Remote Thread for HookInit in hung state");
}
}
}
}
}
}
LOG_I("Resuming threads in target process");
ResumeThread(pi.hThread);
LOG_I("Process Resumed. Waiting for process to exit");
dwResult = WaitForSingleObject(pi.hProcess, INFINITE);
DWORD exitCode = 0;
if (GetExitCodeProcess(pi.hProcess, &exitCode))
{
LOG_I("Process Terminated with exit code %i", exitCode);
}
else
{
LOG_W("Process terminated, unable to determine Exit Code");
}
CloseHandle(pi.hProcess);
CloseHandle(pi.hThread);
}
However in this case I need to capture various debug events of process, and hook the process via Image Execution Debugger registry key (as I don't have control of its launch), including for child processes. While the inject code works fine with previous approach, when launched as a debugger I'm trying to work out how to create the remote thread (and have it complete) before resuming main application execution. While I can create the remote thread fine, it hangs when trying to wait for its completion when using the approach below. I'm trying to work out what method to use to create my remote thread and wait for it to complete before resuming main application.
// Start the child process.
if (!CreateProcess(NULL, // No module name (use command line)
cmd_pos, // Command line
NULL, // Process handle not inheritable
NULL, // Thread handle not inheritable
FALSE, // Set handle inheritance to FALSE
DEBUG_PROCESS, // Debug
NULL, // Use parent's environment block
NULL, // Use parent's starting directory
&si,
&pi)
)
{
dwResult = GetLastError();
LOG_E(L"CreateProcess Failed with Error #%i", dwResult);
return 1;
}
DebugSetProcessKillOnExit(TRUE);
DebugActiveProcess(pi.dwProcessId);
DEBUG_EVENT DebugEv = { 0 };
DWORD dwContinueStatus = DBG_CONTINUE; // exception continuation
LOG_I(L"Debug Process created with PID '%i'", pi.dwProcessId);
LOG_I("Loading Target DLL");
/*
// load DLL in this process first so we can calculate function offset
*/
for (;;)
{
// Wait for a debugging event to occur. The second parameter indicates
// that the function does not return until a debugging event occurs.
WaitForDebugEvent(&DebugEv, INFINITE);
// Process the debugging event code.
switch (DebugEv.dwDebugEventCode)
{
case EXCEPTION_DEBUG_EVENT:
// Process the exception code. When handling
// exceptions, remember to set the continuation
// status parameter (dwContinueStatus). This value
// is used by the ContinueDebugEvent function.
OutputDebugString(L"EXCEPTION\r\n");
switch (DebugEv.u.Exception.ExceptionRecord.ExceptionCode)
{
case EXCEPTION_ACCESS_VIOLATION:
// First chance: Pass this on to the system.
// Last chance: Display an appropriate error.
break;
case EXCEPTION_BREAKPOINT:
// First chance: Display the current
// instruction and register values.
break;
case EXCEPTION_DATATYPE_MISALIGNMENT:
// First chance: Pass this on to the system.
// Last chance: Display an appropriate error.
break;
case EXCEPTION_SINGLE_STEP:
// First chance: Update the display of the
// current instruction and register values.
break;
case DBG_CONTROL_C:
// First chance: Pass this on to the system.
// Last chance: Display an appropriate error.
break;
default:
// Handle other exceptions.
break;
}
break;
case CREATE_THREAD_DEBUG_EVENT:
OutputDebugString(L"CREATETHREAD\r\n");
// dwContinueStatus = OnCreateThreadDebugEvent(&DebugEv);
break;
case CREATE_PROCESS_DEBUG_EVENT:
dwContinueStatus = OnCreateProcessDebugEvent(&DebugEv);
break;
case EXIT_THREAD_DEBUG_EVENT:
// Display the thread's exit code.
OutputDebugString(L"EXITTHREAD\r\n");
// dwContinueStatus = OnExitThreadDebugEvent(&DebugEv);
break;
case EXIT_PROCESS_DEBUG_EVENT:
// Display the process's exit code.
OutputDebugString(L"EXITPROCESS\r\n");
// dwContinueStatus = OnExitProcessDebugEvent(&DebugEv);
break;
case LOAD_DLL_DEBUG_EVENT:
// Read the debugging information included in the newly
// loaded DLL. Be sure to close the handle to the loaded DLL
// with CloseHandle.
OutputDebugString(L"LOADDLL\r\n");
// dwContinueStatus = OnLoadDllDebugEvent(&DebugEv);
break;
case UNLOAD_DLL_DEBUG_EVENT:
// Display a message that the DLL has been unloaded.
OutputDebugString(L"UNLOADDLL\r\n");
// dwContinueStatus = OnUnloadDllDebugEvent(&DebugEv);
break;
case OUTPUT_DEBUG_STRING_EVENT:
OutputDebugString(L"OUTPUTDEBUG\r\n");
// Display the output debugging string.
// dwContinueStatus = OnOutputDebugStringEvent(&DebugEv);
break;
case RIP_EVENT:
OutputDebugString(L"RIP\r\n");
// dwContinueStatus = OnRipEvent(&DebugEv);
break;
}
// Resume executing the thread that reported the debugging event.
OutputDebugString(L"CONTINUE\r\n");
ContinueDebugEvent(DebugEv.dwProcessId,
DebugEv.dwThreadId,
dwContinueStatus);
}
DWORD OnCreateProcessDebugEvent(const LPDEBUG_EVENT DebugEv)
{
DWORD dwResult;
HMODULE hModuleTargetDll = LoadLibraryW(TargetDllFilename);
__int64 iTargetProcAddress = 0;
__int64 iTargetOffset = 0;
if (hModuleTargetDll != NULL)
{
iTargetProcAddress = (__int64)GetProcAddress(hModuleTargetDll, TargetFunctionName);
iTargetOffset = iTargetProcAddress - (__int64)hModuleTargetDll;
LOG_I("Function Target Offset = %i", iTargetOffset);
}
HMODULE hModuleKernel32 = GetModuleHandle(L"kernel32.dll");
LPVOID pLoadLibraryAddress = NULL;
if (hModuleKernel32 != NULL)
{
pLoadLibraryAddress = (LPVOID)GetProcAddress(hModuleKernel32, "LoadLibraryW");
}
else
{
LOG_E("Unable to get module handle for kernel32.dll");
}
if (pLoadLibraryAddress == NULL) {
dwResult = GetLastError();
LOG_E(L"ERROR: Unable to find LoadLibraryW in Kernel32.dll Error: %i", dwResult);
}
// allocate space for LoadLibrary arguments in target process
size_t iTargetDllSize = (wcslen(TargetDllFilename) + 1) * sizeof(wchar_t);
HANDLE hProcess = OpenProcess(PROCESS_ALL_ACCESS, false, DebugEv->dwProcessId);
LPVOID pLoadLibraryArguments = (LPVOID)VirtualAllocEx(
hProcess,
NULL,
iTargetDllSize,
MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE);
if (pLoadLibraryArguments == NULL) {
dwResult = GetLastError();
LOG_E(L"ERROR: Unable to allocate %i bytes in target process Error: %i",
iTargetDllSize,
dwResult);
}
else
{
if (!WriteProcessMemory(
hProcess,
pLoadLibraryArguments,
TargetDllFilename,
iTargetDllSize,
NULL))
{
dwResult = GetLastError();
LOG_E("Unable to write bytes into target process address space. Error %i", dwResult);
}
else
{
LOG_I("LoadLibrary Arguments Successfully written to target process address space.");
HANDLE hThread = NULL;
if (pLoadLibraryAddress != NULL)
{
hThread = CreateRemoteThread(
hProcess,
NULL,
0,
(LPTHREAD_START_ROUTINE)pLoadLibraryAddress,
pLoadLibraryArguments,
NULL,
NULL);
}
if (hThread == NULL) {
dwResult = GetLastError();
LOG_E("The remote thread calling LoadLibrary could not be created. Error %i", dwResult);
}
else {
LOG_I("Remote Thread for LoadLibrary successfully created.");
ResumeThread(hThread);
dwResult = WaitForSingleObject(hThread, dwTimeOut);
if (dwResult == WAIT_FAILED)
{
dwResult = GetLastError();
LOG_I("Remote Thread for LoadLibrary Failed Error %i", dwResult);
}
if (dwResult == WAIT_TIMEOUT)
{
LOG_E("Remote Thread for LoadLibrary in hung state");
}
HMODULE hInjected = GetRemoteModuleHandle(hProcess, TargetDllName);
PHookInit pHookInit = NULL;
if (hInjected == NULL)
{
LOG_E("Unable to get module handle in target process");
}
else
{
pHookInit = (PHookInit)((__int64)hInjected + iTargetOffset);
}
if (pHookInit != NULL)
{
LOG_I("Running HookInit function!");
hThread = CreateRemoteThread(hProcess, NULL, 0, (LPTHREAD_START_ROUTINE)pHookInit, NULL, NULL, NULL);
if (hThread == NULL)
{
dwResult = GetLastError();
LOG_E("The remote thread calling HookInit could not be created. Error %i", dwResult);
}
else
{
LOG_I("HookInit function started!");
dwResult = WaitForSingleObject(hThread, dwTimeOut);
if (dwResult == WAIT_FAILED)
{
dwResult = GetLastError();
LOG_I("Remote Thread for HookInit Failed Error %i", dwResult);
}
if (dwResult == WAIT_TIMEOUT)
{
LOG_E("Remote Thread for HookInit in hung state");
}
}
}
}
}
}
return DBG_CONTINUE;
}

RIOSendEx get error 10022 but connect + RIOSend works well

I'm learning Windows Registered I/O, I found a sample project from the website "https://github.com/zeliard/RIOEchoServer", It does well on my computer.
However, if I change this code from the listening server side to the actively sending client side, I find that connect + RIOSend works fine, but RIOSendEx will definitely give an error code of 10022!
#include <WinSock2.h>
#include <MSWsock.h>
#include <WS2tcpip.h>
#include <stdio.h>
#pragma comment(lib, "ws2_32.lib")
int main(int argc, char * argv[])
{
static const wchar_t SERVER[] = L"127.0.0.1";
static const unsigned short PORTNUM = 54321;
static const DWORD RIO_PENDING_RECVS = 1;
static const DWORD RIO_PENDING_SENDS = 1;
static const DWORD RECV_BUFFER_SIZE = 1024;
static const DWORD SEND_BUFFER_SIZE = 1024;
static const DWORD ADDR_BUFFER_SIZE = 64;
static const DWORD RIO_MAX_RESULTS = 1;
WSADATA wsadata;
if (0 != ::WSAStartup(0x202, &wsadata))
{
printf_s("WSAStartup Error: %d\n", GetLastError());
exit(0);
}
/// RIO socket
SOCKET socket = WSASocket(AF_INET, SOCK_DGRAM, IPPROTO_UDP, NULL, 0, WSA_FLAG_REGISTERED_IO);
if (socket == INVALID_SOCKET)
{
printf_s("WSASocket Error: %d\n", GetLastError());
exit(0);
}
/// RIO function table
GUID functionTableId = WSAID_MULTIPLE_RIO;
DWORD dwBytes = 0;
RIO_EXTENSION_FUNCTION_TABLE rio;
if (NULL != WSAIoctl(socket, SIO_GET_MULTIPLE_EXTENSION_FUNCTION_POINTER, &functionTableId, sizeof(GUID), (void**)&rio, sizeof(rio), &dwBytes, NULL, NULL))
{
printf_s("WSAIoctl Error: %d\n", GetLastError());
exit(0);
}
/// rio's completion manner: iocp
HANDLE hIOCP = ::CreateIoCompletionPort(INVALID_HANDLE_VALUE, 0, 0, 0);
if (NULL == hIOCP)
{
printf_s("CreateIoCompletionPort Error: %d\n", GetLastError());
exit(0);
}
OVERLAPPED overlapped;
RIO_NOTIFICATION_COMPLETION completionType;
completionType.Type = RIO_IOCP_COMPLETION;
completionType.Iocp.IocpHandle = hIOCP;
completionType.Iocp.CompletionKey = NULL;
completionType.Iocp.Overlapped = &overlapped;
/// creating RIO CQ, which is bigger than (or equal to) RQ size
RIO_CQ completionQueue = rio.RIOCreateCompletionQueue(RIO_PENDING_RECVS + RIO_PENDING_SENDS, &completionType);
if (completionQueue == RIO_INVALID_CQ)
{
printf_s("RIOCreateCompletionQueue Error: %d\n", GetLastError());
exit(0);
}
/// creating RIO RQ
/// SEND and RECV within one CQ (you can do with two CQs, seperately)
RIO_RQ requestQueue = rio.RIOCreateRequestQueue(socket, RIO_PENDING_RECVS, 1, RIO_PENDING_SENDS, 1, completionQueue, completionQueue, NULL);
if (requestQueue == RIO_INVALID_RQ)
{
printf_s("RIOCreateRequestQueue Error: %d\n", GetLastError());
exit(0);
}
/// registering RIO buffers for SEND
char sendBuffer[SEND_BUFFER_SIZE];
RIO_BUFFERID sendRioBufferId = rio.RIORegisterBuffer(sendBuffer, static_cast<DWORD>(sizeof(sendBuffer)));
if (sendRioBufferId == RIO_INVALID_BUFFERID)
{
printf_s("RIORegisterBuffer Error: %d\n", GetLastError());
exit(0);
}
RIO_BUF sendRioBuffer;
sendRioBuffer.BufferId = sendRioBufferId;
sendRioBuffer.Offset = 0;
sendRioBuffer.Length = SEND_BUFFER_SIZE;
/// registering RIO buffers for ADDR
char addrBuffer[ADDR_BUFFER_SIZE];
RIO_BUFFERID addrRioBufferId = rio.RIORegisterBuffer(addrBuffer, static_cast<DWORD>(sizeof(addrBuffer)));
if (addrRioBufferId == RIO_INVALID_BUFFERID)
{
printf_s("RIORegisterBuffer Error: %d\n", GetLastError());
exit(0);
}
RIO_BUF addrRioBuffer;
addrRioBuffer.BufferId = addrRioBufferId;
addrRioBuffer.Offset = 0;
addrRioBuffer.Length = ADDR_BUFFER_SIZE;
/// registering RIO buffers for RECV and then, post pre-RECV
char recvBuffer[RECV_BUFFER_SIZE];
RIO_BUFFERID recvRioBufferId = rio.RIORegisterBuffer(recvBuffer, static_cast<DWORD>(sizeof(recvBuffer)));
if (recvRioBufferId == RIO_INVALID_BUFFERID)
{
printf_s("RIORegisterBuffer Error: %d\n", GetLastError());
exit(0);
}
RIO_BUF recvRioBuffer;
recvRioBuffer.BufferId = recvRioBufferId;
recvRioBuffer.Offset = 0;
recvRioBuffer.Length = RECV_BUFFER_SIZE;
/// posting pre RECVs
if (!rio.RIOReceiveEx(requestQueue, &recvRioBuffer, 1, NULL, &addrRioBuffer, NULL, 0, 0, &recvRioBuffer))
{
printf_s("RIOReceive Error: %d\n", GetLastError());
exit(0);
}
//////////////////////////////////////////////////////////////////////////
// active send code begin ...
//////////////////////////////////////////////////////////////////////////
sendRioBuffer.Length = 5;
memcpy_s(sendBuffer, RECV_BUFFER_SIZE, "hello", sendRioBuffer.Length);
sockaddr_in * address = reinterpret_cast<sockaddr_in *>(addrBuffer);
memset(address, 0x0, ADDR_BUFFER_SIZE);
address->sin_family = AF_INET;
address->sin_port = htons(PORTNUM);
if (::InetPton(AF_INET, SERVER, &address->sin_addr) <= 0)
{
printf_s("inet_pton Error: %d\n", GetLastError());
exit(0);
}
#if 0 // connect + RIOSend is OK
if (SOCKET_ERROR == ::connect(socket, reinterpret_cast<struct sockaddr *>(address), sizeof(*address)))
{
printf_s("Connect Error: %d\n", GetLastError());
exit(0);
}
if (!rio.RIOSend(requestQueue, &sendRioBuffer, 1, 0, &sendRioBuffer))
{
printf_s("RIOSend Error: %d\n", GetLastError());
exit(0);
}
#else // RIOSendEx not work
if (!rio.RIOSendEx(requestQueue, &sendRioBuffer, 1, NULL, &addrRioBuffer, NULL, NULL, 0, &sendRioBuffer))
{
printf_s("RIOSendEx Error: %d\n", GetLastError());
exit(0);
}
#endif // 0
INT notifyResult = rio.RIONotify(completionQueue);
if (notifyResult != ERROR_SUCCESS)
{
printf_s("RIONotify Error: %d\n", GetLastError());
exit(0);
}
DWORD numberOfBytes = 0;
ULONG_PTR completionKey = 0;
OVERLAPPED* pOverlapped = 0;
RIORESULT results[RIO_MAX_RESULTS];
if (!::GetQueuedCompletionStatus(hIOCP, &numberOfBytes, &completionKey, &pOverlapped, INFINITE))
{
printf_s("GetQueuedCompletionStatus Error: %d\n", GetLastError());
exit(0);
}
memset(results, 0, sizeof(results));
ULONG numResults = rio.RIODequeueCompletion(completionQueue, results, RIO_MAX_RESULTS);
if (0 == numResults || RIO_CORRUPT_CQ == numResults)
{
printf_s("RIODequeueCompletion Error: %d\n", GetLastError());
exit(0);
}
const RIORESULT & res = results[0];
if (0 != res.Status)
{
printf_s("RIOSend(Ex) Error: %d\n", res.Status);
exit(0);
}
printf_s("RIOSend(Ex) OK\n");
//////////////////////////////////////////////////////////////////////////
// active send code end ...
//////////////////////////////////////////////////////////////////////////
if (SOCKET_ERROR == ::closesocket(socket))
{
printf_s("closesocket Error: %d\n", GetLastError());
}
rio.RIOCloseCompletionQueue(completionQueue);
rio.RIODeregisterBuffer(sendRioBufferId);
rio.RIODeregisterBuffer(recvRioBufferId);
rio.RIODeregisterBuffer(addrRioBufferId);
return 0;
}
wish your help, Thanks!
yanrk
From RIOSendEx documentation:
sends network data on ... a bound ... UDP socket
Your example will work if you add those lines before calling RIOSendEx:
sockaddr_in addr{}; // zero-initialized
addr.sin_family = AF_INET;
if (SOCKET_ERROR == ::bind(socket, (struct sockaddr *)&addr, sizeof(addr)))
{
printf("bind failed\n");
exit(0);
}
if (!rio.RIOSendEx(requestQueue ...
You can bind to some particular port (not recommended):
addr.sin_port = htons(5001);
Or leave it zero for OS to choose.
Aparently, RIOSendEx do not perform an implicit bind (unlike WSASendTo).
The reason for this might be that bind is a complicated process that might involve waiting and APC and all kinds of stuff, while whole idea of RIO is to pay upfront.
connect + RIOSendEx doesn't make sense. Since you specified remote address with connect function you can't send data through that socket anywhere else. RIOSendEx is supposed to be used with datagrams.
You have not set the Destination IP-Address in the RioSendEx-Function:
Copy the content of your address-Buffer into the associated Buffer of addrRioBuffer.
e.g.:
memcpy_s(addrBuffer, ADDR_BUFFER_SIZE, address, sizeof(sockaddr_in));
if (!rio.RIOSendEx(requestQueue, &sendRioBuffer, 1, NULL, &addrRioBuffer, NULL, NULL, 0, &sendRioBuffer))
{
printf_s("RIOSendEx Error: %d\n", GetLastError());
exit(0);
}
// […]
I will post a working exampel in the next day´s.
http://www.thbweb.eu

C++ Win32: How to run pipe client executable with CreateProcessA() in a new console window

In my project I have two applications, one is Pipe Server and Pipe Client(Slave).
I am trying to send text via pipe to display it on client's console. Thus effectively creating disposable consoles.
I have tested the code by manually running the server first and then client. It runs perfectly. Then I added some code in the constructor of Server to invoke Slave.exe with pipename as arguments however the console of Slave disappears after couple seconds.
Slave's Constructor calls this function:
int OpenNamedPipe(std::string pipename)
{
pipeurl += pipename;
hPipe = CreateNamedPipe(
pipeurl .c_str(), // pipe name
PIPE_ACCESS_DUPLEX | FILE_FLAG_OVERLAPPED, // read/write access
PIPE_TYPE_BYTE | // Datatype Byte
PIPE_WAIT, // blocking mode
1, // max. instances
outputBuffer, // output buffer size
inputBuffer, // input buffer size
0, // client time-out
NULL); // default security attribute
if (hPipe == INVALID_HANDLE_VALUE)
{
try
{
Throw_Last_Error("CreateNamedPipe failed");
}
catch (const std::runtime_error err)
{
std::cout << "Runtime Error: " << err.what();
return 0;
}
}
int timeout = 100000;
PROCESS_INFORMATION pi;
ZeroMemory(&pi, sizeof(pi));
STARTUPINFO si;
ZeroMemory(&si, sizeof(si));
int retnVal = CreateProcessA("Slave.exe", (LPSTR)pipename.c_str(), NULL, NULL, NULL, DETACHED_PROCESS, NULL, NULL, &si, &pi);
if (!retnVal)
{
retnVal = GetLastError();
}
if (!ConnectNamedPipe(hPipe, NULL))
{
if (!GetLastError() == ERROR_PIPE_CONNECTED)
{
try
{
Throw_Last_Error("Error while connecting to named pipe.");
}
catch (std::runtime_error err)
{
std::cout << "GLE= " << GetLastError();
Block();
return 0;
}
}
}
std::cout << "Connected to pipe.\n";
return 0;
}
In Client's main program:
int main(int argc, char *argv[])
{
AllocConsole();
std::string argstr = " ";
argstr = argv[1];
PipeClient pc(argstr);
pc.Update();
system("pause");
return 0;
}
Now I need both Server's console and Client's console to remain open for further testing but when Server is waiting for the Slave to connect to pipe, Slave's console and process closes, which shouldn't happen as I have paused it before it can return.
Edit: Pipe Client object constructor:
PipeClient(std::string pipename)
{
pipeName = pipeName + pipename;
Connect();
if (hPipe != INVALID_HANDLE_VALUE || GetLastError() != ERROR_PIPE_BUSY)
{
std::cout << "Created Pipe, GLE=" << GetLastError();
}
if (hPipe == INVALID_HANDLE_VALUE)
{
ThrowLastError("Failed to connect to named pipe.");
}
}
int Connect()
{
while (true)
{
WaitNamedPipeA(pipeName.c_str(), NMPWAIT_WAIT_FOREVER);
hPipe = CreateFileA(
pipeName.c_str(),
GENERIC_READ |
GENERIC_WRITE,
0,
NULL,
OPEN_EXISTING,
0,
NULL
);
if (hPipe != INVALID_HANDLE_VALUE || GetLastError() != ERROR_PIPE_BUSY)
{
std::cout << "Created Pipe, GLE=" << GetLastError();
break;
}
}
return 0;
}
Class Fields:
DWORD inputBuffer = 256;
DWORD outputBuffer = 256;
HANDLE hPipe;
std::string pipeName = "\\\\.\\pipe\\";
char * testpipename = "\\\\.\\pipe\\namedpipe";
Github repo:https://github.com/BhayanakMoth2/PipedConsole
So I fixed the problem, I was not using the CreateProcess function properly.
This should be the fixed function call:
std::string cmd = "Slave.exe " + pipename;
int retnVal = CreateProcessA("Slave.exe", (LPSTR)cmd.c_str(), NULL, NULL, NULL, CREATE_NEW_CONSOLE, NULL, NULL, &si, &pi);
I misread the documentation. And the reason Slave.exe was crashing because the arguments were not being passed properly and so when it reached:
argstr = argv[1]
it crashed silently. The second argument in CreateProcessA() fixes this problem by properly passing the arguments.

computing hashValue of a file

Small piece of code I wrote to list files in a dummy directory and then calculate the hash values.
But when I pass the filename to the calc_hash function , program terminates after computing the hash of two files... with error Unhandled exception at 0x77406850 in hashtest3-cpp.exe: 0xC0000005: Access violation writing location 0x64333782.
Can someone please point out the error ....
code snippet::
#include <stdio.h>
#include <windows.h>
#include <Wincrypt.h>
#include <strsafe.h>
#include <string.h>
#include <tchar.h>
#include <iostream>
using namespace std;
#define BUFSIZE 1024
#define MD5LEN 16
//======================================================VARIABLE INITIALIZATION
DWORD Status = 0;
BOOL bResult = FALSE;
ULONG_PTR hProv = 0;
ULONG_PTR hHash = 0;
HANDLE hFile = NULL;
DWORD i;
DWORD j=0;
BYTE rgbFile[BUFSIZE];
DWORD cbRead = 0;
BYTE rgbHash[MD5LEN];
DWORD cbHash = 0;
LPTSTR filename;
CHAR resultstream[33];
HANDLE hFind;
WIN32_FIND_DATA data;
TCHAR filepath[260] = _T("");
LPWSTR FNAME;
LPWSTR FULLPATH = L"c:\\test\\";
LPWSTR dir = L"c:\\test\\*.*";
LPWSTR DEFAULt = L"";
CHAR rgbDigits[] = "0123456789abcdef";
//=====================================================FUNCTION DECLARATION
void list_files(void);
void calc_hash(void);
//======================================================MAIN START
int main()
{
list_files();
//calc_hash();
getchar();
return 0;
}
//======================================================LISTING FILES IN A DIRECTORY
void list_files()
{
hFind = FindFirstFile(dir, &data);
if (hFind != INVALID_HANDLE_VALUE)
{
do
{
StringCchCopy(filepath, 260, DEFAULt);
resultstream[33] = '\0';
DWORD j=0;
FNAME = data.cFileName;
StringCchCat(filepath, 260, FULLPATH);
StringCchCat(filepath, 260, FNAME);
filename = (LPTSTR)filepath;
printf("\n%ws", filename);
/*calc_hash();
getchar();*/
continue;
}while (FindNextFile(hFind, &data));
}
getchar();
}
//======================================================HASH OF A FILE
void calc_hash()
{
hFile = CreateFile(
filename,
GENERIC_READ,
FILE_SHARE_READ,
NULL,
OPEN_EXISTING,
FILE_FLAG_SEQUENTIAL_SCAN,
NULL
);
if (INVALID_HANDLE_VALUE == hFile)
{
Status = GetLastError();
//printf("\nerror opening file::%d", Status);
goto end;
}
// Get handle to the crypto provider
if (!CryptAcquireContext(
&hProv,
NULL,
NULL,
PROV_RSA_FULL,
CRYPT_VERIFYCONTEXT))
{
Status = GetLastError();
printf("CryptAcquireContext error:: %d\n", Status);
CloseHandle(hFile);
getchar();
goto end;
}
if (!CryptCreateHash(hProv, CALG_MD5, 0, 0, &hHash))
{
Status = GetLastError();
printf("CryptAcquireContext error:: %d\n", Status);
CloseHandle(hFile);
CryptReleaseContext(hProv, 0);
getchar();
goto end;
}
while (bResult = ReadFile(hFile, rgbFile, BUFSIZE, &cbRead, NULL))
{
if (0 == cbRead)
{
break;
}
if (!CryptHashData(hHash, rgbFile, cbRead, 0))
{
Status = GetLastError();
printf("CryptHashData error:: %d\n", Status);
CryptReleaseContext(hProv, 0);
CryptDestroyHash(hHash);
CloseHandle(hFile);
getchar();
goto end;
}
}
//================================GENERATING RESULT=============================
if (!bResult)
{
Status = GetLastError();
printf("ReadFile error:%S:: %d\n",filename, Status);
CryptReleaseContext(hProv, 0);
CryptDestroyHash(hHash);
CloseHandle(hFile);
getchar();
goto end;
}
cbHash = MD5LEN;
if (CryptGetHashParam(hHash, HP_HASHVAL, rgbHash, &cbHash, 0))
{
printf("MD5 hash of file %ws is:- ", filename);
for (i = 0; i < cbHash; i++)
{
resultstream[j]=rgbDigits[rgbHash[i] >> 4];
j++;
resultstream[j]=rgbDigits[rgbHash[i] & 0xf];
j++;
}
resultstream[j] = '\0';
printf("\n%s", resultstream );
printf("\n");
}
else
{
Status = GetLastError();
printf("CryptGetHashParam error:: %d\n", Status);
}
CryptDestroyHash(hHash);
CryptReleaseContext(hProv, 0);
CloseHandle(hFile);
end:;
//getchar();
}
The problem is you declare resultstream as
CHAR resultstream[33];
but then you use in your code
resultstream[33] = '\0';
The resultstream array is 0 indexed so valid values are 0-32, you are accessing memory not allocated for that array (hence the "Access violation")

Is there any way to pair Bluetooth device in Windows programmatically

there's a question:
Is there any way to pair Bluetooth device in Windows programmatically? (c++, c#)
thanks for replies
Yes, the reference documentation is available on MSDN.
32feet.NET is a C# wrapper, available here. Information on pairing is here.
Python is a tempting and overall easy solution, but PyBlueZ does not expose the windows Bluetooth authentication APIs here: https://msdn.microsoft.com/en-us/library/windows/desktop/cc766819(v=vs.85).aspx
One way to get around this is to create a command line tool and use this through Python. To create command line tools for Windows, use Visual Studio and add the necessary libraries to your project linker properties: Bthprops.lib and ws2_32.lib
Below is the code for a project to make a command line tool with 1 parameter, the MAC address, that pairs the specified device using "Just Works" pairing. See commented code for using passkey pairing.
#include "stdafx.h"
#include <initguid.h>
#include <winsock2.h>
#include <BluetoothAPIs.h>
#include <ws2bth.h>
BOOL WINAPI BluetoothAuthCallback(LPVOID pvParam, PBLUETOOTH_AUTHENTICATION_CALLBACK_PARAMS pAuthCallbackParams);
int _tmain(int argc, _TCHAR* argv[])
{
SOCKADDR_BTH sa = { 0 };
int sa_len = sizeof(sa);
DWORD dwRet;
BLUETOOTH_DEVICE_INFO btdi = { 0 };
HBLUETOOTH_AUTHENTICATION_REGISTRATION hRegHandle = 0;
// initialize windows sockets
WORD wVersionRequested;
WSADATA wsaData;
wVersionRequested = MAKEWORD(2, 0);
if (WSAStartup(wVersionRequested, &wsaData) != 0) {
ExitProcess(2);
}
// parse the specified Bluetooth address
if (argc < 2) {
fprintf(stderr, "usage: csbtpair <addr>\n"
"\n addr must be in the form (XX:XX:XX:XX:XX:XX)");
ExitProcess(2);
}
if (SOCKET_ERROR == WSAStringToAddress(argv[1], AF_BTH,
NULL, (LPSOCKADDR)&sa, &sa_len)) {
ExitProcess(2);
}
// setup device info
btdi.dwSize = sizeof(BLUETOOTH_DEVICE_INFO);
btdi.Address.ullLong = sa.btAddr;
btdi.ulClassofDevice = 0;
btdi.fConnected = false;
btdi.fRemembered = false;
btdi.fAuthenticated = false;
// register authentication callback. this prevents UI from showing up.
dwRet = BluetoothRegisterForAuthenticationEx(&btdi, &hRegHandle, &BluetoothAuthCallback, NULL);
if (dwRet != ERROR_SUCCESS)
{
fprintf(stderr, "BluetoothRegisterForAuthenticationEx ret %d\n", dwRet);
ExitProcess(2);
}
// authenticate device (will call authentication callback)
AUTHENTICATION_REQUIREMENTS authreqs = MITMProtectionNotRequired;
fprintf(stderr, "BluetoothAuthReqs = %d\n", authreqs);
dwRet = BluetoothAuthenticateDeviceEx(NULL, NULL, &btdi, NULL, authreqs);
if (dwRet != ERROR_SUCCESS)
{
fprintf(stderr, "BluetoothAuthenticateDevice ret %d\n", dwRet);
if (dwRet == ERROR_CANCELLED)
{
fprintf(stderr, "Cancelled");
}
else if (dwRet == ERROR_INVALID_PARAMETER)
{
fprintf(stderr, "Invalid Parameter");
}
else if (dwRet == ERROR_NO_MORE_ITEMS)
{
fprintf(stderr, "Already paired!");
}
}
fprintf(stderr, "pairing finish\n");
ExitProcess(0);
return 0;
}
// Authentication callback
BOOL WINAPI BluetoothAuthCallback(LPVOID pvParam, PBLUETOOTH_AUTHENTICATION_CALLBACK_PARAMS pAuthCallbackParams)
{
DWORD dwRet;
fprintf(stderr, "BluetoothAuthCallback 0x%x\n", pAuthCallbackParams->deviceInfo.Address.ullLong);
BLUETOOTH_AUTHENTICATE_RESPONSE AuthRes;
AuthRes.authMethod = pAuthCallbackParams->authenticationMethod;
fprintf(stderr, "Authmethod %d\n", AuthRes.authMethod);
// Check to make sure we are using numeric comparison (Just Works)
if (AuthRes.authMethod == BLUETOOTH_AUTHENTICATION_METHOD_NUMERIC_COMPARISON)
{
fprintf(stderr, "Numeric Comparison supported\n");
}
AuthRes.bthAddressRemote = pAuthCallbackParams->deviceInfo.Address;
AuthRes.negativeResponse = FALSE;
// Commented out code is used for pairing using the BLUETOOTH_AUTHENTICATION_METHOD_PASSKEY method
//memcpy_s(AuthRes.pinInfo.pin, sizeof(AuthRes.pinInfo.pin), L"1234", 0);
//AuthRes.pinInfo.pinLength = 0;
// Respond with numerical value for Just Works pairing
AuthRes.numericCompInfo.NumericValue = 1;
// Send authentication response to authenticate device
dwRet = BluetoothSendAuthenticationResponseEx(NULL, &AuthRes);
if (dwRet != ERROR_SUCCESS)
{
fprintf(stderr, "BluetoothSendAuthenticationResponseEx ret %d\n", dwRet);
if (dwRet == ERROR_CANCELLED)
{
fprintf(stderr, "Bluetooth device denied passkey response or communicatino problem.\n");
}
else if (dwRet == E_FAIL)
{
fprintf(stderr, "Device returned a failure code during authentication.\n");
}
else if (dwRet == 1244)
{
fprintf(stderr, "Not authenticated\n");
}
}
else
{
fprintf(stderr, "BluetoothAuthCallback finish\n");
}
return 1; // This value is ignored
}
In lieu of creating this yourself, you may want to try this pre-made solution:
http://bluetoothinstaller.com/bluetooth-command-line-tools/
It did not work for my particular solution.
Then, you will need to run your downloaded or custom command line tool from python as an administrator. To do this reliably, I recommend the stackoverflow question:
How to run python script with elevated privilege on windows
I meet the same problem,and I have resolved the problem, Maybe you can try it:
make a windows tool named pairtool.exe, it help you to pairing with command line. the key api is BluetoothAuthenticateDevice, please refering the functions document
dwRet = BluetoothAuthenticateDevice(NULL, NULL, &btdi, L"1234", 4);
if(dwRet != ERROR_SUCCESS)
{
fprintf(stderr, "BluetoothAuthenticateDevice ret %d\n", dwRet);
ExitProcess(2);
}
python code:
def connect2Btdev(devName):
#found the device addr
addr = inquiry(devName)
if addr == None:
return None
#pairing with pairtool.exe
cmd=r'%s %s' % ('pairtool.exe',addr)
ret = os.system(cmd)
if ret <> 0:
return None
here is all the code of pairtool.exe:
#include "stdafx.h"
#include <initguid.h>
#include <winsock2.h>
#include <BluetoothAPIs.h>
#include <ws2bth.h>
bool BluetoothAuthCallback(LPVOID pvParam, PBLUETOOTH_AUTHENTICATION_CALLBACK_PARAMS pAuthCallbackParams)
{
DWORD dwRet;
fprintf(stderr, "BluetoothAuthCallback 0x%x\n", pAuthCallbackParams->deviceInfo.Address.ullLong);
dwRet = BluetoothSendAuthenticationResponse(NULL, &(pAuthCallbackParams->deviceInfo), L"1234");
if(dwRet != ERROR_SUCCESS)
{
fprintf(stderr, "BluetoothSendAuthenticationResponse ret %d\n", dwRet);
ExitProcess(2);
return 1;
}
fprintf(stderr, "BluetoothAuthCallback finish\n");
ExitProcess(0);
return 1;
}
int _tmain(int argc, _TCHAR* argv[])
{
SOCKADDR_BTH sa = { 0 };
int sa_len = sizeof(sa);
DWORD dwRet;
BLUETOOTH_DEVICE_INFO btdi = {0};
HBLUETOOTH_AUTHENTICATION_REGISTRATION hRegHandle = 0;
// initialize windows sockets
WORD wVersionRequested;
WSADATA wsaData;
wVersionRequested = MAKEWORD( 2, 0 );
if( WSAStartup( wVersionRequested, &wsaData ) != 0 ) {
ExitProcess(2);
}
// parse the specified Bluetooth address
if( argc < 2 ) {
fprintf(stderr, "usage: rfcomm-client <addr>\n"
"\n addr must be in the form (XX:XX:XX:XX:XX:XX)");
ExitProcess(2);
}
if( SOCKET_ERROR == WSAStringToAddress( argv[1], AF_BTH,
NULL, (LPSOCKADDR) &sa, &sa_len ) ) {
ExitProcess(2);
}
//注册回调函数
btdi.dwSize = sizeof(BLUETOOTH_DEVICE_INFO);
btdi.Address.ullLong = sa.btAddr;
btdi.ulClassofDevice = 0;
btdi.fConnected = false;
btdi.fRemembered = false;
btdi.fAuthenticated = false;
dwRet = BluetoothRegisterForAuthenticationEx(&btdi, &hRegHandle, &BluetoothAuthCallback, NULL);
if(dwRet != ERROR_SUCCESS)
{
fprintf(stderr, "BluetoothRegisterForAuthenticationEx ret %d\n", dwRet);
ExitProcess(2);
}
dwRet = BluetoothAuthenticateDevice(NULL, NULL, &btdi, L"1234", 4);
if(dwRet != ERROR_SUCCESS)
{
fprintf(stderr, "BluetoothAuthenticateDevice ret %d\n", dwRet);
ExitProcess(2);
}
Sleep(1000);
fprintf(stderr, "pairing finish\n");
ExitProcess(0);
return 0;
}
Microsoft has introduced Windows.Devices.Enumeration API available for UWP and traditional applications, which makes pairing of bluetooth devices very easy (for details look at the official C# and C++ example). As far as I understand it is the API which is used by built-in "Bluetooth & other devices" UI dialog. As of an example of what console utility you can write using this API you can take a look at my console BluetoothDevicePairing utility.
You can do so by using the functions documented under MSDN Bluetooth Functions.
These enable searching and pairing of bluetooth devices programmatically.

Resources