obs-windows-building
OBS Windows Building
Purpose
Build OBS Studio plugins for Windows using MSVC (Visual Studio) or MinGW. Covers symbol exports, Windows-specific linking, platform source files, and DLL verification.
When NOT to Use
- Cross-compiling from Linux → Use obs-cross-compiling
- Qt/C++ frontend development → Use obs-cpp-qt-patterns
- Audio plugin implementation → Use obs-audio-plugin-writing
- Code review → Use obs-plugin-reviewing
Quick Start: Windows Build in 4 Steps
Step 1: Install Prerequisites
Visual Studio 2022:
- Workload: "Desktop development with C++"
- Individual components: CMake, Windows SDK
Or MinGW (via MSYS2):
pacman -S mingw-w64-x86_64-gcc mingw-w64-x86_64-cmake
Step 2: Create Windows CMake Preset
{
"name": "windows-x64",
"displayName": "Windows x64",
"description": "Build for Windows x64 with Visual Studio",
"binaryDir": "${sourceDir}/build_x64",
"generator": "Visual Studio 17 2022",
"architecture": "x64",
"cacheVariables": {
"OBS_SOURCE_DIR": "${sourceDir}/.deps/windows-x64/obs-studio-32.0.4"
}
}
Step 3: Create .def Export File
Create src/plugin.def:
LIBRARY my-plugin
EXPORTS
obs_module_load
obs_module_unload
obs_module_post_load
obs_module_ver
obs_module_set_pointer
obs_current_module
obs_module_description
obs_module_set_locale
obs_module_free_locale
obs_module_get_string
obs_module_text
Step 4: Configure CMakeLists.txt
if(WIN32)
# Windows system libraries
target_link_libraries(${PROJECT_NAME} PRIVATE ws2_32 comctl32)
# Export module functions via .def file
if(CMAKE_C_COMPILER_ID STREQUAL "GNU")
# MinGW
set_target_properties(${PROJECT_NAME} PROPERTIES
LINK_FLAGS "${CMAKE_CURRENT_SOURCE_DIR}/src/plugin.def -Wl,--unresolved-symbols=ignore-all"
)
else()
# MSVC
set_target_properties(${PROJECT_NAME} PROPERTIES
LINK_FLAGS "/DEF:${CMAKE_CURRENT_SOURCE_DIR}/src/plugin.def"
)
endif()
endif()
MSVC vs MinGW Comparison
| Aspect | MSVC | MinGW |
|---|---|---|
| IDE | Visual Studio | VS Code, CLion |
| Debugging | Full VS debugger | GDB |
| Build speed | Slower | Faster |
| ABI | Native Windows | GCC-based |
| Qt compat | Requires MSVC Qt | Works with MinGW Qt |
| CI/CD | Windows runner | Linux cross-compile |
Recommendation: Use MSVC for native Windows development, MinGW for CI cross-compilation.
Symbol Export with .def Files
Why .def Files?
OBS loads plugins at runtime and looks up functions by name. Without explicit exports:
- MSVC may not export functions without
__declspec(dllexport) - MinGW may export by ordinal only (numbers, not names)
.def File Format
; Comments start with semicolon
LIBRARY my-plugin ; DLL name
EXPORTS
obs_module_load ; Function to export
obs_module_unload
; Add all OBS_DECLARE_MODULE() and OBS_MODULE_USE_DEFAULT_LOCALE() functions
MSVC Linker Flag
set_target_properties(${PROJECT_NAME} PROPERTIES
LINK_FLAGS "/DEF:${CMAKE_CURRENT_SOURCE_DIR}/src/plugin.def"
)
MinGW Linker Flag
set_target_properties(${PROJECT_NAME} PROPERTIES
LINK_FLAGS "${CMAKE_CURRENT_SOURCE_DIR}/src/plugin.def -Wl,--unresolved-symbols=ignore-all"
)
Windows System Libraries
Common Libraries
| Library | Purpose | Header |
|---|---|---|
ws2_32 |
Windows Sockets 2 (networking) | <winsock2.h> |
comctl32 |
Common controls (UI widgets) | <commctrl.h> |
user32 |
Windows API (windows, messages) | <windows.h> |
kernel32 |
Core Windows API | <windows.h> |
ole32 |
COM support | <objbase.h> |
uuid |
GUID/UUID support | <guiddef.h> |
CMake Linking
if(WIN32)
target_link_libraries(${PROJECT_NAME} PRIVATE
ws2_32 # Sockets
comctl32 # UI controls
)
endif()
Include Order (CRITICAL)
/* WRONG - will cause compile errors */
#include <windows.h>
#include <winsock2.h>
/* CORRECT - winsock2.h must come first */
#include <winsock2.h>
#include <windows.h>
Platform-Specific Source Files
Directory Structure
src/
├── plugin-main.c # Cross-platform
├── my-source.c # Cross-platform
└── platform/
├── socket-posix.c # Linux/macOS
└── socket-win32.c # Windows
CMakeLists.txt Pattern
# Common sources
target_sources(${PROJECT_NAME} PRIVATE
src/plugin-main.c
src/my-source.c
)
# Platform-specific sources
if(WIN32)
target_sources(${PROJECT_NAME} PRIVATE
src/platform/socket-win32.c
)
else()
target_sources(${PROJECT_NAME} PRIVATE
src/platform/socket-posix.c
)
endif()
Platform Header Pattern
/* platform.h - Platform abstraction */
#pragma once
#ifdef _WIN32
#include "platform/socket-win32.h"
#else
#include "platform/socket-posix.h"
#endif
/* Common interface */
int platform_socket_init(void);
void platform_socket_cleanup(void);
int platform_socket_send(const char *host, int port, const void *data, size_t len);
Windows-Specific Code Patterns
Winsock Initialization
/* socket-win32.c */
#include <winsock2.h>
#include <ws2tcpip.h>
static bool winsock_initialized = false;
int platform_socket_init(void)
{
WSADATA wsa_data;
int result = WSAStartup(MAKEWORD(2, 2), &wsa_data);
if (result != 0) {
return -1;
}
winsock_initialized = true;
return 0;
}
void platform_socket_cleanup(void)
{
if (winsock_initialized) {
WSACleanup();
winsock_initialized = false;
}
}
Windows API Loader Pattern
For optional Windows APIs (not always available):
/* api-loader.c */
#include <windows.h>
typedef BOOL (WINAPI *SetProcessDpiAwarenessContext_t)(DPI_AWARENESS_CONTEXT);
static SetProcessDpiAwarenessContext_t pSetProcessDpiAwarenessContext = NULL;
void load_optional_apis(void)
{
HMODULE user32 = GetModuleHandleW(L"user32.dll");
if (user32) {
pSetProcessDpiAwarenessContext = (SetProcessDpiAwarenessContext_t)
GetProcAddress(user32, "SetProcessDpiAwarenessContext");
}
}
void set_dpi_awareness(void)
{
if (pSetProcessDpiAwarenessContext) {
pSetProcessDpiAwarenessContext(DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2);
}
}
OBS Plugin Installation Paths
User Installation (Recommended)
%APPDATA%\obs-studio\plugins\my-plugin\
├── bin\
│ └── 64bit\
│ └── my-plugin.dll
└── data\
└── locale\
└── en-US.ini
System Installation
C:\ProgramData\obs-studio\plugins\my-plugin\
├── bin\
│ └── 64bit\
│ └── my-plugin.dll
└── data\
└── locale\
└── en-US.ini
CMake Install Target
if(WIN32)
set(OBS_PLUGIN_DIR "$ENV{APPDATA}/obs-studio/plugins/${PROJECT_NAME}")
endif()
install(TARGETS ${PROJECT_NAME}
RUNTIME DESTINATION ${OBS_PLUGIN_DIR}/bin/64bit
)
install(DIRECTORY data/locale
DESTINATION ${OBS_PLUGIN_DIR}/data
)
DLL Verification
Check File Type
:: PowerShell
Get-Item my-plugin.dll | Select-Object Name, Length
Check Exports with dumpbin (MSVC)
dumpbin /exports my-plugin.dll
Expected output:
ordinal hint RVA name
1 0 00001000 obs_current_module
2 1 00001010 obs_module_description
3 2 00001020 obs_module_load
...
Check Exports with objdump (MinGW)
x86_64-w64-mingw32-objdump -p my-plugin.dll | grep -A 50 "Export Table"
FORBIDDEN Patterns
| Pattern | Problem | Solution |
|---|---|---|
| Missing .def file | Functions not exported by name | Create plugin.def |
| Wrong include order | winsock2 errors | Include winsock2.h before windows.h |
| Missing WSAStartup | Socket functions fail | Call platform_socket_init() |
| Hardcoded paths | Breaks on other machines | Use %APPDATA% or relative paths |
| ANSI APIs | Unicode issues | Use wide (W) APIs or UTF-8 |
| Missing /DEF linker flag | No exports in DLL | Add LINK_FLAGS in CMake |
Troubleshooting
Plugin Not Visible in OBS
Check:
- DLL is in
bin/64bit/subdirectory - Path is correct:
%APPDATA%\obs-studio\plugins\{name}\bin\64bit\ - DLL exports are present:
dumpbin /exports my-plugin.dll
"Entry Point Not Found" Error
Cause: Missing obs_module_load export
Fix: Ensure .def file includes obs_module_load and linker flag is set.
Winsock Errors
Symptom: Socket functions return -1 or WSANOTINITIALISED
Fix: Call WSAStartup() before any socket operations.
Unicode/ANSI Mismatch
Symptom: String corruption, "???" characters
Fix:
/* Use wide APIs or define UNICODE */
#define UNICODE
#define _UNICODE
#include <windows.h>
Build Commands
Visual Studio
:: Configure
cmake --preset windows-x64
:: Build
cmake --build --preset windows-x64 --config RelWithDebInfo
:: Install
cmake --install build_x64 --config RelWithDebInfo
MinGW (MSYS2)
# Configure
cmake -G "Ninja" -B build -DCMAKE_BUILD_TYPE=RelWithDebInfo
# Build
cmake --build build
# Install
cmake --install build
External Documentation
Context7
mcp__context7__query-docs
libraryId: "/obsproject/obs-studio"
query: "Windows plugin build Visual Studio MSVC"
Official References
- OBS Windows Build: https://obsproject.com/wiki/Building-OBS-Studio
- Visual Studio Docs: https://docs.microsoft.com/en-us/visualstudio/
- Windows SDK: https://developer.microsoft.com/en-us/windows/downloads/windows-sdk/
Related Skills
- obs-cross-compiling - Cross-compile from Linux to Windows
- obs-cpp-qt-patterns - Qt frontend integration
- obs-plugin-developing - Plugin architecture overview
- obs-audio-plugin-writing - Audio plugin implementation
Related Agent
Use obs-plugin-expert for coordinated guidance across all OBS plugin skills.
More from meriley/claude-code-skills
vendure-admin-ui-writing
Create Vendure Admin UI extensions with React components, route registration, navigation menus, and GraphQL integration. Handles useQuery, useMutation, useInjector patterns. Use when building Admin UI features for Vendure plugins.
33vendure-graphql-writing
Extend Vendure GraphQL schema with custom types, queries, mutations, and resolvers. Handles RequestContext threading, permissions, and dual Shop/Admin API separation. Use when adding GraphQL endpoints to Vendure.
31vendure-admin-ui-reviewing
Review Vendure Admin UI extensions for React pattern violations, missing hooks, improper state management, and UI anti-patterns. Use when reviewing Admin UI PRs or auditing UI quality.
26vendure-entity-reviewing
Review Vendure entities for missing VendureEntity inheritance, improper decorators, missing migrations, index violations, and TypeORM anti-patterns. Use when reviewing entity PRs or auditing data models.
22vendure-graphql-reviewing
Review Vendure GraphQL resolvers for missing RequestContext, improper permissions, DTO violations, and schema extension issues. Use when reviewing GraphQL PRs or auditing API quality.
21run-tests
⚠️ MANDATORY - Automatically invoked by safe-commit. Executes comprehensive testing suite including unit tests (minimum 90% coverage), integration tests, and E2E tests (100% pass required). Reports coverage and failures. MUST pass before commit. NEVER run tests manually before commit.
7