Given the following code that should connect to a sftp server and print the name of the first file in that server. It looks like it returns the error 12002 which means that the request timed out. I've tried to run it with 2 different servers. Is there something fundamentally wrong in my code?
#include "stdafx.h"
#include <Windows.h>
#include <Wininet.h>
#include <iostream>
using namespace std;
#pragma comment(lib, "wininet.lib")
int main()
{
HINTERNET hInternet = InternetOpen(NULL, INTERNET_OPEN_TYPE_PRECONFIG, NULL, NULL, 0);
if (hInternet == NULL)
{
_tprintf(_T("An error has occured while trying to open the internet connection\n"));
return 1;
}
HINTERNET hFTP = InternetConnect(
hInternet,
L"test.rebex.net", // this is a sftp server publicly available
22,
L"demo",
L"password",
INTERNET_SERVICE_FTP,
INTERNET_FLAG_PASSIVE,
0
);
if (hFTP == NULL)
{
cout << GetLastError() << endl;
_tprintf(_T("Couldn't connect to the ftp server\n"));
return 1;
}
_tprintf(_T("%d\n"), hFTP);
WIN32_FIND_DATA fd;
FtpFindFirstFile(hFTP, L".", &fd, INTERNET_FLAG_RELOAD, NULL);
_tprintf(_T("%s"), fd.cFileName);
InternetCloseHandle(hFTP);
InternetCloseHandle(hInternet);
return 0;
}
SFTP (FTP over SSH) is very different than FTPS (FTP over SSL), which is probably what you want instead.
But either way, WinInet simply does not support SFTP or FTPS (the INTERNET_FLAG_SECURE flag is only supported for HTTPS).
You will have to use another FTP library that supports SFTP/FTPS.
I have some legacy code that provides a list of the available COM ports on the PC by calling the EnumPorts() function and then filtering for the port names that start with "COM".
For testing purposes it would be very useful if I could use this code with something like com0com, which provides pairs of virtual COM ports looped together as a null-modem.
However the com0com ports are not found by the EnumPorts() function (even without filtering for "COM"). HyperTerminal and SysInternals PortMon can both see them, so I'm sure it is installed correctly.
So is there some other Win32 function that provides a definitive list of available serial ports?
The EnumSerialPorts v1.20 suggested by Nick D uses nine different methods to list the serial ports! We're certainly not short on choice, though the results seem to vary.
To save others the trouble, I'll list them here and indicate their success in finding the com0com ports on my PC (XP Pro SP2):
CreateFile("COM" + 1->255) as suggested by Wael Dalloul
✔ Found com0com ports, took 234ms.
QueryDosDevice()
✔ Found com0com ports, took 0ms.
GetDefaultCommConfig("COM" + 1->255)
✔ Found com0com ports, took 235ms.
"SetupAPI1" using calls to SETUPAPI.DLL
✔ Found com0com ports, also reported "friendly names", took 15ms.
"SetupAPI2" using calls to SETUPAPI.DLL
✘ Did not find com0com ports, reported "friendly names", took 32ms.
EnumPorts()
✘ Reported some non-COM ports, did not find com0com ports, took 15ms.
Using WMI calls
✔ Found com0com ports, also reported "friendly names", took 47ms.
COM Database using calls to MSPORTS.DLL
✔/✘ Reported some non-COM ports, found com0com ports, took 16ms.
Iterate over registry key HKEY_LOCAL_MACHINE\HARDWARE\DEVICEMAP\SERIALCOMM
✔ Found com0com ports, took 0ms. This is apparently what SysInternals PortMon uses.
Based on those results I think the WMI method probably suits my requirements best as it is relatively fast and as a bonus it also gives the friendly names (e.g. "Communications Port (COM1)", "com0com - serial port emulator").
It appears that it's not a simple task.
Check out this: EnumSerialPorts v1.20
you can make loop for example from 1 to 50 and try to open each port. If the port is available, the open will work. If the port is in use, you'll get a sharing error. If the port is not installed, you'll get a file not found error.
to open the port use CreateFile API:
HANDLE Port = CreateFile(
"\\\\.\\COM1",
GENERIC_READ | GENERIC_WRITE,
0,
NULL,
OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL,
NULL);
then check the result.
In my case, I need both the full names and COM port addresses. I have physical serial ports, USB serial ports, and com0com virtual serial ports.
Like the accepted answer suggests, I use WMI calls. SELECT * FROM Win32_PnPEntity find all devices. It returns physical devices like this, and address can be parsed from Caption:
Serial Port for Barcode Scanner (COM13)
However, for com0com ports Caption is like this (no address):
com0com - serial port emulator
SELECT * FROM Win32_SerialPort returns addresses (DeviceID), as well as full names (Name). However, it only finds physical serial ports and com0com ports, not USB serial ports.
So in the end, I need two WMI calls: SELECT * FROM Win32_SerialPort (address is DeviceID) and SELECT * FROM Win32_PnPEntity WHERE Name LIKE '%(COM%' (address can be parsed from Caption). I have narrowed down the Win32_PnPEntity call, because it only needs to find devices that were not found in the first call.
This C++ code can be used to find all serial ports:
// Return list of serial ports as (number, name)
std::map<int, std::wstring> enumerateSerialPorts()
{
std::map<int, std::wstring> result;
HRESULT hres;
hres = CoInitializeEx(0, COINIT_APARTMENTTHREADED);
if (SUCCEEDED(hres) || hres == RPC_E_CHANGED_MODE) {
hres = CoInitializeSecurity(
NULL,
-1, // COM authentication
NULL, // Authentication services
NULL, // Reserved
RPC_C_AUTHN_LEVEL_DEFAULT, // Default authentication
RPC_C_IMP_LEVEL_IMPERSONATE, // Default Impersonation
NULL, // Authentication info
EOAC_NONE, // Additional capabilities
NULL // Reserved
);
if (SUCCEEDED(hres) || hres == RPC_E_TOO_LATE) {
IWbemLocator *pLoc = NULL;
hres = CoCreateInstance(
CLSID_WbemLocator,
0,
CLSCTX_INPROC_SERVER,
IID_IWbemLocator, (LPVOID *) &pLoc);
if (SUCCEEDED(hres)) {
IWbemServices *pSvc = NULL;
// Connect to the root\cimv2 namespace with
// the current user and obtain pointer pSvc
// to make IWbemServices calls.
hres = pLoc->ConnectServer(
bstr_t(L"ROOT\\CIMV2"), // Object path of WMI namespace
NULL, // User name. NULL = current user
NULL, // User password. NULL = current
0, // Locale. NULL indicates current
NULL, // Security flags.
0, // Authority (for example, Kerberos)
0, // Context object
&pSvc // pointer to IWbemServices proxy
);
if (SUCCEEDED(hres)) {
hres = CoSetProxyBlanket(
pSvc, // Indicates the proxy to set
RPC_C_AUTHN_WINNT, // RPC_C_AUTHN_xxx
RPC_C_AUTHZ_NONE, // RPC_C_AUTHZ_xxx
NULL, // Server principal name
RPC_C_AUTHN_LEVEL_CALL, // RPC_C_AUTHN_LEVEL_xxx
RPC_C_IMP_LEVEL_IMPERSONATE, // RPC_C_IMP_LEVEL_xxx
NULL, // client identity
EOAC_NONE // proxy capabilities
);
if (SUCCEEDED(hres)) {
// Use Win32_PnPEntity to find actual serial ports and USB-SerialPort devices
// This is done first, because it also finds some com0com devices, but names are worse
IEnumWbemClassObject* pEnumerator = NULL;
hres = pSvc->ExecQuery(
bstr_t(L"WQL"),
bstr_t(L"SELECT Name FROM Win32_PnPEntity WHERE Name LIKE '%(COM%'"),
WBEM_FLAG_FORWARD_ONLY | WBEM_FLAG_RETURN_IMMEDIATELY,
NULL,
&pEnumerator);
if (SUCCEEDED(hres)) {
constexpr size_t max_ports = 30;
IWbemClassObject *pclsObj[max_ports] = {};
ULONG uReturn = 0;
do {
hres = pEnumerator->Next(WBEM_INFINITE, max_ports, pclsObj, &uReturn);
if (SUCCEEDED(hres)) {
for (ULONG jj = 0; jj < uReturn; jj++) {
VARIANT vtProp;
pclsObj[jj]->Get(L"Name", 0, &vtProp, 0, 0);
// Name should be for example "Serial Port for Barcode Scanner (COM13)"
const std::wstring deviceName = vtProp.bstrVal;
const std::wstring prefix = L"(COM";
size_t ind = deviceName.find(prefix);
if (ind != std::wstring::npos) {
std::wstring nbr;
for (size_t i = ind + prefix.length();
i < deviceName.length() && isdigit(deviceName[i]); i++)
{
nbr += deviceName[i];
}
try {
const int portNumber = boost::lexical_cast<int>(nbr);
result[portNumber] = deviceName;
}
catch (...) {}
}
VariantClear(&vtProp);
pclsObj[jj]->Release();
}
}
} while (hres == WBEM_S_NO_ERROR);
pEnumerator->Release();
}
// Use Win32_SerialPort to find physical ports and com0com virtual ports
// This is more reliable, because address doesn't have to be parsed from the name
pEnumerator = NULL;
hres = pSvc->ExecQuery(
bstr_t(L"WQL"),
bstr_t(L"SELECT DeviceID, Name FROM Win32_SerialPort"),
WBEM_FLAG_FORWARD_ONLY | WBEM_FLAG_RETURN_IMMEDIATELY,
NULL,
&pEnumerator);
if (SUCCEEDED(hres)) {
constexpr size_t max_ports = 30;
IWbemClassObject *pclsObj[max_ports] = {};
ULONG uReturn = 0;
do {
hres = pEnumerator->Next(WBEM_INFINITE, max_ports, pclsObj, &uReturn);
if (SUCCEEDED(hres)) {
for (ULONG jj = 0; jj < uReturn; jj++) {
VARIANT vtProp1, vtProp2;
pclsObj[jj]->Get(L"DeviceID", 0, &vtProp1, 0, 0);
pclsObj[jj]->Get(L"Name", 0, &vtProp2, 0, 0);
const std::wstring deviceID = vtProp1.bstrVal;
if (deviceID.substr(0, 3) == L"COM") {
const int portNumber = boost::lexical_cast<int>(deviceID.substr(3));
const std::wstring deviceName = vtProp2.bstrVal;
result[portNumber] = deviceName;
}
VariantClear(&vtProp1);
VariantClear(&vtProp2);
pclsObj[jj]->Release();
}
}
} while (hres == WBEM_S_NO_ERROR);
pEnumerator->Release();
}
}
pSvc->Release();
}
pLoc->Release();
}
}
CoUninitialize();
}
if (FAILED(hres)) {
std::stringstream ss;
ss << "Enumerating serial ports failed. Error code: " << int(hres);
throw std::runtime_error(ss.str());
}
return result;
}
It's available now in Windows, GetCommPorts can directly return a list of comm ports
Gets an array that contains the well-formed COM ports.
This function obtains the COM port numbers from the
HKLM\Hardware\DeviceMap\SERIALCOMM registry key and then writes them
to a caller-supplied array. If the array is too small, the function
gets the necessary size.
you will need to add this code in order to link the function correctly
#pragma comment (lib, "OneCore.lib")
I have reorganized PJ Naughter 's EnumSerialPorts as more portable and individual forms, that is more useful.
For better in compatibility, I use C, instead of C++.
If you need or be interested in it, please visit the post in my blogger.
Like in the title - I've been working on code in C++ that changes my IP address. The program works "alright" and shows it added new IP, however the old IP stay the same and doesn't change.
So what I have to fix in my code to get rid off of the problem
If entire script is wrong I would really appreciate an example of program which would work.
The code:
#ifndef WIN32_LEAN_AND_MEAN
#define WIN32_LEAN_AND_MEAN
#endif
#include <windows.h>
#include <winsock2.h>
#include <iphlpapi.h>
#include <stdio.h>
#include <stdlib.h>
#pragma comment(lib, "iphlpapi.lib")
#pragma comment(lib, "ws2_32.lib")
int main()
{
PMIB_IPADDRTABLE pIPAddrTable;
DWORD dwSize = 0;
DWORD dwRetVal;
UINT IPAddress;
UINT IPMask;
ULONG NTEContext = 0;
ULONG NTEInstance = 0;
LPVOID lpMsgBuf;
pIPAddrTable = (MIB_IPADDRTABLE *)malloc(sizeof(MIB_IPADDRTABLE));
if (GetIpAddrTable(pIPAddrTable, &dwSize, 0) == ERROR_INSUFFICIENT_BUFFER) {
pIPAddrTable = (MIB_IPADDRTABLE *)malloc(dwSize);
}
if ((dwRetVal = GetIpAddrTable(pIPAddrTable, &dwSize, 0)) == NO_ERROR) {
printf("\tAddress: %ld\n", pIPAddrTable->table[0].dwAddr);
printf("\tMask: %ld\n", pIPAddrTable->table[0].dwMask);
}
else {
printf("Call to GetIpAddrTable failed.\n");
}
IPAddress = inet_addr("192.168.0.2");
IPMask = inet_addr("255.255.255.0");
if ((dwRetVal = AddIPAddress(IPAddress,
IPMask,
pIPAddrTable->table[0].dwIndex,
&NTEContext, &NTEInstance)) == NO_ERROR) {
printf("\tIP address added.\n");
}
else {
printf("Error adding IP address.\n");
if (FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, NULL, dwRetVal, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // Default language
(LPTSTR)& lpMsgBuf, 0, NULL)) {
printf("\tError: %s", lpMsgBuf);
}
LocalFree(lpMsgBuf);
}
system("c:\\windows\\system32\\ipconfig");
system("pause");
}
Footnote: Program was modified from an example from Microsoft https://msdn.microsoft.com/en-us/library/windows/desktop/aa365875(v=vs.85).aspx Though, it contains error which makes my VS to reach breakpoints caused by 'GlobalFree(pIPAddrTable);'
If anyone interested I'm inviting for a C++ project on GitHub
All is conformant with MSDN documentation. The page on AddIpAddress states:
The AddIPAddress function is used to add a new IPv4 address entry on a local computer. The IPv4 address added by the AddIPAddress function is not persistent...
That means that calling this function will have no effect on pre-existing network addresses and just add a temporary new one.
If you want the change the static network address, you should try to use the EnableStatic method of the Win32_NetworkAdapterConfiguration class.
I get an 10051 socket error every time I try to use this code:
USES_CONVERSION;
LPTSTR addr = A2W("192.168.1.209");
m_pSMACLPRCli = new CSMACLPRCli(addr, 12010, m_hWnd);
m_pSMACLPRCli->StartThread();
This is the constructor for m_pSMACLPRCli:
CSMACLPRCli::CSMACLPRCli(LPTSTR lpsztIPAddress, int nPort, HWND hParentWnd)
And this is how I create the socket and connect:
void CBlockingSocket::Create(int nType /* = SOCK_STREAM */)
{
ASSERT(m_hSocket == NULL);
if ((m_hSocket = socket(AF_INET, nType, 0)) == INVALID_SOCKET)
{
TRACE("\n Socket Error !1 (%d)\n", WSAGetLastError());
int err = WSAGetLastError();
}
}
BOOL CBlockingSocket::Connect(LPCSOCKADDR psa)
{
ASSERT(m_hSocket != NULL);
// should timeout by itself
if (connect(m_hSocket, psa, sizeof(SOCKADDR)) == SOCKET_ERROR)
{
int nLastErr = WSAGetLastError();
return FALSE;
}
return TRUE;
}
The real funny thing is that when I use the exact same code, class structure etc. in a VS2008 project, everything works as expected, but when I use it in a VS2010 project, at connect() I get a 10051 error, Network is unreachable.
EDIT: The original VS2010 proj. is compiled using UNICODE. I've made a new VS2010 using MULTI-BYTE for testing and the connect() method returns no error, and ... connects. Could it be something wrong with my way of passing the address string to the constructor?
USES_CONVERSION;
LPTSTR addr = A2W("192.168.1.209");
m_pSMACLPRCli = new CSMACLPRCli(addr, 12010, m_hWnd);
m_pSMACLPRCli->StartThread();
SOLVED:
The real problem was not the connect() method, but had to do with my way of passing the address string to a constructor of a sockaddr object.
The constructor:
CSockAddr(const char *pchIP, const USHORT ushPort = 0) // dotted IP addr string
{
sin_family = AF_INET;
sin_port = htons(ushPort);
sin_addr.s_addr = inet_addr(pchIP);
}
Constructor call used by me:
CString m_strSrvIPAddr;
CSockAddr saServer((char *) LPTSTR(LPCTSTR(m_strSrvIPAddr)), USHORT(m_nPort));
I changed the call to this:
CStringA strAddr(m_strSrvIPAddr);
CSockAddr saServer((const char *) strAddr, USHORT(m_nPort));
So I had to do a conversion of the string from UNICODE to MULTI_BYTE.
When calling connect(), you need to use sizeof(SOCKADDR_IN) or sizeof(SOCKADDR_IN6) depending on what psa is actually pointing at. I would suggest having the caller pass in the actual size value:
BOOL CBlockingSocket::Connect(LPCSOCKADDR psa, int sasize)
{
ASSERT(m_hSocket != INVALID_SOCKET);
// should timeout by itself
if (connect(m_hSocket, psa, sasize) == SOCKET_ERROR)
{
int nLastErr = WSAGetLastError();
return FALSE;
}
return TRUE;
}
SOCKADDR_IN sa;
...
Connect((LPSOCKADDR)&sa, sizeof(sa));
Alternatively, it would be better to use SOCKADDR_STORAGE and just type-cast it when passing it to connect():
BOOL CBlockingSocket::Connect(const SOCKADDR_STORAGE *psa)
{
ASSERT(m_hSocket != INVALID_SOCKET);
// should timeout by itself
if (connect(m_hSocket, (LPCSOCKADDR)psa, sizeof(SOCKADDR_STORAGE)) == SOCKET_ERROR)
{
int nLastErr = WSAGetLastError();
return FALSE;
}
return TRUE;
}
SOCKADDR_STORAGE sa;
...
Connect(&sa);
It seems that this problem is not related to your code or the development environment but rather to the network the computer is connected to.
The output of ipconfig will let you know your IP address and the subnet mask which will allow you to understand which network you belong to.
The 192.186.0.0 network is a local address (behind a NAT) and you will not be able to connect to an address on this network unless you are part of it. If your IP address does not start with 192.168 it is the cause for the error you're getting.
I am developing an application for Windows Vista and 7 in Visual Studio C++, in which I have to assign static IP address to a network card and establish a connection. For this, I am entering the Ip values in registry along with setting the Enable DHCP value to 0. Then I need to disable and then enable the network card for these values to take effect. For this, I am using "INetConnectionManager" in the following code:
CoInitialize(0);
typedef void (__stdcall * PNcFreeNetconProperties)(NETCON_PROPERTIES* pProps);
HMODULE hmod = LoadLibrary(L"netshell.dll");
if (!hmod)
return false;
LPNcFreeNetconProperties NcFreeNetconProperties =
(LPNcFreeNetconProperties)GetProcAddress(hmod, "NcFreeNetconProperties");
if (!NcFreeNetconProperties )
return false;
INetConnectionManager * pMan = 0;
HRESULT hres = CoCreateInstance(CLSID_ConnectionManager,
0,
CLSCTX_ALL,
__uuidof(INetConnectionManager),
(void**)&pMan);
if (SUCCEEDED(hres))
{
IEnumNetConnection * pEnum = 0;
hres = pMan->EnumConnections(NCME_DEFAULT, &pEnum);
if (SUCCEEDED(hres))
{
INetConnection * pCon = 0;
ULONG count;
bool done = false;
while (pEnum->Next(1, &pCon, &count) == S_OK && !done)
{
NETCON_PROPERTIES * pProps = 0;
hres = pCon->GetProperties(&pProps);
if (SUCCEEDED(hres))
{
if (wcscmp(pProps-pszwDeviceName, AdapterName) == 0)
{
if (bEnable)
result = (pCon->Connect() == S_OK);
else
result = (pCon->Disconnect() == S_OK);
done = true;
}
NcFreeNetconProperties(pProps);
}
pCon->Release();
}
pEnum->Release();
}
pMan->Release();
}
FreeLibrary(hmod);
CoUninitialize();
It's disabling and enabling the network card very well, BUT the autoconfiguration IPv4 values are getting set instead of the static values in the registry. This strangely works properly for DHCP connection but not for static connection.
NOTE: I even tried SetIfEntry for it, but it fails to disable or enable Network Card.
Please suggest where I am doing wrong or anything I am missing.
You can use AddIPAddress:
http://msdn.microsoft.com/en-us/library/aa365801%28v=vs.85%29.aspx
Does INetConnectionManager supported on Windows VISTA and Win7? I have implemented the same code what you have written here but I get access denied for pCon->Connect when application runs on non admin login. Therefore, it looks like that we need to elevate the com object using COM Moniker.
Regards
IP_Telephony