Port ITT API Reference Collector to cross-platform Windows and Linux build (#231)

* add Windows support for ITT Reference Collector
diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml
index 659c1d6..85ef689 100644
--- a/.github/workflows/main.yml
+++ b/.github/workflows/main.yml
@@ -39,6 +39,36 @@
       #    doesn't work in case of CMake + VS (https://github.com/fortran-lang/setup-fortran/issues/45)
       run: python buildall.py --force_bits 64 -ft ${{ matrix.optional_args }}
 
+  refcol_smoke:
+    name: Reference collector smoke test
+    runs-on: ${{ matrix.os }}
+    strategy:
+      fail-fast: false
+      matrix:
+        include:
+          - os: ubuntu-latest
+            lib: build_linux/64/bin/libittnotify_refcol.so
+            exe: build_linux/64/bin/refcol_smoke_test
+            build_dir: build_linux/64
+          - os: windows-latest
+            lib: build_win/64/bin/libittnotify_refcol.dll
+            exe: build_win/64/bin/refcol_smoke_test.exe
+            build_dir: build_win/64
+    defaults:
+      run:
+        shell: bash
+    steps:
+    - name: Checkout sources
+      uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
+    - name: Build reference collector library
+      run: python buildall.py --force_bits 64 --refcol ${{ runner.os == 'Windows' && '--cmake_gen ninja' || '' }}
+    - name: Build smoke test
+      run: |
+        cmake ${{ matrix.build_dir }} -DITT_API_REFCOL_SMOKE_TESTS=ON
+        cmake --build ${{ matrix.build_dir }} --target refcol_smoke_test
+    - name: Run smoke test
+      run: python src/ittnotify_refcol/tests/run_smoke_test.py --lib ${{ matrix.lib }} --exe ${{ matrix.exe }}
+
   cpp_wrapper:
     name: Check C++ wrapper
     runs-on: ${{ matrix.os }}
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 8c97cf9..bf9dd45 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -27,6 +27,7 @@
 option(ITT_API_IPT_SUPPORT "ptmarks support" OFF)
 option(ITT_API_FORTRAN_SUPPORT "fortran support" OFF)
 option(ITT_API_CPP_SUPPORT "C++ wrapper support" OFF)
+option(ITT_API_REFERENCE_COLLECTOR "Build reference collector shared library" OFF)
 option(ITT_API_INSTALL "Enable ITT API installation rules" ON)
 
 if(FORCE_32 AND UNIX)
@@ -141,6 +142,10 @@
 set(JITPROFILING_SRC "src/ittnotify/jitprofiling.c")
 add_library(jitprofiling STATIC ${JITPROFILING_SRC})
 
+if(ITT_API_REFERENCE_COLLECTOR)
+    add_subdirectory(src/ittnotify_refcol)
+endif()
+
 if(WIN32)
     set_target_properties(ittnotify PROPERTIES OUTPUT_NAME libittnotify)
     set_target_properties(jitprofiling PROPERTIES OUTPUT_NAME libjitprofiling)
diff --git a/README.md b/README.md
index 0176d25..1ae094d 100644
--- a/README.md
+++ b/README.md
@@ -52,6 +52,7 @@
   -ft, --fortran  enable fortran support
   -cpp, --cpp     enable C++ wrapper support
   --force_bits    specify bit version for the target
+  --refcol        enable reference collector build
   --vs            specify visual studio version (Windows only)
   --cmake_gen     specify cmake build generator (Windows only)
 ```
diff --git a/buildall.py b/buildall.py
index 180f7fe..a68a679 100755
--- a/buildall.py
+++ b/buildall.py
@@ -120,6 +120,8 @@
     parser.add_argument(
         "-cpp", "--cpp", help="enable C++ wrapper support", action="store_true")
     parser.add_argument(
+        "--refcol", help="enable reference collector build", action="store_true")
+    parser.add_argument(
         "--force_bits", choices=["32", "64"], help="specify bit version for the target")
     if sys.platform == 'win32' and vs_versions:
         parser.add_argument(
@@ -180,7 +182,8 @@
             ('-DCMAKE_VERBOSE_MAKEFILE:BOOL=ON' if args.verbose else ''),
             ("-DITT_API_IPT_SUPPORT=1" if args.ptmark else ""),
             ("-DITT_API_FORTRAN_SUPPORT=1" if args.fortran else ""),
-            ("-DITT_API_CPP_SUPPORT=ON" if args.cpp else "")
+            ("-DITT_API_CPP_SUPPORT=ON" if args.cpp else ""),
+            ("-DITT_API_REFERENCE_COLLECTOR=ON" if args.refcol else ""),
         ])))
 
         if sys.platform == 'win32':
diff --git a/docs/src/build.rst b/docs/src/build.rst
index 62f6999..60de39d 100644
--- a/docs/src/build.rst
+++ b/docs/src/build.rst
@@ -56,6 +56,8 @@
       -v, --verbose   enable verbose output from build process
       -pt, --ptmark   enable anomaly detection support
       -ft, --fortran  enable fortran support
+      -cpp, --cpp     enable C++ wrapper support
       --force_bits    specify bit version for the target
+      --refcol        enable reference collector build
       --vs            specify visual studio version (Windows only)
       --cmake_gen     specify cmake build generator (Windows only)
diff --git a/docs/src/ref_collector.rst b/docs/src/ref_collector.rst
index 5afa5da..09dec38 100644
--- a/docs/src/ref_collector.rst
+++ b/docs/src/ref_collector.rst
@@ -7,10 +7,42 @@
 This is a reference implementation of the ITT API **dynamic** part that
 performs tracing data from ITT API function calls to log files.
 
-
 To use this solution, build the collector as a shared library and point the
-full library path to the ``INTEL_LIBITTNOTIFY64`` or ``INTEL_LIBITTNOTIFY32``
-environment variable:
+full library path to the `INTEL_LIBITTNOTIFY64` or `INTEL_LIBITTNOTIFY32`
+environment variable.
+
+
+Building
+--------
+
+Use CMake from the repository root, enabling the ``ITT_API_REFERENCE_COLLECTOR`` option:
+
+.. code-block:: console
+
+    cmake -B <build_dir> -DITT_API_REFERENCE_COLLECTOR=ON
+    cmake --build <build_dir>
+
+Alternatively, use the provided ``buildall.py`` script:
+
+.. code-block:: console
+
+    python buildall.py --refcol
+
+The shared library is placed in the ``bin/`` subdirectory of the build directory:
+
+.. list-table::
+   :header-rows: 1
+
+   * - Platform
+     - Library name
+   * - Linux
+     - ``libittnotify_refcol.so``
+   * - Windows
+     - ``libittnotify_refcol.dll``
+
+
+Usage
+-----
 
 
 **On Linux**
@@ -18,8 +50,7 @@
 
 .. code-block:: bash
 
-    make
-    export INTEL_LIBITTNOTIFY64=<build_dir>/libittnotify_refcol.so
+    export INTEL_LIBITTNOTIFY64=<build_dir>/bin/libittnotify_refcol.so
 
 
 **On FreeBSD**
@@ -27,12 +58,23 @@
 
 .. code-block:: bash
 
-    make
-    setenv INTEL_LIBITTNOTIFY64 <build_dir>/libittnotify_refcol.so
+    setenv INTEL_LIBITTNOTIFY64 <build_dir>/bin/libittnotify_refcol.so
 
 
-By default, log files save in the Temp directory. To change the location,
-use the ``INTEL_LIBITTNOTIFY_LOG_DIR`` environment variable:
+**On Windows**
+
+
+.. code-block:: bat
+
+    set INTEL_LIBITTNOTIFY64=<build_dir>\bin\libittnotify_refcol.dll
+
+
+Log File Location
+-----------------
+
+By default, log files are saved in the system temporary directory. Each run
+creates a file named ``libittnotify_refcol_<timestamp>.log``. To change the
+location, use the ``INTEL_LIBITTNOTIFY_LOG_DIR`` environment variable:
 
 
 **On Linux**
@@ -40,7 +82,6 @@
 
 .. code-block:: bash
 
-
     export INTEL_LIBITTNOTIFY_LOG_DIR=<log_dir>
 
 
@@ -49,16 +90,25 @@
 
 .. code-block:: bash
 
-
     setenv INTEL_LIBITTNOTIFY_LOG_DIR <log_dir>
 
 
+**On Windows**
+
+
+.. code-block:: bat
+
+    set INTEL_LIBITTNOTIFY_LOG_DIR=<log_dir>
+
+
+Extending
+---------
+
 This implementation adds logging of some of the ITT API function calls. Adding
 logging of other ITT API function calls is welcome. The solution provides 4
-functions with different log levels that take ``printf`` format for logging:
+functions with different log levels that take `printf` format for logging:
 
-
-.. code-block:: cpp
+.. code-block:: c
 
     LOG_FUNC_CALL_INFO(const char *msg_format, ...);
     LOG_FUNC_CALL_WARN(const char *msg_format, ...);
diff --git a/src/ittnotify_refcol/CMakeLists.txt b/src/ittnotify_refcol/CMakeLists.txt
new file mode 100644
index 0000000..977c7c7
--- /dev/null
+++ b/src/ittnotify_refcol/CMakeLists.txt
@@ -0,0 +1,39 @@
+#
+#  Copyright (C) 2026 Intel Corporation
+#  SPDX-License-Identifier: (BSD-3-Clause OR GPL-2.0-only)
+#
+
+add_library(ittnotify_refcol SHARED itt_refcol_impl.c)
+
+target_include_directories(ittnotify_refcol
+    PUBLIC $<BUILD_INTERFACE:${CMAKE_SOURCE_DIR}/include>
+    PRIVATE ${CMAKE_SOURCE_DIR}/src/ittnotify
+)
+
+target_link_libraries(ittnotify_refcol PRIVATE ${CMAKE_DL_LIBS})
+set_target_properties(ittnotify_refcol PROPERTIES
+    LINKER_LANGUAGE C
+    RUNTIME_OUTPUT_DIRECTORY ${LIBRARY_OUTPUT_PATH}
+    ARCHIVE_OUTPUT_DIRECTORY ${LIBRARY_OUTPUT_PATH})
+
+if(WIN32)
+    target_compile_definitions(ittnotify_refcol PRIVATE _CRT_SECURE_NO_WARNINGS)
+    set_target_properties(ittnotify_refcol PROPERTIES
+        OUTPUT_NAME libittnotify_refcol
+        PREFIX ""
+        WINDOWS_EXPORT_ALL_SYMBOLS ON)
+else()
+    set_target_properties(ittnotify_refcol PROPERTIES OUTPUT_NAME ittnotify_refcol)
+endif()
+
+option(ITT_API_REFCOL_SMOKE_TESTS "Build reference collector smoke tests" OFF)
+if(ITT_API_REFCOL_SMOKE_TESTS)
+    add_executable(refcol_smoke_test tests/smoke_test.c)
+    target_include_directories(refcol_smoke_test
+        PRIVATE ${CMAKE_SOURCE_DIR}/include
+                ${CMAKE_SOURCE_DIR}/src/ittnotify
+    )
+    target_link_libraries(refcol_smoke_test PRIVATE ittnotify ${CMAKE_DL_LIBS})
+    set_target_properties(refcol_smoke_test PROPERTIES
+        RUNTIME_OUTPUT_DIRECTORY ${LIBRARY_OUTPUT_PATH})
+endif()
diff --git a/src/ittnotify_refcol/Makefile b/src/ittnotify_refcol/Makefile
deleted file mode 100644
index a92dc67..0000000
--- a/src/ittnotify_refcol/Makefile
+++ /dev/null
@@ -1,14 +0,0 @@
-INCLUDE_DIR = ../../include/
-SRC_DIR = ../ittnotify/
-SOURCE_NAME = itt_refcol_impl.c
-OBJ_NAME = itt_refcol_impl.o
-LIB_NAME = libittnotify_refcol.so
-CC ?= gcc
-CFLAGS = -I$(INCLUDE_DIR) -I$(SRC_DIR)
-
-build:
-	$(CC) -fPIC $(CFLAGS) -c $(SOURCE_NAME)
-	$(CC) -shared -o $(LIB_NAME) $(OBJ_NAME)
-
-clean:
-	 rm $(OBJ_NAME) $(LIB_NAME)
diff --git a/src/ittnotify_refcol/README.md b/src/ittnotify_refcol/README.md
index ee30fc5..2036a56 100644
--- a/src/ittnotify_refcol/README.md
+++ b/src/ittnotify_refcol/README.md
@@ -5,24 +5,53 @@
 
 To use this solution, build the collector as a shared library and point the
 full library path to the `INTEL_LIBITTNOTIFY64` or `INTEL_LIBITTNOTIFY32`
-environment variable:
+environment variable.
+
+## Building
+
+Use CMake from the repository root, enabling the `ITT_API_REFERENCE_COLLECTOR` option:
+
+```
+cmake -B <build_dir> -DITT_API_REFERENCE_COLLECTOR=ON
+cmake --build <build_dir>
+```
+
+The shared library is placed in the `bin/` subdirectory of the CMake build
+directory. Alternatively, use the provided `buildall.py` script:
+
+```
+python buildall.py --refcol
+```
+
+The shared library is placed in the `bin/` subdirectory of the CMake build directory:
+
+| Platform | Library name                   |
+|----------|--------------------------------|
+| Linux    | `libittnotify_refcol.so`       |
+| Windows  | `libittnotify_refcol.dll`      |
+
+## Usage
 
 **On Linux**
 
 ```
-make
-export INTEL_LIBITTNOTIFY64=<build_dir>/libittnotify_refcol.so
+export INTEL_LIBITTNOTIFY64=<build_dir>/bin/libittnotify_refcol.so
 ```
 
 **On FreeBSD**
 
 ```
-make
-setenv INTEL_LIBITTNOTIFY64 <build_dir>/libittnotify_refcol.so
+setenv INTEL_LIBITTNOTIFY64 <build_dir>/bin/libittnotify_refcol.so
 ```
 
-By default, log files save in the `tmp` directory. To change the location,
-use the `INTEL_LIBITTNOTIFY_LOG_DIR` environment variable:
+**On Windows**
+
+```
+set INTEL_LIBITTNOTIFY64=<build_dir>\bin\libittnotify_refcol.dll
+```
+
+By default, log files are saved in the system temporary directory. To change
+the location, use the `INTEL_LIBITTNOTIFY_LOG_DIR` environment variable:
 
 **On Linux**
 
@@ -35,11 +64,16 @@
 setenv INTEL_LIBITTNOTIFY_LOG_DIR <log_dir>
 ```
 
+**On Windows**
+```
+set INTEL_LIBITTNOTIFY_LOG_DIR=<log_dir>
+```
+
 This implementation adds logging of some of the ITT API function calls. Adding
 logging of other ITT API function calls is welcome. The solution provides 4
 functions with different log levels that take `printf` format for logging:
 
-```
+```c
 LOG_FUNC_CALL_INFO(const char *msg_format, ...);
 LOG_FUNC_CALL_WARN(const char *msg_format, ...);
 LOG_FUNC_CALL_ERROR(const char *msg_format, ...);
diff --git a/src/ittnotify_refcol/itt_refcol_impl.c b/src/ittnotify_refcol/itt_refcol_impl.c
index 42571e8..1f245d3 100644
--- a/src/ittnotify_refcol/itt_refcol_impl.c
+++ b/src/ittnotify_refcol/itt_refcol_impl.c
@@ -4,8 +4,10 @@
   SPDX-License-Identifier: GPL-2.0-only OR BSD-3-Clause
 */
 
+#include <inttypes.h>
 #include <stdio.h>
 #include <stdarg.h>
+#include <stdint.h>
 #include <stdlib.h>
 #include <string.h>
 #include <time.h>
@@ -45,15 +47,34 @@
     __itt_histogram*       histogram_list;
 } g_ref_collector_global = {MUTEX_INITIALIZER, 0, NULL, NULL, NULL, NULL};
 
+#ifdef _WIN32
+    #define REFCOL_LOCALTIME(out_tm, in_time) (localtime_s((out_tm), (in_time)) == 0)
+#else
+    #define REFCOL_LOCALTIME(out_tm, in_time) (localtime_r((in_time), (out_tm)) != NULL)
+#endif
+
 static char* log_file_name_generate()
 {
     time_t time_now = time(NULL);
-    struct tm* time_info = localtime(&time_now);
+    struct tm time_info;
     char* log_file_name = malloc(sizeof(char) * (LOG_BUFFER_MAX_SIZE/2));
 
-    sprintf(log_file_name,"libittnotify_refcol_%d%d%d%d%d%d.log",
-            time_info->tm_year+1900, time_info->tm_mon+1, time_info->tm_mday,
-            time_info->tm_hour, time_info->tm_min, time_info->tm_sec);
+    if (log_file_name == NULL)
+    {
+        printf("ERROR: Failed to allocate memory for log file name\n");
+        return NULL;
+    }
+
+    if (!REFCOL_LOCALTIME(&time_info, &time_now))
+    {
+        printf("ERROR: Failed to get local time for log file name\n");
+        free(log_file_name);
+        return NULL;
+    }
+
+    snprintf(log_file_name, LOG_BUFFER_MAX_SIZE/2, "libittnotify_refcol_%d%d%d%d%d%d.log",
+             time_info.tm_year+1900, time_info.tm_mon+1, time_info.tm_mday,
+             time_info.tm_hour, time_info.tm_min, time_info.tm_sec);
 
     return log_file_name;
 }
@@ -72,9 +93,9 @@
         if (log_dir != NULL)
         {
             #ifdef _WIN32
-                sprintf(file_name_buffer,"%s\\%s", log_dir, log_file);
+                snprintf(file_name_buffer, sizeof(file_name_buffer), "%s\\%s", log_dir, log_file);
             #else
-                sprintf(file_name_buffer,"%s/%s", log_dir, log_file);
+                snprintf(file_name_buffer, sizeof(file_name_buffer), "%s/%s", log_dir, log_file);
             #endif
         }
         else
@@ -83,10 +104,14 @@
                 char* temp_dir = getenv("TEMP");
                 if (temp_dir != NULL)
                 {
-                    sprintf(file_name_buffer,"%s\\%s", temp_dir, log_file);
+                    snprintf(file_name_buffer, sizeof(file_name_buffer), "%s\\%s", temp_dir, log_file);
+                }
+                else
+                {
+                    snprintf(file_name_buffer, sizeof(file_name_buffer), "%s", log_file);
                 }
             #else
-                sprintf(file_name_buffer,"/tmp/%s", log_file);
+                snprintf(file_name_buffer, sizeof(file_name_buffer), "/tmp/%s", log_file);
             #endif
         }
         free(log_file);
@@ -122,7 +147,7 @@
 
 // Cleanup hook called at program exit via atexit().
 // Releases all resources: closes log file, frees all ITT objects.
-static void ref_collector_release()
+static void ref_collector_release(void)
 {
     static int released = 0;
     if (released) return;
@@ -261,7 +286,9 @@
     uint32_t result_len = 0;
     va_list message_args;
 
-    result_len += sprintf(log_buffer, "[%s] %s(...) - ", log_level_str[log_level] ,function_name);
+    result_len += snprintf(log_buffer, LOG_BUFFER_MAX_SIZE, "[%s] %s(...) - ", log_level_str[log_level], function_name);
+    if (result_len >= LOG_BUFFER_MAX_SIZE)
+        result_len = LOG_BUFFER_MAX_SIZE - 1;
     va_start(message_args, message_format);
     vsnprintf(log_buffer + result_len, LOG_BUFFER_MAX_SIZE - result_len, message_format, message_args);
     va_end(message_args);
@@ -292,36 +319,36 @@
     switch (type)
     {
     case __itt_metadata_u64:
-        for (uint16_t i = 0; i < size; i++)
-            offset += sprintf(metadata_str + offset, "%lu;", ((uint64_t*)metadata)[i]);
+        for (uint16_t i = 0; i < size && offset < LOG_BUFFER_MAX_SIZE - 1; i++)
+            offset += snprintf(metadata_str + offset, LOG_BUFFER_MAX_SIZE - offset, "%" PRIu64 ";", ((uint64_t*)metadata)[i]);
         break;
     case __itt_metadata_s64:
-        for (uint16_t i = 0; i < size; i++)
-            offset += sprintf(metadata_str + offset, "%ld;", ((int64_t*)metadata)[i]);
+        for (uint16_t i = 0; i < size && offset < LOG_BUFFER_MAX_SIZE - 1; i++)
+            offset += snprintf(metadata_str + offset, LOG_BUFFER_MAX_SIZE - offset, "%" PRId64 ";", ((int64_t*)metadata)[i]);
         break;
     case __itt_metadata_u32:
-        for (uint16_t i = 0; i < size; i++)
-            offset += sprintf(metadata_str + offset, "%u;", ((uint32_t*)metadata)[i]);
+        for (uint16_t i = 0; i < size && offset < LOG_BUFFER_MAX_SIZE - 1; i++)
+            offset += snprintf(metadata_str + offset, LOG_BUFFER_MAX_SIZE - offset, "%u;", ((uint32_t*)metadata)[i]);
         break;
     case __itt_metadata_s32:
-        for (uint16_t i = 0; i < size; i++)
-            offset += sprintf(metadata_str + offset, "%d;", ((int32_t*)metadata)[i]);
+        for (uint16_t i = 0; i < size && offset < LOG_BUFFER_MAX_SIZE - 1; i++)
+            offset += snprintf(metadata_str + offset, LOG_BUFFER_MAX_SIZE - offset, "%d;", ((int32_t*)metadata)[i]);
         break;
     case __itt_metadata_u16:
-        for (uint16_t i = 0; i < size; i++)
-            offset += sprintf(metadata_str + offset, "%u;", ((uint16_t*)metadata)[i]);
+        for (uint16_t i = 0; i < size && offset < LOG_BUFFER_MAX_SIZE - 1; i++)
+            offset += snprintf(metadata_str + offset, LOG_BUFFER_MAX_SIZE - offset, "%u;", ((uint16_t*)metadata)[i]);
         break;
     case __itt_metadata_s16:
-        for (uint16_t i = 0; i < size; i++)
-            offset += sprintf(metadata_str + offset, "%d;", ((int16_t*)metadata)[i]);
+        for (uint16_t i = 0; i < size && offset < LOG_BUFFER_MAX_SIZE - 1; i++)
+            offset += snprintf(metadata_str + offset, LOG_BUFFER_MAX_SIZE - offset, "%d;", ((int16_t*)metadata)[i]);
         break;
     case __itt_metadata_float:
-        for (uint16_t i = 0; i < size; i++)
-            offset += sprintf(metadata_str + offset, "%f;", ((float*)metadata)[i]);
+        for (uint16_t i = 0; i < size && offset < LOG_BUFFER_MAX_SIZE - 1; i++)
+            offset += snprintf(metadata_str + offset, LOG_BUFFER_MAX_SIZE - offset, "%f;", ((float*)metadata)[i]);
         break;
     case __itt_metadata_double:
-        for (uint16_t i = 0; i < size; i++)
-            offset += sprintf(metadata_str + offset, "%lf;", ((double*)metadata)[i]);
+        for (uint16_t i = 0; i < size && offset < LOG_BUFFER_MAX_SIZE - 1; i++)
+            offset += snprintf(metadata_str + offset, LOG_BUFFER_MAX_SIZE - offset, "%lf;", ((double*)metadata)[i]);
         break;
     default:
         printf("ERROR: Unknown metadata type\n");
@@ -343,7 +370,7 @@
         case __itt_context_device:
         case __itt_context_units:
         case __itt_context_pci_addr:
-            sprintf(metadata_str, "%s;", ((char*)metadata));
+            snprintf(metadata_str, LOG_BUFFER_MAX_SIZE/4, "%s;", ((char*)metadata));
             break;
         case __itt_context_max_val:
         case __itt_context_tid:
@@ -354,7 +381,7 @@
         case __itt_context_cpu_instructions_flag:
         case __itt_context_cpu_cycles_flag:
         case __itt_context_is_abs_val_flag:
-            sprintf(metadata_str, "%lu;", *(uint64_t*)metadata);
+            snprintf(metadata_str, LOG_BUFFER_MAX_SIZE/4, "%" PRIu64 ";", *(uint64_t*)metadata);
             break;
         default:
             printf("ERROR: Unknown context metadata type\n");
@@ -364,7 +391,48 @@
     return metadata_str;
 }
 
+#ifdef _WIN32
+static char* wchar2char(const wchar_t* wide_str)
+{
+    if (wide_str == NULL)
+    {
+        return NULL;
+    }
+
+    int narrow_size = WideCharToMultiByte(CP_UTF8, 0, wide_str, -1, NULL, 0, NULL, NULL);
+    if (narrow_size <= 0)
+    {
+        return NULL;
+    }
+
+    char* narrow_str = (char*)malloc((size_t)narrow_size);
+    if (narrow_str == NULL)
+    {
+        return NULL;
+    }
+
+    if (!WideCharToMultiByte(CP_UTF8, 0, wide_str, -1, narrow_str, narrow_size, NULL, NULL))
+    {
+        free(narrow_str);
+        return NULL;
+    }
+
+    return narrow_str;
+}
+#endif
+
+#ifdef _WIN32
+ITT_EXTERN_C __itt_domain* ITTAPI __itt_domain_createW(const wchar_t *name)
+{
+    char* name_a = wchar2char(name);
+    __itt_domain* result = __itt_domain_createA(name_a);
+    free(name_a);
+    return result;
+}
+ITT_EXTERN_C __itt_domain* ITTAPI __itt_domain_createA(const char *name)
+#else
 ITT_EXTERN_C __itt_domain* ITTAPI __itt_domain_create(const char *name)
+#endif
 {
     if (!g_ref_collector_global.mutex_initialized || name == NULL)
     {
@@ -396,7 +464,18 @@
     return h;
 }
 
+#ifdef _WIN32
+ITT_EXTERN_C __itt_string_handle* ITTAPI __itt_string_handle_createW(const wchar_t* name)
+{
+    char* name_a = wchar2char(name);
+    __itt_string_handle* result = __itt_string_handle_createA(name_a);
+    free(name_a);
+    return result;
+}
+ITT_EXTERN_C __itt_string_handle* ITTAPI __itt_string_handle_createA(const char* name)
+#else
 ITT_EXTERN_C __itt_string_handle* ITTAPI __itt_string_handle_create(const char* name)
+#endif
 {
     if (!g_ref_collector_global.mutex_initialized || name == NULL)
     {
@@ -428,21 +507,69 @@
     return h;
 }
 
+#ifdef _WIN32
+ITT_EXTERN_C __itt_counter ITTAPI __itt_counter_createW(const wchar_t *name, const wchar_t *domain)
+{
+    char* name_a = wchar2char(name);
+    char* domain_a = wchar2char(domain);
+    __itt_counter result = __itt_counter_createA(name_a, domain_a);
+    free(name_a);
+    free(domain_a);
+    return result;
+}
+ITT_EXTERN_C __itt_counter ITTAPI __itt_counter_createA(const char *name, const char *domain)
+#else
 ITT_EXTERN_C __itt_counter ITTAPI __itt_counter_create(const char *name, const char *domain)
+#endif
 {
     LOG_FUNC_CALL_INFO("function call");
     return __itt_counter_create_typed(name, domain, __itt_metadata_u64);
 }
 
+#ifdef _WIN32
+ITT_EXTERN_C __itt_counter ITTAPI __itt_counter_createW_v3(
+    const __itt_domain* domain, const wchar_t* name, __itt_metadata_type type)
+{
+    if (domain == NULL) return NULL;
+    char* name_a = wchar2char(name);
+    if (name_a == NULL) return NULL;
+    __itt_counter result = __itt_counter_create_typed(name_a, domain->nameA, type);
+    free(name_a);
+    return result;
+}
+ITT_EXTERN_C __itt_counter ITTAPI __itt_counter_createA_v3(
+    const __itt_domain* domain, const char* name, __itt_metadata_type type)
+#else
 ITT_EXTERN_C __itt_counter ITTAPI __itt_counter_create_v3(
     const __itt_domain* domain, const char* name, __itt_metadata_type type)
+#endif
 {
+    if (domain == NULL)
+    {
+        LOG_FUNC_CALL_WARN("domain is NULL");
+        return NULL;
+    }
     LOG_FUNC_CALL_INFO("function call");
     return __itt_counter_create_typed(name, domain->nameA, type);
 }
 
+#ifdef _WIN32
+ITT_EXTERN_C __itt_counter ITTAPI __itt_counter_create_typedW(
+    const wchar_t *name, const wchar_t *domain, __itt_metadata_type type)
+{
+    char* name_a = wchar2char(name);
+    char* domain_a = wchar2char(domain);
+    __itt_counter result = __itt_counter_create_typedA(name_a, domain_a, type);
+    free(name_a);
+    free(domain_a);
+    return result;
+}
+ITT_EXTERN_C __itt_counter ITTAPI __itt_counter_create_typedA(
+    const char *name, const char *domain, __itt_metadata_type type)
+#else
 ITT_EXTERN_C __itt_counter ITTAPI __itt_counter_create_typed(
     const char *name, const char *domain, __itt_metadata_type type)
+#endif
 {
     if (!g_ref_collector_global.mutex_initialized || name == NULL || domain == NULL)
     {
@@ -478,8 +605,21 @@
     return (__itt_counter)h;
 }
 
+#ifdef _WIN32
+ITT_EXTERN_C __itt_histogram* ITTAPI __itt_histogram_createW(
+    const __itt_domain* domain, const wchar_t* name, __itt_metadata_type x_type, __itt_metadata_type y_type)
+{
+    char* name_a = wchar2char(name);
+    __itt_histogram* result = __itt_histogram_createA(domain, name_a, x_type, y_type);
+    free(name_a);
+    return result;
+}
+ITT_EXTERN_C __itt_histogram* ITTAPI __itt_histogram_createA(
+    const __itt_domain* domain, const char* name, __itt_metadata_type x_type, __itt_metadata_type y_type)
+#else
 ITT_EXTERN_C __itt_histogram* ITTAPI __itt_histogram_create(
     const __itt_domain* domain, const char* name, __itt_metadata_type x_type, __itt_metadata_type y_type)
+#endif
 {
     if (!g_ref_collector_global.mutex_initialized || name == NULL || domain == NULL)
     {
@@ -644,7 +784,7 @@
         (void)id;
         (void)key;
         char* metadata_str = get_metadata_elements(count, type, data);
-        LOG_FUNC_CALL_INFO("function args: domain=%s metadata_size=%lu metadata[]=%s",
+        LOG_FUNC_CALL_INFO("function args: domain=%s metadata_size=%zu metadata[]=%s",
                             domain->nameA, count, metadata_str);
         free(metadata_str);
     }
@@ -690,7 +830,7 @@
         {
             char* x_data_str = get_metadata_elements(length, hist->x_type, x_data);
             char* y_data_str = get_metadata_elements(length, hist->y_type, y_data);
-            LOG_FUNC_CALL_INFO("function args: domain=%s name=%s histogram_size=%lu x[]=%s y[]=%s",
+            LOG_FUNC_CALL_INFO("function args: domain=%s name=%s histogram_size=%zu x[]=%s y[]=%s",
                                 hist->domain->nameA, hist->nameA, length, x_data_str, y_data_str);
             free(x_data_str);
             free(y_data_str);
@@ -698,7 +838,7 @@
         else
         {
             char* y_data_str = get_metadata_elements(length, hist->y_type, y_data);
-            LOG_FUNC_CALL_INFO("function args: domain=%s name=%s histogram_size=%lu y[]=%s",
+            LOG_FUNC_CALL_INFO("function args: domain=%s name=%s histogram_size=%zu y[]=%s",
                                 hist->domain->nameA, hist->nameA, length, y_data_str);
             free(y_data_str);
         }
@@ -717,13 +857,13 @@
         char context_metadata[LOG_BUFFER_MAX_SIZE];
         context_metadata[0] = '\0';
         uint16_t offset = 0;
-        for(size_t i=0; i<length; i++)
+        for(size_t i=0; i<length && offset < LOG_BUFFER_MAX_SIZE - 1; i++)
         {
             char* context_metadata_element = get_context_metadata_element(metadata[i].type, metadata[i].value);
-            offset += sprintf(context_metadata+ offset, "%s", context_metadata_element);
+            offset += snprintf(context_metadata + offset, LOG_BUFFER_MAX_SIZE - offset, "%s", context_metadata_element);
             free(context_metadata_element);
         }
-        LOG_FUNC_CALL_INFO("function args: counter_name=%s context_metadata_size=%lu context_metadata[]=%s",
+        LOG_FUNC_CALL_INFO("function args: counter_name=%s context_metadata_size=%zu context_metadata[]=%s",
                             counter_info->nameA, length, context_metadata);
     }
     else
@@ -738,7 +878,7 @@
     {
         __itt_counter_info_t* counter_info = (__itt_counter_info_t*)counter;
         uint64_t value = *(uint64_t*)value_ptr;
-        LOG_FUNC_CALL_INFO("function args: counter_name=%s counter_value=%lu",
+        LOG_FUNC_CALL_INFO("function args: counter_name=%s counter_value=%" PRIu64,
                             counter_info->nameA, value);
     }
     else
diff --git a/src/ittnotify_refcol/tests/run_smoke_test.py b/src/ittnotify_refcol/tests/run_smoke_test.py
new file mode 100644
index 0000000..cea0c65
--- /dev/null
+++ b/src/ittnotify_refcol/tests/run_smoke_test.py
@@ -0,0 +1,77 @@
+#!/usr/bin/env python
+#
+# Copyright (C) 2026 Intel Corporation
+# SPDX-License-Identifier: GPL-2.0-only OR BSD-3-Clause
+#
+
+import argparse
+import glob
+import os
+import subprocess
+import sys
+import tempfile
+
+
+EXPECTED_SYMBOLS = [
+    "__itt_task_begin",
+    "__itt_task_end",
+    "__itt_metadata_add",
+]
+
+
+def main():
+    parser = argparse.ArgumentParser(description="Run reference collector smoke test")
+    parser.add_argument("--lib", required=True, help="Path to libittnotify_refcol shared library")
+    parser.add_argument("--exe", required=True, help="Path to refcol_smoke_test executable")
+    parser.add_argument("--log-dir", help="Directory for log files (default: temp dir)")
+    args = parser.parse_args()
+
+    lib = os.path.abspath(args.lib)
+    exe = os.path.abspath(args.exe)
+
+    if not os.path.isfile(lib):
+        print(f"ERROR: library not found: {lib}")
+        return 1
+    if not os.path.isfile(exe):
+        print(f"ERROR: executable not found: {exe}")
+        return 1
+
+    log_dir = os.path.abspath(args.log_dir) if args.log_dir else tempfile.mkdtemp(prefix="refcol_logs_")
+    os.makedirs(log_dir, exist_ok=True)
+
+    env = os.environ.copy()
+    env["INTEL_LIBITTNOTIFY64"] = lib
+    env["INTEL_LIBITTNOTIFY_LOG_DIR"] = log_dir
+
+    print(f"Library:  {lib}")
+    print(f"Exe:      {exe}")
+    print(f"Log dir:  {log_dir}")
+
+    result = subprocess.run([exe], env=env)
+    if result.returncode != 0:
+        print(f"ERROR: smoke test executable exited with code {result.returncode}")
+        return 1
+
+    logs = glob.glob(os.path.join(log_dir, "libittnotify_refcol_*.log"))
+    if not logs:
+        print("ERROR: no log file found in", log_dir)
+        return 1
+
+    log_path = logs[0]
+    print(f"Log file: {log_path}")
+
+    with open(log_path, encoding="utf-8", errors="replace") as f:
+        content = f.read()
+
+    missing = [sym for sym in EXPECTED_SYMBOLS if sym not in content]
+    if missing:
+        for sym in missing:
+            print(f"ERROR: '{sym}' not found in log")
+        return 1
+
+    print("Smoke test passed.")
+    return 0
+
+
+if __name__ == "__main__":
+    sys.exit(main())
diff --git a/src/ittnotify_refcol/tests/smoke_test.c b/src/ittnotify_refcol/tests/smoke_test.c
new file mode 100644
index 0000000..4c2a5bf
--- /dev/null
+++ b/src/ittnotify_refcol/tests/smoke_test.c
@@ -0,0 +1,43 @@
+/*
+  Copyright (C) 2026 Intel Corporation
+
+  SPDX-License-Identifier: GPL-2.0-only OR BSD-3-Clause
+*/
+
+#include "ittnotify.h"
+#include "ittnotify_types.h"
+
+int main(void)
+{
+    __itt_domain* domain = __itt_domain_create("smoke_test_domain");
+    __itt_string_handle* handle = __itt_string_handle_create("smoke_test_handler");
+    __itt_string_handle* handle_work = __itt_string_handle_create("smoke_test_worker");
+
+    const int n = 10;
+    int a[10][10], b[10][10], mul[10][10], i, j, k, count = 0;
+
+    for (i = 0; i < n; i++)
+        for (j = 0; j < n; j++)
+        {
+            a[i][j] = i;
+            b[i][j] = j;
+            mul[i][j] = 0;
+        }
+
+    for (i = 0; i < n; i++)
+        for (j = 0; j < n; j++)
+            for (k = 0; k < n; k++)
+            {
+                mul[i][j] += a[i][k] * b[k][j];
+                count++;
+                if (count % 100 == 0)
+                {
+                    __itt_task_begin(domain, __itt_null, __itt_null, handle_work);
+                    unsigned long long data[5] = { i, j, k, count, mul[i][j] };
+                    __itt_metadata_add(domain, __itt_null, handle, __itt_metadata_u64, 5, data);
+                    __itt_task_end(domain);
+                }
+            }
+
+    return 0;
+}