Refactor plugin third-party layout to UE standard

Plugin was non-autonomous: Build.cs referenced ../../../../Dependencies
(SDK installed externally) and copied dpwin64.dll into the project's
Binaries at build time, requiring manual DLL placement. Migrate to the
canonical UE third-party pattern so the plugin self-contains everything
and UBT handles staging.

- Embed minimal SDK files inside the plugin: dris.h, dpwin64.lib in
  Source/ThirdParty/DinkeyPro/, dpwin64.dll in Binaries/ThirdParty/DinkeyPro/Win64/.
- Switch from runtime GetProcAddress to import-lib delay-load (better
  protection: calls go through PE import table instead of named lookup).
- Load DLL via IPluginManager::FindPlugin()->GetBaseDir() — no more
  ProjectDir-based path, no more manual File.Copy in Build.cs.
- Drop Win32 dead code; plugin is Win64-only per the .uplugin whitelist.
- Update .gitignore to keep Binaries/ThirdParty/ tracked while ignoring
  build artifacts under Binaries/Win64/.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-05-06 07:42:58 +02:00
parent 57f144a4bc
commit 2915e04380
7 changed files with 157 additions and 134 deletions

11
.gitignore vendored
View File

@@ -1,8 +1,11 @@
# Unreal generated / per-machine
Unreal/PS_Dinkey/.vs/
Unreal/PS_Dinkey/Binaries/Win64/PS_DinkeyEditor.target
Unreal/PS_Dinkey/Binaries/
Unreal/PS_Dinkey/DerivedDataCache/
Unreal/PS_Dinkey/Intermediate/
Unreal/PS_Dinkey/Plugins/PS_Dinkey/Binaries/Win64/UE4Editor.modules
Unreal/PS_Dinkey/Plugins/PS_Dinkey/Binaries/
Unreal/PS_Dinkey/Plugins/PS_Dinkey/Intermediate/
Unreal/PS_Dinkey/Saved/
# Plugin generated — but keep Binaries/ThirdParty (DLLs shipped with the plugin)
Unreal/PS_Dinkey/Plugins/PS_Dinkey/Binaries/*
!Unreal/PS_Dinkey/Plugins/PS_Dinkey/Binaries/ThirdParty/
Unreal/PS_Dinkey/Plugins/PS_Dinkey/Intermediate/

View File

@@ -8,26 +8,21 @@
DECLARE_LOG_CATEGORY_EXTERN(LogDinkeyPlugin, Log, All);
typedef struct DRIS;
UCLASS()
class UDinkey : public UObject
{
GENERATED_UCLASS_BODY()
public:
#if DINKEY_DYNAMIC_LINKAGE
static void *dpWinDllHandle;
typedef int(*_DDProtCheck)(DRIS *dris, BYTE *data);
static _DDProtCheck DDProtCheck;
static void Initialize();
static void Finalize();
#endif
UFUNCTION(BlueprintCallable, Category = "Dinkey")
static int ProtCheck(); // a standard protection check
private:
static void* DpWinDllHandle;
static void random_set(void *buffer, int length);
static void DisplayError(int ret_code, int extended_error);
};

View File

@@ -1,107 +1,37 @@
// Copyright 2017 Polymorph. All Rights Reserved.
#define DYNAMIC_LINKAGE
using System.IO;
using UnrealBuildTool;
public class DinkeyPlugin : ModuleRules
{
private string ModulePath { get { return ModuleDirectory; } }
private string ThirdPartyPath { get { return Path.GetFullPath(Path.Combine(ModulePath, "../../../../Dependencies")); } }
public DinkeyPlugin(ReadOnlyTargetRules Target) : base(Target)
{
PublicDependencyModuleNames.AddRange(
new string[]
{
"Core",
// ... add other public dependencies that you statically link with here ...
}
);
PrivateDependencyModuleNames.AddRange(
new string[]
{
"CoreUObject",
"Engine",
"Slate",
"SlateCore",
// ... add private dependencies that you statically link with here ...
}
);
DynamicallyLoadedModuleNames.AddRange(
new string[]
{
// ... add any modules that your module loads dynamically here ...
}
);
PublicDependencyModuleNames.AddRange(new string[]
{
"Core",
"Projects",
});
#if DYNAMIC_LINKAGE
PublicDefinitions.Add("DINKEY_DYNAMIC_LINKAGE=1");
#endif
// Dinkey dependencies
string dinkeyPlatformString = (Target.Platform == UnrealTargetPlatform.Win64 ? "64" : "32");
//string dinkeySDKPath = Path.Combine(ThirdPartyPath, "DinkeyPro/Dinkey Pro 7.2.0/Samples/C");
string dinkeySDKPath = Path.Combine(ThirdPartyPath, "DinkeyPro/Dinkey Pro 7.6.1/Samples/C");
PrivateDependencyModuleNames.AddRange(new string[]
{
"CoreUObject",
"Engine",
"Slate",
"SlateCore",
});
// Compile/Link dependencies
PublicIncludePaths.Add(dinkeySDKPath);
#if DYNAMIC_LINKAGE
string dpwinDLLName = string.Format("dpwin{0}.dll", dinkeyPlatformString);
//string dinkeyDLLsPath = Path.Combine(ThirdPartyPath, string.Format("DinkeyPro/Dinkey Pro 7.2.0/Modules/{0}bit", dinkeyPlatformString));
//string dinkeyDLLsPath = Path.Combine(ThirdPartyPath, string.Format("DinkeyPro/Dinkey Pro 7.6.1/Modules/{0}bit", dinkeyPlatformString));
//string dpwinDLLPath = Path.Combine(dinkeyDLLsPath, dpwinDLLName);
// Runtime dependencies (DLLs)
PublicDelayLoadDLLs.Add(dpwinDLLName);
if (Target.Platform == UnrealTargetPlatform.Win64)
{
string ThirdPartyDir = Path.Combine(PluginDirectory, "Source", "ThirdParty", "DinkeyPro");
// RuntimeDependencies.Add(string.Format("$(EngineDir)/Binaries/ThirdParty/DinkeyPro/Win{0}/{1}", dinkeyPlatformString, dpwinDLLName));
PublicSystemIncludePaths.Add(Path.Combine(ThirdPartyDir, "Include"));
PublicAdditionalLibraries.Add(Path.Combine(ThirdPartyDir, "Lib", "Win64", "dpwin64.lib"));
if (Target.Type == TargetRules.TargetType.Editor) {
string srcPath = Path.Combine(ModulePath, "../../ThirdParty/Win64/dpwin64.dll");
string destPath = Path.Combine(ModulePath, "../../../../Binaries/Win64/dpwin64.dll");
// Only copy if destination is missing or source is newer
bool needCopy = !File.Exists(destPath);
if (!needCopy)
{
var srcInfo = new FileInfo(srcPath);
var destInfo = new FileInfo(destPath);
needCopy = srcInfo.LastWriteTimeUtc > destInfo.LastWriteTimeUtc
|| srcInfo.Length != destInfo.Length;
}
if (needCopy)
{
try
{
File.Copy(srcPath, destPath, true);
}
catch (IOException)
{
// DLL is locked by a running process (e.g. editor during Live Coding) — skip copy.
// The existing DLL is already in place and will be used as-is.
System.Console.WriteLine("DinkeyPlugin: Could not copy dpwin64.dll (file locked). Using existing copy.");
}
}
PublicDelayLoadDLLs.Add("dpwin64.dll");
RuntimeDependencies.Add(
Path.Combine(PluginDirectory, "Binaries", "ThirdParty", "DinkeyPro", "Win64", "dpwin64.dll"),
StagedFileType.NonUFS);
}
RuntimeDependencies.Add(string.Format("$(ProjectDir)/Binaries/Win{0}/{1}", dinkeyPlatformString, dpwinDLLName));
/* if (Target.Type == TargetRules.TargetType.Editor) {
// Copy DLLs into Project Binaries Engine folders
string dinkeyUnrealEngineThirdPartyPath = Path.Combine(Target.RelativeEnginePath, string.Format("Binaries/ThirdParty/DinkeyPro/Win{0}", dinkeyPlatformString));
Directory.CreateDirectory(dinkeyUnrealEngineThirdPartyPath);
File.Copy(string.Format("$(ProjectDir)/Binaries/Win{0}/{1}", dinkeyPlatformString, dpwinDLLName), Path.Combine(dinkeyUnrealEngineThirdPartyPath, dpwinDLLName), true);
}*/
#else
// PublicAdditionalLibraries.Add(Path.Combine(dinkeySDKPath, string.Format("import_libs/dpwin{0}{1}.lib", dinkeyPlatformString, dinkeyConfigurationString)));
PublicAdditionalLibraries.Add(Path.Combine(dinkeySDKPath, string.Format("object_modules/dpwin{0}.obj", dinkeyPlatformString)));
#endif
}
}

View File

@@ -1,57 +1,52 @@
// Copyright 2017 Polymorph. All Rights Reserved.
#include "Dinkey.h"
#include "Interfaces/IPluginManager.h"
#include "Misc/Paths.h"
#include "dris.h" // for DRIS definition + DDProtCheck declaration
DEFINE_LOG_CATEGORY(LogDinkeyPlugin);
#include "dris.h" // for DRIS definition
#define DISABLE_PROTECTION 0
#define ASTERION_PS 13290
#define MY_PRODCODE "PROSERVE" // Dongle Product Code
#ifdef DINKEY_DYNAMIC_LINKAGE
void *UDinkey::dpWinDllHandle = NULL;
UDinkey::_DDProtCheck UDinkey::DDProtCheck = NULL;
void* UDinkey::DpWinDllHandle = nullptr;
void UDinkey::Initialize() {
UE_LOG(LogDinkeyPlugin, Log, TEXT("UDinkey::Initialize()"));
// Load the Dinkey dynamic library
#if PLATFORM_64BITS
FString Platform = TEXT("64");
#else
FString Platform = TEXT("32");
#endif
//FString dpwinDLLFilename = FPaths::EngineDir() / FString::Printf(TEXT("Binaries/ThirdParty/DinkeyPro/Win%s/dpwin%s.dll"), *Platform, *Platform);
FString dpwinDLLFilename = FPaths::ProjectDir() / FString::Printf(TEXT("Binaries/Win%s/dpwin%s.dll"), *Platform, *Platform);
if (FPaths::FileExists(dpwinDLLFilename)) {
dpWinDllHandle = FPlatformProcess::GetDllHandle(*dpwinDLLFilename);
const TSharedPtr<IPlugin> Plugin = IPluginManager::Get().FindPlugin(TEXT("DinkeyPlugin"));
if (!Plugin.IsValid()) {
UE_LOG(LogDinkeyPlugin, Fatal, TEXT("DinkeyPlugin not found by IPluginManager"));
return;
}
if (dpWinDllHandle != NULL) {
DDProtCheck = NULL;
DDProtCheck = (_DDProtCheck)FPlatformProcess::GetDllExport(dpWinDllHandle, TEXT("DDProtCheck"));
const FString DllPath = FPaths::Combine(
Plugin->GetBaseDir(),
TEXT("Binaries/ThirdParty/DinkeyPro/Win64/dpwin64.dll"));
if (!FPaths::FileExists(DllPath)) {
UE_LOG(LogDinkeyPlugin, Fatal, TEXT("Dinkey DLL not found at: %s"), *DllPath);
return;
}
if (dpWinDllHandle == NULL || DDProtCheck == NULL) {
UE_LOG(LogDinkeyPlugin, Fatal, TEXT("Unable to load Dinkey Library : "));
DpWinDllHandle = FPlatformProcess::GetDllHandle(*DllPath);
if (DpWinDllHandle == nullptr) {
UE_LOG(LogDinkeyPlugin, Fatal, TEXT("Failed to load Dinkey DLL: %s"), *DllPath);
}
}
void UDinkey::Finalize() {
UE_LOG(LogDinkeyPlugin, Log, TEXT("UDinkey::Finalize()"));
// Unload the Dinkey dynamic library
if (dpWinDllHandle != NULL) {
DDProtCheck = NULL;
FPlatformProcess::FreeDllHandle(dpWinDllHandle);
dpWinDllHandle = NULL;
if (DpWinDllHandle != nullptr) {
FPlatformProcess::FreeDllHandle(DpWinDllHandle);
DpWinDllHandle = nullptr;
}
}
#endif
UDinkey::UDinkey(const FObjectInitializer& ObjectInitializer)
: Super(ObjectInitializer) {
@@ -69,16 +64,15 @@ int UDinkey::ProtCheck() {
dris.function = PROTECTION_CHECK; // standard protection check
dris.flags = 0; // no extra flags, but you may want to specify some if you want to start a network user or decrement execs,...
#ifdef DINKEY_DYNAMIC_LINKAGE
if (DDProtCheck == NULL) {
UE_LOG(LogDinkeyPlugin, Error, TEXT("Dinkey library not correctly loaded !"));
if (DpWinDllHandle == nullptr) {
UE_LOG(LogDinkeyPlugin, Error, TEXT("Dinkey library not loaded!"));
#if DISABLE_PROTECTION || WITH_EDITOR
return 0;
#else
return -1;
#endif
}
#endif
ret_code = DDProtCheck(&dris, NULL);
if (ret_code != 0) {

View File

@@ -0,0 +1,101 @@
// dris.h
// Dinkey Runtime Information Structure (DRIS)
// !! this file should not be modified
#pragma pack(1)
typedef struct DRIS {
// the first 4 fields are never encrypted
char header[4]; // should be set to DRIS
// inputs
int size; // size of this structure
int seed1; // seed for data/dris encryption
int seed2; // as above
// (maybe encrypted from now on)
int function; // specify only one function
int flags; // options for the function selected. To use more than one OR them together: OPTION1 | OPTION2...
unsigned int execs_decrement; // amount by which to dec execs if we use flag: DEC_MANY_EXECS
int data_crypt_key_num; // number of the key (1-3) that the dongle uses to encrypt or decrypt user data
int rw_offset; // offset in the dongle data area to read or write data
int rw_length; // length of data are to read/write/encrypt/decrypt
char *rw_data_ptr; // pointer to data to write / be read
char alt_licence_name[256]; // protection check for different licence instead of the default one, must be null-terminated
int var_a; // variable values for user algorithm
int var_b;
int var_c;
int var_d;
int var_e;
int var_f;
int var_g;
int var_h;
int alg_number; // the number of the user algorithm that you want to execute
// outputs
int ret_code; // return code from the protection check
int ext_err; // extended error
int type; // type of dongle detected. 1 = Pro, 2 = FD
int model; // model of dongle detected. 1 = Lite, 2 = Plus, 4 = Net 5, 7 = Net Unlimited
int sdsn; // Software Developer's Serial Number
char prodcode[12]; // Product Code (null-terminated)
unsigned int dongle_number;
int update_number;
unsigned int data_area_size; // size of the data area used in the dongle detected
int max_alg_num; // the maximum algorithm number in the dongle detected
int execs; // executions left: -1 indicates 'no limit'
int exp_day; // expiry day: -1 indicates 'no limit'
int exp_month; // expiry month: -1 indicates 'no limit'
int exp_year; // expiry year: -1 indicates 'no limit'
unsigned int features; // features value
int net_users; // maximum number of network users for the dongle detected: -1 indicates 'no limit'
int alg_answer; // answer to the user algorithm executed with the given variable values
unsigned int fd_capacity; // capacity of the data area in FD dongle.
char fd_drive[128]; // drive letter of FD dongle detected e.g. 'f:\' (or mount name under Linux)
int swkey_type; // 0 = no swkey detected, 1 = temporary software key, 2 = demo software key
int swkey_exp_day; // software key expiry date (if software key detected)
int swkey_exp_month;
int swkey_exp_year;
} DRIS;
typedef struct NU_INFO {
char licenceName[256];
char userName[50];
char computerName[256];
char ipAddress[16];
} NU_INFO;
#pragma pack()
// functions - must specify only one
#define PROTECTION_CHECK 1 // checks for dongle, check program params...
#define EXECUTE_ALGORITHM 2 // protection check + calculate answer for specified algorithm with specified inputs
#define WRITE_DATA_AREA 3 // protection check + writes dongle data area
#define READ_DATA_AREA 4 // protection check + reads dongle data area
#define ENCRYPT_USER_DATA 5 // protection check + the dongle will encrypt user data
#define DECRYPT_USER_DATA 6 // protection check + the dongle will decrypt user data
#define FAST_PRESENCE_CHECK 7 // checks for the presence of the correct dongle only with minimal security, no flags allowed.
#define STOP_NET_USER 8 // stops a network user (a protection check is NOT performed)
// flags - can specify as many as you like
#define DEC_ONE_EXEC 1 // decrement execs by 1
#define DEC_MANY_EXECS 2 // decrement execs by number specified in execs_decrement
#define START_NET_USER 4 // starts a network user
#define USE_FUNCTION_ARGUMENT 16 // use the extra argument in the function for pointers
#define CHECK_LOCAL_FIRST 32 // always look in local ports before looking in network ports
#define CHECK_NETWORK_FIRST 64 // always look on the network before looking in local ports
#define USE_ALT_LICENCE_NAME 128 // use name specified in alt_licence_name instead of the default one
#define DONT_SET_MAXDAYS_EXPIRY 256 // if the max days expiry date has not been calculated then do not do it this time
#define MATCH_DONGLE_NUMBER 512 // restrict the search to match the dongle number specified in the DRIS
#define DONT_RETURN_FD_DRIVE 1024 // if an FD dongle has been detected then don't return the flash drive/mount name in the DRIS
// Runtime module API declarations
#ifdef __cplusplus
extern "C" {
#endif
int WINAPI DDProtCheck(DRIS *dris, unsigned char *data);
int WINAPI DDGetNetUserList(char *licence_name, int *num_net_users, NU_INFO *nu_info, int num_info_structs, int *extended_error);
#ifdef __cplusplus
}
#endif