From 08ed22ed4d8cb44c27b91f9d8c32265ddbb9b835 Mon Sep 17 00:00:00 2001 From: dongwlin Date: Sat, 25 Apr 2026 14:24:52 +0800 Subject: [PATCH 1/9] fix: release partially loaded native libraries on init failure Co-authored-by: Copilot --- internal/native/agent_client.go | 7 +++- internal/native/agent_server.go | 7 +++- internal/native/framework.go | 61 ++++++++++++++++--------------- internal/native/native.go | 65 ++++++++++++++++++++++++--------- internal/native/toolkit.go | 11 ++++-- 5 files changed, 97 insertions(+), 54 deletions(-) diff --git a/internal/native/agent_client.go b/internal/native/agent_client.go index a1d1200..6f5a26b 100644 --- a/internal/native/agent_client.go +++ b/internal/native/agent_client.go @@ -8,7 +8,10 @@ import ( "github.com/ebitengine/purego" ) -var maaAgentClient uintptr +var ( + maaAgentClient uintptr + maaAgentClientName = "MaaAgentClient" +) var ( MaaAgentClientCreateV2 func(identifier uintptr) uintptr @@ -35,7 +38,7 @@ func initAgentClient(libDir string) error { handle, err := openLibrary(libPath) if err != nil { return &LibraryLoadError{ - LibraryName: "MaaAgentClient", + LibraryName: maaAgentClientName, LibraryPath: libPath, Err: err, } diff --git a/internal/native/agent_server.go b/internal/native/agent_server.go index 038a801..1d3039c 100644 --- a/internal/native/agent_server.go +++ b/internal/native/agent_server.go @@ -9,7 +9,10 @@ import ( "github.com/ebitengine/purego" ) -var maaAgentServer uintptr +var ( + maaAgentServer uintptr + maaAgentServerName = "MaaAgentServer" +) var ( MaaAgentServerRegisterCustomRecognition func(name string, recognition MaaCustomRecognitionCallback, transArg unsafe.Pointer) bool @@ -31,7 +34,7 @@ func initAgentServer(libDir string) error { handle, err := openLibrary(libPath) if err != nil { return &LibraryLoadError{ - LibraryName: "MaaAgentServer", + LibraryName: maaAgentServerName, LibraryPath: libPath, Err: err, } diff --git a/internal/native/framework.go b/internal/native/framework.go index b2afa88..82391a0 100644 --- a/internal/native/framework.go +++ b/internal/native/framework.go @@ -9,7 +9,10 @@ import ( "github.com/ebitengine/purego" ) -var maaFramework uintptr +var ( + maaFramework uintptr + maaFrameworkName = "MaaFramework" +) var ( MaaVersion func() string @@ -43,13 +46,13 @@ var ( MaaTaskerGetResource func(tasker uintptr) uintptr MaaTaskerGetController func(tasker uintptr) uintptr MaaTaskerClearCache func(tasker uintptr) bool - MaaTaskerGetRecognitionDetail func(tasker uintptr, recoId int64, nodeName uintptr, algorithm uintptr, hit *bool, box uintptr, detailJson uintptr, raw uintptr, draws uintptr) bool - MaaTaskerGetActionDetail func(tasker uintptr, actionId int64, nodeName uintptr, action uintptr, box uintptr, success *bool, detailJson uintptr) bool - MaaTaskerGetWaitFreezesDetail func(tasker uintptr, wfId int64, nodeName uintptr, phase uintptr, success *bool, elapsedMs *uint64, recoIdList uintptr, recoIdListSize *uint64, roi uintptr) bool - MaaTaskerGetNodeDetail func(tasker uintptr, nodeId int64, nodeName uintptr, recoId *int64, actionId *int64, completed *bool) bool - MaaTaskerGetTaskDetail func(tasker uintptr, taskId int64, entry uintptr, nodeIdList uintptr, nodeIdListSize *uint64, status *int32) bool - MaaTaskerGetLatestNode func(tasker uintptr, taskName string, latestId *int64) bool - MaaTaskerOverridePipeline func(tasker uintptr, taskId int64, pipelineOverride string) bool + MaaTaskerGetRecognitionDetail func(tasker uintptr, recoId int64, nodeName uintptr, algorithm uintptr, hit *bool, box uintptr, detailJson uintptr, raw uintptr, draws uintptr) bool + MaaTaskerGetActionDetail func(tasker uintptr, actionId int64, nodeName uintptr, action uintptr, box uintptr, success *bool, detailJson uintptr) bool + MaaTaskerGetWaitFreezesDetail func(tasker uintptr, wfId int64, nodeName uintptr, phase uintptr, success *bool, elapsedMs *uint64, recoIdList uintptr, recoIdListSize *uint64, roi uintptr) bool + MaaTaskerGetNodeDetail func(tasker uintptr, nodeId int64, nodeName uintptr, recoId *int64, actionId *int64, completed *bool) bool + MaaTaskerGetTaskDetail func(tasker uintptr, taskId int64, entry uintptr, nodeIdList uintptr, nodeIdListSize *uint64, status *int32) bool + MaaTaskerGetLatestNode func(tasker uintptr, taskName string, latestId *int64) bool + MaaTaskerOverridePipeline func(tasker uintptr, taskId int64, pipelineOverride string) bool ) type MaaCustomRecognitionCallback func(context uintptr, taskId int64, currentTaskName, customRecognitionName, customRecognitionParam *byte, image, roi, transArg, outBox, outDetail uintptr) uintptr @@ -199,9 +202,9 @@ const ( type MaaMacOSInputMethod uint64 const ( - MaaMacOSInputMethod_None MaaMacOSInputMethod = 0 - MaaMacOSInputMethod_GlobalEvent MaaMacOSInputMethod = 1 - MaaMacOSInputMethod_PostToPid MaaMacOSInputMethod = 1 << 1 + MaaMacOSInputMethod_None MaaMacOSInputMethod = 0 + MaaMacOSInputMethod_GlobalEvent MaaMacOSInputMethod = 1 + MaaMacOSInputMethod_PostToPid MaaMacOSInputMethod = 1 << 1 ) // NOTE: MaaDbgControllerCreate is intentionally NOT implemented in the Go binding. @@ -212,23 +215,23 @@ const ( // The api-check CI tool also blacklists MaaDbgControllerCreate for the same reason. var ( - MaaAdbControllerCreate func(adbPath, address string, screencapMethods uint64, inputMethods uint64, config, agentPath string) uintptr - MaaPlayCoverControllerCreate func(address, uuid string) uintptr - MaaWin32ControllerCreate func(hWnd unsafe.Pointer, screencapMethods uint64, mouseMethod, keyboardMethod uint64) uintptr - MaaWlRootsControllerCreate func(wlrSocketPath string) uintptr - MaaCustomControllerCreate func(controller unsafe.Pointer, controllerArg uintptr) uintptr - MaaGamepadControllerCreate func(hWnd unsafe.Pointer, gamepadType MaaGamepadType, screencapMethod uint64) uintptr - MaaMacOSControllerCreate func(windowID uint32, screencapMethod MaaMacOSScreencapMethod, inputMethod MaaMacOSInputMethod) uintptr - MaaAndroidNativeControllerCreate func(configJson string) uintptr - MaaReplayControllerCreate func(recordingPath string) uintptr - MaaRecordControllerCreate func(inner uintptr, recordingPath string) uintptr - MaaControllerDestroy func(ctrl uintptr) - MaaControllerAddSink func(ctrl uintptr, sink MaaEventCallback, transArg uintptr) int64 - MaaControllerRemoveSink func(ctrl uintptr, sinkId int64) - MaaControllerClearSinks func(ctrl uintptr) - MaaControllerSetOption func(ctrl uintptr, key MaaCtrlOption, value unsafe.Pointer, valSize uint64) bool - MaaControllerPostConnection func(ctrl uintptr) int64 - MaaControllerPostClick func(ctrl uintptr, x, y int32) int64 + MaaAdbControllerCreate func(adbPath, address string, screencapMethods uint64, inputMethods uint64, config, agentPath string) uintptr + MaaPlayCoverControllerCreate func(address, uuid string) uintptr + MaaWin32ControllerCreate func(hWnd unsafe.Pointer, screencapMethods uint64, mouseMethod, keyboardMethod uint64) uintptr + MaaWlRootsControllerCreate func(wlrSocketPath string) uintptr + MaaCustomControllerCreate func(controller unsafe.Pointer, controllerArg uintptr) uintptr + MaaGamepadControllerCreate func(hWnd unsafe.Pointer, gamepadType MaaGamepadType, screencapMethod uint64) uintptr + MaaMacOSControllerCreate func(windowID uint32, screencapMethod MaaMacOSScreencapMethod, inputMethod MaaMacOSInputMethod) uintptr + MaaAndroidNativeControllerCreate func(configJson string) uintptr + MaaReplayControllerCreate func(recordingPath string) uintptr + MaaRecordControllerCreate func(inner uintptr, recordingPath string) uintptr + MaaControllerDestroy func(ctrl uintptr) + MaaControllerAddSink func(ctrl uintptr, sink MaaEventCallback, transArg uintptr) int64 + MaaControllerRemoveSink func(ctrl uintptr, sinkId int64) + MaaControllerClearSinks func(ctrl uintptr) + MaaControllerSetOption func(ctrl uintptr, key MaaCtrlOption, value unsafe.Pointer, valSize uint64) bool + MaaControllerPostConnection func(ctrl uintptr) int64 + MaaControllerPostClick func(ctrl uintptr, x, y int32) int64 // for adb controller, contact means finger id (0 for first finger, 1 for second finger, etc) // for win32 controller, contact means mouse button id (0 for left, 1 for right, 2 for middle) MaaControllerPostClickV2 func(ctrl uintptr, x, y, contact, pressure int32) int64 @@ -386,7 +389,7 @@ func initFramework(libDir string) error { handle, err := openLibrary(libPath) if err != nil { return &LibraryLoadError{ - LibraryName: "MaaFramework", + LibraryName: maaFrameworkName, LibraryPath: libPath, Err: err, } diff --git a/internal/native/native.go b/internal/native/native.go index 4d521f3..4b17520 100644 --- a/internal/native/native.go +++ b/internal/native/native.go @@ -2,6 +2,27 @@ package native +import ( + "errors" + "fmt" +) + +type Library struct { + name string + init func(string) error + release func() error +} + +var ( + libraries = []Library{ + {name: maaFrameworkName, init: initFramework, release: unregisterFramework}, + {name: maaToolkitName, init: initToolkit, release: unregisterToolkit}, + {name: maaAgentServerName, init: initAgentServer, release: unregisterAgentServer}, + {name: maaAgentClientName, init: initAgentClient, release: unregisterAgentClient}, + } + loadedLibs []Library +) + func Init(libDir string) error { err := handleLibDir(libDir) @@ -9,35 +30,45 @@ func Init(libDir string) error { return err } - initFns := []func(libDir string) error{ - initFramework, - initToolkit, - initAgentServer, - initAgentClient, - } + for _, lib := range libraries { + if err := lib.init(libDir); err != nil { + releaseErr := Release() - for _, initFn := range initFns { - if err := initFn(libDir); err != nil { + if releaseErr != nil { + return fmt.Errorf("%w; error while releasing already loaded libraries: %w", err, releaseErr) + } return err } + loadedLibs = append(loadedLibs, lib) } return nil } func Release() error { - releaseFns := []func() error{ - unregisterFramework, - unregisterToolkit, - unregisterAgentServer, - unregisterAgentClient, - } - for _, releaseFn := range releaseFns { - if err := releaseFn(); err != nil { - return err + var ( + errs []error + failed []Library + ) + + for i := len(loadedLibs) - 1; i >= 0; i-- { + lib := loadedLibs[i] + if err := lib.release(); err != nil { + errs = append(errs, err) + failed = append(failed, lib) } } + for i, j := 0, len(failed)-1; i < j; i, j = i+1, j-1 { + failed[i], failed[j] = failed[j], failed[i] + } + + loadedLibs = failed + + if len(errs) > 0 { + return fmt.Errorf("failed to release libraries: %w", errors.Join(errs...)) + } + return nil } diff --git a/internal/native/toolkit.go b/internal/native/toolkit.go index 8955d99..ed255d7 100644 --- a/internal/native/toolkit.go +++ b/internal/native/toolkit.go @@ -9,7 +9,10 @@ import ( "github.com/ebitengine/purego" ) -var maaToolkit uintptr +var ( + maaToolkit uintptr + maaToolkitName = "MaaToolkit" +) var MaaToolkitConfigInitOption func(userPath, defaultJson string) bool @@ -48,8 +51,8 @@ var ( ) var ( - MaaToolkitMacOSCheckPermission func(perm MaaMacOSPermission) bool - MaaToolkitMacOSRequestPermission func(perm MaaMacOSPermission) bool + MaaToolkitMacOSCheckPermission func(perm MaaMacOSPermission) bool + MaaToolkitMacOSRequestPermission func(perm MaaMacOSPermission) bool MaaToolkitMacOSRevealPermissionSettings func(perm MaaMacOSPermission) bool ) @@ -60,7 +63,7 @@ func initToolkit(libDir string) error { handle, err := openLibrary(libPath) if err != nil { return &LibraryLoadError{ - LibraryName: "MaaToolkit", + LibraryName: maaToolkitName, LibraryPath: libPath, Err: err, } From 9bcc4925164ea4a124dafd1cb7b1a4ef1b4789d2 Mon Sep 17 00:00:00 2001 From: dongwlin Date: Sat, 25 Apr 2026 14:36:27 +0800 Subject: [PATCH 2/9] refactor: rename Init to Initialize and Release to Shutdown for clarity --- internal/native/native.go | 6 +++--- maa.go | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/internal/native/native.go b/internal/native/native.go index 4b17520..18c17be 100644 --- a/internal/native/native.go +++ b/internal/native/native.go @@ -23,7 +23,7 @@ var ( loadedLibs []Library ) -func Init(libDir string) error { +func Initialize(libDir string) error { err := handleLibDir(libDir) if err != nil { @@ -32,7 +32,7 @@ func Init(libDir string) error { for _, lib := range libraries { if err := lib.init(libDir); err != nil { - releaseErr := Release() + releaseErr := Shutdown() if releaseErr != nil { return fmt.Errorf("%w; error while releasing already loaded libraries: %w", err, releaseErr) @@ -45,7 +45,7 @@ func Init(libDir string) error { return nil } -func Release() error { +func Shutdown() error { var ( errs []error diff --git a/maa.go b/maa.go index e0b3c88..85ca559 100644 --- a/maa.go +++ b/maa.go @@ -155,14 +155,14 @@ func Init(opts ...InitOption) error { opt(&cfg) } - if err := native.Init(cfg.LibDir); err != nil { + if err := native.Initialize(cfg.LibDir); err != nil { return err } success := false defer func() { if !success { - _ = native.Release() + _ = native.Shutdown() } }() @@ -221,7 +221,7 @@ func Release() error { return ErrNotInitialized } - if err := native.Release(); err != nil { + if err := native.Shutdown(); err != nil { return err } From 44ac811df19e88f428d0b777a8cf299b9647b222 Mon Sep 17 00:00:00 2001 From: dongwlin Date: Sat, 25 Apr 2026 15:02:21 +0800 Subject: [PATCH 3/9] refactor: make Init idempotent by removing ErrAlreadyInitialized --- maa.go | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/maa.go b/maa.go index 85ca559..61d64bb 100644 --- a/maa.go +++ b/maa.go @@ -10,7 +10,6 @@ import ( var ( inited bool - ErrAlreadyInitialized = errors.New("maa framework already initialized") ErrNotInitialized = errors.New("maa framework not initialized") ErrSetLogDir = errors.New("failed to set log directory") ErrSetSaveDraw = errors.New("failed to set save draw option") @@ -146,7 +145,7 @@ func WithJSONDecoder(decoder JSONDecoder) InitOption { func Init(opts ...InitOption) error { if inited { - return ErrAlreadyInitialized + return nil } cfg := initConfig{} From b159daa7dec92e227ed67769dce697dc7099f725 Mon Sep 17 00:00:00 2001 From: dongwlin Date: Sat, 25 Apr 2026 15:06:43 +0800 Subject: [PATCH 4/9] refactor: rename Init to Initialize in TestMain for consistency --- internal/buffer/main_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/internal/buffer/main_test.go b/internal/buffer/main_test.go index 8e51aa7..17d22bf 100644 --- a/internal/buffer/main_test.go +++ b/internal/buffer/main_test.go @@ -8,7 +8,7 @@ import ( ) func TestMain(m *testing.M) { - native.Init("") + native.Initialize("") os.Exit(m.Run()) } From 5919381086f93cecd7d544ce3ff3c5c2e8a6d31a Mon Sep 17 00:00:00 2001 From: dongwlin Date: Sat, 25 Apr 2026 17:05:44 +0800 Subject: [PATCH 5/9] refactor: make Release idempotent by removing ErrNotInitialized Co-authored-by: Copilot --- maa.go | 6 ------ 1 file changed, 6 deletions(-) diff --git a/maa.go b/maa.go index 61d64bb..830ec30 100644 --- a/maa.go +++ b/maa.go @@ -10,7 +10,6 @@ import ( var ( inited bool - ErrNotInitialized = errors.New("maa framework not initialized") ErrSetLogDir = errors.New("failed to set log directory") ErrSetSaveDraw = errors.New("failed to set save draw option") ErrSetStdoutLevel = errors.New("failed to set stdout level") @@ -213,13 +212,8 @@ func IsInited() bool { } // Release releases the dynamic library resources of the MAA framework and unregisters its related functions. -// It must be called only after the framework has been initialized via Init. func Release() error { - if !inited { - return ErrNotInitialized - } - if err := native.Shutdown(); err != nil { return err } From f33aff22a9740d639af1fa690f69205520ad3fce Mon Sep 17 00:00:00 2001 From: dongwlin Date: Sun, 26 Apr 2026 02:13:24 +0800 Subject: [PATCH 6/9] refactor: centralize native symbol registration and cleanup - move native symbol bindings into Entry tables - clear registered function vars after successful unload - update api-check for Entry-based registrations Co-authored-by: Copilot --- internal/native/agent_client.go | 53 ++- internal/native/agent_server.go | 43 ++- internal/native/framework.go | 357 +++++++++--------- internal/native/native.go | 22 +- internal/native/toolkit.go | 75 ++-- tools/api-check/README.md | 4 +- .../internal/checker/native_check.go | 148 +++++--- .../internal/checker/native_check_test.go | 65 ++++ 8 files changed, 483 insertions(+), 284 deletions(-) create mode 100644 tools/api-check/internal/checker/native_check_test.go diff --git a/internal/native/agent_client.go b/internal/native/agent_client.go index 6f5a26b..13116a5 100644 --- a/internal/native/agent_client.go +++ b/internal/native/agent_client.go @@ -31,6 +31,24 @@ var ( MaaAgentClientGetCustomActionList func(client uintptr, buffer uintptr) bool ) +var agentClientEntries = []Entry{ + {&MaaAgentClientCreateV2, "MaaAgentClientCreateV2"}, + {&MaaAgentClientCreateTcp, "MaaAgentClientCreateTcp"}, + {&MaaAgentClientDestroy, "MaaAgentClientDestroy"}, + {&MaaAgentClientIdentifier, "MaaAgentClientIdentifier"}, + {&MaaAgentClientBindResource, "MaaAgentClientBindResource"}, + {&MaaAgentClientRegisterResourceSink, "MaaAgentClientRegisterResourceSink"}, + {&MaaAgentClientRegisterControllerSink, "MaaAgentClientRegisterControllerSink"}, + {&MaaAgentClientRegisterTaskerSink, "MaaAgentClientRegisterTaskerSink"}, + {&MaaAgentClientConnect, "MaaAgentClientConnect"}, + {&MaaAgentClientDisconnect, "MaaAgentClientDisconnect"}, + {&MaaAgentClientConnected, "MaaAgentClientConnected"}, + {&MaaAgentClientAlive, "MaaAgentClientAlive"}, + {&MaaAgentClientSetTimeout, "MaaAgentClientSetTimeout"}, + {&MaaAgentClientGetCustomRecognitionList, "MaaAgentClientGetCustomRecognitionList"}, + {&MaaAgentClientGetCustomActionList, "MaaAgentClientGetCustomActionList"}, +} + func initAgentClient(libDir string) error { libName := getMaaAgentClientLibrary() libPath := filepath.Join(libDir, libName) @@ -65,23 +83,24 @@ func getMaaAgentClientLibrary() string { } func registerAgentClient() { - purego.RegisterLibFunc(&MaaAgentClientCreateV2, maaAgentClient, "MaaAgentClientCreateV2") - purego.RegisterLibFunc(&MaaAgentClientCreateTcp, maaAgentClient, "MaaAgentClientCreateTcp") - purego.RegisterLibFunc(&MaaAgentClientDestroy, maaAgentClient, "MaaAgentClientDestroy") - purego.RegisterLibFunc(&MaaAgentClientIdentifier, maaAgentClient, "MaaAgentClientIdentifier") - purego.RegisterLibFunc(&MaaAgentClientBindResource, maaAgentClient, "MaaAgentClientBindResource") - purego.RegisterLibFunc(&MaaAgentClientRegisterResourceSink, maaAgentClient, "MaaAgentClientRegisterResourceSink") - purego.RegisterLibFunc(&MaaAgentClientRegisterControllerSink, maaAgentClient, "MaaAgentClientRegisterControllerSink") - purego.RegisterLibFunc(&MaaAgentClientRegisterTaskerSink, maaAgentClient, "MaaAgentClientRegisterTaskerSink") - purego.RegisterLibFunc(&MaaAgentClientConnect, maaAgentClient, "MaaAgentClientConnect") - purego.RegisterLibFunc(&MaaAgentClientDisconnect, maaAgentClient, "MaaAgentClientDisconnect") - purego.RegisterLibFunc(&MaaAgentClientConnected, maaAgentClient, "MaaAgentClientConnected") - purego.RegisterLibFunc(&MaaAgentClientAlive, maaAgentClient, "MaaAgentClientAlive") - purego.RegisterLibFunc(&MaaAgentClientSetTimeout, maaAgentClient, "MaaAgentClientSetTimeout") - purego.RegisterLibFunc(&MaaAgentClientGetCustomRecognitionList, maaAgentClient, "MaaAgentClientGetCustomRecognitionList") - purego.RegisterLibFunc(&MaaAgentClientGetCustomActionList, maaAgentClient, "MaaAgentClientGetCustomActionList") + for _, entry := range agentClientEntries { + purego.RegisterLibFunc(entry.ptrToFunc, maaAgentClient, entry.name) + } } -func unregisterAgentClient() error { - return unloadLibrary(maaAgentClient) +func releaseAgentClient() error { + err := unloadLibrary(maaAgentClient) + if err != nil { + return err + } + + unregisterAgentClient() + + return nil +} + +func unregisterAgentClient() { + for _, entry := range agentClientEntries { + clearFuncVar(entry.ptrToFunc) + } } diff --git a/internal/native/agent_server.go b/internal/native/agent_server.go index 1d3039c..c5ee6ee 100644 --- a/internal/native/agent_server.go +++ b/internal/native/agent_server.go @@ -27,6 +27,19 @@ var ( MaaAgentServerDetach func() ) +var agentServerEntries = []Entry{ + {&MaaAgentServerRegisterCustomRecognition, "MaaAgentServerRegisterCustomRecognition"}, + {&MaaAgentServerRegisterCustomAction, "MaaAgentServerRegisterCustomAction"}, + {&MaaAgentServerAddResourceSink, "MaaAgentServerAddResourceSink"}, + {&MaaAgentServerAddControllerSink, "MaaAgentServerAddControllerSink"}, + {&MaaAgentServerAddTaskerSink, "MaaAgentServerAddTaskerSink"}, + {&MaaAgentServerAddContextSink, "MaaAgentServerAddContextSink"}, + {&MaaAgentServerStartUp, "MaaAgentServerStartUp"}, + {&MaaAgentServerShutDown, "MaaAgentServerShutDown"}, + {&MaaAgentServerJoin, "MaaAgentServerJoin"}, + {&MaaAgentServerDetach, "MaaAgentServerDetach"}, +} + func initAgentServer(libDir string) error { libName := getMaaAgentServerLibrary() libPath := filepath.Join(libDir, libName) @@ -61,18 +74,24 @@ func getMaaAgentServerLibrary() string { } func registerAgentServer() { - purego.RegisterLibFunc(&MaaAgentServerRegisterCustomRecognition, maaAgentServer, "MaaAgentServerRegisterCustomRecognition") - purego.RegisterLibFunc(&MaaAgentServerRegisterCustomAction, maaAgentServer, "MaaAgentServerRegisterCustomAction") - purego.RegisterLibFunc(&MaaAgentServerAddResourceSink, maaAgentServer, "MaaAgentServerAddResourceSink") - purego.RegisterLibFunc(&MaaAgentServerAddControllerSink, maaAgentServer, "MaaAgentServerAddControllerSink") - purego.RegisterLibFunc(&MaaAgentServerAddTaskerSink, maaAgentServer, "MaaAgentServerAddTaskerSink") - purego.RegisterLibFunc(&MaaAgentServerAddContextSink, maaAgentServer, "MaaAgentServerAddContextSink") - purego.RegisterLibFunc(&MaaAgentServerStartUp, maaAgentServer, "MaaAgentServerStartUp") - purego.RegisterLibFunc(&MaaAgentServerShutDown, maaAgentServer, "MaaAgentServerShutDown") - purego.RegisterLibFunc(&MaaAgentServerJoin, maaAgentServer, "MaaAgentServerJoin") - purego.RegisterLibFunc(&MaaAgentServerDetach, maaAgentServer, "MaaAgentServerDetach") + for _, entry := range agentServerEntries { + purego.RegisterLibFunc(entry.ptrToFunc, maaAgentServer, entry.name) + } } -func unregisterAgentServer() error { - return unloadLibrary(maaAgentServer) +func releaseAgentServer() error { + err := unloadLibrary(maaAgentServer) + if err != nil { + return err + } + + unregisterAgentServer() + + return nil +} + +func unregisterAgentServer() { + for _, entry := range agentServerEntries { + clearFuncVar(entry.ptrToFunc) + } } diff --git a/internal/native/framework.go b/internal/native/framework.go index 82391a0..4bb9188 100644 --- a/internal/native/framework.go +++ b/internal/native/framework.go @@ -382,6 +382,173 @@ var ( MaaGlobalLoadPlugin func(path string) bool ) +var frameworkEntries = []Entry{ + {&MaaVersion, "MaaVersion"}, + {&MaaTaskerCreate, "MaaTaskerCreate"}, + {&MaaTaskerDestroy, "MaaTaskerDestroy"}, + {&MaaTaskerAddSink, "MaaTaskerAddSink"}, + {&MaaTaskerRemoveSink, "MaaTaskerRemoveSink"}, + {&MaaTaskerClearSinks, "MaaTaskerClearSinks"}, + {&MaaTaskerAddContextSink, "MaaTaskerAddContextSink"}, + {&MaaTaskerRemoveContextSink, "MaaTaskerRemoveContextSink"}, + {&MaaTaskerClearContextSinks, "MaaTaskerClearContextSinks"}, + {&MaaTaskerSetOption, "MaaTaskerSetOption"}, + {&MaaTaskerBindResource, "MaaTaskerBindResource"}, + {&MaaTaskerBindController, "MaaTaskerBindController"}, + {&MaaTaskerInited, "MaaTaskerInited"}, + {&MaaTaskerPostTask, "MaaTaskerPostTask"}, + {&MaaTaskerPostRecognition, "MaaTaskerPostRecognition"}, + {&MaaTaskerPostAction, "MaaTaskerPostAction"}, + {&MaaTaskerStopping, "MaaTaskerStopping"}, + {&MaaTaskerStatus, "MaaTaskerStatus"}, + {&MaaTaskerWait, "MaaTaskerWait"}, + {&MaaTaskerRunning, "MaaTaskerRunning"}, + {&MaaTaskerPostStop, "MaaTaskerPostStop"}, + {&MaaTaskerGetResource, "MaaTaskerGetResource"}, + {&MaaTaskerGetController, "MaaTaskerGetController"}, + {&MaaTaskerClearCache, "MaaTaskerClearCache"}, + {&MaaTaskerGetRecognitionDetail, "MaaTaskerGetRecognitionDetail"}, + {&MaaTaskerGetActionDetail, "MaaTaskerGetActionDetail"}, + {&MaaTaskerGetWaitFreezesDetail, "MaaTaskerGetWaitFreezesDetail"}, + {&MaaTaskerGetNodeDetail, "MaaTaskerGetNodeDetail"}, + {&MaaTaskerGetTaskDetail, "MaaTaskerGetTaskDetail"}, + {&MaaTaskerGetLatestNode, "MaaTaskerGetLatestNode"}, + {&MaaTaskerOverridePipeline, "MaaTaskerOverridePipeline"}, + {&MaaResourceCreate, "MaaResourceCreate"}, + {&MaaResourceDestroy, "MaaResourceDestroy"}, + {&MaaResourceAddSink, "MaaResourceAddSink"}, + {&MaaResourceRemoveSink, "MaaResourceRemoveSink"}, + {&MaaResourceClearSinks, "MaaResourceClearSinks"}, + {&MaaResourceRegisterCustomRecognition, "MaaResourceRegisterCustomRecognition"}, + {&MaaResourceUnregisterCustomRecognition, "MaaResourceUnregisterCustomRecognition"}, + {&MaaResourceClearCustomRecognition, "MaaResourceClearCustomRecognition"}, + {&MaaResourceRegisterCustomAction, "MaaResourceRegisterCustomAction"}, + {&MaaResourceUnregisterCustomAction, "MaaResourceUnregisterCustomAction"}, + {&MaaResourceClearCustomAction, "MaaResourceClearCustomAction"}, + {&MaaResourcePostBundle, "MaaResourcePostBundle"}, + {&MaaResourcePostOcrModel, "MaaResourcePostOcrModel"}, + {&MaaResourcePostPipeline, "MaaResourcePostPipeline"}, + {&MaaResourcePostImage, "MaaResourcePostImage"}, + {&MaaResourceOverridePipeline, "MaaResourceOverridePipeline"}, + {&MaaResourceOverrideNext, "MaaResourceOverrideNext"}, + {&MaaResourceOverrideImage, "MaaResourceOverrideImage"}, + {&MaaResourceGetNodeData, "MaaResourceGetNodeData"}, + {&MaaResourceClear, "MaaResourceClear"}, + {&MaaResourceStatus, "MaaResourceStatus"}, + {&MaaResourceWait, "MaaResourceWait"}, + {&MaaResourceLoaded, "MaaResourceLoaded"}, + {&MaaResourceSetOption, "MaaResourceSetOption"}, + {&MaaResourceGetHash, "MaaResourceGetHash"}, + {&MaaResourceGetNodeList, "MaaResourceGetNodeList"}, + {&MaaResourceGetCustomRecognitionList, "MaaResourceGetCustomRecognitionList"}, + {&MaaResourceGetCustomActionList, "MaaResourceGetCustomActionList"}, + {&MaaResourceGetDefaultRecognitionParam, "MaaResourceGetDefaultRecognitionParam"}, + {&MaaResourceGetDefaultActionParam, "MaaResourceGetDefaultActionParam"}, + {&MaaAdbControllerCreate, "MaaAdbControllerCreate"}, + {&MaaPlayCoverControllerCreate, "MaaPlayCoverControllerCreate"}, + {&MaaWin32ControllerCreate, "MaaWin32ControllerCreate"}, + {&MaaWlRootsControllerCreate, "MaaWlRootsControllerCreate"}, + {&MaaCustomControllerCreate, "MaaCustomControllerCreate"}, + {&MaaGamepadControllerCreate, "MaaGamepadControllerCreate"}, + {&MaaMacOSControllerCreate, "MaaMacOSControllerCreate"}, + {&MaaAndroidNativeControllerCreate, "MaaAndroidNativeControllerCreate"}, + {&MaaReplayControllerCreate, "MaaReplayControllerCreate"}, + {&MaaRecordControllerCreate, "MaaRecordControllerCreate"}, + {&MaaControllerDestroy, "MaaControllerDestroy"}, + {&MaaControllerAddSink, "MaaControllerAddSink"}, + {&MaaControllerRemoveSink, "MaaControllerRemoveSink"}, + {&MaaControllerClearSinks, "MaaControllerClearSinks"}, + {&MaaControllerSetOption, "MaaControllerSetOption"}, + {&MaaControllerPostConnection, "MaaControllerPostConnection"}, + {&MaaControllerPostClick, "MaaControllerPostClick"}, + {&MaaControllerPostClickV2, "MaaControllerPostClickV2"}, + {&MaaControllerPostSwipe, "MaaControllerPostSwipe"}, + {&MaaControllerPostSwipeV2, "MaaControllerPostSwipeV2"}, + {&MaaControllerPostClickKey, "MaaControllerPostClickKey"}, + {&MaaControllerPostInputText, "MaaControllerPostInputText"}, + {&MaaControllerPostStartApp, "MaaControllerPostStartApp"}, + {&MaaControllerPostStopApp, "MaaControllerPostStopApp"}, + {&MaaControllerPostTouchDown, "MaaControllerPostTouchDown"}, + {&MaaControllerPostTouchMove, "MaaControllerPostTouchMove"}, + {&MaaControllerPostTouchUp, "MaaControllerPostTouchUp"}, + {&MaaControllerPostRelativeMove, "MaaControllerPostRelativeMove"}, + {&MaaControllerPostKeyDown, "MaaControllerPostKeyDown"}, + {&MaaControllerPostKeyUp, "MaaControllerPostKeyUp"}, + {&MaaControllerPostScreencap, "MaaControllerPostScreencap"}, + {&MaaControllerPostScroll, "MaaControllerPostScroll"}, + {&MaaControllerPostInactive, "MaaControllerPostInactive"}, + {&MaaControllerPostShell, "MaaControllerPostShell"}, + {&MaaControllerGetShellOutput, "MaaControllerGetShellOutput"}, + {&MaaControllerStatus, "MaaControllerStatus"}, + {&MaaControllerWait, "MaaControllerWait"}, + {&MaaControllerConnected, "MaaControllerConnected"}, + {&MaaControllerCachedImage, "MaaControllerCachedImage"}, + {&MaaControllerGetUuid, "MaaControllerGetUuid"}, + {&MaaControllerGetResolution, "MaaControllerGetResolution"}, + {&MaaControllerGetInfo, "MaaControllerGetInfo"}, + {&MaaContextRunTask, "MaaContextRunTask"}, + {&MaaContextRunRecognition, "MaaContextRunRecognition"}, + {&MaaContextRunAction, "MaaContextRunAction"}, + {&MaaContextRunRecognitionDirect, "MaaContextRunRecognitionDirect"}, + {&MaaContextRunActionDirect, "MaaContextRunActionDirect"}, + {&MaaContextWaitFreezes, "MaaContextWaitFreezes"}, + {&MaaContextOverridePipeline, "MaaContextOverridePipeline"}, + {&MaaContextOverrideNext, "MaaContextOverrideNext"}, + {&MaaContextOverrideImage, "MaaContextOverrideImage"}, + {&MaaContextGetNodeData, "MaaContextGetNodeData"}, + {&MaaContextGetTaskId, "MaaContextGetTaskId"}, + {&MaaContextGetTasker, "MaaContextGetTasker"}, + {&MaaContextClone, "MaaContextClone"}, + {&MaaContextSetAnchor, "MaaContextSetAnchor"}, + {&MaaContextGetAnchor, "MaaContextGetAnchor"}, + {&MaaContextGetHitCount, "MaaContextGetHitCount"}, + {&MaaContextClearHitCount, "MaaContextClearHitCount"}, + {&MaaStringBufferCreate, "MaaStringBufferCreate"}, + {&MaaStringBufferDestroy, "MaaStringBufferDestroy"}, + {&MaaStringBufferIsEmpty, "MaaStringBufferIsEmpty"}, + {&MaaStringBufferClear, "MaaStringBufferClear"}, + {&MaaStringBufferGet, "MaaStringBufferGet"}, + {&MaaStringBufferSize, "MaaStringBufferSize"}, + {&MaaStringBufferSet, "MaaStringBufferSet"}, + {&MaaStringBufferSetEx, "MaaStringBufferSetEx"}, + {&MaaStringListBufferCreate, "MaaStringListBufferCreate"}, + {&MaaStringListBufferDestroy, "MaaStringListBufferDestroy"}, + {&MaaStringListBufferIsEmpty, "MaaStringListBufferIsEmpty"}, + {&MaaStringListBufferSize, "MaaStringListBufferSize"}, + {&MaaStringListBufferAt, "MaaStringListBufferAt"}, + {&MaaStringListBufferAppend, "MaaStringListBufferAppend"}, + {&MaaStringListBufferRemove, "MaaStringListBufferRemove"}, + {&MaaStringListBufferClear, "MaaStringListBufferClear"}, + {&MaaImageBufferCreate, "MaaImageBufferCreate"}, + {&MaaImageBufferDestroy, "MaaImageBufferDestroy"}, + {&MaaImageBufferIsEmpty, "MaaImageBufferIsEmpty"}, + {&MaaImageBufferClear, "MaaImageBufferClear"}, + {&MaaImageBufferGetRawData, "MaaImageBufferGetRawData"}, + {&MaaImageBufferWidth, "MaaImageBufferWidth"}, + {&MaaImageBufferHeight, "MaaImageBufferHeight"}, + {&MaaImageBufferChannels, "MaaImageBufferChannels"}, + {&MaaImageBufferType, "MaaImageBufferType"}, + {&MaaImageBufferSetRawData, "MaaImageBufferSetRawData"}, + {&MaaImageBufferResize, "MaaImageBufferResize"}, + {&MaaImageListBufferCreate, "MaaImageListBufferCreate"}, + {&MaaImageListBufferDestroy, "MaaImageListBufferDestroy"}, + {&MaaImageListBufferIsEmpty, "MaaImageListBufferIsEmpty"}, + {&MaaImageListBufferSize, "MaaImageListBufferSize"}, + {&MaaImageListBufferAt, "MaaImageListBufferAt"}, + {&MaaImageListBufferAppend, "MaaImageListBufferAppend"}, + {&MaaImageListBufferRemove, "MaaImageListBufferRemove"}, + {&MaaImageListBufferClear, "MaaImageListBufferClear"}, + {&MaaRectCreate, "MaaRectCreate"}, + {&MaaRectDestroy, "MaaRectDestroy"}, + {&MaaRectGetX, "MaaRectGetX"}, + {&MaaRectGetY, "MaaRectGetY"}, + {&MaaRectGetW, "MaaRectGetW"}, + {&MaaRectGetH, "MaaRectGetH"}, + {&MaaRectSet, "MaaRectSet"}, + {&MaaGlobalSetOption, "MaaGlobalSetOption"}, + {&MaaGlobalLoadPlugin, "MaaGlobalLoadPlugin"}, +} + func initFramework(libDir string) error { libName := getMaaFrameworkLibrary() libPath := filepath.Join(libDir, libName) @@ -416,178 +583,24 @@ func getMaaFrameworkLibrary() string { } func registerFramework() { - purego.RegisterLibFunc(&MaaVersion, maaFramework, "MaaVersion") - // Tasker - purego.RegisterLibFunc(&MaaTaskerCreate, maaFramework, "MaaTaskerCreate") - purego.RegisterLibFunc(&MaaTaskerDestroy, maaFramework, "MaaTaskerDestroy") - purego.RegisterLibFunc(&MaaTaskerAddSink, maaFramework, "MaaTaskerAddSink") - purego.RegisterLibFunc(&MaaTaskerRemoveSink, maaFramework, "MaaTaskerRemoveSink") - purego.RegisterLibFunc(&MaaTaskerClearSinks, maaFramework, "MaaTaskerClearSinks") - purego.RegisterLibFunc(&MaaTaskerAddContextSink, maaFramework, "MaaTaskerAddContextSink") - purego.RegisterLibFunc(&MaaTaskerRemoveContextSink, maaFramework, "MaaTaskerRemoveContextSink") - purego.RegisterLibFunc(&MaaTaskerClearContextSinks, maaFramework, "MaaTaskerClearContextSinks") - purego.RegisterLibFunc(&MaaTaskerSetOption, maaFramework, "MaaTaskerSetOption") - purego.RegisterLibFunc(&MaaTaskerBindResource, maaFramework, "MaaTaskerBindResource") - purego.RegisterLibFunc(&MaaTaskerBindController, maaFramework, "MaaTaskerBindController") - purego.RegisterLibFunc(&MaaTaskerInited, maaFramework, "MaaTaskerInited") - purego.RegisterLibFunc(&MaaTaskerPostTask, maaFramework, "MaaTaskerPostTask") - purego.RegisterLibFunc(&MaaTaskerPostRecognition, maaFramework, "MaaTaskerPostRecognition") - purego.RegisterLibFunc(&MaaTaskerPostAction, maaFramework, "MaaTaskerPostAction") - purego.RegisterLibFunc(&MaaTaskerStopping, maaFramework, "MaaTaskerStopping") - purego.RegisterLibFunc(&MaaTaskerStatus, maaFramework, "MaaTaskerStatus") - purego.RegisterLibFunc(&MaaTaskerWait, maaFramework, "MaaTaskerWait") - purego.RegisterLibFunc(&MaaTaskerRunning, maaFramework, "MaaTaskerRunning") - purego.RegisterLibFunc(&MaaTaskerPostStop, maaFramework, "MaaTaskerPostStop") - purego.RegisterLibFunc(&MaaTaskerGetResource, maaFramework, "MaaTaskerGetResource") - purego.RegisterLibFunc(&MaaTaskerGetController, maaFramework, "MaaTaskerGetController") - purego.RegisterLibFunc(&MaaTaskerClearCache, maaFramework, "MaaTaskerClearCache") - purego.RegisterLibFunc(&MaaTaskerGetRecognitionDetail, maaFramework, "MaaTaskerGetRecognitionDetail") - purego.RegisterLibFunc(&MaaTaskerGetActionDetail, maaFramework, "MaaTaskerGetActionDetail") - purego.RegisterLibFunc(&MaaTaskerGetWaitFreezesDetail, maaFramework, "MaaTaskerGetWaitFreezesDetail") - purego.RegisterLibFunc(&MaaTaskerGetNodeDetail, maaFramework, "MaaTaskerGetNodeDetail") - purego.RegisterLibFunc(&MaaTaskerGetTaskDetail, maaFramework, "MaaTaskerGetTaskDetail") - purego.RegisterLibFunc(&MaaTaskerGetLatestNode, maaFramework, "MaaTaskerGetLatestNode") - purego.RegisterLibFunc(&MaaTaskerOverridePipeline, maaFramework, "MaaTaskerOverridePipeline") - // Resource - purego.RegisterLibFunc(&MaaResourceCreate, maaFramework, "MaaResourceCreate") - purego.RegisterLibFunc(&MaaResourceDestroy, maaFramework, "MaaResourceDestroy") - purego.RegisterLibFunc(&MaaResourceAddSink, maaFramework, "MaaResourceAddSink") - purego.RegisterLibFunc(&MaaResourceRemoveSink, maaFramework, "MaaResourceRemoveSink") - purego.RegisterLibFunc(&MaaResourceClearSinks, maaFramework, "MaaResourceClearSinks") - purego.RegisterLibFunc(&MaaResourceRegisterCustomRecognition, maaFramework, "MaaResourceRegisterCustomRecognition") - purego.RegisterLibFunc(&MaaResourceUnregisterCustomRecognition, maaFramework, "MaaResourceUnregisterCustomRecognition") - purego.RegisterLibFunc(&MaaResourceClearCustomRecognition, maaFramework, "MaaResourceClearCustomRecognition") - purego.RegisterLibFunc(&MaaResourceRegisterCustomAction, maaFramework, "MaaResourceRegisterCustomAction") - purego.RegisterLibFunc(&MaaResourceUnregisterCustomAction, maaFramework, "MaaResourceUnregisterCustomAction") - purego.RegisterLibFunc(&MaaResourceClearCustomAction, maaFramework, "MaaResourceClearCustomAction") - purego.RegisterLibFunc(&MaaResourcePostBundle, maaFramework, "MaaResourcePostBundle") - purego.RegisterLibFunc(&MaaResourcePostOcrModel, maaFramework, "MaaResourcePostOcrModel") - purego.RegisterLibFunc(&MaaResourcePostPipeline, maaFramework, "MaaResourcePostPipeline") - purego.RegisterLibFunc(&MaaResourcePostImage, maaFramework, "MaaResourcePostImage") - purego.RegisterLibFunc(&MaaResourceOverridePipeline, maaFramework, "MaaResourceOverridePipeline") - purego.RegisterLibFunc(&MaaResourceOverrideNext, maaFramework, "MaaResourceOverrideNext") - purego.RegisterLibFunc(&MaaResourceOverrideImage, maaFramework, "MaaResourceOverrideImage") - purego.RegisterLibFunc(&MaaResourceGetNodeData, maaFramework, "MaaResourceGetNodeData") - purego.RegisterLibFunc(&MaaResourceClear, maaFramework, "MaaResourceClear") - purego.RegisterLibFunc(&MaaResourceStatus, maaFramework, "MaaResourceStatus") - purego.RegisterLibFunc(&MaaResourceWait, maaFramework, "MaaResourceWait") - purego.RegisterLibFunc(&MaaResourceLoaded, maaFramework, "MaaResourceLoaded") - purego.RegisterLibFunc(&MaaResourceSetOption, maaFramework, "MaaResourceSetOption") - purego.RegisterLibFunc(&MaaResourceGetHash, maaFramework, "MaaResourceGetHash") - purego.RegisterLibFunc(&MaaResourceGetNodeList, maaFramework, "MaaResourceGetNodeList") - purego.RegisterLibFunc(&MaaResourceGetCustomRecognitionList, maaFramework, "MaaResourceGetCustomRecognitionList") - purego.RegisterLibFunc(&MaaResourceGetCustomActionList, maaFramework, "MaaResourceGetCustomActionList") - purego.RegisterLibFunc(&MaaResourceGetDefaultRecognitionParam, maaFramework, "MaaResourceGetDefaultRecognitionParam") - purego.RegisterLibFunc(&MaaResourceGetDefaultActionParam, maaFramework, "MaaResourceGetDefaultActionParam") - // Controller - purego.RegisterLibFunc(&MaaAdbControllerCreate, maaFramework, "MaaAdbControllerCreate") - purego.RegisterLibFunc(&MaaPlayCoverControllerCreate, maaFramework, "MaaPlayCoverControllerCreate") - purego.RegisterLibFunc(&MaaWin32ControllerCreate, maaFramework, "MaaWin32ControllerCreate") - purego.RegisterLibFunc(&MaaWlRootsControllerCreate, maaFramework, "MaaWlRootsControllerCreate") - purego.RegisterLibFunc(&MaaCustomControllerCreate, maaFramework, "MaaCustomControllerCreate") - purego.RegisterLibFunc(&MaaGamepadControllerCreate, maaFramework, "MaaGamepadControllerCreate") - purego.RegisterLibFunc(&MaaMacOSControllerCreate, maaFramework, "MaaMacOSControllerCreate") - purego.RegisterLibFunc(&MaaAndroidNativeControllerCreate, maaFramework, "MaaAndroidNativeControllerCreate") - purego.RegisterLibFunc(&MaaReplayControllerCreate, maaFramework, "MaaReplayControllerCreate") - purego.RegisterLibFunc(&MaaRecordControllerCreate, maaFramework, "MaaRecordControllerCreate") - purego.RegisterLibFunc(&MaaControllerDestroy, maaFramework, "MaaControllerDestroy") - purego.RegisterLibFunc(&MaaControllerAddSink, maaFramework, "MaaControllerAddSink") - purego.RegisterLibFunc(&MaaControllerRemoveSink, maaFramework, "MaaControllerRemoveSink") - purego.RegisterLibFunc(&MaaControllerClearSinks, maaFramework, "MaaControllerClearSinks") - purego.RegisterLibFunc(&MaaControllerSetOption, maaFramework, "MaaControllerSetOption") - purego.RegisterLibFunc(&MaaControllerPostConnection, maaFramework, "MaaControllerPostConnection") - purego.RegisterLibFunc(&MaaControllerPostClick, maaFramework, "MaaControllerPostClick") - purego.RegisterLibFunc(&MaaControllerPostClickV2, maaFramework, "MaaControllerPostClickV2") - purego.RegisterLibFunc(&MaaControllerPostSwipe, maaFramework, "MaaControllerPostSwipe") - purego.RegisterLibFunc(&MaaControllerPostSwipeV2, maaFramework, "MaaControllerPostSwipeV2") - purego.RegisterLibFunc(&MaaControllerPostClickKey, maaFramework, "MaaControllerPostClickKey") - purego.RegisterLibFunc(&MaaControllerPostInputText, maaFramework, "MaaControllerPostInputText") - purego.RegisterLibFunc(&MaaControllerPostStartApp, maaFramework, "MaaControllerPostStartApp") - purego.RegisterLibFunc(&MaaControllerPostStopApp, maaFramework, "MaaControllerPostStopApp") - purego.RegisterLibFunc(&MaaControllerPostTouchDown, maaFramework, "MaaControllerPostTouchDown") - purego.RegisterLibFunc(&MaaControllerPostTouchMove, maaFramework, "MaaControllerPostTouchMove") - purego.RegisterLibFunc(&MaaControllerPostTouchUp, maaFramework, "MaaControllerPostTouchUp") - purego.RegisterLibFunc(&MaaControllerPostRelativeMove, maaFramework, "MaaControllerPostRelativeMove") - purego.RegisterLibFunc(&MaaControllerPostKeyDown, maaFramework, "MaaControllerPostKeyDown") - purego.RegisterLibFunc(&MaaControllerPostKeyUp, maaFramework, "MaaControllerPostKeyUp") - purego.RegisterLibFunc(&MaaControllerPostScreencap, maaFramework, "MaaControllerPostScreencap") - purego.RegisterLibFunc(&MaaControllerPostScroll, maaFramework, "MaaControllerPostScroll") - purego.RegisterLibFunc(&MaaControllerPostInactive, maaFramework, "MaaControllerPostInactive") - purego.RegisterLibFunc(&MaaControllerPostShell, maaFramework, "MaaControllerPostShell") - purego.RegisterLibFunc(&MaaControllerGetShellOutput, maaFramework, "MaaControllerGetShellOutput") - purego.RegisterLibFunc(&MaaControllerStatus, maaFramework, "MaaControllerStatus") - purego.RegisterLibFunc(&MaaControllerWait, maaFramework, "MaaControllerWait") - purego.RegisterLibFunc(&MaaControllerConnected, maaFramework, "MaaControllerConnected") - purego.RegisterLibFunc(&MaaControllerCachedImage, maaFramework, "MaaControllerCachedImage") - purego.RegisterLibFunc(&MaaControllerGetUuid, maaFramework, "MaaControllerGetUuid") - purego.RegisterLibFunc(&MaaControllerGetResolution, maaFramework, "MaaControllerGetResolution") - purego.RegisterLibFunc(&MaaControllerGetInfo, maaFramework, "MaaControllerGetInfo") - // Context - purego.RegisterLibFunc(&MaaContextRunTask, maaFramework, "MaaContextRunTask") - purego.RegisterLibFunc(&MaaContextRunRecognition, maaFramework, "MaaContextRunRecognition") - purego.RegisterLibFunc(&MaaContextRunAction, maaFramework, "MaaContextRunAction") - purego.RegisterLibFunc(&MaaContextRunRecognitionDirect, maaFramework, "MaaContextRunRecognitionDirect") - purego.RegisterLibFunc(&MaaContextRunActionDirect, maaFramework, "MaaContextRunActionDirect") - purego.RegisterLibFunc(&MaaContextWaitFreezes, maaFramework, "MaaContextWaitFreezes") - purego.RegisterLibFunc(&MaaContextOverridePipeline, maaFramework, "MaaContextOverridePipeline") - purego.RegisterLibFunc(&MaaContextOverrideNext, maaFramework, "MaaContextOverrideNext") - purego.RegisterLibFunc(&MaaContextOverrideImage, maaFramework, "MaaContextOverrideImage") - purego.RegisterLibFunc(&MaaContextGetNodeData, maaFramework, "MaaContextGetNodeData") - purego.RegisterLibFunc(&MaaContextGetTaskId, maaFramework, "MaaContextGetTaskId") - purego.RegisterLibFunc(&MaaContextGetTasker, maaFramework, "MaaContextGetTasker") - purego.RegisterLibFunc(&MaaContextClone, maaFramework, "MaaContextClone") - purego.RegisterLibFunc(&MaaContextSetAnchor, maaFramework, "MaaContextSetAnchor") - purego.RegisterLibFunc(&MaaContextGetAnchor, maaFramework, "MaaContextGetAnchor") - purego.RegisterLibFunc(&MaaContextGetHitCount, maaFramework, "MaaContextGetHitCount") - purego.RegisterLibFunc(&MaaContextClearHitCount, maaFramework, "MaaContextClearHitCount") - // Buffer - purego.RegisterLibFunc(&MaaStringBufferCreate, maaFramework, "MaaStringBufferCreate") - purego.RegisterLibFunc(&MaaStringBufferDestroy, maaFramework, "MaaStringBufferDestroy") - purego.RegisterLibFunc(&MaaStringBufferIsEmpty, maaFramework, "MaaStringBufferIsEmpty") - purego.RegisterLibFunc(&MaaStringBufferClear, maaFramework, "MaaStringBufferClear") - purego.RegisterLibFunc(&MaaStringBufferGet, maaFramework, "MaaStringBufferGet") - purego.RegisterLibFunc(&MaaStringBufferSize, maaFramework, "MaaStringBufferSize") - purego.RegisterLibFunc(&MaaStringBufferSet, maaFramework, "MaaStringBufferSet") - purego.RegisterLibFunc(&MaaStringBufferSetEx, maaFramework, "MaaStringBufferSetEx") - purego.RegisterLibFunc(&MaaStringListBufferCreate, maaFramework, "MaaStringListBufferCreate") - purego.RegisterLibFunc(&MaaStringListBufferDestroy, maaFramework, "MaaStringListBufferDestroy") - purego.RegisterLibFunc(&MaaStringListBufferIsEmpty, maaFramework, "MaaStringListBufferIsEmpty") - purego.RegisterLibFunc(&MaaStringListBufferSize, maaFramework, "MaaStringListBufferSize") - purego.RegisterLibFunc(&MaaStringListBufferAt, maaFramework, "MaaStringListBufferAt") - purego.RegisterLibFunc(&MaaStringListBufferAppend, maaFramework, "MaaStringListBufferAppend") - purego.RegisterLibFunc(&MaaStringListBufferRemove, maaFramework, "MaaStringListBufferRemove") - purego.RegisterLibFunc(&MaaStringListBufferClear, maaFramework, "MaaStringListBufferClear") - purego.RegisterLibFunc(&MaaImageBufferCreate, maaFramework, "MaaImageBufferCreate") - purego.RegisterLibFunc(&MaaImageBufferDestroy, maaFramework, "MaaImageBufferDestroy") - purego.RegisterLibFunc(&MaaImageBufferIsEmpty, maaFramework, "MaaImageBufferIsEmpty") - purego.RegisterLibFunc(&MaaImageBufferClear, maaFramework, "MaaImageBufferClear") - purego.RegisterLibFunc(&MaaImageBufferGetRawData, maaFramework, "MaaImageBufferGetRawData") - purego.RegisterLibFunc(&MaaImageBufferWidth, maaFramework, "MaaImageBufferWidth") - purego.RegisterLibFunc(&MaaImageBufferHeight, maaFramework, "MaaImageBufferHeight") - purego.RegisterLibFunc(&MaaImageBufferChannels, maaFramework, "MaaImageBufferChannels") - purego.RegisterLibFunc(&MaaImageBufferType, maaFramework, "MaaImageBufferType") - purego.RegisterLibFunc(&MaaImageBufferSetRawData, maaFramework, "MaaImageBufferSetRawData") - purego.RegisterLibFunc(&MaaImageBufferResize, maaFramework, "MaaImageBufferResize") - purego.RegisterLibFunc(&MaaImageListBufferCreate, maaFramework, "MaaImageListBufferCreate") - purego.RegisterLibFunc(&MaaImageListBufferDestroy, maaFramework, "MaaImageListBufferDestroy") - purego.RegisterLibFunc(&MaaImageListBufferIsEmpty, maaFramework, "MaaImageListBufferIsEmpty") - purego.RegisterLibFunc(&MaaImageListBufferSize, maaFramework, "MaaImageListBufferSize") - purego.RegisterLibFunc(&MaaImageListBufferAt, maaFramework, "MaaImageListBufferAt") - purego.RegisterLibFunc(&MaaImageListBufferAppend, maaFramework, "MaaImageListBufferAppend") - purego.RegisterLibFunc(&MaaImageListBufferRemove, maaFramework, "MaaImageListBufferRemove") - purego.RegisterLibFunc(&MaaImageListBufferClear, maaFramework, "MaaImageListBufferClear") - purego.RegisterLibFunc(&MaaRectCreate, maaFramework, "MaaRectCreate") - purego.RegisterLibFunc(&MaaRectDestroy, maaFramework, "MaaRectDestroy") - purego.RegisterLibFunc(&MaaRectGetX, maaFramework, "MaaRectGetX") - purego.RegisterLibFunc(&MaaRectGetY, maaFramework, "MaaRectGetY") - purego.RegisterLibFunc(&MaaRectGetW, maaFramework, "MaaRectGetW") - purego.RegisterLibFunc(&MaaRectGetH, maaFramework, "MaaRectGetH") - purego.RegisterLibFunc(&MaaRectSet, maaFramework, "MaaRectSet") - // Global - purego.RegisterLibFunc(&MaaGlobalSetOption, maaFramework, "MaaGlobalSetOption") - purego.RegisterLibFunc(&MaaGlobalLoadPlugin, maaFramework, "MaaGlobalLoadPlugin") + for _, entry := range frameworkEntries { + purego.RegisterLibFunc(entry.ptrToFunc, maaFramework, entry.name) + } } -func unregisterFramework() error { - return unloadLibrary(maaFramework) +func releaseFramework() error { + err := unloadLibrary(maaFramework) + if err != nil { + return err + } + + unregisterFramework() + + return nil +} + +func unregisterFramework() { + for _, entry := range frameworkEntries { + clearFuncVar(entry.ptrToFunc) + } } diff --git a/internal/native/native.go b/internal/native/native.go index 18c17be..217343b 100644 --- a/internal/native/native.go +++ b/internal/native/native.go @@ -5,6 +5,7 @@ package native import ( "errors" "fmt" + "reflect" ) type Library struct { @@ -13,12 +14,17 @@ type Library struct { release func() error } +type Entry struct { + ptrToFunc any + name string +} + var ( libraries = []Library{ - {name: maaFrameworkName, init: initFramework, release: unregisterFramework}, - {name: maaToolkitName, init: initToolkit, release: unregisterToolkit}, - {name: maaAgentServerName, init: initAgentServer, release: unregisterAgentServer}, - {name: maaAgentClientName, init: initAgentClient, release: unregisterAgentClient}, + {name: maaFrameworkName, init: initFramework, release: releaseFramework}, + {name: maaToolkitName, init: initToolkit, release: releaseToolkit}, + {name: maaAgentServerName, init: initAgentServer, release: releaseAgentServer}, + {name: maaAgentClientName, init: initAgentClient, release: releaseAgentClient}, } loadedLibs []Library ) @@ -72,3 +78,11 @@ func Shutdown() error { return nil } + +func clearFuncVar(ptr any) { + val := reflect.ValueOf(ptr) + if val.Kind() != reflect.Ptr || val.Elem().Kind() != reflect.Func { + return + } + val.Elem().Set(reflect.Zero(val.Elem().Type())) +} diff --git a/internal/native/toolkit.go b/internal/native/toolkit.go index ed255d7..b5a91ff 100644 --- a/internal/native/toolkit.go +++ b/internal/native/toolkit.go @@ -56,6 +56,33 @@ var ( MaaToolkitMacOSRevealPermissionSettings func(perm MaaMacOSPermission) bool ) +var toolkitEntries = []Entry{ + {&MaaToolkitConfigInitOption, "MaaToolkitConfigInitOption"}, + {&MaaToolkitAdbDeviceListCreate, "MaaToolkitAdbDeviceListCreate"}, + {&MaaToolkitAdbDeviceListDestroy, "MaaToolkitAdbDeviceListDestroy"}, + {&MaaToolkitAdbDeviceFind, "MaaToolkitAdbDeviceFind"}, + {&MaaToolkitAdbDeviceFindSpecified, "MaaToolkitAdbDeviceFindSpecified"}, + {&MaaToolkitAdbDeviceListSize, "MaaToolkitAdbDeviceListSize"}, + {&MaaToolkitAdbDeviceListAt, "MaaToolkitAdbDeviceListAt"}, + {&MaaToolkitAdbDeviceGetName, "MaaToolkitAdbDeviceGetName"}, + {&MaaToolkitAdbDeviceGetAdbPath, "MaaToolkitAdbDeviceGetAdbPath"}, + {&MaaToolkitAdbDeviceGetAddress, "MaaToolkitAdbDeviceGetAddress"}, + {&MaaToolkitAdbDeviceGetScreencapMethods, "MaaToolkitAdbDeviceGetScreencapMethods"}, + {&MaaToolkitAdbDeviceGetInputMethods, "MaaToolkitAdbDeviceGetInputMethods"}, + {&MaaToolkitAdbDeviceGetConfig, "MaaToolkitAdbDeviceGetConfig"}, + {&MaaToolkitDesktopWindowListCreate, "MaaToolkitDesktopWindowListCreate"}, + {&MaaToolkitDesktopWindowListDestroy, "MaaToolkitDesktopWindowListDestroy"}, + {&MaaToolkitDesktopWindowFindAll, "MaaToolkitDesktopWindowFindAll"}, + {&MaaToolkitDesktopWindowListSize, "MaaToolkitDesktopWindowListSize"}, + {&MaaToolkitDesktopWindowListAt, "MaaToolkitDesktopWindowListAt"}, + {&MaaToolkitDesktopWindowGetHandle, "MaaToolkitDesktopWindowGetHandle"}, + {&MaaToolkitDesktopWindowGetClassName, "MaaToolkitDesktopWindowGetClassName"}, + {&MaaToolkitDesktopWindowGetWindowName, "MaaToolkitDesktopWindowGetWindowName"}, + {&MaaToolkitMacOSCheckPermission, "MaaToolkitMacOSCheckPermission"}, + {&MaaToolkitMacOSRequestPermission, "MaaToolkitMacOSRequestPermission"}, + {&MaaToolkitMacOSRevealPermissionSettings, "MaaToolkitMacOSRevealPermissionSettings"}, +} + func initToolkit(libDir string) error { libName := getMaaToolkitLibrary() libPath := filepath.Join(libDir, libName) @@ -90,36 +117,24 @@ func getMaaToolkitLibrary() string { } func registerToolkit() { - // Config - purego.RegisterLibFunc(&MaaToolkitConfigInitOption, maaToolkit, "MaaToolkitConfigInitOption") - // AdbDevice - purego.RegisterLibFunc(&MaaToolkitAdbDeviceListCreate, maaToolkit, "MaaToolkitAdbDeviceListCreate") - purego.RegisterLibFunc(&MaaToolkitAdbDeviceListDestroy, maaToolkit, "MaaToolkitAdbDeviceListDestroy") - purego.RegisterLibFunc(&MaaToolkitAdbDeviceFind, maaToolkit, "MaaToolkitAdbDeviceFind") - purego.RegisterLibFunc(&MaaToolkitAdbDeviceFindSpecified, maaToolkit, "MaaToolkitAdbDeviceFindSpecified") - purego.RegisterLibFunc(&MaaToolkitAdbDeviceListSize, maaToolkit, "MaaToolkitAdbDeviceListSize") - purego.RegisterLibFunc(&MaaToolkitAdbDeviceListAt, maaToolkit, "MaaToolkitAdbDeviceListAt") - purego.RegisterLibFunc(&MaaToolkitAdbDeviceGetName, maaToolkit, "MaaToolkitAdbDeviceGetName") - purego.RegisterLibFunc(&MaaToolkitAdbDeviceGetAdbPath, maaToolkit, "MaaToolkitAdbDeviceGetAdbPath") - purego.RegisterLibFunc(&MaaToolkitAdbDeviceGetAddress, maaToolkit, "MaaToolkitAdbDeviceGetAddress") - purego.RegisterLibFunc(&MaaToolkitAdbDeviceGetScreencapMethods, maaToolkit, "MaaToolkitAdbDeviceGetScreencapMethods") - purego.RegisterLibFunc(&MaaToolkitAdbDeviceGetInputMethods, maaToolkit, "MaaToolkitAdbDeviceGetInputMethods") - purego.RegisterLibFunc(&MaaToolkitAdbDeviceGetConfig, maaToolkit, "MaaToolkitAdbDeviceGetConfig") - // DesktopWindow - purego.RegisterLibFunc(&MaaToolkitDesktopWindowListCreate, maaToolkit, "MaaToolkitDesktopWindowListCreate") - purego.RegisterLibFunc(&MaaToolkitDesktopWindowListDestroy, maaToolkit, "MaaToolkitDesktopWindowListDestroy") - purego.RegisterLibFunc(&MaaToolkitDesktopWindowFindAll, maaToolkit, "MaaToolkitDesktopWindowFindAll") - purego.RegisterLibFunc(&MaaToolkitDesktopWindowListSize, maaToolkit, "MaaToolkitDesktopWindowListSize") - purego.RegisterLibFunc(&MaaToolkitDesktopWindowListAt, maaToolkit, "MaaToolkitDesktopWindowListAt") - purego.RegisterLibFunc(&MaaToolkitDesktopWindowGetHandle, maaToolkit, "MaaToolkitDesktopWindowGetHandle") - purego.RegisterLibFunc(&MaaToolkitDesktopWindowGetClassName, maaToolkit, "MaaToolkitDesktopWindowGetClassName") - purego.RegisterLibFunc(&MaaToolkitDesktopWindowGetWindowName, maaToolkit, "MaaToolkitDesktopWindowGetWindowName") - // MacOS - purego.RegisterLibFunc(&MaaToolkitMacOSCheckPermission, maaToolkit, "MaaToolkitMacOSCheckPermission") - purego.RegisterLibFunc(&MaaToolkitMacOSRequestPermission, maaToolkit, "MaaToolkitMacOSRequestPermission") - purego.RegisterLibFunc(&MaaToolkitMacOSRevealPermissionSettings, maaToolkit, "MaaToolkitMacOSRevealPermissionSettings") + for _, entry := range toolkitEntries { + purego.RegisterLibFunc(entry.ptrToFunc, maaToolkit, entry.name) + } } -func unregisterToolkit() error { - return unloadLibrary(maaToolkit) +func releaseToolkit() error { + err := unloadLibrary(maaToolkit) + if err != nil { + return err + } + + unregisterToolkit() + + return nil +} + +func unregisterToolkit() { + for _, entry := range toolkitEntries { + clearFuncVar(entry.ptrToFunc) + } } diff --git a/tools/api-check/README.md b/tools/api-check/README.md index 3512f63..8af55ed 100644 --- a/tools/api-check/README.md +++ b/tools/api-check/README.md @@ -2,7 +2,7 @@ `tools/api-check` is a consistency checker for: -- `internal/native` registered C symbols (`purego.RegisterLibFunc`) +- `internal/native` registered C symbols declared in `[]Entry` tables and bound by `purego.RegisterLibFunc` - exported C functions in header files - `CustomController` interface vs `MaaCustomControllerCallbacks` - controller method enums/constants in `controller/adb` and `controller/win32` vs `MaaDef.h` @@ -14,7 +14,7 @@ It checks both symbol coverage and function signatures. - Native API coverage: - header function exists but Go is not registering it - Go registers function not found in headers - - `RegisterLibFunc` arg mismatch: first arg var name != third arg symbol string + - `Entry` mismatch: `ptrToFunc` target name != symbol string - Native API signature consistency: - compare Go var function signature vs C exported function signature - compare params/returns with strict arity/order diff --git a/tools/api-check/internal/checker/native_check.go b/tools/api-check/internal/checker/native_check.go index 2ac5938..cec7a55 100644 --- a/tools/api-check/internal/checker/native_check.go +++ b/tools/api-check/internal/checker/native_check.go @@ -126,63 +126,117 @@ func parseGoRegistrations(nativeFiles map[string][]string) (map[string]map[strin } varSigs, varDeclLocs := parseGoVarFuncSignaturesWithLoc(parsedFile, fset, file) - ast.Inspect(parsedFile, func(n ast.Node) bool { - call, ok := n.(*ast.CallExpr) - if !ok { - return true + entryRegistrations, entryIssues := parseGoEntryRegistrations(parsedFile, fset, file, module) + issues = append(issues, entryIssues...) + for _, registration := range entryRegistrations { + result[module][registration.name] = struct{}{} + if sig, ok := varSigs[registration.funcVar]; ok { + goSigs[module][registration.name] = sig } - - switch fun := call.Fun.(type) { - case *ast.Ident: - if fun.Name != "RegisterLibFunc" { - return true - } - case *ast.SelectorExpr: - if fun.Sel == nil || fun.Sel.Name != "RegisterLibFunc" { - return true - } - default: - return true + if declLoc, ok := varDeclLocs[registration.funcVar]; ok { + goDeclLocs[module][registration.name] = declLoc } - - if len(call.Args) < 3 { - return true + goRegisterLocs[module][registration.name] = goRegistrationLoc{ + file: registration.file, + line: registration.line, } + } + } + } - funcVar := extractRegisterFuncVarName(call.Args[0]) - lit, ok := call.Args[2].(*ast.BasicLit) - if !ok || lit.Kind != token.STRING { - return true - } + return result, goSigs, goRegisterLocs, goDeclLocs, issues, nil +} - name, err := strconv.Unquote(lit.Value) - if err != nil || name == "" { - return true - } - pos := fset.Position(call.Pos()) - if funcVar != "" && funcVar != name { - issues = append(issues, issue{ - section: sectionNativeAPI, - message: fmt.Sprintf("[%s] RegisterLibFunc argument mismatch in %s:%d: var=%s symbol=%s", module, filepath.Clean(file), pos.Line, funcVar, name), - }) - } - result[module][name] = struct{}{} - if sig, ok := varSigs[funcVar]; ok { - goSigs[module][name] = sig - } - if declLoc, ok := varDeclLocs[funcVar]; ok { - goDeclLocs[module][name] = declLoc +type goEntryRegistration struct { + funcVar string + name string + file string + line int +} + +func parseGoEntryRegistrations(parsedFile *ast.File, fset *token.FileSet, sourceFile, module string) ([]goEntryRegistration, []issue) { + registrations := make([]goEntryRegistration, 0) + issues := make([]issue, 0) + + for _, decl := range parsedFile.Decls { + gen, ok := decl.(*ast.GenDecl) + if !ok || gen.Tok != token.VAR { + continue + } + + for _, spec := range gen.Specs { + valueSpec, ok := spec.(*ast.ValueSpec) + if !ok { + continue + } + + for _, value := range valueSpec.Values { + entriesLit, ok := value.(*ast.CompositeLit) + if !ok || !isEntrySliceLiteral(entriesLit.Type) { + continue } - goRegisterLocs[module][name] = goRegistrationLoc{ - file: filepath.Clean(file), - line: pos.Line, + + for _, entryExpr := range entriesLit.Elts { + registration, ok := parseGoEntryRegistration(entryExpr, fset, sourceFile) + if !ok { + continue + } + + if registration.funcVar != registration.name { + issues = append(issues, issue{ + section: sectionNativeAPI, + message: fmt.Sprintf("[%s] Entry mismatch in %s:%d: var=%s symbol=%s", module, registration.file, registration.line, registration.funcVar, registration.name), + }) + } + + registrations = append(registrations, registration) } - return true - }) + } } } - return result, goSigs, goRegisterLocs, goDeclLocs, issues, nil + return registrations, issues +} + +func isEntrySliceLiteral(expr ast.Expr) bool { + arrayType, ok := expr.(*ast.ArrayType) + if !ok { + return false + } + + ident, ok := arrayType.Elt.(*ast.Ident) + return ok && ident.Name == "Entry" +} + +func parseGoEntryRegistration(entryExpr ast.Expr, fset *token.FileSet, sourceFile string) (goEntryRegistration, bool) { + entryLit, ok := entryExpr.(*ast.CompositeLit) + if !ok || len(entryLit.Elts) < 2 { + return goEntryRegistration{}, false + } + + funcVar := extractRegisterFuncVarName(entryLit.Elts[0]) + nameLit, ok := entryLit.Elts[1].(*ast.BasicLit) + if !ok || nameLit.Kind != token.STRING { + return goEntryRegistration{}, false + } + + name, err := strconv.Unquote(nameLit.Value) + if err != nil || name == "" { + return goEntryRegistration{}, false + } + + pos := fset.Position(entryLit.Pos()) + file := filepath.Clean(sourceFile) + if pos.Filename != "" { + file = filepath.Clean(pos.Filename) + } + + return goEntryRegistration{ + funcVar: funcVar, + name: name, + file: file, + line: pos.Line, + }, true } func extractRegisterFuncVarName(arg ast.Expr) string { diff --git a/tools/api-check/internal/checker/native_check_test.go b/tools/api-check/internal/checker/native_check_test.go new file mode 100644 index 0000000..ab9f93c --- /dev/null +++ b/tools/api-check/internal/checker/native_check_test.go @@ -0,0 +1,65 @@ +package checker + +import ( + "os" + "path/filepath" + "strings" + "testing" +) + +func TestParseGoRegistrations_UsesEntryTables(t *testing.T) { + t.Parallel() + + dir := t.TempDir() + file := filepath.Join(dir, "native.go") + src := `package native + +var ( + MaaFoo func(uintptr, string) bool + MaaBar func() +) + +var entries = []Entry{ + {&MaaFoo, "MaaFoo"}, + {&MaaBar, "MaaBarAlias"}, +} +` + if err := os.WriteFile(file, []byte(src), 0o600); err != nil { + t.Fatalf("write go file: %v", err) + } + + registered, goSigs, registerLocs, declLocs, issues, err := parseGoRegistrations(map[string][]string{ + "framework": {file}, + }) + if err != nil { + t.Fatalf("parse go registrations: %v", err) + } + + if _, ok := registered["framework"]["MaaFoo"]; !ok { + t.Fatalf("expected MaaFoo to be registered") + } + if _, ok := registered["framework"]["MaaBarAlias"]; !ok { + t.Fatalf("expected MaaBarAlias to be registered") + } + + fooSig, ok := goSigs["framework"]["MaaFoo"] + if !ok { + t.Fatalf("expected MaaFoo signature to be collected") + } + if got, want := strings.Join(fooSig.params, ","), "ptr,cstring"; got != want { + t.Fatalf("unexpected MaaFoo params: got=%q want=%q", got, want) + } + if got, want := strings.Join(fooSig.returns, ","), "bool"; got != want { + t.Fatalf("unexpected MaaFoo returns: got=%q want=%q", got, want) + } + + if got := declLocs["framework"]["MaaFoo"]; got.file != filepath.Clean(file) || got.line <= 0 { + t.Fatalf("unexpected declaration location: %+v", got) + } + if got := registerLocs["framework"]["MaaFoo"]; got.file != filepath.Clean(file) || got.line <= 0 { + t.Fatalf("unexpected registration location: %+v", got) + } + + assertIssueContains(t, issues, "[framework] Entry mismatch") + assertIssueContains(t, issues, "var=MaaBar symbol=MaaBarAlias") +} From 702eb310b7d23f1df8a2078ad5ba82aea1795da0 Mon Sep 17 00:00:00 2001 From: dongwlin Date: Mon, 27 Apr 2026 11:05:21 +0800 Subject: [PATCH 7/9] fix: change library name variables to constants Co-authored-by: Copilot --- internal/native/agent_client.go | 7 +++---- internal/native/agent_server.go | 7 +++---- internal/native/framework.go | 7 +++---- internal/native/toolkit.go | 7 +++---- 4 files changed, 12 insertions(+), 16 deletions(-) diff --git a/internal/native/agent_client.go b/internal/native/agent_client.go index 13116a5..13743cc 100644 --- a/internal/native/agent_client.go +++ b/internal/native/agent_client.go @@ -8,10 +8,9 @@ import ( "github.com/ebitengine/purego" ) -var ( - maaAgentClient uintptr - maaAgentClientName = "MaaAgentClient" -) +var maaAgentClient uintptr + +const maaAgentClientName = "MaaAgentClient" var ( MaaAgentClientCreateV2 func(identifier uintptr) uintptr diff --git a/internal/native/agent_server.go b/internal/native/agent_server.go index c5ee6ee..03db8b5 100644 --- a/internal/native/agent_server.go +++ b/internal/native/agent_server.go @@ -9,10 +9,9 @@ import ( "github.com/ebitengine/purego" ) -var ( - maaAgentServer uintptr - maaAgentServerName = "MaaAgentServer" -) +var maaAgentServer uintptr + +const maaAgentServerName = "MaaAgentServer" var ( MaaAgentServerRegisterCustomRecognition func(name string, recognition MaaCustomRecognitionCallback, transArg unsafe.Pointer) bool diff --git a/internal/native/framework.go b/internal/native/framework.go index 4bb9188..8ada3ed 100644 --- a/internal/native/framework.go +++ b/internal/native/framework.go @@ -9,10 +9,9 @@ import ( "github.com/ebitengine/purego" ) -var ( - maaFramework uintptr - maaFrameworkName = "MaaFramework" -) +var maaFramework uintptr + +const maaFrameworkName = "MaaFramework" var ( MaaVersion func() string diff --git a/internal/native/toolkit.go b/internal/native/toolkit.go index b5a91ff..47d5ce6 100644 --- a/internal/native/toolkit.go +++ b/internal/native/toolkit.go @@ -9,10 +9,9 @@ import ( "github.com/ebitengine/purego" ) -var ( - maaToolkit uintptr - maaToolkitName = "MaaToolkit" -) +var maaToolkit uintptr + +const maaToolkitName = "MaaToolkit" var MaaToolkitConfigInitOption func(userPath, defaultJson string) bool From 16debdd53a6297000e6d22c1d00b7e8819d52d82 Mon Sep 17 00:00:00 2001 From: dongwlin Date: Wed, 29 Apr 2026 00:07:55 +0800 Subject: [PATCH 8/9] docs: update CHANGELOG --- CHANGELOG.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 1fc58f7..f4f4548 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -113,6 +113,9 @@ - `Init()` 不再隐式应用默认全局配置,仅在显式传入对应 `WithXxx` 时才会调用设置。 - `defaultInitConfig()` 已移除,`Init()` 现在直接使用 `initConfig{}` 初始化。 - `WithPluginPaths` 会对输入切片进行拷贝,避免外部后续修改影响已构建的选项。 +- `Init()` 与 `Release()` 现为幂等操作:重复初始化或在未初始化状态下释放都会直接返回 `nil`;原导出的 `ErrAlreadyInitialized`、`ErrNotInitialized` 已移除。 +- `Init()` 过程中若某个原生库加载失败,会自动释放此前已成功加载的库,避免残留半初始化状态。 +- `LibraryLoadError` 现在会稳定包含库名与尝试加载的完整路径,便于排查动态库装载问题。 #### Toolkit From a7e6d01128a8079f9780406d6795bf5bd45be12f Mon Sep 17 00:00:00 2001 From: dongwlin Date: Wed, 29 Apr 2026 00:12:29 +0800 Subject: [PATCH 9/9] docs: add concurrency safety notes to Init, IsInited, and Release functions Co-authored-by: Copilot --- maa.go | 3 +++ 1 file changed, 3 insertions(+) diff --git a/maa.go b/maa.go index 830ec30..fc552bd 100644 --- a/maa.go +++ b/maa.go @@ -140,6 +140,7 @@ func WithJSONDecoder(decoder JSONDecoder) InitOption { // Init loads the dynamic library related to the MAA framework and registers its related functions. // It must be called before invoking any other MAA-related functions. +// It must not be called concurrently with Release or other MAA-related functions. // Note: If this function is not called before other MAA functions, it will trigger a null pointer panic. func Init(opts ...InitOption) error { @@ -207,11 +208,13 @@ func Init(opts ...InitOption) error { } // IsInited checks if the MAA framework has been initialized. +// It must not be called concurrently with Init or Release. func IsInited() bool { return inited } // Release releases the dynamic library resources of the MAA framework and unregisters its related functions. +// It must not be called concurrently with Init or other MAA-related functions. func Release() error { if err := native.Shutdown(); err != nil {