First Windows x64 native extension
Build a DLL and check class, constants, and unloading.
This example targets Windows x64, Reckfell 1.0.0, and MSVC C++ tools. It registers abstract DocsNativeProbe : Entity, ProductVersion=10000, and EntityPathVariant=22. It cannot be instantiated as a character: this checks building and ABI integration.
1. Prepare source and descriptor
Create a separate test project. Save native_probe.cpp and native_probe.reckfellextension:
#include "reckfellextension_interface.h"
// This example is deliberately Windows x64 only, not a general C++ binding.
struct NativeStringName { void *opaque; };
static_assert(sizeof(NativeStringName) == 8, "Check StringName size against extension_api.json");
static_assert(RECKFELLEXTENSION_VARIANT_TYPE_ENTITY_PATH == 22, "Unexpected EntityPath ABI");
static ReckfellExtensionClassLibraryPtr library;
static ReckfellExtensionInterfaceStringNameNewWithUtf8Chars name_new;
static ReckfellExtensionPtrDestructor name_destroy;
static ReckfellExtensionInterfaceClassdbRegisterExtensionClass6 register_class;
static ReckfellExtensionInterfaceClassdbUnregisterExtensionClass unregister_class;
static ReckfellExtensionInterfaceClassdbRegisterExtensionClassIntegerConstant register_constant;
static void free_instance(void *, ReckfellExtensionClassInstancePtr) {}
static void initialize(void *, ReckfellExtensionInitializationLevel level) {
if (level != RECKFELLEXTENSION_INITIALIZATION_SCENE) return;
NativeStringName name, parent, enum_name, constant;
name_new(&name, "DocsNativeProbe");
name_new(&parent, "Entity");
name_new(&enum_name, "");
ReckfellExtensionClassCreationInfo6 info = {};
info.is_abstract = 1;
info.is_exposed = 1;
info.free_instance_func = free_instance;
register_class(library, &name, &parent, &info);
name_new(&constant, "ProductVersion");
register_constant(library, &name, &enum_name, &constant, 10000, 0);
name_destroy(&constant);
name_new(&constant, "EntityPathVariant");
register_constant(library, &name, &enum_name, &constant, 22, 0);
name_destroy(&constant);
name_destroy(&enum_name);
name_destroy(&parent);
name_destroy(&name);
}
static void deinitialize(void *, ReckfellExtensionInitializationLevel level) {
if (level != RECKFELLEXTENSION_INITIALIZATION_SCENE) return;
NativeStringName name;
name_new(&name, "DocsNativeProbe");
unregister_class(library, &name);
name_destroy(&name);
}
extern "C" __declspec(dllexport) ReckfellExtensionBool reckfell_docs_init(
ReckfellExtensionInterfaceGetProcAddress get_proc,
ReckfellExtensionClassLibraryPtr native_library,
ReckfellExtensionInitialization *initialization) {
library = native_library;
name_new = (ReckfellExtensionInterfaceStringNameNewWithUtf8Chars)get_proc("string_name_new_with_utf8_chars");
register_class = (ReckfellExtensionInterfaceClassdbRegisterExtensionClass6)get_proc("classdb_register_extension_class6");
unregister_class = (ReckfellExtensionInterfaceClassdbUnregisterExtensionClass)get_proc("classdb_unregister_extension_class");
register_constant = (ReckfellExtensionInterfaceClassdbRegisterExtensionClassIntegerConstant)get_proc("classdb_register_extension_class_integer_constant");
auto get_destructor = (ReckfellExtensionInterfaceVariantGetPtrDestructor)get_proc("variant_get_ptr_destructor");
if (!name_new || !register_class || !unregister_class || !register_constant || !get_destructor) return 0;
name_destroy = get_destructor(RECKFELLEXTENSION_VARIANT_TYPE_STRING_NAME);
if (!name_destroy) return 0;
initialization->minimum_initialization_level = RECKFELLEXTENSION_INITIALIZATION_SCENE;
initialization->userdata = nullptr;
initialization->initialize = initialize;
initialization->deinitialize = deinitialize;
return 1;
}[configuration]
entry_symbol = "reckfell_docs_init"
compatibility_minimum = "1.0.0"
reloadable = false
[libraries]
windows.x86_64 = "./native_probe.windows.x86_64.dll"extern "C" preserves reckfell_docs_init, which must match entry_symbol. A missing required get_proc function causes initialization to return 0 rather than call a null pointer. Eight-byte StringName storage is specific to checked Windows x64; see ABI and lifetime.
2. Build the DLL
Open MSVC x64 Developer PowerShell. Enter the source directory, set $Sdk to your installed ExtensionSDK absolute path, and run:
cl /nologo /std:c++17 /EHsc /LD /W3 /WX /I"$Sdk/include" native_probe.cpp /Fe:native_probe.windows.x86_64.dllCheck the exit code. The required output is native_probe.windows.x86_64.dll; .lib, .exp, and .obj are not the loadable library. Other checked builders: CMake and SCons.
3. Check loading
Place the DLL and descriptor together in asset://addons/native_probe/. ./native_probe.windows.x86_64.dll is relative to the descriptor. OS and architecture must match; renaming a DLL does not fix its architecture.
In C#, call ReckfellExtensionManager.LoadExtension("asset://addons/native_probe/native_probe.reckfellextension") and check LoadStatus.Ok. Then ClassDB.ClassExists("DocsNativeProbe") should be true and ClassDB.ClassGetIntegerConstant("DocsNativeProbe", "ProductVersion") should equal 10000. Editor scanning provides automatic project discovery; mouse actions were not tested here.
Reloading the same path returned AlreadyLoaded. After UnloadExtension(), the class and path disappeared from registries. Do not replace a DLL used by a running process. The example disables hot reload with reloadable=false.
Diagnose failures
File missing: check both paths. Entry point missing: check exports, extern "C", and entry_symbol. Library cannot open: check architecture and runtime dependencies. Initialization rejected: check required functions and SDK version. Class registered but cannot instantiate: expected, because it is abstract.
All three builds loaded, passed constants across the ABI, and unloaded. Instantiable native objects, other platforms, and live-object reload were not checked. No engine modification or rebuild is required.