YY-Thunks icon indicating copy to clipboard operation
YY-Thunks copied to clipboard

分享一种可以在 CMake+MSVC 中使用 yy-thinks 的方法 | Share an approach to link yy-thunks in CMake+MSVC

Open BH2WFR opened this issue 1 year ago • 6 comments

以下代码可以让项目正常链接到 yy-thunks 的 .obj 库: the code below can let the CMake project correctly link yy-thunks .obj libraries.

set(CMAKE_MSVC_RUNTIME_LIBRARY "MultiThreaded$<$<CONFIG:Debug>:Debug>" CACHE STRING "" FORCE) 	   # /MTd in Debug, /MT in Release
# set(CMAKE_MSVC_RUNTIME_LIBRARY "MultiThreaded$<$<CONFIG:Debug>:DebugDLL>" CACHE STRING "" FORCE) # /MDd in Debug, /MT in Release
# set(CMAKE_MSVC_RUNTIME_LIBRARY "MultiThreaded$<$<CONFIG:Debug>:Debug>DLL" CACHE STRING "" FORCE) # /MDd in Debug, /MD in Release (默认)



#* yy-thunks (MSVC only)
set(YY_THUNKS_DIR "D:/3rdlibs/YY-Thunks")	#* yy-thunks 根目录
set(YY_THUNKS_WIN_VERSION "WinXP")	#* 用于匹配文件名, 可用值: `WinXP` `Vista` `Win7` `Win8` `Win10.0.10240` `Win10.0.19041`, 用于匹配文件名
set(YY_THUNKS_WIN_VER_STR "5.1")	#* Windows 子系统版本, 5.1:XP, 5.2:2003, 6.0:Vista, 6.1:Win7, 6.2:Win8, 6.3:Win8.1, 10.0:Win10/11, 留空则默认
set(YY_THUNKS_ARCH	"x64")
if(CMAKE_SIZEOF_VOID_P EQUAL 4) # 32 位
	set(YY_THUNKS_ARCH "x86")
endif()
if(CMAKE_CXX_COMPILER_ID MATCHES "MSVC") #* MSVC, 抢在系统库 lib 前挂入 yy-thunks 的 obj 文件
	set(YY_THUNKS_OBJ_NAME "${YY_THUNKS_DIR}/objs/${YY_THUNKS_ARCH}/YY_Thunks_for_${YY_THUNKS_WIN_VERSION}.obj") # 查找 obj 文件
	set(CMAKE_CXX_STANDARD_LIBRARIES "\"${YY_THUNKS_OBJ_NAME}\" ${CMAKE_CXX_STANDARD_LIBRARIES}")
	set(CMAKE_C_STANDARD_LIBRARIES   "\"${YY_THUNKS_OBJ_NAME}\" ${CMAKE_C_STANDARD_LIBRARIES}")
	if(YY_THUNKS_WIN_VER_STR)
		add_link_options("-SUBSYSTEM:$<IF:$<BOOL:${CMAKE_WIN32_EXECUTABLE}>,WINDOWS,CONSOLE>,${YY_THUNKS_WIN_VER_STR}")
	endif()
else()
	message(WARNING "yy-thunks obj files only support MSVC linker, this operation will be ignored...")
endif()

. .

使用方法: 在上述代码中分别设置 YY_THUNKS_DIR YY_THUNKS_WIN_VERSION YY_THUNKS_WIN_VER_STR 三个变量, 指定路径和目标系统版本 . Usage: set variables YY_THUNKS_DIR YY_THUNKS_WIN_VERSION YY_THUNKS_WIN_VER_STR to your path and target system version

. . 如需运行在 Windows XP 中, 需要在所有 DLL 项目中额外调用: If you need to run in Windows XP, the code below is needed in each DLL target. target_link_options(${PROJECT_NAME} PRIVATE "/ENTRY:DllMainCRTStartupForYY_Thunks")

BH2WFR avatar Jun 25 '24 09:06 BH2WFR

请问是否可以考虑提交PR,给yY-Thunks提供官方cmake支持。

mingkuang-Chuyu avatar Jun 25 '24 14:06 mingkuang-Chuyu

请问是否可以考虑提交PR,给yY-Thunks提供官方cmake支持。

我交了一个PR,把这个写到 Readme.md 里面了

BH2WFR avatar Jun 26 '24 07:06 BH2WFR

vc-ltl 需要设置下 set(WindowsTargetPlatformMinVersion "5.1.2600.0")

yuejiyueren avatar Dec 09 '24 09:12 yuejiyueren

更新一下: 目前这个版本仍然有很多漏洞, 也不支持 clang, 看看有没有大佬愿意补充: 最好给它做成一个 import target,

#* 查找 YY-Thunks 并链接, 暂仅支持 MSVC
# _yy_thunks_dir 为 yy-thunks 根路径
# _winver 为 Windows 版本, 如 "10.0.19041.0"
#		(MSVC) `WinXP` `Vista` `Win7` `Win8` `Win10.0.10240`(Windows 10) `Win10.0.19041`(Windows 11)
#		(Clang) `5.1.2195.0` `5.1.2600.0` `5.2.3790.1180` `6.0.6000.0` `6.1.7600.0` `6.2.9200.0` `10.0.10240.0` `10.0.19041.0`
# _subsystem_ver 为子系统版本, 如 "5.01", 用于生成 链接时的最低子系统要求, 可以置空, 则不改变子系统版本
#		需要先定义 `CMAKE_WIN32_EXECUTABLE` 变量, 用于判断是否为 GUI 程序
# 		可用值: 5.0:2000; 5.1:XP_32bit; 5.2:XP_64bit/2003; 6.0:Vista/2008; 6.1:Win7/2008R2; 6.2:Win8/2012; 6.3:Win8.1/2012R2;
#		10.0:Win10/11/2016..2025
function(PROJ_FIND_YY_THUNKS   _yy_thunks_dir _winver _subsystem_ver)
	if(NOT WIN32) # 仅支持 Windows
		message(WARNING "YY-Thunks is only supported on Windows.")
		return()
	endif()
	if(NOT CMAKE_CXX_COMPILER_ID STREQUAL "MSVC") # 仅支持 MSVC
		message(WARNING "YY-Thunks is only supported in MSVC compiler, but now is ${CMAKE_CXX_COMPILER_ID} compiler.")
		return()
	endif()
	if(NOT CMAKE_SYSTEM_PROCESSOR MATCHES "(x86(_64)?)|(AMD64)|(i[3-6]86)") # 仅支持 x86 和 x86_64 架构
		message(WARNING "YY-Thunks is only supported on x86 and x86_64 architectures.")
		return()
	endif()
	#if(NOT CMAKE_BUILD_TYPE MATCHES "Release") # 仅支持 Release 模式
	#	message(WARNING "YY-Thunks is only supported in Release mode.")
	#	return()
	#endif()

	
	set(YY_THUNKS_ARCH	"x64")
	if(CMAKE_SIZEOF_VOID_P EQUAL 4) # 32 位
		set(YY_THUNKS_ARCH "x86")
	endif()
	get_filename_component(_yy_thunks_dir "${_yy_thunks_dir}" ABSOLUTE)
	
	set(PROJ_YY_THUNKS_FOUND FALSE PARENT_SCOPE) # 是否找到
	message("\n-> YY_THUNKS: ===============")
	#* MSVC, 抢在系统库 lib 前挂入 yy-thunks 的 obj 文件
	get_filename_component(YY_THUNKS_OBJ_PATH "${YY_THUNKS_Root}/objs/${YY_THUNKS_ARCH}/YY_Thunks_for_${_winver}.obj" ABSOLUTE)
	if(EXISTS ${YY_THUNKS_OBJ_PATH})
		set(PROJ_YY_THUNKS_FOUND TRUE PARENT_SCOPE)
		# link_libraries("${YY_THUNKS_OBJ_PATH}")
		set(CMAKE_CXX_STANDARD_LIBRARIES "\"${YY_THUNKS_OBJ_PATH}\" ${CMAKE_CXX_STANDARD_LIBRARIES}")
		set(CMAKE_C_STANDARD_LIBRARIES   "\"${YY_THUNKS_OBJ_PATH}\" ${CMAKE_C_STANDARD_LIBRARIES}")
		if(_subsystem_ver)
			add_link_options("-SUBSYSTEM:$<IF:$<BOOL:${CMAKE_WIN32_EXECUTABLE}>,WINDOWS,CONSOLE>,${_subsystem_ver}")
			# set(CMAKE_CREATE_WIN32_EXE   "${CMAKE_CREATE_WIN32_EXE} -subsystem:windows,${_subsystem_ver}" PARENT_SCOPE) # 这个是 VC_LTL 用到的方式, 但是我不知道是否能工作
			# set(CMAKE_CREATE_CONSOLE_EXE "${CMAKE_CREATE_CONSOLE_EXE} -subsystem:console,${_subsystem_ver}" PARENT_SCOPE)
		endif()
	else() # cannot find obj file
		message(WARNING "YY-Thunks cannot find the file `${YY_THUNKS_OBJ_PATH}`!")
	endif()
	message("    YY-Thunks found: ${PROJ_YY_THUNKS_FOUND}")
	message("    lib path: `${YY_THUNKS_OBJ_PATH}`")
	message("    subsystem version: `${_subsystem_ver}`")
	message("    CMAKE_WIN32_EXECUTABLE: `${CMAKE_WIN32_EXECUTABLE}`")
	message("")
endfunction()




#* Windows xp 中, 需要更改 DLL 入口点以可以在 WinXP 中使用 thread_local, 仅限动态链接库!!
function(PROJ_YY_THUNKS_WINXP_DLL _targetName)
	if(NOT WIN32) # 仅支持 Windows
		message(WARNING "YY-Thunks is only supported on Windows.")
		return()
	endif()
	if(NOT CMAKE_CXX_COMPILER_ID STREQUAL "MSVC") # 仅支持 MSVC
		message(WARNING "YY-Thunks is only supported in MSVC compiler, but now is ${CMAKE_CXX_COMPILER_ID} compiler.")
		return()
	endif()
	if(NOT CMAKE_SYSTEM_PROCESSOR MATCHES "(x86(_64)?)|(AMD64)|(i[3-6]86)") # 仅支持 x86 和 x86_64 架构
		message(WARNING "YY-Thunks is only supported on x86 and x86_64 architectures.")
		return()
	endif()
	#if(NOT CMAKE_BUILD_TYPE MATCHES "Release") # 仅支持 Release 模式
	#	message(WARNING "YY-Thunks is only supported in Release mode.")
	#	return()
	#endif()
	if(NOT PROJ_YY_THUNKS_FOUND)
		message(WARNING "YY-Thunks not found, please call `PROJ_FIND_YY_THUNKS` first.")
		return()
	endif()
	target_link_options(${_targetName} PRIVATE "/ENTRY:DllMainCRTStartupForYY_Thunks")
endfunction()

BH2WFR avatar Dec 31 '24 07:12 BH2WFR

其实可以吧,可以使用nuget,cmake是支持的,唯一要求就是生成器需要使用VS(msbuild)。

mingkuang-Chuyu avatar Jan 15 '25 12:01 mingkuang-Chuyu

here's what i use (.lib variant)

YYThunksHelper.cmake

# Adapted from https://github.com/Chuyu-Team/VC-LTL5/blob/01004dfc9d575e72f3c027a2da51aba9328cdd27/config/config.cmake

cmake_minimum_required(VERSION 3.13)

if(NOT SupportWinXP)
    set(SupportWinXP "false")
endif()

if(NOT SupportWin2K)
    set(SupportWin2K "false")
endif()

if(NOT SupportYY)
    set(InternalSupportYY "true")
else()
    set(InternalSupportYY ${SupportYY})
endif()

if(${InternalSupportYY} STREQUAL "true")
    if(NOT CMAKE_SYSTEM_NAME)
        message(WARNING "YY-Thunks not load, because CMAKE_SYSTEM_NAME is not defined!!!")
        set(InternalSupportYY "false")
    elseif(NOT ${CMAKE_SYSTEM_NAME} STREQUAL "Windows")
        message(WARNING "YY-Thunks not load, because ${CMAKE_SYSTEM_NAME} is not supported!!!")
        set(InternalSupportYY "false")
    endif()
endif()

if(${InternalSupportYY} STREQUAL "true")
    if(YY_Thunks_Platform)
        #外部已经定义
    elseif(CMAKE_GENERATOR_PLATFORM)
        # -A 参数已经传递,仅在 CMake 3.1更高版本中支持
        message("CMAKE_GENERATOR_PLATFORM = " ${CMAKE_GENERATOR_PLATFORM})
        string(TOLOWER "${CMAKE_GENERATOR_PLATFORM}" CMAKE_GENERATOR_PLATFORM_LOWER)
        if(${CMAKE_GENERATOR_PLATFORM_LOWER} STREQUAL "win32")
            set(YY_Thunks_Platform "x86")
        elseif(${CMAKE_GENERATOR_PLATFORM_LOWER} STREQUAL "x64")
            set(YY_Thunks_Platform "x64")
        else()
            message(WARNING "YY-Thunks not load, Unknown Platform!!!")
            set(InternalSupportYY "false")
        endif()
    elseif(CMAKE_VS_PLATFORM_NAME)
        # CMake 3.1以及更早版本不支持 -A 参数,因此通过 CMAKE_VS_PLATFORM_NAME解决
        message("CMAKE_VS_PLATFORM_NAME = " ${CMAKE_VS_PLATFORM_NAME})
        string(TOLOWER "${CMAKE_VS_PLATFORM_NAME}" CMAKE_VS_PLATFORM_NAME_LOWER)
        if(${CMAKE_VS_PLATFORM_NAME_LOWER} STREQUAL "win32")
            set(YY_Thunks_Platform "x86")
        elseif(${CMAKE_VS_PLATFORM_NAME_LOWER} STREQUAL "x64")
            set(YY_Thunks_Platform "x64")
        else()
            message(WARNING "YY-Thunks not load, Unknown Platform!!!")
            set(InternalSupportYY "false")
        endif()
    elseif(MSVC_VERSION)
        message("load CheckSymbolExists......")

        include(CheckSymbolExists)

        check_symbol_exists("_M_IX86" "" _M_IX86)
        check_symbol_exists("_M_AMD64" "" _M_AMD64)

        if(_M_AMD64)
            set(YY_Thunks_Platform "x64")
        elseif(_M_IX86)
            set(YY_Thunks_Platform "x86")
        else()
            message(WARNING "YY-Thunks not load, Unknown Platform!!!")
            set(InternalSupportYY "false")
        endif()
    elseif(VCPKG_TARGET_ARCHITECTURE)
        #为了兼容VCPKG
        message("VCPKG_TARGET_ARCHITECTURE = " ${VCPKG_TARGET_ARCHITECTURE})
        string(TOLOWER "${VCPKG_TARGET_ARCHITECTURE}" VCPKG_TARGET_ARCHITECTURE_LOWER)
        if(${VCPKG_TARGET_ARCHITECTURE_LOWER} STREQUAL "x86")
            set(YY_Thunks_Platform "x86")
        elseif(${VCPKG_TARGET_ARCHITECTURE_LOWER} STREQUAL "x64")
            set(YY_Thunks_Platform "x64")
        else()
            message(WARNING "YY-Thunks not load, Unknown Platform!!!")
            set(InternalSupportYY "false")
        endif()
    elseif(DEFINED ENV{VSCMD_ARG_TGT_ARCH})
        #VSCMD_ARG_TGT_ARCH参数只有2017才有,因此兼容性更差
        message("VSCMD_ARG_TGT_ARCH = " $ENV{VSCMD_ARG_TGT_ARCH})
        string(TOLOWER "$ENV{VSCMD_ARG_TGT_ARCH}" VSCMD_ARG_TGT_ARCH_LOWER)
        if(${VSCMD_ARG_TGT_ARCH_LOWER} STREQUAL "x86")
            set(YY_Thunks_Platform "x86")
        elseif(${VSCMD_ARG_TGT_ARCH_LOWER} STREQUAL "x64")
            set(YY_Thunks_Platform "x64")
        else()
            message(WARNING "YY-Thunks not load, Unknown Platform!!!")
            set(InternalSupportYY "false")
        endif()
    elseif(DEFINED ENV{LIB})
        #采用更加奇葩发方式,检测lib目录是否包含特定后缀
        message("LIB = $ENV{LIB}")

        string(TOLOWER "$ENV{LIB}" YY_LIB_TMP)

        if("${YY_LIB_TMP}" MATCHES "\\x64;")
            set(YY_Thunks_Platform "x64")
        elseif("${YY_LIB_TMP}" MATCHES "\\x86;")
            set(YY_Thunks_Platform "x86")
        elseif("${YY_LIB_TMP}" MATCHES "\\lib;")
            #为了兼容VS 2015
            set(YY_Thunks_Platform "x86")
        else()
            message(WARNING "YY-Thunks not load, Unknown Platform!!!")
            set(InternalSupportYY "false")
        endif()
    else()
        message(WARNING "YY-Thunks not load, Unknown Platform!!!")
        set(InternalSupportYY "false")
    endif()
endif()


if(NOT ${InternalSupportYY} STREQUAL "false")
    #获取最佳TargetPlatform,一般默认值是 6.0.6000.0
    if(WindowsTargetPlatformMinVersion)
        # 故意不用 VERSION_GREATER_EQUAL,因为它在 3.7 才得到支持
        if(NOT ${WindowsTargetPlatformMinVersion} VERSION_LESS 10.0.19041.0)
            set(InternalYYVersion "10.0.19041.0")
        elseif(NOT ${WindowsTargetPlatformMinVersion} VERSION_LESS 10.0.10240.0)
            set(InternalYYVersion "10.0.10240.0")
        elseif(NOT ${WindowsTargetPlatformMinVersion} VERSION_LESS 6.2.9200.0)
            set(InternalYYVersion "6.2.9200.0")
        elseif(NOT ${WindowsTargetPlatformMinVersion} VERSION_LESS 6.1.7600.0)
            set(InternalYYVersion "6.1.7600.0")
        elseif(NOT ${WindowsTargetPlatformMinVersion} VERSION_LESS 6.0.6000.0)
            set(InternalYYVersion "6.0.6000.0")
        elseif(${YY_Thunks_Platform} STREQUAL "x64")
            set(InternalYYVersion "5.2.3790.1830")
        elseif(NOT ${WindowsTargetPlatformMinVersion} VERSION_LESS 5.1.2600.0)
            set(InternalYYVersion "5.1.2600.0")
        else()
            set(InternalYYVersion "5.0.2195.0")
        endif()
    else()
        #默认值
        if(NOT ${SupportWinXP} STREQUAL "true" AND NOT ${SupportWin2K} STREQUAL "true")
            set(InternalYYVersion "6.0.6000.0")
        elseif(${YY_Thunks_Platform} STREQUAL "x64")
            set(InternalYYVersion "5.2.3790.1830")
        elseif(${SupportWin2K} STREQUAL "true")
            set(InternalYYVersion "5.0.2195.0")
        else()
            set(InternalYYVersion "5.1.2600.0")
        endif()
    endif()

    set(YY_Thunks_Dir ${YY_Thunks_Root}/Lib/${InternalYYVersion}/${YY_Thunks_Platform})
    set(YY_Thunks_Obj ${YY_Thunks_Dir}/YY_Thunks_for_${InternalYYVersion}.obj)

    if (NOT EXISTS ${YY_Thunks_Obj})
        unset(YY_Thunks_Dir)
        unset(YY_Thunks_Obj)
        message(FATAL_ERROR "YY-Thunks can't find lib files, please download the binary files from https://github.com/Chuyu-Team/YY-Thunks/releases/latest then continue.")
    endif()

    if(${YY_Thunks_Platform} STREQUAL "x64")
        set(YY_Thunks_LinkerSubsystemMinVersion "5.02")
    else()
        set(YY_Thunks_LinkerSubsystemMinVersion "5.01")
    endif()

	set(YY_Thunks_MinVersion ${InternalYYVersion})

    file(GLOB YY_Thunks_Libs "${YY_Thunks_Dir}/*.lib")

    message("--------------------------------------------------------------------------------")
    message("|                                  YY-Thunks                                   |")
    message("--------------------------------------------------------------------------------")
    message("")
    message(" YY-Thunks Path          :" ${YY_Thunks_Root})
    message(" YY-Thunks Tools Version :" $ENV{VCToolsVersion})
    message(" Min Subsystem Version   :" ${YY_Thunks_LinkerSubsystemMinVersion})
    message(" Version                 :" ${YY_Thunks_MinVersion})
    message(" Platform                :" ${YY_Thunks_Platform})

    if(YY_Thunks_SetStandardLibraries)
        link_directories(${YY_Thunks_Dir})
        string(REPLACE " " ";" standard_libs "${CMAKE_CXX_STANDARD_LIBRARIES} ${CMAKE_C_STANDARD_LIBRARIES}")
        list(TRANSFORM standard_libs TOLOWER)
        list(REMOVE_DUPLICATES standard_libs)
        foreach(lib ${YY_Thunks_Libs})
            get_filename_component(lib "${lib}" NAME)
            string(TOLOWER "${lib}" lib)
            if(lib IN_LIST standard_libs)
                message(" WinAPI proxied          :" ${lib})
            endif()
        endforeach()
    endif()

    message("")
endif()

CMakeLists.txt

...
set(YY_Thunks_Root "path/to/extracted/YY-Thunks-Lib/")
set(YY_Thunks_SetStandardLibraries True)
include(path/to/YYThunksHelper.cmake)
...
...
# modify targets as needed, for example (shared library):
target_link_options(target PRIVATE "/ENTRY:DllMainCRTStartupForYY_Thunks")
target_link_options(target PRIVATE "/SUBSYSTEM:WINDOWS,${YY_Thunks_LinkerSubsystemMinVersion}")
...

illnyang avatar May 24 '25 14:05 illnyang