From 8f6135c835254017b3b005a8cc1a51953db26902 Mon Sep 17 00:00:00 2001 From: Andrey Vukolov Date: Thu, 30 Jul 2026 16:55:16 +0200 Subject: [PATCH 1/8] Tweaks to build the whole project on Windows with CMake/MSVC 19 in 2026 - Fixed the PLC port definitions mistakenly left in the BSD section in the test program - Added CMake tweaks to successfully build the project on Windows natively using git/MSVC/PowerShell - Implemented TcAdsDll_ROOT CMake variable to let the build system catch the libraries from non-standard locations. Tested on non-native installation in Windows 10.0.19045 and SDK 10.0.26100.0 --- .gitignore | 1 + AdsLibTestRef/main.cpp | 4 +++- cmake/FindTcAdsDll.cmake | 20 +++++++++++++------- 3 files changed, 17 insertions(+), 8 deletions(-) diff --git a/.gitignore b/.gitignore index f073c906..8efc3aed 100644 --- a/.gitignore +++ b/.gitignore @@ -44,6 +44,7 @@ build/ bld/ [Bb]in/ [Oo]bj/ +[Ii]nstall/ # Visual Studo 2015 cache/options directory .vs/ diff --git a/AdsLibTestRef/main.cpp b/AdsLibTestRef/main.cpp index ed20966e..2b42e7e2 100644 --- a/AdsLibTestRef/main.cpp +++ b/AdsLibTestRef/main.cpp @@ -15,10 +15,12 @@ using USHORT = unsigned short; #define TCADSDLL_API #define TCADSDLL_EXPORT #define NULL nullptr -#define AMSPORT_R0_PLC_TC3 851 #else #include #endif + +#define AMSPORT_R0_PLC_TC3 851 + #include #ifndef GLOBALERR_TARGET_PORT #define GLOBALERR_TARGET_PORT 0x06 diff --git a/cmake/FindTcAdsDll.cmake b/cmake/FindTcAdsDll.cmake index ee44c0a0..563b16ad 100644 --- a/cmake/FindTcAdsDll.cmake +++ b/cmake/FindTcAdsDll.cmake @@ -20,7 +20,12 @@ if (NOT WIN32) message(WARNING "FindTcAdsDll.cmake only tested on WINDOWS") endif () -if (WIN32) +if (DEFINED TcAdsDll_ROOT) + message(STATUS "FindTcAdsDll.cmake: DLL lookup directory ${TcAdsDll_ROOT}") + set(_TcAdsDll_PATH "${TcAdsDll_ROOT}") +endif () + +if (WIN32 AND NOT DEFINED TcAdsDll_ROOT) # Typical install locations on Windows set(_TcAdsDll_PATH "$ENV{SystemDrive}/TwinCAT/AdsApi/TcAdsDll") else () @@ -28,6 +33,7 @@ else () set(_TcAdsDll_PATH) endif () # Find the include headers + find_path(TcAdsDll_INCLUDE_DIR NAMES TcAdsApi.h TcAdsDef.h PATHS "${_TcAdsDll_PATH}" @@ -42,23 +48,23 @@ if (WIN32) message(STATUS "Looking for TcAdsDll in ROOT ${TcAdsDll_ROOT_DIR}") endif () if (CMAKE_SIZEOF_VOID_P EQUAL 8) - set(TcAdsDll_IMPLIB_DIR "${TcAdsDll_ROOT_DIR}/x64/lib") + set(TcAdsDll_IMPLIB_DIR "${TcAdsDll_ROOT_DIR}/Lib/x64") set(TcAdsDll_DLL_DIR "${TcAdsDll_ROOT_DIR}/x64") + set(TcAdsDll_COMMON_DIR "${TcAdsDll_ROOT_DIR}/../../Common64") elseif (CMAKE_SIZEOF_VOID_P EQUAL 4) set(TcAdsDll_IMPLIB_DIR "${TcAdsDll_ROOT_DIR}/Lib") set(TcAdsDll_DLL_DIR "${TcAdsDll_ROOT_DIR}") + set(TcAdsDll_COMMON_DIR "${TcAdsDll_ROOT_DIR}/../../Common32") endif () - # Use NO_DEFAULT_PATH so that we only look in the provided location. If not set - # the find_library will find the 32 bit version first regardless of the config - # which leads to errors. find_library(TcAdsDll_IMPLIB - NAMES TcAdsDll + NAMES TcAdsDll.lib PATHS "${TcAdsDll_IMPLIB_DIR}" NO_DEFAULT_PATH ) + message(STATUS ${TcAdsDll_IMPLIB}) find_file(TcAdsDll_LIBRARY NAMES TcAdsDll.dll - PATHS "${TcAdsDll_DLL_DIR}" + PATHS "${TcAdsDll_DLL_DIR}" "${TcAdsDll_COMMON_DIR}" NO_DEFAULT_PATH ) else () From 7b73a4b4f9ed5cb46f8913504acfb0f01d31b622 Mon Sep 17 00:00:00 2001 From: Andrey Vukolov Date: Thu, 30 Jul 2026 17:07:51 +0200 Subject: [PATCH 2/8] README update --- README.md | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/README.md b/README.md index cef41cf9..1fdac4f3 100644 --- a/README.md +++ b/README.md @@ -98,6 +98,14 @@ ninja -C example/build # and run the example ./example/build/example ``` +## On Windows with TwinCAT installed (PowerShell) +```ps1 +mkdir build && cd build +# Use TcAdsDll_ROOT if AdsApi is installed somewhere in a different location +# Here it is taken from the TWINCAT_ADS_API_ROOT environment variable +cmake -DTcAdsDll_ROOT="$TWINCAT_ADS_API_ROOT" .. +cmake --build . --config Release --parallel ((Get-CimInstance –ClassName Win32_Processor).NumberOfLogicalProcessors | Measure-Object -Sum).Sum +``` Note: the name of the binaries which use the TwinCAT ADS Router have a different naming scheme From b6029c2c23ffc6fd4fa01d9a73ac1438aff1fb51 Mon Sep 17 00:00:00 2001 From: Andrey Vukolov Date: Thu, 6 Aug 2026 17:19:31 +0200 Subject: [PATCH 3/8] Old/new TwinCAT version installation paths fix [@juarezr-mvtec] --- cmake/FindTcAdsDll.cmake | 23 +++++++++++++---------- 1 file changed, 13 insertions(+), 10 deletions(-) diff --git a/cmake/FindTcAdsDll.cmake b/cmake/FindTcAdsDll.cmake index 563b16ad..2dccdc38 100644 --- a/cmake/FindTcAdsDll.cmake +++ b/cmake/FindTcAdsDll.cmake @@ -21,13 +21,17 @@ if (NOT WIN32) endif () if (DEFINED TcAdsDll_ROOT) - message(STATUS "FindTcAdsDll.cmake: DLL lookup directory ${TcAdsDll_ROOT}") set(_TcAdsDll_PATH "${TcAdsDll_ROOT}") endif () if (WIN32 AND NOT DEFINED TcAdsDll_ROOT) # Typical install locations on Windows - set(_TcAdsDll_PATH "$ENV{SystemDrive}/TwinCAT/AdsApi/TcAdsDll") + set(_TcAdsDll_PATH_OLD + "$ENV{SystemDrive}/TwinCAT/AdsApi/TcAdsDll" # Old location (TwinCAT < 3.5) + ) + set(_TcAdsDll_PATH_NEW + "$ENV{ProgramFiles(x86)}/Beckhoff/TwinCAT/AdsApi/TcAdsDll" # New location (TwinCAT 3.5+) + ) else () # TODO: Linux not tested. Set additional known default locations to search. set(_TcAdsDll_PATH) @@ -36,7 +40,7 @@ endif () find_path(TcAdsDll_INCLUDE_DIR NAMES TcAdsApi.h TcAdsDef.h - PATHS "${_TcAdsDll_PATH}" + PATHS "${_TcAdsDll_PATH_OLD}" "${_TcAdsDll_PATH_NEW}" PATH_SUFFIXES "Include" "include" ) # Find all related files base on the include files location. This is done @@ -45,26 +49,25 @@ find_path(TcAdsDll_INCLUDE_DIR if (WIN32) cmake_path(GET TcAdsDll_INCLUDE_DIR PARENT_PATH TcAdsDll_ROOT_DIR) if (NOT TcAdsDll_FIND_QUIETLY) - message(STATUS "Looking for TcAdsDll in ROOT ${TcAdsDll_ROOT_DIR}") + message(STATUS "Searching TcAdsDll in ROOT ${TcAdsDll_ROOT_DIR}") endif () if (CMAKE_SIZEOF_VOID_P EQUAL 8) set(TcAdsDll_IMPLIB_DIR "${TcAdsDll_ROOT_DIR}/Lib/x64") - set(TcAdsDll_DLL_DIR "${TcAdsDll_ROOT_DIR}/x64") - set(TcAdsDll_COMMON_DIR "${TcAdsDll_ROOT_DIR}/../../Common64") + set(TcAdsDll_DLL_DIR_OLD "${TcAdsDll_ROOT_DIR}/x64") + set(TcAdsDll_DLL_DIR_NEW "${TcAdsDll_ROOT_DIR}/../../Common64") elseif (CMAKE_SIZEOF_VOID_P EQUAL 4) set(TcAdsDll_IMPLIB_DIR "${TcAdsDll_ROOT_DIR}/Lib") - set(TcAdsDll_DLL_DIR "${TcAdsDll_ROOT_DIR}") - set(TcAdsDll_COMMON_DIR "${TcAdsDll_ROOT_DIR}/../../Common32") + set(TcAdsDll_DLL_DIR_OLD "${TcAdsDll_ROOT_DIR}") + set(TcAdsDll_DLL_DIR_NEW "${TcAdsDll_ROOT_DIR}/../../Common32") endif () find_library(TcAdsDll_IMPLIB NAMES TcAdsDll.lib PATHS "${TcAdsDll_IMPLIB_DIR}" NO_DEFAULT_PATH ) - message(STATUS ${TcAdsDll_IMPLIB}) find_file(TcAdsDll_LIBRARY NAMES TcAdsDll.dll - PATHS "${TcAdsDll_DLL_DIR}" "${TcAdsDll_COMMON_DIR}" + PATHS "${TcAdsDll_DLL_DIR_OLD}" "${TcAdsDll_DLL_DIR_NEW}" NO_DEFAULT_PATH ) else () From a5049def19dc0ece9f56a583663d7a9b44fe6b42 Mon Sep 17 00:00:00 2001 From: Andrey Vukolov Date: Fri, 7 Aug 2026 10:51:37 +0200 Subject: [PATCH 4/8] CMake module introspection fix, useless comments removed --- cmake/FindTcAdsDll.cmake | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/cmake/FindTcAdsDll.cmake b/cmake/FindTcAdsDll.cmake index 2dccdc38..b84f5994 100644 --- a/cmake/FindTcAdsDll.cmake +++ b/cmake/FindTcAdsDll.cmake @@ -26,10 +26,8 @@ endif () if (WIN32 AND NOT DEFINED TcAdsDll_ROOT) # Typical install locations on Windows - set(_TcAdsDll_PATH_OLD + list(APPEND _TcAdsDll_PATH "$ENV{SystemDrive}/TwinCAT/AdsApi/TcAdsDll" # Old location (TwinCAT < 3.5) - ) - set(_TcAdsDll_PATH_NEW "$ENV{ProgramFiles(x86)}/Beckhoff/TwinCAT/AdsApi/TcAdsDll" # New location (TwinCAT 3.5+) ) else () @@ -53,21 +51,25 @@ if (WIN32) endif () if (CMAKE_SIZEOF_VOID_P EQUAL 8) set(TcAdsDll_IMPLIB_DIR "${TcAdsDll_ROOT_DIR}/Lib/x64") - set(TcAdsDll_DLL_DIR_OLD "${TcAdsDll_ROOT_DIR}/x64") - set(TcAdsDll_DLL_DIR_NEW "${TcAdsDll_ROOT_DIR}/../../Common64") + list(APPEND TcAdsDll_DLL_DIR + "${TcAdsDll_ROOT_DIR}/x64" + "${TcAdsDll_ROOT_DIR}/../../Common64" + ) elseif (CMAKE_SIZEOF_VOID_P EQUAL 4) set(TcAdsDll_IMPLIB_DIR "${TcAdsDll_ROOT_DIR}/Lib") - set(TcAdsDll_DLL_DIR_OLD "${TcAdsDll_ROOT_DIR}") - set(TcAdsDll_DLL_DIR_NEW "${TcAdsDll_ROOT_DIR}/../../Common32") + list(APPEND TcAdsDll_DLL_DIR + "${TcAdsDll_ROOT_DIR}" + "${TcAdsDll_ROOT_DIR}/../../Common32" + ) endif () find_library(TcAdsDll_IMPLIB NAMES TcAdsDll.lib - PATHS "${TcAdsDll_IMPLIB_DIR}" + PATHS ${TcAdsDll_IMPLIB_DIR} NO_DEFAULT_PATH ) find_file(TcAdsDll_LIBRARY NAMES TcAdsDll.dll - PATHS "${TcAdsDll_DLL_DIR_OLD}" "${TcAdsDll_DLL_DIR_NEW}" + PATHS ${TcAdsDll_DLL_DIR} NO_DEFAULT_PATH ) else () From 811005a879b6bd2dbffa768f2681a62a5dfa301f Mon Sep 17 00:00:00 2001 From: Andrey Vukolov Date: Fri, 7 Aug 2026 11:06:44 +0200 Subject: [PATCH 5/8] CMake module introspection fix, useless comments removed --- cmake/FindTcAdsDll.cmake | 2 -- 1 file changed, 2 deletions(-) diff --git a/cmake/FindTcAdsDll.cmake b/cmake/FindTcAdsDll.cmake index b84f5994..7eba61a2 100644 --- a/cmake/FindTcAdsDll.cmake +++ b/cmake/FindTcAdsDll.cmake @@ -31,7 +31,6 @@ if (WIN32 AND NOT DEFINED TcAdsDll_ROOT) "$ENV{ProgramFiles(x86)}/Beckhoff/TwinCAT/AdsApi/TcAdsDll" # New location (TwinCAT 3.5+) ) else () - # TODO: Linux not tested. Set additional known default locations to search. set(_TcAdsDll_PATH) endif () # Find the include headers @@ -73,7 +72,6 @@ if (WIN32) NO_DEFAULT_PATH ) else () - # TODO: Linux not tested. We just try to look for the library by name. find_library(TcAdsDll_LIBRARY NAMES TcAdsDll ) From b33ed9092d2555cd9a5b27b6af1e3cc2f38679b7 Mon Sep 17 00:00:00 2001 From: Andrey Vukolov Date: Fri, 7 Aug 2026 12:53:42 +0200 Subject: [PATCH 6/8] CMake module introspection fix (tests by @juarezr-mvtec) --- cmake/FindTcAdsDll.cmake | 24 ++++++++++++++++++------ 1 file changed, 18 insertions(+), 6 deletions(-) diff --git a/cmake/FindTcAdsDll.cmake b/cmake/FindTcAdsDll.cmake index 7eba61a2..81147ae3 100644 --- a/cmake/FindTcAdsDll.cmake +++ b/cmake/FindTcAdsDll.cmake @@ -37,7 +37,7 @@ endif () find_path(TcAdsDll_INCLUDE_DIR NAMES TcAdsApi.h TcAdsDef.h - PATHS "${_TcAdsDll_PATH_OLD}" "${_TcAdsDll_PATH_NEW}" + PATHS "${_TcAdsDll_PATH}" "${_TcAdsDll_PATH}" PATH_SUFFIXES "Include" "include" ) # Find all related files base on the include files location. This is done @@ -49,13 +49,19 @@ if (WIN32) message(STATUS "Searching TcAdsDll in ROOT ${TcAdsDll_ROOT_DIR}") endif () if (CMAKE_SIZEOF_VOID_P EQUAL 8) - set(TcAdsDll_IMPLIB_DIR "${TcAdsDll_ROOT_DIR}/Lib/x64") + list(APPEND TcAdsDll_IMPLIB_DIR + "${TcAdsDll_ROOT_DIR}/x64/lib" # Old location (TwinCAT < 3.5) + "${TcAdsDll_ROOT_DIR}/Lib/x64" # New location (TwinCAT 3.5+) + ) list(APPEND TcAdsDll_DLL_DIR "${TcAdsDll_ROOT_DIR}/x64" "${TcAdsDll_ROOT_DIR}/../../Common64" ) elseif (CMAKE_SIZEOF_VOID_P EQUAL 4) - set(TcAdsDll_IMPLIB_DIR "${TcAdsDll_ROOT_DIR}/Lib") + list(APPEND TcAdsDll_IMPLIB_DIR + "${TcAdsDll_ROOT_DIR}/lib" # Old location (TwinCAT < 3.5) + "${TcAdsDll_ROOT_DIR}/Lib" # New location (TwinCAT 3.5+) + ) list(APPEND TcAdsDll_DLL_DIR "${TcAdsDll_ROOT_DIR}" "${TcAdsDll_ROOT_DIR}/../../Common32" @@ -78,11 +84,17 @@ else () endif () -include(FindPackageHandleStandardArgs) -find_package_handle_standard_args(TcAdsDll +if (WIN32) + find_package_handle_standard_args(TcAdsDll + REQUIRED_VARS TcAdsDll_INCLUDE_DIR TcAdsDll_LIBRARY TcAdsDll_IMPLIB + VERSION_VAR TcAdsDll_VERSION + ) +else () + find_package_handle_standard_args(TcAdsDll REQUIRED_VARS TcAdsDll_INCLUDE_DIR TcAdsDll_LIBRARY VERSION_VAR TcAdsDll_VERSION -) + ) +endif () if (NOT TcAdsDll_FOUND) if (NOT TcAdsDll_FIND_QUIETLY) From 9dca3b5355d2f91eab14dd303282fe149f739816 Mon Sep 17 00:00:00 2001 From: Andrey Vukolov Date: Fri, 7 Aug 2026 13:00:39 +0200 Subject: [PATCH 7/8] Duplication fix --- cmake/FindTcAdsDll.cmake | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmake/FindTcAdsDll.cmake b/cmake/FindTcAdsDll.cmake index 81147ae3..298049d4 100644 --- a/cmake/FindTcAdsDll.cmake +++ b/cmake/FindTcAdsDll.cmake @@ -37,7 +37,7 @@ endif () find_path(TcAdsDll_INCLUDE_DIR NAMES TcAdsApi.h TcAdsDef.h - PATHS "${_TcAdsDll_PATH}" "${_TcAdsDll_PATH}" + PATHS "${_TcAdsDll_PATH}" PATH_SUFFIXES "Include" "include" ) # Find all related files base on the include files location. This is done From 78caf910e476a06eaa7841e40d948cec0bcc337b Mon Sep 17 00:00:00 2001 From: Andrey Vukolov Date: Fri, 7 Aug 2026 14:42:57 +0200 Subject: [PATCH 8/8] Path resolution fixes --- cmake/FindTcAdsDll.cmake | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/cmake/FindTcAdsDll.cmake b/cmake/FindTcAdsDll.cmake index 298049d4..c6e120eb 100644 --- a/cmake/FindTcAdsDll.cmake +++ b/cmake/FindTcAdsDll.cmake @@ -28,7 +28,7 @@ if (WIN32 AND NOT DEFINED TcAdsDll_ROOT) # Typical install locations on Windows list(APPEND _TcAdsDll_PATH "$ENV{SystemDrive}/TwinCAT/AdsApi/TcAdsDll" # Old location (TwinCAT < 3.5) - "$ENV{ProgramFiles(x86)}/Beckhoff/TwinCAT/AdsApi/TcAdsDll" # New location (TwinCAT 3.5+) + "$ENV{ProgramFiles\(x86\)}/Beckhoff/TwinCAT/AdsApi/TcAdsDll" # New location (TwinCAT 3.5+) ) else () set(_TcAdsDll_PATH) @@ -37,7 +37,7 @@ endif () find_path(TcAdsDll_INCLUDE_DIR NAMES TcAdsApi.h TcAdsDef.h - PATHS "${_TcAdsDll_PATH}" + PATHS ${_TcAdsDll_PATH} PATH_SUFFIXES "Include" "include" ) # Find all related files base on the include files location. This is done @@ -83,6 +83,7 @@ else () ) endif () +include(FindPackageHandleStandardArgs) if (WIN32) find_package_handle_standard_args(TcAdsDll