Win10+VS2012下OpenCL代码编译链接错误求助及配置指导
硬件与软件环境
- 硬件:Intel Core I7 6700、32GB内存、NVIDIA GT1030
- 软件:Windows 10 64位、Visual Studio 2012专业版
问题现象
编译《OpenCL Parallel Programming Development Cookbook》中的代码时,出现以下LNK2001未解析外部符号错误:
Error LNK2001: unresolved external symbol _clGetDeviceInfo@20
Error LNK2001: unresolved external symbol _clGetPlatformIDs@12
Error LNK2001: unresolved external symbol _clGetPlatformInfo@20
Error LNK2001: unresolved external symbol _clGetDeviceIDs@24
相关代码如下:
#define CL_TARGET_OPENCL_VERSION 120 #include "windows.h" #include <stdio.h> #include <stdlib.h> #include "CL/cl.h" #include "malloc.h" #include "resconsts.h" #include <iostream> // cout #include <fstream> // ifstream in ("test.txt") using namespace std; // Без этой строки не работает "static ofstream fLog()" static ofstream fout ("logOCL_MAIN_cpp.txt"); typedef unsigned char Byte; void displayPlatformInfo(cl_platform_id id, cl_platform_info param_name, const char* paramNameAsStr) { cl_int error = 0; size_t paramSize = 0; clGetPlatformInfo(id, param_name, 0, NULL, ¶mSize); error = clGetPlatformInfo(id, param_name, 0, NULL, ¶mSize); char* moreInfo = (char*) alloca(sizeof(char)*paramSize); error = clGetPlatformInfo(id, param_name, paramSize, moreInfo, NULL); if (error != CL_SUCCESS) fout << "Unable to find any OpenCL Platform information"; fout << "paramNameAsStr = " << paramNameAsStr << endl << "moreInfo = " << moreInfo << endl; } void displayDeviceDetails(cl_device_id id, cl_device_info param_name, const char* paramNameAsStr) { cl_int error = 0; size_t paramSize = 0; error = clGetDeviceInfo(id, param_name, 0, NULL, ¶mSize); if (error != CL_SUCCESS) { fout << "Unable to obtain device info for param" << endl; return; } // The cl_device_info are preprocessor directives defined in cl.h switch (param_name) { case CL_DEVICE_TYPE: { cl_device_type *devType = (cl_device_type*)alloca(sizeof(cl_device_type)* paramSize); error = clGetDeviceInfo(id, param_name, paramSize, devType, NULL); if (error != CL_SUCCESS) { fout << "Unable to obtain device info for param" << endl; return; } switch (*devType) { case CL_DEVICE_TYPE_CPU: { fout << "CPU detected" << endl; break; } case CL_DEVICE_TYPE_GPU: { fout << "GPU detected" << endl; break; } case CL_DEVICE_TYPE_DEFAULT: { fout << "default detected" << endl; break; } } break; } } } void displayDeviceInfo(cl_platform_id id, cl_device_type dev_type) { cl_int error = 0; cl_uint numOfDevices = 0; // Determine how many devices are connected to your platform. error = clGetDeviceIDs(id, dev_type, 0, NULL, &numOfDevices); if (error != CL_SUCCESS) { fout << "Unable to obtain any OpenCL compliant device info" << endl; exit(1); } cl_device_id *devices = (cl_device_id*)alloca(sizeof(cl_device_id)*numOfDevices); // Load the information about devices into the variable "devices". error = clGetDeviceIDs(id, dev_type, numOfDevices, devices, NULL); if (error != CL_SUCCESS) { fout << "Unable to obtain any OpenCL compliant device info" << endl; exit(1); } fout << "Number of detected OpenCL devices = " << numOfDevices << endl; // We attempt to retrieve some informaion about the devices. for (int i = 0; i < numOfDevices; ++i) { displayDeviceDetails(devices[i], CL_DEVICE_TYPE, "CL_DEVICE_TYPE"); displayDeviceDetails(devices[i], CL_DEVICE_VENDOR_ID, "CL_DEVICE_VENDOR_ID"); displayDeviceDetails(devices[i], CL_DEVICE_MAX_COMPUTE_UNITS, "CL_DEVICE_MAX_COMPUTE_UNITS"); displayDeviceDetails(devices[i], CL_DEVICE_MAX_WORK_ITEM_DIMENSIONS, "CL_DEVICE_MAX_WORK_ITEM_DIMENSIONS"); displayDeviceDetails(devices[i], CL_DEVICE_MAX_WORK_ITEM_SIZES, "CL_DEVICE_MAX_WORK_ITEM_SIZES"); displayDeviceDetails(devices[i], CL_DEVICE_MAX_WORK_GROUP_SIZE, "CL_DEVICE_MAX_WORK_GROUP_SIZE"); } } int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) { cl_platform_id *platforms; cl_uint numOfPlatforms; cl_int error; error = clGetPlatformIDs(0, NULL, &numOfPlatforms); if (error < 0) { cout << "Unable to find any OpenCL platforms" << endl; exit(1); } platforms = (cl_platform_id*) alloca (sizeof(cl_platform_id)*numOfPlatforms); cout << "Number of OpenCL platforms found: " << numOfPlatforms << endl; for (cl_uint i = 0; i < numOfPlatforms; ++i) { displayPlatformInfo(platforms[i], CL_PLATFORM_PROFILE, "CL_PLATFORM_PROFILE"); displayPlatformInfo(platforms[i], CL_PLATFORM_VERSION, "CL_PLATFORM_VERSION"); displayPlatformInfo(platforms[i], CL_PLATFORM_NAME, "CL_PLATFORM_NAME"); displayPlatformInfo(platforms[i], CL_PLATFORM_VENDOR, "CL_PLATFORM_VENDOR"); displayPlatformInfo(platforms[i], CL_PLATFORM_EXTENSIONS, "CL_PLATFORM_EXTENSIONS"); } return 0; }
核心原因
LNK2001错误是因为项目未正确链接OpenCL库文件,编译器能找到头文件但链接器找不到函数的实现。
Visual Studio 2012详细配置步骤
步骤1:安装兼容的OpenCL SDK
VS2012仅支持CUDA 8.0及更早版本(CUDA 9.0+不再支持VS2012),推荐安装NVIDIA CUDA Toolkit 8.0(适配NVIDIA GT1030),默认安装路径为C:\Program Files\NVIDIA GPU Computing Toolkit\CUDA\v8.0。
步骤2:配置项目包含目录
- 右键项目 → 属性 → 配置属性 → VC++目录 → 包含目录
- 添加OpenCL头文件路径:
C:\Program Files\NVIDIA GPU Computing Toolkit\CUDA\v8.0\include
(若使用Khronos SDK,替换为SDK的include文件夹路径)
步骤3:配置项目库目录
- 同样在VC++目录 → 库目录
- 添加OpenCL库文件路径:
C:\Program Files\NVIDIA GPU Computing Toolkit\CUDA\v8.0\lib\x64(64位项目)或C:\Program Files\NVIDIA GPU Computing Toolkit\CUDA\v8.0\lib\Win32(32位项目)
步骤4:配置链接器依赖
- 配置属性 → 链接器 → 输入 → 附加依赖项
- 添加
OpenCL.lib(无需写完整路径,链接器会从库目录查找)
可选:代码中直接指定链接库
在代码开头添加一行:
#pragma comment(lib, "OpenCL.lib")
这样无需手动配置附加依赖项,适合快速测试。
代码修正建议
原代码存在两处逻辑错误,需修正以保证运行正常:
- WinMain中未填充平台数组:获取平台数量后,需调用
clGetPlatformIDs填充platforms数组,否则会访问非法内存。修改后的WinMain片段:
platforms = (cl_platform_id*) alloca (sizeof(cl_platform_id)*numOfPlatforms); // 新增:获取平台列表 error = clGetPlatformIDs(numOfPlatforms, platforms, NULL); if (error != CL_SUCCESS) { cout << "Failed to get OpenCL platforms" << endl; exit(1); } cout << "Number of OpenCL platforms found: " << numOfPlatforms << endl;
- displayDeviceDetails未处理非CL_DEVICE_TYPE参数:原代码仅处理设备类型,其他参数(如厂商ID、计算单元数)未输出,补充处理逻辑:
void displayDeviceDetails(cl_device_id id, cl_device_info param_name, const char* paramNameAsStr) { cl_int error = 0; size_t paramSize = 0; error = clGetDeviceInfo(id, param_name, 0, NULL, ¶mSize); if (error != CL_SUCCESS) { fout << "Unable to obtain device info for param" << endl; return; } fout << paramNameAsStr << ": "; // 处理不同类型的设备信息 switch (param_name) { case CL_DEVICE_TYPE: { cl_device_type *devType = (cl_device_type*)alloca(sizeof(cl_device_type)*paramSize); error = clGetDeviceInfo(id, param_name, paramSize, devType, NULL); if (error != CL_SUCCESS) { fout << "Unable to obtain device info" << endl; return; } switch (*devType) { case CL_DEVICE_TYPE_CPU: fout << "CPU"; break; case CL_DEVICE_TYPE_GPU: fout << "GPU"; break; case CL_DEVICE_TYPE_DEFAULT: fout << "Default"; break; default: fout << "Unknown"; break; } break; } case CL_DEVICE_VENDOR_ID: case CL_DEVICE_MAX_COMPUTE_UNITS: case CL_DEVICE_MAX_WORK_ITEM_DIMENSIONS: case CL_DEVICE_MAX_WORK_GROUP_SIZE: { cl_uint *value = (cl_uint*)alloca(paramSize); error = clGetDeviceInfo(id, param_name, paramSize, value, NULL); if (error != CL_SUCCESS) { fout << "Unable to obtain device info" << endl; return; } fout << *value; break; } case CL_DEVICE_MAX_WORK_ITEM_SIZES: { size_t *sizes = (size_t*)alloca(paramSize); error = clGetDeviceInfo(id, param_name, paramSize, sizes, NULL); if (error != CL_SUCCESS) { fout << "Unable to obtain device info" << endl; return; } fout << "["; for(size_t j=0; j<paramSize/sizeof(size_t); j++){ if(j>0) fout << ", "; fout << sizes[j]; } fout << "]"; break; } default: { char* value = (char*)alloca(paramSize); error = clGetDeviceInfo(id, param_name, paramSize, value, NULL); if (error != CL_SUCCESS) { fout << "Unable to obtain device info" << endl; return; } fout << value; break; } } fout << endl; }
验证编译与运行
完成配置和代码修正后,选择x64平台(匹配64位系统),点击编译,LNK2001错误会消失。运行程序后,会生成logOCL_MAIN_cpp.txt文件,包含OpenCL平台和设备的详细信息。
内容的提问来源于stack exchange,提问作者John Smit

