PROFINET IO Controller Stack 1.0.0
Modern C++ implementation of a PROFINET IO Controller stack
Loading...
Searching...
No Matches
platformWindows.cpp
Go to the documentation of this file.
1
3#ifndef NOMINMAX
4#define NOMINMAX
5#endif
6#ifndef WIN32_LEAN_AND_MEAN
7#define WIN32_LEAN_AND_MEAN
8#endif
9#include <winsock2.h>
10#include <windows.h>
11#include <iphlpapi.h>
12
13#include <algorithm>
14#include <filesystem>
15#include <limits>
16#include <string>
17#include <stdexcept>
18#include <string_view>
19#include <vector>
20
21#include "profinet/util.h"
22#include "profinet/exceptions.h"
23
24namespace profinet
25{
26namespace
27{
28constexpr std::string_view kNpcapDevicePrefix = R"(\Device\NPF_)";
29
34std::wstring Utf8ToWide(const std::string& value)
35{
36 if (value.empty())
37 {
38 return {};
39 }
40
41 if (value.size() > static_cast<std::size_t>(std::numeric_limits<int>::max()))
42 {
43 return {};
44 }
45
46 const int sourceLength = static_cast<int>(value.size());
47 const int requiredLength = MultiByteToWideChar(
48 CP_UTF8,
49 MB_ERR_INVALID_CHARS,
50 value.data(),
51 sourceLength,
52 nullptr,
53 0);
54
55 if (requiredLength <= 0)
56 {
57 return {};
58 }
59
60 std::wstring result(static_cast<std::size_t>(requiredLength), L'\0');
61
62 const int convertedLength = MultiByteToWideChar(
63 CP_UTF8,
64 MB_ERR_INVALID_CHARS,
65 value.data(),
66 sourceLength,
67 result.data(),
68 requiredLength);
69
70 if (convertedLength != requiredLength)
71 {
72 return {};
73 }
74
75 return result;
76}
77
82std::string NormalizeWindowsInterfaceName(const std::string& interface)
83{
84 if (interface.starts_with(kNpcapDevicePrefix))
85 {
86 return interface.substr(kNpcapDevicePrefix.size());
87 }
88
89 return interface;
90}
91} // namespace
92
93MacAddress GetInterfaceMac(const std::string& interface)
94{
95 if (interface.empty())
96 {
97 throw SocketError("Interface name cannot be empty");
98 }
99
100 const std::string normalizedInterface = NormalizeWindowsInterfaceName(interface);
101 const std::wstring wideInterface = Utf8ToWide(normalizedInterface);
102
103 ULONG bufferSize = 15U * 1024U;
104 std::vector<std::uint8_t> buffer(bufferSize);
105
106 PIP_ADAPTER_ADDRESSES adapters = nullptr;
107 ULONG result = ERROR_BUFFER_OVERFLOW;
108
109 while (result == ERROR_BUFFER_OVERFLOW)
110 {
111 buffer.resize(bufferSize);
112 adapters = reinterpret_cast<PIP_ADAPTER_ADDRESSES>(buffer.data());
113
114 result = GetAdaptersAddresses(
115 AF_UNSPEC,
116 0,
117 nullptr,
118 adapters,
119 &bufferSize);
120 }
121
122 if (result != NO_ERROR)
123 {
124 throw SocketError("Failed to enumerate Windows network interfaces: error " +
125 std::to_string(result));
126 }
127
128 for (const IP_ADAPTER_ADDRESSES* adapter = adapters;
129 adapter != nullptr;
130 adapter = adapter->Next)
131 {
132 const bool adapterNameMatches =
133 adapter->AdapterName != nullptr && normalizedInterface == adapter->AdapterName;
134
135 const bool friendlyNameMatches =
136 adapter->FriendlyName != nullptr &&
137 !wideInterface.empty() &&
138 wideInterface == adapter->FriendlyName;
139
140 const bool descriptionMatches =
141 adapter->Description != nullptr &&
142 !wideInterface.empty() &&
143 wideInterface == adapter->Description;
144
145 if (!adapterNameMatches && !friendlyNameMatches && !descriptionMatches)
146 {
147 continue;
148 }
149
150 if (adapter->PhysicalAddressLength != macAddressLength)
151 {
152 throw SocketError("Interface '" + interface +
153 "' does not expose a six-byte MAC address");
154 }
155
156 MacAddress mac{};
157 std::ranges::copy_n(
158 adapter->PhysicalAddress,
160 mac.begin());
161
162 return mac;
163 }
164
165 throw SocketError("Network interface not found: '" + interface + "'");
166}
167
168std::filesystem::path GetExecutableDirectory()
169{
170 std::vector<wchar_t> buffer(512U);
171
172 for (;;)
173 {
174 const DWORD length = GetModuleFileNameW(
175 nullptr,
176 buffer.data(),
177 static_cast<DWORD>(buffer.size()));
178
179 if (length == 0U)
180 {
181 const DWORD error = GetLastError();
182 throw std::runtime_error(
183 "Failed to get executable path: " + std::to_string(error));
184 }
185
186 if (length < buffer.size())
187 {
188 return std::filesystem::path(
189 std::wstring(buffer.data(), buffer.data() + length))
190 .parent_path();
191 }
192
193 buffer.resize(buffer.size() * 2U);
194
195 if (buffer.size() > static_cast<std::size_t>(std::numeric_limits<DWORD>::max()))
196 {
197 throw std::runtime_error("Executable path exceeds the supported Windows path length");
198 }
199 }
200}
201
202} // namespace profinet
Declares exceptions and error types used by the PROFINET IO controller stack.
MacAddress GetInterfaceMac(const std::string &interface)
Get the MAC address assigned to a network interface.
std::array< std::uint8_t, macAddressLength > MacAddress
A 6-byte Ethernet MAC address.
Definition util.h:48
std::filesystem::path GetExecutableDirectory()
Retrieves the absolute directory path of the currently running executable.
constexpr int macAddressLength
Constant lenght of a mac address.
Definition util.h:37
asio::error_code error
Bytes buffer
std::uint16_t length
Definition test_dcp.cpp:391
Declares Linux Ethernet, addressing, timing, and utility helpers used by the PROFINET stack.