├── .gitignore ├── Cargo.toml ├── LICENSE ├── README.md ├── addin1c-test ├── Cargo.toml ├── README.md ├── build.rs ├── crate-info.md ├── include │ ├── AddInDefBase.h │ ├── ComponentBase.h │ ├── IMemoryManager.h │ ├── addinlib.h │ ├── com.h │ └── types.h └── src │ ├── component_base.cpp │ ├── ffi.rs │ ├── lib.rs │ ├── memory_manager.cpp │ ├── memory_manager.rs │ ├── test_addin_lib.rs │ ├── test_addin_object.rs │ ├── tests.rs │ ├── tm.rs │ ├── variant.cpp │ └── variant.rs ├── addin1c ├── Cargo.toml ├── README.md └── src │ ├── connection.rs │ ├── cstr1c.rs │ ├── ffi.rs │ ├── lib.rs │ ├── macros.rs │ ├── memory_manager.rs │ ├── simple.rs │ ├── tm.rs │ ├── tvariant.rs │ └── variant.rs ├── conf1c ├── Configuration.xml ├── DataProcessors │ ├── Обработка1.xml │ └── Обработка1 │ │ └── Forms │ │ ├── Форма.xml │ │ └── Форма │ │ └── Ext │ │ ├── Form.xml │ │ └── Form │ │ └── Module.bsl ├── Ext │ └── HomePageWorkArea.xml └── Languages │ └── Русский.xml ├── example-test ├── Cargo.toml ├── README.md └── src │ └── main.rs └── example ├── Cargo.toml └── src ├── addin1.rs ├── addin2.rs └── lib.rs /.gitignore: -------------------------------------------------------------------------------- 1 | target 2 | Cargo.lock 3 | .vscode 4 | conf1c/ConfigDumpInfo.xml 5 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [workspace] 2 | members = ["addin1c", "addin1c-test", "example", "example-test"] 3 | resolver = "2" 4 | 5 | [profile.release] 6 | lto = true 7 | codegen-units = 1 8 | strip = true 9 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2022 https://github.com/medigor 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # addin1c 2 | 3 | Репозиторий содержит библиотеки для разработки и тестирования внешних компонент для `1С:Предприятие` с помощью `Rust`: 4 | 5 | - [addin1c](addin1c/) - Библиотека для разработки внешних компонент. Позволяет разрабатывать внешнюю компоненту без использования `C++`, также для сборки для `Windows` не требуется `msvc`. 6 | - [addin1c-test](addin1c-test/) - Библитека для разработка тестов внешних компонент, позволяет убеиться в надежности разработанных внешних компонент запустив с помощью `valgrind`, дополнительно см. [README.md](addin1c-test/README.md). 7 | - [example](example/) - Пример разработки внешних компонент, включает 2 компоненты: 8 | - [addin1.rs](example/src/addin1.rs) использует низкоуровный интерфей 9 | - [addin2.rs](example/src/addin2.rs) - использует высокоуровневый интерфейс 10 | - [conf1c](conf1c/) - конфигурация 1С для тестирования примеров внешних компонент 11 | - [Код в форме обработке](conf1c/DataProcessors/Обработка1/Forms/Форма/Ext/Form/Module.bsl) - тут расположен весь код для тестирования 12 | - [example-test](example-test/) - разработанный тест для примера внешней компоненты. 13 | -------------------------------------------------------------------------------- /addin1c-test/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "addin1c-test" 3 | version = "0.2.0" 4 | edition = "2021" 5 | description = "library for testing 1C:Enterpirse 8 add-ins" 6 | license = "MIT" 7 | categories = [] 8 | repository = "https://github.com/medigor/addin1c" 9 | homepage = "https://github.com/medigor/addin1c" 10 | keywords = ["1c-enterpirse", "native-api", "addin"] 11 | readme = "crate-info.md" 12 | 13 | [dependencies] 14 | libloading = "0.8" 15 | utf16_lit = "2.0" 16 | 17 | [build-dependencies] 18 | cc = "1.0" 19 | -------------------------------------------------------------------------------- /addin1c-test/README.md: -------------------------------------------------------------------------------- 1 | # addin1c-test 2 | 3 | Библиотека для тестирования внешних компонент. 4 | 5 | ## Зачем нужна 6 | Основные тесты нужно писать на самой 1С. Но хочется также проверять корректность использования памяти, тем более что эта технология создания компонент не использует `C++`, теоретически у кого-то могут возникнуть сомнения в корректности этой технологии. Для проверки корректности я использую [valgrind](https://valgrind.org/), но запускать вместе с 1С относительно долго и вывод этой утилиты показывает также ошибки самой 1С. Для этого и нужна эта библиотека, чтобы протестировать с помощью `Valgrind` полученную внешнюю компоненту. 7 | 8 | ## Как устроена 9 | Хоть сама технология не использует `C++`, для тестирования внешних компонент придется его использовать. Из `Rust`нет возможности напрямую использовать `C++`, но можно сделать обертки с `C ABI` и их использовать. Собирать статическую библиотеку буду с крейтом [cc](https://crates.io/crates/cc). 10 | 11 | ## Ограничения 12 | - Из предущего раздела следует вывод - для теста на `Windows` нужны инструменты сборки `msvc`, хотя для самих компонент такого требования нет. 13 | - `Valgrind` не поддерживает работу на `Windows`, возможно там можно использовать [drmemory](https://drmemory.org/). 14 | 15 | ## API 16 | см. [пример кода](../example-test/) 17 | -------------------------------------------------------------------------------- /addin1c-test/build.rs: -------------------------------------------------------------------------------- 1 | fn main() { 2 | let mut build = cc::Build::new(); 3 | 4 | if let Ok(target) = std::env::var("TARGET") { 5 | if target.contains("windows") { 6 | build.define("_WINDOWS", ""); 7 | } 8 | } 9 | 10 | build 11 | .cpp(true) 12 | .file("src/memory_manager.cpp") 13 | .file("src/variant.cpp") 14 | .file("src/component_base.cpp") 15 | .include("include") 16 | .compile("helper1c"); 17 | 18 | println!("cargo:rerun-if-changed=src/component_base.cpp"); 19 | println!("cargo:rerun-if-changed=src/memory_manager.cpp"); 20 | println!("cargo:rerun-if-changed=src/variant.cpp"); 21 | println!("cargo:rerun-if-changed=include"); 22 | } 23 | -------------------------------------------------------------------------------- /addin1c-test/crate-info.md: -------------------------------------------------------------------------------- 1 | # addin1c-test 2 | 3 | Library for testing 1C:Enterprise 8 add-ins 4 | 5 | [Documentation](README.md) 6 | -------------------------------------------------------------------------------- /addin1c-test/include/AddInDefBase.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Warning!!! 3 | * DO NOT ALTER THIS FILE! 4 | */ 5 | 6 | #ifndef __ADAPTER_DEF_H__ 7 | #define __ADAPTER_DEF_H__ 8 | #include "types.h" 9 | 10 | struct IInterface 11 | { 12 | }; 13 | 14 | 15 | enum Interfaces 16 | { 17 | eIMsgBox = 0, 18 | eIPlatformInfo, 19 | #if defined(__ANDROID__) 20 | eIAndroidComponentHelper, 21 | #endif 22 | eIAttachedInfo, 23 | }; 24 | 25 | //////////////////////////////////////////////////////////////////////////////// 26 | /** 27 | * This class serves as representation of a platform for external 28 | * components External components use it to communicate with a platform. 29 | * 30 | */ 31 | /// Base interface for object components. 32 | class IAddInDefBase 33 | { 34 | public: 35 | virtual ~IAddInDefBase() {} 36 | /// Adds the error message 37 | /** 38 | * @param wcode - error code 39 | * @param source - source of error 40 | * @param descr - description of error 41 | * @param scode - error code (HRESULT) 42 | * @return the result of 43 | */ 44 | virtual bool ADDIN_API AddError(unsigned short wcode, const WCHAR_T* source, 45 | const WCHAR_T* descr, long scode) = 0; 46 | 47 | /// Reads a property value 48 | /** 49 | * @param wszPropName -property name 50 | * @param pVal - value being returned 51 | * @param pErrCode - error code (if any error occured) 52 | * @param errDescriptor - error description (if any error occured) 53 | * @return the result of read. 54 | */ 55 | virtual bool ADDIN_API Read(WCHAR_T* wszPropName, 56 | tVariant* pVal, 57 | long *pErrCode, 58 | WCHAR_T** errDescriptor) = 0; 59 | /// Writes a property value 60 | /** 61 | * @param wszPropName - property name 62 | * @param pVar - new property value 63 | * @return the result of write. 64 | */ 65 | virtual bool ADDIN_API Write(WCHAR_T* wszPropName, 66 | tVariant *pVar) = 0; 67 | 68 | ///Registers profile components 69 | /** 70 | * @param wszProfileName - profile name 71 | * @return the result of 72 | */ 73 | virtual bool ADDIN_API RegisterProfileAs(WCHAR_T* wszProfileName) = 0; 74 | 75 | /// Changes the depth of event buffer 76 | /** 77 | * @param lDepth - new depth of event buffer 78 | * @return the result of 79 | */ 80 | virtual bool ADDIN_API SetEventBufferDepth(long lDepth) = 0; 81 | /// Returns the depth of event buffer 82 | /** 83 | * @return the depth of event buffer 84 | */ 85 | virtual long ADDIN_API GetEventBufferDepth() = 0; 86 | /// Registers external event 87 | /** 88 | * @param wszSource - source of event 89 | * @param wszMessage - event message 90 | * @param wszData - message parameters 91 | * @return the result of 92 | */ 93 | virtual bool ADDIN_API ExternalEvent(WCHAR_T* wszSource, 94 | WCHAR_T* wszMessage, 95 | WCHAR_T* wszData) = 0; 96 | /// Clears event buffer 97 | /** 98 | */ 99 | virtual void ADDIN_API CleanEventBuffer() = 0; 100 | 101 | /// Sets status line contents 102 | /** 103 | * @param wszStatusLine - new status line contents 104 | * @return the result of 105 | */ 106 | virtual bool ADDIN_API SetStatusLine(WCHAR_T* wszStatusLine) = 0; 107 | /// Resets the status line contents 108 | /** 109 | * @return the result of 110 | */ 111 | virtual void ADDIN_API ResetStatusLine() = 0; 112 | }; 113 | 114 | class IAddInDefBaseEx : 115 | public IAddInDefBase 116 | { 117 | public: 118 | virtual ~IAddInDefBaseEx() {} 119 | 120 | virtual IInterface* ADDIN_API GetInterface(Interfaces iface) = 0; 121 | }; 122 | 123 | struct IMsgBox : 124 | public IInterface 125 | { 126 | virtual bool ADDIN_API Confirm(const WCHAR_T* queryText, tVariant* retVal) = 0; 127 | 128 | virtual bool ADDIN_API Alert(const WCHAR_T* text) = 0; 129 | }; 130 | 131 | struct IPlatformInfo : 132 | public IInterface 133 | { 134 | enum AppType 135 | { 136 | eAppUnknown = -1, 137 | eAppThinClient = 0, 138 | eAppThickClient, 139 | eAppWebClient, 140 | eAppServer, 141 | eAppExtConn, 142 | eAppMobileClient, 143 | eAppMobileServer, 144 | }; 145 | 146 | struct AppInfo 147 | { 148 | const WCHAR_T* AppVersion; 149 | const WCHAR_T* UserAgentInformation; 150 | AppType Application; 151 | }; 152 | 153 | virtual const AppInfo* ADDIN_API GetPlatformInfo() = 0; 154 | }; 155 | struct IAttachedInfo : 156 | public IInterface 157 | { 158 | enum AttachedType 159 | { 160 | eAttachedIsolated = 0, 161 | eAttachedNotIsolated, 162 | }; 163 | virtual const AttachedType ADDIN_API GetAttachedInfo() = 0; 164 | }; 165 | #endif //__ADAPTER_DEF_H__ 166 | -------------------------------------------------------------------------------- /addin1c-test/include/ComponentBase.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Warning!!! 3 | * DO NOT ALTER THIS FILE! 4 | */ 5 | 6 | 7 | #ifndef __COMPONENT_BASE_H__ 8 | #define __COMPONENT_BASE_H__ 9 | 10 | #include "types.h" 11 | //////////////////////////////////////////////////////////////////////////////// 12 | /** 13 | * The given interface is intended for initialization and 14 | * uninitialization of component and its adjustments 15 | */ 16 | /// Interface of component initialization. 17 | class IInitDoneBase 18 | { 19 | public: 20 | virtual ~IInitDoneBase() {} 21 | /// Initializes component 22 | /** 23 | * @param disp - 1C:Enterpise interface 24 | * @return the result of 25 | */ 26 | virtual bool ADDIN_API Init(void* disp) = 0; 27 | /// Sets the memory manager 28 | /* 29 | * @param mem - pointer to memory manager interface. 30 | * @return the result of 31 | */ 32 | virtual bool ADDIN_API setMemManager(void* mem) = 0; 33 | 34 | /// Returns component version 35 | /** 36 | * @return - component version (2000 - version 2) 37 | */ 38 | virtual long ADDIN_API GetInfo() = 0; 39 | 40 | /// Uninitializes component 41 | /** 42 | * Component here should release all consumed resources. 43 | */ 44 | virtual void ADDIN_API Done() = 0; 45 | 46 | }; 47 | /////////////////////////////////////////////////////////////////////////// 48 | /** 49 | * The given interface defines methods that are intented to be used by the Platform 50 | */ 51 | /// Interface describing extension of language. 52 | class ILanguageExtenderBase 53 | { 54 | public: 55 | virtual ~ILanguageExtenderBase(){} 56 | /// Registers language extension 57 | /** 58 | * @param wsExtensionName - extension name 59 | * @return the result of 60 | */ 61 | virtual bool ADDIN_API RegisterExtensionAs(WCHAR_T** wsExtensionName) = 0; 62 | 63 | /// Returns number of component properties 64 | /** 65 | * @return number of properties 66 | */ 67 | virtual long ADDIN_API GetNProps() = 0; 68 | 69 | /// Finds property by name 70 | /** 71 | * @param wsPropName - property name 72 | * @return property index or -1, if it is not found 73 | */ 74 | virtual long ADDIN_API FindProp(const WCHAR_T* wsPropName) = 0; 75 | 76 | /// Returns property name 77 | /** 78 | * @param lPropNum - property index (starting with 0) 79 | * @param lPropAlias - 0 - international alias, 80 | * 1 - russian alias. (International alias is required) 81 | * @return proeprty name or 0 if it is not found 82 | */ 83 | virtual const WCHAR_T* ADDIN_API GetPropName(long lPropNum, 84 | long lPropAlias) = 0; 85 | 86 | /// Returns property value 87 | /** 88 | * @param lPropNum - property index (starting with 0) 89 | * @param pvarPropVal - the pointer to a variable for property value 90 | * @return the result of 91 | */ 92 | virtual bool ADDIN_API GetPropVal(const long lPropNum, 93 | tVariant* pvarPropVal) = 0; 94 | 95 | /// Sets the property value 96 | /** 97 | * @param lPropNum - property index (starting with 0) 98 | * @param varPropVal - the pointer to a variable for property value 99 | * @return the result of 100 | */ 101 | virtual bool ADDIN_API SetPropVal(const long lPropNum, 102 | tVariant* varPropVal) = 0; 103 | 104 | /// Is property readable? 105 | /** 106 | * @param lPropNum - property index (starting with 0) 107 | * @return true if property is readable 108 | */ 109 | virtual bool ADDIN_API IsPropReadable(const long lPropNum) = 0; 110 | 111 | /// Is property writable? 112 | /** 113 | * @param lPropNum - property index (starting with 0) 114 | * @return true if property is writable 115 | */ 116 | virtual bool ADDIN_API IsPropWritable(const long lPropNum) = 0; 117 | 118 | /// Returns number of component methods 119 | /** 120 | * @return number of component methods 121 | */ 122 | virtual long ADDIN_API GetNMethods() = 0; 123 | 124 | /// Finds a method by name 125 | /** 126 | * @param wsMethodName - method name 127 | * @return - method index 128 | */ 129 | virtual long ADDIN_API FindMethod(const WCHAR_T* wsMethodName) = 0; 130 | 131 | /// Returns method name 132 | /** 133 | * @param lMethodNum - method index(starting with 0) 134 | * @param lMethodAlias - 0 - international alias, 135 | * 1 - russian alias. (International alias is required) 136 | * @return method name or 0 if method is not found 137 | */ 138 | virtual const WCHAR_T* ADDIN_API GetMethodName(const long lMethodNum, 139 | const long lMethodAlias) = 0; 140 | 141 | /// Returns number of method parameters 142 | /** 143 | * @param lMethodNum - method index (starting with 0) 144 | * @return number of parameters 145 | */ 146 | virtual long ADDIN_API GetNParams(const long lMethodNum) = 0; 147 | 148 | /// Returns default value of method parameter 149 | /** 150 | * @param lMethodNum - method index(starting with 0) 151 | * @param lParamNum - parameter index (starting with 0) 152 | * @param pvarParamDefValue - the pointer to a variable for default value 153 | * @return the result of 154 | */ 155 | virtual bool ADDIN_API GetParamDefValue(const long lMethodNum, 156 | const long lParamNum, 157 | tVariant *pvarParamDefValue) = 0; 158 | 159 | /// Does the method have a return value? 160 | /** 161 | * @param lMethodNum - method index (starting with 0) 162 | * @return true if the method has a return value 163 | */ 164 | virtual bool ADDIN_API HasRetVal(const long lMethodNum) = 0; 165 | 166 | /// Calls the method as a procedure 167 | /** 168 | * @param lMethodNum - method index (starting with 0) 169 | * @param paParams - the pointer to array of method parameters 170 | * @param lSizeArray - the size of array 171 | * @return the result of 172 | */ 173 | virtual bool ADDIN_API CallAsProc(const long lMethodNum, 174 | tVariant* paParams, 175 | const long lSizeArray) = 0; 176 | 177 | /// Calls the method as a function 178 | /** 179 | * @param lMethodNum - method index (starting with 0) 180 | * @param pvarRetValue - the pointer to returned value 181 | * @param paParams - the pointer to array of method parameters 182 | * @param lSizeArray - the size of array 183 | * @return the result of 184 | */ 185 | virtual bool ADDIN_API CallAsFunc(const long lMethodNum, 186 | tVariant* pvarRetValue, 187 | tVariant* paParams, 188 | const long lSizeArray) = 0; 189 | }; 190 | /////////////////////////////////////////////////////////////////////////// 191 | /** 192 | * This interface is used to change component locale 193 | */ 194 | /// Base interface for component localization. 195 | class LocaleBase 196 | { 197 | public: 198 | virtual ~LocaleBase(){} 199 | /// Changes component locale 200 | /** 201 | * @param loc - new locale (for Windows - rus_RUS, 202 | * for Linux - ru_RU, etc...) 203 | */ 204 | virtual void ADDIN_API SetLocale(const WCHAR_T* loc) = 0; 205 | }; 206 | 207 | /////////////////////////////////////////////////////////////////////// 208 | /// class UserLanguageBase- ��������� ��������� ����� ���������� 209 | /** 210 | * ���� ��������� ������������ ��� ��������� ����������� ���������� 211 | */ 212 | class UserLanguageBase 213 | { 214 | public: 215 | virtual ~UserLanguageBase() {} 216 | /// ��������� ������ ���������� 217 | /** 218 | * @param const char16_t* lang - ��������������� ���� (ru, etc...) 219 | */ 220 | virtual void ADDIN_API SetUserInterfaceLanguageCode(const WCHAR_T* lang) = 0; 221 | }; 222 | 223 | /////////////////////////////////////////////////////////////////////////// 224 | /** 225 | * The given interface is generalized, for its obligatory inheritance 226 | * in implementing components. 227 | */ 228 | /// Base interface describing object as a set of properties and methods. 229 | class IComponentBase : 230 | public IInitDoneBase, 231 | public ILanguageExtenderBase, 232 | public LocaleBase, 233 | public UserLanguageBase 234 | { 235 | public: 236 | virtual ~IComponentBase(){} 237 | }; 238 | 239 | enum AppCapabilities 240 | { 241 | eAppCapabilitiesInvalid = -1, 242 | eAppCapabilities1 = 1, 243 | eAppCapabilities2 = 2, 244 | eAppCapabilities3 = 3, 245 | eAppCapabilitiesLast = eAppCapabilities3, 246 | }; 247 | 248 | enum AttachType 249 | { 250 | eCanAttachNotIsolated = 1, 251 | eCanAttachIsolated, 252 | eCanAttachAny, 253 | }; 254 | 255 | /// Announcements of exported functions 256 | /** 257 | * These functions should be implemented that component can be loaded and created. 258 | */ 259 | // extern "C" long GetClassObject(const WCHAR_T*, IComponentBase** pIntf); 260 | // extern "C" long DestroyObject(IComponentBase** pIntf); 261 | // extern "C" const WCHAR_T* GetClassNames(); 262 | // extern "C" AppCapabilities SetPlatformCapabilities(const AppCapabilities capabilities); 263 | // extern "C" AttachType GetAttachType(); 264 | 265 | // typedef long (*GetClassObjectPtr)(const WCHAR_T* wsName, IComponentBase** pIntf); 266 | // typedef long (*DestroyObjectPtr)(IComponentBase** pIntf); 267 | // typedef const WCHAR_T* (*GetClassNamesPtr)(); 268 | // typedef AppCapabilities (*SetPlatformCapabilitiesPtr)(const AppCapabilities capabilities); 269 | // typedef AttachType (*GetAttachTypePtr)(); 270 | 271 | #endif //__COMPONENT_BASE_H__ 272 | -------------------------------------------------------------------------------- /addin1c-test/include/IMemoryManager.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Warning!!! 3 | * DO NOT ALTER THIS FILE! 4 | */ 5 | 6 | 7 | #ifndef __IMEMORY_MANAGER_H__ 8 | #define __IMEMORY_MANAGER_H__ 9 | 10 | /////////////////////////////////////////////////////////////////////////////// 11 | /** 12 | * The given class allocates and releases memory for a component 13 | */ 14 | /// Interface representing memory manager. 15 | class IMemoryManager 16 | { 17 | public: 18 | virtual ~IMemoryManager() {} 19 | /// Allocates memory of specified size 20 | /** 21 | * @param pMemory - the double pointer to variable, that will hold newly 22 | * allocated block of memory of NULL if allocation fails. 23 | * @param ulCountByte - memory size 24 | * @return the result of 25 | */ 26 | virtual bool ADDIN_API AllocMemory (void** pMemory, unsigned long ulCountByte) = 0; 27 | /// Releases memory 28 | /** 29 | * @param pMemory - The double pointer to the memory block being released 30 | */ 31 | virtual void ADDIN_API FreeMemory (void** pMemory) = 0; 32 | }; 33 | 34 | #endif //__IMEMORY_MANAGER_H__ 35 | -------------------------------------------------------------------------------- /addin1c-test/include/addinlib.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | enum AddInComponentType 4 | { 5 | eAddInCom = 1, 6 | eAddInNative, 7 | eAddInJava, 8 | eAddInvalid = -1 9 | }; 10 | -------------------------------------------------------------------------------- /addin1c-test/include/com.h: -------------------------------------------------------------------------------- 1 | 2 | #ifndef __COM_H__ 3 | #define __COM_H__ 4 | 5 | #if defined(__linux__) || defined(__APPLE__) || defined(__ANDROID__) 6 | 7 | #ifdef __ANDROID__ 8 | 9 | typedef struct { 10 | unsigned int Data1; 11 | unsigned short Data2; 12 | unsigned short Data3; 13 | unsigned char Data4[ 8 ]; 14 | } uuid_t; 15 | 16 | #else 17 | // #include 18 | #endif //__ANDROID__ 19 | 20 | #ifndef __ENVIRONMENT_IPHONE_OS_VERSION_MIN_REQUIRED__ // iOS 21 | #include 22 | #endif //!__ENVIRONMENT_IPHONE_OS_VERSION_MIN_REQUIRED__ 23 | 24 | #pragma GCC system_header 25 | 26 | typedef long HRESULT; 27 | 28 | #ifdef __GNUC__ 29 | #define STDMETHODCALLTYPE __attribute__ ((__stdcall__)) 30 | #define DECLSPEC_NOTHROW __attribute__ ((nothrow)) 31 | #define STDMETHOD(method) virtual DECLSPEC_NOTHROW HRESULT STDMETHODCALLTYPE method 32 | #else 33 | #define STDMETHODCALLTYPE 34 | #endif 35 | 36 | #define __stdcall STDMETHODCALLTYPE 37 | #define near 38 | #define far 39 | #define CONST const 40 | #define FAR far 41 | 42 | typedef unsigned long DWORD; 43 | #ifndef __ENVIRONMENT_IPHONE_OS_VERSION_MIN_REQUIRED__ // iOS 44 | typedef int BOOL; 45 | #elif defined(__LP64__) 46 | typedef bool BOOL; 47 | #else 48 | typedef signed char BOOL; 49 | #endif //!__ENVIRONMENT_IPHONE_OS_VERSION_MIN_REQUIRED__ 50 | 51 | typedef void VOID; 52 | typedef short SHORT; 53 | typedef unsigned char BYTE; 54 | typedef unsigned short WORD; 55 | typedef float FLOAT; 56 | typedef FLOAT *PFLOAT; 57 | typedef BOOL near *PBOOL; 58 | typedef BOOL far *LPBOOL; 59 | typedef BYTE near *PBYTE; 60 | typedef BYTE far *LPBYTE; 61 | typedef int near *PINT; 62 | typedef int far *LPINT; 63 | typedef WORD near *PWORD; 64 | typedef WORD far *LPWORD; 65 | typedef long far *LPLONG; 66 | typedef DWORD near *PDWORD; 67 | typedef DWORD far *LPDWORD; 68 | typedef void far *LPVOID; 69 | typedef CONST void far *LPCVOID; 70 | typedef wchar_t *BSTR; 71 | typedef long SCODE; 72 | typedef int INT; 73 | typedef unsigned int UINT; 74 | typedef unsigned int *PUINT; 75 | typedef wchar_t WCHAR; 76 | typedef wchar_t OLECHAR; 77 | typedef wchar_t *LPOLESTR; 78 | typedef const wchar_t *LPCOLESTR; 79 | typedef DWORD LCID; 80 | typedef PDWORD PLCID; 81 | typedef long LONG; 82 | typedef unsigned long ULONG; 83 | typedef long long LONGLONG; 84 | typedef unsigned long long ULONGLONG; 85 | typedef LONG DISPID; 86 | typedef double DOUBLE; 87 | typedef double DATE; 88 | typedef short VARIANT_BOOL; 89 | typedef void *PVOID; 90 | typedef char CHAR; 91 | typedef CONST CHAR *LPCSTR; 92 | typedef unsigned short USHORT; 93 | typedef void *HMODULE; 94 | #define OLESTR(str) L##str 95 | 96 | // typedef uuid_t GUID; 97 | // typedef uuid_t IID; 98 | // typedef uuid_t UUID; 99 | #define REFIID const IID & 100 | #define MAX_PATH 260 101 | 102 | #define IsEqualIID(x,y) uuid_compare((x),(y)) 103 | #ifdef __GNUC__ 104 | #define LoadLibraryA(x) dlopen((x), RTLD_LAZY) 105 | #define FreeLibrary(x) dlclose((x)) 106 | #define GetProcAddress(x, y) dlsym((x), (y)) 107 | #endif //__GNUC__ 108 | 109 | #define E_FAIL 0x80004005L 110 | #define S_OK 0L 111 | #define S_FALSE 1L 112 | #define E_NOINTERFACE 0x80004002L 113 | #define E_NOTIMPL 0x80004001L 114 | #define E_INVALIDARG 0x80070057L 115 | #define E_UNEXPECTED 0x8000FFFFL 116 | #define E_OUTOFMEMORY 0x8007000EL 117 | #define DISP_E_UNKNOWNNAME 0x80020006L 118 | #define DISPID_UNKNOWN ( -1 ) 119 | #define TRUE 1 120 | #define FALSE 0 121 | 122 | typedef long ITypeInfo; 123 | 124 | #if defined (__GNUC__) && !defined (NONAMELESSUNION) 125 | __extension__ /* no named members */ 126 | #endif 127 | union tCY { 128 | __extension__ struct 129 | { 130 | unsigned long Lo; 131 | long Hi; 132 | }; 133 | long long int64; 134 | }; 135 | typedef union tagCY CY; 136 | #define CLSIDFromString(x,y) uuid_parse((x),(unsigned char*)(y)) 137 | 138 | #endif //defined(__linux__) || defined(__APPLE__) 139 | 140 | #endif //__COM_H__ 141 | -------------------------------------------------------------------------------- /addin1c-test/include/types.h: -------------------------------------------------------------------------------- 1 | 2 | #ifndef __CON_TYPES_H__ 3 | #define __CON_TYPES_H__ 4 | 5 | #if defined(_WINDOWS) || defined(WINAPI_FAMILY) 6 | #include 7 | #endif 8 | 9 | #if defined(WINAPI_FAMILY) 10 | #include 11 | #endif 12 | 13 | #if __GNUC__ >=3 14 | #pragma GCC system_header 15 | #endif 16 | 17 | #include "com.h" 18 | #include 19 | #include 20 | #include 21 | #include 22 | 23 | #define EXTERN_C extern "C" 24 | 25 | #ifdef __GNUC__ 26 | #define _ANONYMOUS_UNION __extension__ 27 | #define _ANONYMOUS_STRUCT __extension__ 28 | #else 29 | #define _ANONYMOUS_UNION 30 | #define _ANONYMOUS_STRUCT 31 | #endif //__GNUC__ 32 | 33 | #ifdef NONAMELESSUNION 34 | #define __VARIANT_NAME_1 u 35 | #define __VARIANT_NAME_2 iface 36 | #define __VARIANT_NAME_3 str 37 | #define __VARIANT_NAME_4 wstr 38 | #else 39 | #define __VARIANT_NAME_1 40 | #define __VARIANT_NAME_2 41 | #define __VARIANT_NAME_3 42 | #define __VARIANT_NAME_4 43 | #endif //NONAMELESSUNION 44 | 45 | #define RESULT_FROM_ERRNO(x) ((long)(x) <= 0 ? ((long)(x)) \ 46 | : ((long) (((x) & 0x0000FFFF) | (BASE_ERRNO << 16) | 0x80000000))) 47 | 48 | #define ADDIN_E_NONE 1000 49 | #define ADDIN_E_ORDINARY 1001 50 | #define ADDIN_E_ATTENTION 1002 51 | #define ADDIN_E_IMPORTANT 1003 52 | #define ADDIN_E_VERY_IMPORTANT 1004 53 | #define ADDIN_E_INFO 1005 54 | #define ADDIN_E_FAIL 1006 55 | #define ADDIN_E_MSGBOX_ATTENTION 1007 56 | #define ADDIN_E_MSGBOX_INFO 1008 57 | #define ADDIN_E_MSGBOX_FAIL 1009 58 | 59 | #ifndef ADDIN_API 60 | #ifdef _WINDOWS 61 | #define ADDIN_API __stdcall 62 | #else 63 | //#define ADDIN_API __attribute__ ((__stdcall__)) 64 | #define ADDIN_API 65 | #endif //_WINDOWS 66 | #endif //ADDIN_API 67 | 68 | #include 69 | 70 | #ifdef _WINDOWS 71 | #define WCHAR_T char16_t 72 | #else 73 | #define WCHAR_T char16_t 74 | #endif //_WINDOWS 75 | typedef unsigned short TYPEVAR; 76 | enum ENUMVAR 77 | { 78 | VTYPE_EMPTY = 0, 79 | VTYPE_NULL, 80 | VTYPE_I2, //int16_t 81 | VTYPE_I4, //int32_t 82 | VTYPE_R4, //float 83 | VTYPE_R8, //double 84 | VTYPE_DATE, //DATE (double) 85 | VTYPE_TM, //struct tm 86 | VTYPE_PSTR, //struct str string 87 | VTYPE_INTERFACE, //struct iface 88 | VTYPE_ERROR, //int32_t errCode 89 | VTYPE_BOOL, //bool 90 | VTYPE_VARIANT, //struct _tVariant * 91 | VTYPE_I1, //int8_t 92 | VTYPE_UI1, //uint8_t 93 | VTYPE_UI2, //uint16_t 94 | VTYPE_UI4, //uint32_t 95 | VTYPE_I8, //int64_t 96 | VTYPE_UI8, //uint64_t 97 | VTYPE_INT, //int Depends on architecture 98 | VTYPE_UINT, //unsigned int Depends on architecture 99 | VTYPE_HRESULT, //long hRes 100 | VTYPE_PWSTR, //struct wstr 101 | VTYPE_BLOB, //means in struct str binary data contain 102 | VTYPE_CLSID, //UUID 103 | VTYPE_STR_BLOB = 0xfff, 104 | VTYPE_VECTOR = 0x1000, 105 | VTYPE_ARRAY = 0x2000, 106 | VTYPE_BYREF = 0x4000, //Only with struct _tVariant * 107 | VTYPE_RESERVED = 0x8000, 108 | VTYPE_ILLEGAL = 0xffff, 109 | VTYPE_ILLEGALMASKED = 0xfff, 110 | VTYPE_TYPEMASK = 0xfff 111 | } ; 112 | #if defined (__GNUC__) && !defined (NONAMELESSUNION) 113 | __extension__ /* no named members */ 114 | #endif 115 | struct _tVariant 116 | { 117 | _ANONYMOUS_UNION union 118 | { 119 | int8_t i8Val; 120 | int16_t shortVal; 121 | int32_t lVal; 122 | int intVal; 123 | unsigned int uintVal; 124 | int64_t llVal; 125 | uint8_t ui8Val; 126 | uint16_t ushortVal; 127 | uint32_t ulVal; 128 | uint64_t ullVal; 129 | int32_t errCode; 130 | long hRes; 131 | float fltVal; 132 | double dblVal; 133 | bool bVal; 134 | char chVal; 135 | wchar_t wchVal; 136 | DATE date; 137 | // IID IDVal; 138 | struct _tVariant *pvarVal; 139 | struct tm tmVal; 140 | _ANONYMOUS_STRUCT struct 141 | { 142 | void* pInterfaceVal; 143 | // IID InterfaceID; 144 | } __VARIANT_NAME_2/*iface*/; 145 | _ANONYMOUS_STRUCT struct 146 | { 147 | char* pstrVal; 148 | uint32_t strLen; //count of bytes 149 | } __VARIANT_NAME_3/*str*/; 150 | _ANONYMOUS_STRUCT struct 151 | { 152 | WCHAR_T* pwstrVal; 153 | uint32_t wstrLen; //count of symbol 154 | } __VARIANT_NAME_4/*wstr*/; 155 | } __VARIANT_NAME_1; 156 | uint32_t cbElements; //Dimension for an one-dimensional array in pvarVal 157 | TYPEVAR vt; 158 | }; 159 | typedef struct _tVariant tVariant; 160 | typedef tVariant tVariantArg; 161 | 162 | 163 | #if defined(NONAMELESSUNION) 164 | #define TV_JOIN(X, Y) ((X)->u.Y) 165 | #else 166 | #define TV_JOIN(X, Y) ((X)->Y) 167 | #endif 168 | 169 | #define TV_VT(X) ((X)->vt) 170 | #define TV_ISBYREF(X) (TV_VT(X)&VT_BYREF) 171 | #define TV_ISARRAY(X) (TV_VT(X)&VT_ARRAY) 172 | #define TV_ISVECTOR(X) (TV_VT(X)&VT_VECTOR) 173 | #define TV_NONE(X) TV_I2(X) 174 | 175 | #define TV_UI1(X) TV_JOIN(X, ui8Val) 176 | #define TV_I2(X) TV_JOIN(X, shortVal) 177 | #define TV_I4(X) TV_JOIN(X, lVal) 178 | #define TV_I8(X) TV_JOIN(X, llVal) 179 | #define TV_R4(X) TV_JOIN(X, fltVal) 180 | #define TV_R8(X) TV_JOIN(X, dblVal) 181 | #define TV_I1(X) TV_JOIN(X, i8Val) 182 | #define TV_UI2(X) TV_JOIN(X, ushortVal) 183 | #define TV_UI4(X) TV_JOIN(X, ulVal) 184 | #define TV_UI8(X) TV_JOIN(X, ullVal) 185 | #define TV_INT(X) TV_JOIN(X, intVal) 186 | #define TV_UINT(X) TV_JOIN(X, uintVal) 187 | 188 | #ifdef _WIN64 189 | #define TV_INT_PTR(X) TV_JOIN(X, llVal) 190 | #define TV_UINT_PTR(X) TV_JOIN(X, ullVal) 191 | #else 192 | #define TV_INT_PTR(X) TV_JOIN(X, lVal) 193 | #define TV_UINT_PTR(X) TV_JOIN(X, ulVal) 194 | #endif 195 | 196 | 197 | #define TV_DATE(X) TV_JOIN(X, date) 198 | #define TV_STR(X) TV_JOIN(X, pstrVal) 199 | #define TV_WSTR(X) TV_JOIN(X, pwstrVal) 200 | #define TV_BOOL(X) TV_JOIN(X, bVal) 201 | #define TV_UNKNOWN(X) TV_JOIN(X, pInterfaceVal) 202 | #define TV_VARIANTREF(X) TV_JOIN(X, pvarVal) 203 | 204 | void tVarInit(tVariant* tvar); 205 | 206 | inline 207 | void tVarInit(tVariant* tvar) 208 | { 209 | assert(tvar != NULL); 210 | memset(tvar, 0, sizeof(tVariant)); 211 | TV_VT(tvar) = VTYPE_EMPTY; 212 | } 213 | //----------------------------------------------------------------------------// 214 | // static setter functions... 215 | 216 | #define DATA_SET_BEGIN(data_) \ 217 | tVarInit(data_); 218 | 219 | #define DATA_SET_END(data_, type_) \ 220 | TV_VT(data_) = type_; 221 | 222 | 223 | #define DATA_SET(data_, type_, member_, value_) \ 224 | DATA_SET_BEGIN(data_) \ 225 | TV_JOIN(data_, member_) = value_; \ 226 | DATA_SET_END(data_, type_) 227 | 228 | #define DATA_SET_WITH_CAST(data_, type_, member_, cast_, value_) \ 229 | DATA_SET_BEGIN(data_) \ 230 | TV_JOIN(data_, member_) = cast_ value_; \ 231 | DATA_SET_END(data_, type_) 232 | 233 | #endif //__CON_TYPES_H__ 234 | -------------------------------------------------------------------------------- /addin1c-test/src/component_base.cpp: -------------------------------------------------------------------------------- 1 | #include "ComponentBase.h" 2 | #include "IMemoryManager.h" 3 | 4 | extern "C" bool RegisterExtensionAs(IComponentBase *component, char16_t **wsExtensionName) 5 | { 6 | return component->RegisterExtensionAs(wsExtensionName); 7 | } 8 | 9 | extern "C" bool SetMemManager(IComponentBase *component, void *disp) 10 | { 11 | return component->setMemManager(disp); 12 | } 13 | 14 | extern "C" long FindProp(IComponentBase *component, const char16_t *name) 15 | { 16 | return component->FindProp(name); 17 | } 18 | 19 | extern "C" bool SetPropVal(IComponentBase *component, long num, tVariant *value) 20 | { 21 | return component->SetPropVal(num, value); 22 | } 23 | 24 | extern "C" bool GetPropVal(IComponentBase *component, long num, tVariant *value) 25 | { 26 | return component->GetPropVal(num, value); 27 | } 28 | 29 | extern "C" long FindMethod(IComponentBase *component, const char16_t *name) 30 | { 31 | return component->FindMethod(name); 32 | } 33 | 34 | extern "C" long GetNParams(IComponentBase *component, long num) 35 | { 36 | return component->GetNParams(num); 37 | } 38 | 39 | extern "C" bool CallAsFunc(IComponentBase *component, long num, tVariant *pvarRetValue, tVariant *params, long len) 40 | { 41 | return component->CallAsFunc(num, pvarRetValue, params, len); 42 | } 43 | -------------------------------------------------------------------------------- /addin1c-test/src/ffi.rs: -------------------------------------------------------------------------------- 1 | use std::ffi::{c_long, c_void}; 2 | 3 | // component_base 4 | extern "C" { 5 | 6 | pub fn SetMemManager(component: *mut c_void, memory_manager: *const c_void) -> bool; 7 | 8 | pub fn RegisterExtensionAs(component: *mut c_void, name: *mut *mut u16) -> bool; 9 | 10 | pub fn FindProp(component: *mut c_void, name: *const u16) -> c_long; 11 | 12 | pub fn SetPropVal(component: *mut c_void, num: c_long, val: *const c_void) -> bool; 13 | 14 | pub fn GetPropVal(component: *mut c_void, num: c_long, val: *const c_void) -> bool; 15 | 16 | pub fn FindMethod(component: *mut c_void, name: *const u16) -> c_long; 17 | 18 | pub fn GetNParams(component: *mut c_void, num: c_long) -> c_long; 19 | 20 | pub fn CallAsFunc( 21 | component: *mut c_void, 22 | num: c_long, 23 | ret_value: *const c_void, 24 | params: *const c_void, 25 | len: c_long, 26 | ) -> bool; 27 | 28 | } 29 | 30 | // memory_manager 31 | extern "C" { 32 | pub fn CreateMemoryManager() -> *const c_void; 33 | 34 | pub fn DeleteMemoryManager(mem: *const c_void); 35 | 36 | pub fn FreeMemory(mem: *const c_void, ptr: *mut *mut c_void); 37 | } 38 | 39 | // variant 40 | extern "C" { 41 | 42 | pub fn GetTypeVariant(ptr: *const c_void) -> u16; 43 | 44 | pub fn SetEmptyVariant(ptr: *const c_void); 45 | 46 | pub fn GetValVariantBool(ptr: *const c_void) -> bool; 47 | 48 | pub fn SetValVariantBool(ptr: *const c_void, val: bool); 49 | 50 | pub fn GetValVariantI4(ptr: *const c_void) -> i32; 51 | 52 | pub fn SetValVariantI4(ptr: *const c_void, val: i32); 53 | 54 | pub fn GetValVariantR8(ptr: *const c_void) -> f64; 55 | 56 | pub fn SetValVariantR8(ptr: *const c_void, val: f64); 57 | 58 | pub fn GetLenVariantString(ptr: *const c_void) -> u32; 59 | 60 | pub fn GetValVariantString(ptr: *const c_void) -> *const u16; 61 | 62 | pub fn SetValVariantString(ptr: *const c_void, val: *const u16, len: u32); 63 | } 64 | -------------------------------------------------------------------------------- /addin1c-test/src/lib.rs: -------------------------------------------------------------------------------- 1 | mod ffi; 2 | mod memory_manager; 3 | mod test_addin_lib; 4 | mod test_addin_object; 5 | mod tm; 6 | mod variant; 7 | 8 | #[cfg(test)] 9 | mod tests; 10 | 11 | pub use test_addin_lib::TestAddinLib; 12 | pub use variant::Variant; 13 | 14 | pub use utf16_lit::utf16; 15 | pub use utf16_lit::utf16_null; 16 | 17 | /// Null terminated utf-16 static string, used for names 18 | #[macro_export] 19 | macro_rules! name { 20 | ($text:expr) => { 21 | &addin1c_test::utf16_null!($text) 22 | }; 23 | } 24 | 25 | /// Non null utf-16 static string, used for 1c-strings 26 | #[macro_export] 27 | macro_rules! str1c { 28 | ($text:expr) => { 29 | &addin1c_test::utf16!($text) 30 | }; 31 | } 32 | -------------------------------------------------------------------------------- /addin1c-test/src/memory_manager.cpp: -------------------------------------------------------------------------------- 1 | #include "types.h" 2 | #include "IMemoryManager.h" 3 | 4 | class MemoryManager : public IMemoryManager 5 | { 6 | 7 | public: 8 | virtual bool ADDIN_API AllocMemory(void **pMemory, unsigned long ulCountByte) 9 | { 10 | auto ptr = new char[ulCountByte]; 11 | *pMemory = ptr; 12 | return true; 13 | } 14 | 15 | virtual void ADDIN_API FreeMemory(void **pMemory) 16 | { 17 | delete[] (char *)*pMemory; 18 | pMemory = 0; 19 | } 20 | }; 21 | 22 | extern "C" IMemoryManager *CreateMemoryManager() 23 | { 24 | return new MemoryManager(); 25 | } 26 | 27 | extern "C" void DeleteMemoryManager(IMemoryManager *mem) 28 | { 29 | delete mem; 30 | } 31 | 32 | extern "C" bool AllocMemory(IMemoryManager *mem, void **pMemory, unsigned long ulCountByte) 33 | { 34 | return mem->AllocMemory(pMemory, ulCountByte); 35 | } 36 | 37 | extern "C" void FreeMemory(IMemoryManager *mem, void ** pMemory) 38 | { 39 | mem->FreeMemory(pMemory); 40 | } -------------------------------------------------------------------------------- /addin1c-test/src/memory_manager.rs: -------------------------------------------------------------------------------- 1 | use std::{ffi::c_void, fmt, ops::Deref}; 2 | 3 | use crate::ffi::{CreateMemoryManager, DeleteMemoryManager, FreeMemory}; 4 | 5 | pub struct MemoryManager(*const c_void); 6 | 7 | #[derive(Debug)] 8 | pub struct AllocError(usize); 9 | 10 | impl fmt::Display for AllocError { 11 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 12 | write!(f, "Failed to allocate {} bytes", self.0) 13 | } 14 | } 15 | 16 | impl std::error::Error for AllocError {} 17 | 18 | impl MemoryManager { 19 | pub fn new() -> Self { 20 | let ptr = unsafe { CreateMemoryManager() }; 21 | Self(ptr) 22 | } 23 | 24 | pub fn free_str(&self, ptr: *mut *mut u16) { 25 | unsafe { 26 | FreeMemory(self.0, ptr as _); 27 | } 28 | } 29 | } 30 | 31 | impl Drop for MemoryManager { 32 | fn drop(&mut self) { 33 | unsafe { DeleteMemoryManager(self.0) }; 34 | } 35 | } 36 | 37 | impl Deref for MemoryManager { 38 | type Target = *const c_void; 39 | 40 | fn deref(&self) -> &Self::Target { 41 | &self.0 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /addin1c-test/src/test_addin_lib.rs: -------------------------------------------------------------------------------- 1 | use std::{ 2 | collections::HashMap, 3 | error::Error, 4 | ffi::{c_long, c_void, OsStr}, 5 | ptr::{self}, 6 | slice, 7 | }; 8 | 9 | use libloading::Library; 10 | 11 | use crate::{memory_manager::MemoryManager, test_addin_object::TestAddinObject}; 12 | 13 | pub struct TestAddinLib { 14 | lib: Library, 15 | classes: HashMap>, 16 | pub(crate) mem: MemoryManager, 17 | } 18 | 19 | pub(crate) fn str1c_to_string(classes: *const u16) -> String { 20 | let mut ptr = classes; 21 | unsafe { 22 | let len = loop { 23 | if *ptr == 0 { 24 | break ptr.offset_from(classes); 25 | } 26 | ptr = ptr.add(1); 27 | }; 28 | 29 | let slice = slice::from_raw_parts(classes, len as _); 30 | String::from_utf16_lossy(slice) 31 | } 32 | } 33 | 34 | impl TestAddinLib { 35 | pub fn new

(file_name: P) -> Result> 36 | where 37 | P: AsRef, 38 | { 39 | unsafe { 40 | let lib = { Library::new(file_name)? }; 41 | let mem = MemoryManager::new(); 42 | 43 | let get_class_names: libloading::Symbol *const u16> = 44 | { lib.get(b"GetClassNames")? }; 45 | 46 | let class_names = { get_class_names() }; 47 | 48 | let class_names = str1c_to_string(class_names); 49 | 50 | let this = Self { 51 | lib, 52 | classes: HashMap::>::new(), 53 | mem, 54 | }; 55 | let mut classes = HashMap::>::new(); 56 | 57 | for name in class_names.split('|') { 58 | let mut class = name.encode_utf16().collect::>(); 59 | class.push(0); 60 | 61 | let component = this.get_class_object(&class)?; 62 | let object = TestAddinObject::new(&this, component); 63 | 64 | if !object.set_mem_manager(*this.mem) { 65 | return Err(format!("Не удалось установить MemManager: {name}").into()); 66 | } 67 | 68 | let extension_name = object.register_extension_as()?; 69 | 70 | classes.insert(extension_name, class); 71 | } 72 | 73 | Ok(TestAddinLib { 74 | lib: this.lib, 75 | classes, 76 | mem: this.mem, 77 | }) 78 | } 79 | } 80 | 81 | fn get_class_object(&self, name: &[u16]) -> Result<*mut c_void, Box> { 82 | let mut component = ptr::null_mut::(); 83 | let get_class_object: libloading::Symbol< 84 | unsafe extern "C" fn(name: *const u16, component: *mut *mut c_void) -> c_long, 85 | > = unsafe { self.lib.get(b"GetClassObject")? }; 86 | if unsafe { get_class_object(name.as_ptr(), &mut component) } == 0 { 87 | return Err("Не удалось создать объект".into()); 88 | }; 89 | Ok(component) 90 | } 91 | 92 | pub(crate) fn destroy_object(&self, ptr: *mut *mut c_void) -> Result<(), Box> { 93 | unsafe { 94 | let destroy_object: libloading::Symbol< 95 | unsafe extern "C" fn(component: *mut *mut c_void) -> c_long, 96 | > = { self.lib.get(b"DestroyObject")? }; 97 | 98 | destroy_object(ptr); 99 | Ok(()) 100 | } 101 | } 102 | 103 | pub fn new_addin(&self, name: &str) -> Result> { 104 | let class = self.classes.get(name).ok_or("Class not found")?; 105 | let component = self.get_class_object(class)?; 106 | let object = TestAddinObject::new(self, component); 107 | if !object.set_mem_manager(*self.mem) { 108 | return Err(format!("Не удалось установить MemManager: {name}").into()); 109 | } 110 | Ok(object) 111 | } 112 | } 113 | -------------------------------------------------------------------------------- /addin1c-test/src/test_addin_object.rs: -------------------------------------------------------------------------------- 1 | use std::{error::Error, ffi::c_void, ptr::{null, null_mut}}; 2 | 3 | use crate::{ 4 | ffi::{ 5 | CallAsFunc, FindMethod, FindProp, GetNParams, GetPropVal, RegisterExtensionAs, 6 | SetMemManager, SetPropVal, 7 | }, 8 | test_addin_lib::{str1c_to_string, TestAddinLib}, 9 | Variant, 10 | }; 11 | 12 | pub struct TestAddinObject<'a> { 13 | ptr: *mut c_void, 14 | lib: &'a TestAddinLib, 15 | } 16 | 17 | impl<'a> TestAddinObject<'a> { 18 | pub(crate) fn new(lib: &'a TestAddinLib, ptr: *mut c_void) -> Self { 19 | Self { lib, ptr } 20 | } 21 | 22 | pub(crate) fn set_mem_manager(&self, mem: *const c_void) -> bool { 23 | unsafe { SetMemManager(self.ptr, mem) } 24 | } 25 | 26 | pub(crate) fn register_extension_as(&self) -> Result> { 27 | let mut class_name = null_mut::(); 28 | if !unsafe { RegisterExtensionAs(self.ptr, &mut class_name) } { 29 | return Err("Не удалось получить имя класса:".into()); 30 | } 31 | 32 | let extenion_name = str1c_to_string(class_name); 33 | self.lib.mem.free_str(&mut class_name); 34 | Ok(extenion_name) 35 | } 36 | 37 | pub fn set_property(&self, name: &[u16], val: &Variant) -> Result<(), Box> { 38 | let num = unsafe { FindProp(self.ptr, name.as_ptr()) }; 39 | if num < 0 { 40 | return Err("Property not found".into()); 41 | } 42 | if unsafe { SetPropVal(self.ptr, num, val.as_ptr()) } { 43 | Ok(()) 44 | } else { 45 | Err("Failed to set property".into()) 46 | } 47 | } 48 | 49 | pub fn get_property(&self, name: &[u16]) -> Result> { 50 | let num = unsafe { FindProp(self.ptr, name.as_ptr()) }; 51 | if num < 0 { 52 | return Err("Property not found".into()); 53 | } 54 | let val = Variant::new(); 55 | if unsafe { GetPropVal(self.ptr, num, val.as_ptr()) } { 56 | Ok(val) 57 | } else { 58 | Err("Failed to get property".into()) 59 | } 60 | } 61 | 62 | pub fn call_as_func( 63 | &self, 64 | name: &[u16], 65 | params: &mut [Variant; PARAMS], 66 | ) -> Result> { 67 | let num = unsafe { FindMethod(self.ptr, name.as_ptr()) }; 68 | if num < 0 { 69 | return Err("Method not found".into()); 70 | } 71 | 72 | let nparams = unsafe { GetNParams(self.ptr, num) } as usize; 73 | #[allow(clippy::comparison_chain)] 74 | if nparams > PARAMS { 75 | return Err("Too many parameters".into()); 76 | } else if nparams < PARAMS { 77 | return Err("Not enough parameters".into()); 78 | } 79 | 80 | let result = Variant::new(); 81 | 82 | if !unsafe { 83 | CallAsFunc( 84 | self.ptr, 85 | num, 86 | result.as_ptr(), 87 | if PARAMS > 0 {params[0].as_ptr()} else {null()}, 88 | PARAMS as _, 89 | ) 90 | } { 91 | return Err("Failed to call function".into()); 92 | }; 93 | 94 | Ok(result) 95 | } 96 | } 97 | 98 | impl Drop for TestAddinObject<'_> { 99 | fn drop(&mut self) { 100 | let _ = self.lib.destroy_object(&mut self.ptr); 101 | } 102 | } 103 | -------------------------------------------------------------------------------- /addin1c-test/src/tests.rs: -------------------------------------------------------------------------------- 1 | use crate::Variant; 2 | 3 | extern "C" { 4 | pub fn SizeOfVariant() -> std::ffi::c_ulong; 5 | } 6 | 7 | #[test] 8 | fn size_of_variant_correct() { 9 | assert_eq!(size_of::(), unsafe { SizeOfVariant() } as usize); 10 | } 11 | -------------------------------------------------------------------------------- /addin1c-test/src/tm.rs: -------------------------------------------------------------------------------- 1 | use std::ffi::c_int; 2 | 3 | #[repr(C)] 4 | #[derive(Debug, Clone, Copy, Default)] 5 | pub struct Tm { 6 | pub sec: c_int, // seconds after the minute - [0, 60] including leap second 7 | pub min: c_int, // minutes after the hour - [0, 59] 8 | pub hour: c_int, // hours since midnight - [0, 23] 9 | pub mday: c_int, // day of the month - [1, 31] 10 | pub mon: c_int, // months since January - [0, 11] 11 | pub year: c_int, // years since 1900 12 | pub wday: c_int, // days since Sunday - [0, 6] 13 | pub yday: c_int, // days since January 1 - [0, 365] 14 | pub isdst: c_int, // daylight savings time flag 15 | 16 | #[cfg(target_family = "unix")] 17 | pub gmtoff: std::ffi::c_long, // seconds east of UTC 18 | #[cfg(target_family = "unix")] 19 | pub zone: std::ffi::c_char, // timezone abbreviation 20 | } 21 | -------------------------------------------------------------------------------- /addin1c-test/src/variant.cpp: -------------------------------------------------------------------------------- 1 | #include "types.h" 2 | 3 | extern "C" unsigned long SizeOfVariant() 4 | { 5 | return sizeof(tVariant); 6 | } 7 | 8 | extern "C" unsigned short GetTypeVariant(tVariant *variant) 9 | { 10 | return variant->vt; 11 | } 12 | 13 | extern "C" void SetEmptyVariant(tVariant *variant) 14 | { 15 | if (variant->vt == VTYPE_PWSTR) 16 | { 17 | delete[] variant->pwstrVal; 18 | variant->pwstrVal = 0; 19 | variant->wstrLen = 0; 20 | } 21 | else if (variant->vt == VTYPE_BLOB) 22 | { 23 | delete[] variant->pstrVal; 24 | variant->pstrVal = 0; 25 | variant->strLen = 0; 26 | } 27 | variant->vt = VTYPE_EMPTY; 28 | } 29 | 30 | extern "C" bool GetValVariantBool(tVariant *variant) 31 | { 32 | return TV_BOOL(variant); 33 | } 34 | 35 | extern "C" void SetValVariantBool(tVariant *variant, bool val) 36 | { 37 | SetEmptyVariant(variant); 38 | TV_VT(variant) = VTYPE_BOOL; 39 | TV_BOOL(variant) = val; 40 | } 41 | 42 | extern "C" int32_t GetValVariantI4(tVariant *variant) 43 | { 44 | return TV_I4(variant); 45 | } 46 | 47 | extern "C" void SetValVariantI4(tVariant *variant, int32_t val) 48 | { 49 | SetEmptyVariant(variant); 50 | TV_VT(variant) = VTYPE_I4; 51 | TV_I4(variant) = val; 52 | } 53 | 54 | extern "C" double GetValVariantR8(tVariant *variant) 55 | { 56 | return TV_R8(variant); 57 | } 58 | 59 | extern "C" void SetValVariantR8(tVariant *variant, double val) 60 | { 61 | SetEmptyVariant(variant); 62 | TV_VT(variant) = VTYPE_R8; 63 | TV_R8(variant) = val; 64 | } 65 | 66 | extern "C" uint32_t GetLenVariantString(tVariant *variant) 67 | { 68 | return variant->wstrLen; 69 | } 70 | 71 | extern "C" char16_t *GetValVariantString(tVariant *variant) 72 | { 73 | return variant->pwstrVal; 74 | } 75 | 76 | extern "C" void SetValVariantString(tVariant *variant, char16_t *str, uint32_t len) 77 | { 78 | SetEmptyVariant(variant); 79 | 80 | auto val = new char16_t[len]; 81 | memcpy(val, str, len * sizeof(char16_t)); 82 | 83 | TV_VT(variant) = VTYPE_PWSTR; 84 | variant->pwstrVal = val; 85 | variant->wstrLen = len; 86 | } 87 | -------------------------------------------------------------------------------- /addin1c-test/src/variant.rs: -------------------------------------------------------------------------------- 1 | use core::slice; 2 | use std::{ffi::c_void, fmt::Debug}; 3 | 4 | use crate::{ffi::*, tm::Tm}; 5 | 6 | #[repr(u16)] 7 | #[derive(PartialEq, Eq, Debug)] 8 | pub enum VariantType { 9 | Empty = 0, 10 | I4 = 3, 11 | R8 = 5, 12 | TM = 7, 13 | Bool = 11, 14 | Pwstr = 22, 15 | Blob = 23, 16 | Undefined = 0xFFFF, 17 | } 18 | 19 | impl From for VariantType { 20 | fn from(value: u16) -> Self { 21 | match value { 22 | 0 => VariantType::Empty, 23 | 3 => VariantType::I4, 24 | 5 => VariantType::R8, 25 | 7 => VariantType::TM, 26 | 11 => VariantType::Bool, 27 | 22 => VariantType::Pwstr, 28 | 23 => VariantType::Blob, 29 | _ => VariantType::Undefined, 30 | } 31 | } 32 | } 33 | 34 | #[repr(C)] 35 | #[derive(Debug, Clone, Copy)] 36 | struct DataStr { 37 | pub ptr: *mut u16, 38 | pub len: u32, 39 | } 40 | 41 | #[repr(C)] 42 | #[derive(Debug, Clone, Copy)] 43 | struct DataBlob { 44 | pub ptr: *mut u8, 45 | pub len: u32, 46 | } 47 | 48 | #[repr(C)] 49 | union VariantValue { 50 | pub bool: bool, 51 | pub i32: i32, 52 | pub f64: f64, 53 | pub tm: Tm, 54 | pub data_str: DataStr, 55 | pub data_blob: DataBlob, 56 | } 57 | 58 | #[repr(C)] 59 | pub struct Variant { 60 | value: VariantValue, 61 | elements: u32, 62 | vt: u16, 63 | } 64 | 65 | impl Debug for Variant { 66 | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { 67 | let variant_type = self.get_type(); 68 | let mut debug_struct = f.debug_struct("Variant"); 69 | debug_struct.field("vt", &variant_type); 70 | 71 | let value: &dyn std::fmt::Debug = match variant_type { 72 | VariantType::Empty => &"", 73 | VariantType::I4 => unsafe { &self.value.i32 }, 74 | VariantType::R8 => unsafe { &self.value.f64 }, 75 | VariantType::TM => unsafe { &self.value.tm }, 76 | VariantType::Bool => unsafe { &self.value.bool }, 77 | VariantType::Pwstr => unsafe { &self.value.data_str }, 78 | VariantType::Blob => unsafe { &self.value.data_blob }, 79 | VariantType::Undefined => &"", 80 | }; 81 | debug_struct.field("value", value).finish() 82 | } 83 | } 84 | 85 | impl Default for Variant { 86 | fn default() -> Self { 87 | Self { 88 | value: VariantValue { 89 | tm: Default::default(), 90 | }, 91 | elements: 0, 92 | vt: VariantType::Empty as _, 93 | } 94 | } 95 | } 96 | 97 | impl Variant { 98 | pub fn new() -> Self { 99 | Default::default() 100 | } 101 | 102 | pub fn as_ptr(&self) -> *const c_void { 103 | self as *const Variant as *const c_void 104 | } 105 | 106 | pub fn get_type(&self) -> VariantType { 107 | let vt = unsafe { GetTypeVariant(self.as_ptr()) }; 108 | vt.into() 109 | } 110 | 111 | pub fn get_empty(&self) -> Option<()> { 112 | if self.get_type() == VariantType::Empty { 113 | Some(()) 114 | } else { 115 | None 116 | } 117 | } 118 | 119 | pub fn set_empty(&mut self) { 120 | unsafe { SetEmptyVariant(self.as_ptr()) }; 121 | } 122 | 123 | pub fn get_bool(&self) -> Option { 124 | if self.get_type() == VariantType::Bool { 125 | Some(unsafe { GetValVariantBool(self.as_ptr()) }) 126 | } else { 127 | None 128 | } 129 | } 130 | 131 | pub fn set_bool(&mut self, val: bool) { 132 | unsafe { SetValVariantBool(self.as_ptr(), val) }; 133 | } 134 | 135 | pub fn new_bool(val: bool) -> Variant { 136 | let mut variant = Variant::new(); 137 | variant.set_bool(val); 138 | variant 139 | } 140 | 141 | pub fn get_i32(&self) -> Option { 142 | if self.get_type() == VariantType::I4 { 143 | Some(unsafe { GetValVariantI4(self.as_ptr()) }) 144 | } else { 145 | None 146 | } 147 | } 148 | 149 | pub fn set_i32(&mut self, val: i32) { 150 | unsafe { SetValVariantI4(self.as_ptr(), val) }; 151 | } 152 | 153 | pub fn new_i32(val: i32) -> Variant { 154 | let mut variant = Variant::new(); 155 | variant.set_i32(val); 156 | variant 157 | } 158 | 159 | pub fn get_f64(&self) -> Option { 160 | if self.get_type() == VariantType::R8 { 161 | Some(unsafe { GetValVariantR8(self.as_ptr()) }) 162 | } else { 163 | None 164 | } 165 | } 166 | 167 | pub fn set_f64(&mut self, val: f64) { 168 | unsafe { SetValVariantR8(self.as_ptr(), val) }; 169 | } 170 | 171 | pub fn new_f64(val: f64) -> Variant { 172 | let mut variant = Variant::new(); 173 | variant.set_f64(val); 174 | variant 175 | } 176 | 177 | pub fn get_str(&self) -> Option<&[u16]> { 178 | if self.get_type() == VariantType::Pwstr { 179 | let len = unsafe { GetLenVariantString(self.as_ptr()) } as usize; 180 | let val = unsafe { GetValVariantString(self.as_ptr()) }; 181 | let data = unsafe { slice::from_raw_parts(val, len) }; 182 | Some(data) 183 | } else { 184 | None 185 | } 186 | } 187 | 188 | pub fn set_str(&mut self, val: &[u16]) { 189 | unsafe { SetValVariantString(self.as_ptr(), val.as_ptr(), val.len() as u32) }; 190 | } 191 | 192 | pub fn new_str(val: &[u16]) -> Variant { 193 | let mut variant = Variant::new(); 194 | variant.set_str(val); 195 | variant 196 | } 197 | } 198 | 199 | impl Drop for Variant { 200 | fn drop(&mut self) { 201 | unsafe { SetEmptyVariant(self.as_ptr()) }; 202 | } 203 | } 204 | -------------------------------------------------------------------------------- /addin1c/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "addin1c" 3 | version = "0.7.0" 4 | edition = "2021" 5 | description = "Helper for creating 1C:Enterpirse 8 add-ins with Native API technology" 6 | license = "MIT" 7 | categories = [] 8 | repository = "https://github.com/medigor/addin1c" 9 | homepage = "https://github.com/medigor/addin1c" 10 | keywords = ["1c-enterprise", "native-api", "addin"] 11 | readme = "README.md" 12 | 13 | [dependencies] 14 | smallvec = "1.13" 15 | utf16_lit = "2.0" 16 | chrono = { version = "0.4", optional = true } 17 | 18 | [features] 19 | chrono = ["dep:chrono"] 20 | -------------------------------------------------------------------------------- /addin1c/README.md: -------------------------------------------------------------------------------- 1 | # addin1c 2 | Helper for creating 1C:Enterprise 8 add-ins with Native API technology 3 | 4 | References: 5 | * [1C:Enterprise Guide (ru)](https://its.1c.ru/db/metod8dev#content:3221:hdoc) 6 | * [1C:Enterprise Guide (en)](https://kb.1ci.com/1C_Enterprise_Platform/Guides/Developer_Guides/Extra/Add-in_Development_Technology/Creating_add-ins_with_Native_API_technology/) 7 | 8 | See [example](example): 9 | * [addin1.rs](example/src/addin1.rs) - raw interface 10 | * [addin2.rs](example/src/addin2.rs) - simple interface 11 | 12 | ## Debugging in Visual Studio Code 13 | Create a `.vscode/launch.json` file: 14 | ```json 15 | { 16 | "version": "0.2.0", 17 | "configurations": [ 18 | { 19 | "type": "lldb", 20 | "request": "launch", 21 | "name": "Debug 1С", 22 | "program": "path/to/1cv8c", 23 | "args": [ 24 | "/IBName", 25 | "Test1" 26 | ], 27 | "cwd": "${workspaceFolder}", 28 | "preLaunchTask": "rust: cargo build", 29 | "env": {"DISPLAY": ":1"}, // only for Linux 30 | } 31 | ] 32 | } 33 | ``` 34 | -------------------------------------------------------------------------------- /addin1c/src/connection.rs: -------------------------------------------------------------------------------- 1 | use std::ffi::{c_long, c_ushort}; 2 | 3 | use crate::{tvariant::TVariant, CStr1C}; 4 | 5 | #[repr(C)] 6 | struct ConnectionVTable { 7 | dtor: usize, 8 | #[cfg(target_family = "unix")] 9 | dtor2: usize, 10 | add_error: 11 | unsafe extern "system" fn(&Connection, c_ushort, *const u16, *const u16, c_long) -> bool, 12 | read: unsafe extern "system" fn( 13 | &Connection, 14 | *mut u16, 15 | &mut TVariant, 16 | c_long, 17 | *mut *mut u16, 18 | ) -> bool, 19 | write: unsafe extern "system" fn(&Connection, *mut u16, &mut TVariant) -> bool, 20 | register_profile_as: unsafe extern "system" fn(&Connection, *mut u16) -> bool, 21 | set_event_buffer_depth: unsafe extern "system" fn(&Connection, c_long) -> bool, 22 | get_event_buffer_depth: unsafe extern "system" fn(&Connection) -> c_long, 23 | external_event: 24 | unsafe extern "system" fn(&Connection, *const u16, *const u16, *const u16) -> bool, 25 | clean_event_buffer: unsafe extern "system" fn(&Connection), 26 | set_status_line: unsafe extern "system" fn(&Connection, *mut u16) -> bool, 27 | reset_status_line: unsafe extern "system" fn(&Connection), 28 | } 29 | 30 | #[repr(C)] 31 | pub struct Connection { 32 | vptr1: &'static ConnectionVTable, 33 | } 34 | 35 | impl Connection { 36 | pub fn external_event( 37 | &self, 38 | source: impl AsRef, 39 | message: impl AsRef, 40 | data: impl AsRef, 41 | ) -> bool { 42 | unsafe { 43 | (self.vptr1.external_event)( 44 | self, 45 | source.as_ref().as_ptr(), 46 | message.as_ref().as_ptr(), 47 | data.as_ref().as_ptr(), 48 | ) 49 | } 50 | } 51 | 52 | pub fn set_event_buffer_depth(&self, depth: c_long) -> bool { 53 | unsafe { (self.vptr1.set_event_buffer_depth)(self, depth) } 54 | } 55 | 56 | pub fn get_event_buffer_depth(&self) -> c_long { 57 | unsafe { (self.vptr1.get_event_buffer_depth)(self) } 58 | } 59 | 60 | pub fn clean_event_buffer(&self) { 61 | unsafe { (self.vptr1.clean_event_buffer)(self) } 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /addin1c/src/cstr1c.rs: -------------------------------------------------------------------------------- 1 | use std::{ops::Deref, slice::from_raw_parts}; 2 | 3 | #[derive(PartialEq)] 4 | pub struct CStr1C([u16]); 5 | 6 | impl CStr1C { 7 | /// Wraps a raw 1C string with a safe string wrapper. 8 | /// 9 | /// # SAFETY 10 | /// 11 | /// Slice must be a nul-terminated string. 12 | pub const unsafe fn from_bytes_unchecked(bytes: &[u16]) -> &Self { 13 | debug_assert!(!bytes.is_empty() && bytes[bytes.len() - 1] == 0); 14 | unsafe { &*(bytes as *const [u16] as *const CStr1C) } 15 | } 16 | 17 | /// Wraps a raw 1C string with a safe string wrapper. 18 | /// 19 | /// # SAFETY 20 | /// 21 | /// The memory pointed to by ptr must contain a valid nul-terminated string. 22 | pub unsafe fn from_ptr<'a>(s: *const u16) -> &'a Self { 23 | let mut len = 0; 24 | while *s.add(len) != 0 { 25 | len += 1; 26 | } 27 | len += 1; 28 | 29 | CStr1C::from_bytes_unchecked(from_raw_parts(s, len)) 30 | } 31 | } 32 | 33 | impl AsRef for CStr1C { 34 | fn as_ref(&self) -> &CStr1C { 35 | self 36 | } 37 | } 38 | 39 | impl Deref for CStr1C { 40 | type Target = [u16]; 41 | 42 | fn deref(&self) -> &Self::Target { 43 | &self.0 44 | } 45 | } 46 | 47 | pub struct CString1C(Vec); 48 | 49 | impl CString1C { 50 | pub fn new(str: &str) -> Self { 51 | let mut buf = Vec::with_capacity(str.len() + 1); 52 | buf.extend(str.encode_utf16()); 53 | buf.push(0); 54 | Self(buf) 55 | } 56 | } 57 | 58 | impl Deref for CString1C { 59 | type Target = CStr1C; 60 | 61 | fn deref(&self) -> &Self::Target { 62 | unsafe { CStr1C::from_bytes_unchecked(self.0.as_ref()) } 63 | } 64 | } 65 | 66 | impl AsRef for CString1C { 67 | fn as_ref(&self) -> &CStr1C { 68 | self 69 | } 70 | } 71 | 72 | impl From<&str> for CString1C { 73 | fn from(value: &str) -> Self { 74 | CString1C::new(value) 75 | } 76 | } 77 | 78 | /// Null terminated utf-16 static string, used for names 79 | #[macro_export] 80 | macro_rules! cstr1c { 81 | ($text:expr) => { 82 | const { unsafe { addin1c::CStr1C::from_bytes_unchecked(&addin1c::utf16_null!($text)) } } 83 | }; 84 | } 85 | -------------------------------------------------------------------------------- /addin1c/src/ffi.rs: -------------------------------------------------------------------------------- 1 | use std::{ 2 | ffi::{c_long, c_void}, 3 | ptr::{self}, 4 | slice::from_raw_parts_mut, 5 | }; 6 | 7 | use smallvec::SmallVec; 8 | 9 | use crate::{ 10 | memory_manager::MemoryManager, tvariant::TVariant, variant::Variant, CStr1C, Connection, 11 | }; 12 | 13 | #[allow(unused_variables)] 14 | pub trait Addin { 15 | fn init(&mut self, interface: &'static Connection) -> bool { 16 | true 17 | } 18 | 19 | /// default 2000, don't use version 1000, because static objects are created 20 | fn get_info(&mut self) -> u16 { 21 | 2000 22 | } 23 | fn done(&mut self) {} 24 | fn register_extension_as(&mut self) -> &CStr1C; 25 | fn get_n_props(&mut self) -> usize { 26 | 0 27 | } 28 | fn find_prop(&mut self, name: &CStr1C) -> Option { 29 | None 30 | } 31 | fn get_prop_name(&mut self, num: usize, alias: usize) -> Option<&'static CStr1C> { 32 | None 33 | } 34 | fn get_prop_val(&mut self, num: usize, val: &mut Variant) -> bool { 35 | false 36 | } 37 | fn set_prop_val(&mut self, num: usize, val: &Variant) -> bool { 38 | false 39 | } 40 | fn is_prop_readable(&mut self, num: usize) -> bool { 41 | false 42 | } 43 | fn is_prop_writable(&mut self, num: usize) -> bool { 44 | false 45 | } 46 | fn get_n_methods(&mut self) -> usize { 47 | 0 48 | } 49 | fn find_method(&mut self, name: &CStr1C) -> Option { 50 | None 51 | } 52 | fn get_method_name(&mut self, num: usize, alias: usize) -> Option<&'static CStr1C> { 53 | None 54 | } 55 | fn get_n_params(&mut self, num: usize) -> usize { 56 | 0 57 | } 58 | fn get_param_def_value(&mut self, method_num: usize, param_num: usize, value: Variant) -> bool { 59 | true 60 | } 61 | fn has_ret_val(&mut self, method_num: usize) -> bool { 62 | false 63 | } 64 | fn call_as_proc(&mut self, method_num: usize, params: &mut [Variant]) -> bool { 65 | false 66 | } 67 | fn call_as_func( 68 | &mut self, 69 | method_num: usize, 70 | params: &mut [Variant], 71 | val: &mut Variant, 72 | ) -> bool { 73 | false 74 | } 75 | fn set_locale(&mut self, loc: &[u16]) {} 76 | fn set_user_interface_language_code(&mut self, lang: &[u16]) {} 77 | } 78 | 79 | #[repr(C)] 80 | struct This { 81 | ptr: *mut Component, 82 | } 83 | 84 | impl This { 85 | unsafe fn get_component(&mut self) -> &mut Component { 86 | let new_ptr = (self as *mut This as *mut c_void) 87 | .sub(OFFSET * std::mem::size_of::()); 88 | &mut *(new_ptr as *mut Component) 89 | } 90 | } 91 | 92 | #[repr(C)] 93 | struct InitDoneBaseVTable { 94 | dtor: usize, 95 | #[cfg(target_family = "unix")] 96 | dtor2: usize, 97 | init: unsafe extern "system" fn(&mut This, &'static Connection) -> bool, 98 | set_mem_manager: 99 | unsafe extern "system" fn(&mut This, &'static MemoryManager) -> bool, 100 | get_info: unsafe extern "system" fn(&mut This) -> c_long, 101 | done: unsafe extern "system" fn(&mut This), 102 | } 103 | 104 | unsafe extern "system" fn init( 105 | this: &mut This, 106 | interface: &'static Connection, 107 | ) -> bool { 108 | let component = this.get_component(); 109 | component.addin.init(interface) 110 | } 111 | 112 | unsafe extern "system" fn set_mem_manager( 113 | this: &mut This, 114 | mem: &'static MemoryManager, 115 | ) -> bool { 116 | let component = this.get_component(); 117 | component.memory = Some(mem); 118 | true 119 | } 120 | 121 | unsafe extern "system" fn get_info( 122 | this: &mut This, 123 | ) -> c_long { 124 | let component = this.get_component(); 125 | component.addin.get_info() as c_long 126 | } 127 | 128 | unsafe extern "system" fn done(this: &mut This) { 129 | let component = this.get_component(); 130 | component.addin.done() 131 | } 132 | 133 | #[repr(C)] 134 | struct LanguageExtenderBaseVTable { 135 | dtor: usize, 136 | #[cfg(target_family = "unix")] 137 | dtor2: usize, 138 | register_extension_as: unsafe extern "system" fn(&mut This, *mut *mut u16) -> bool, 139 | get_n_props: unsafe extern "system" fn(&mut This) -> c_long, 140 | find_prop: unsafe extern "system" fn(&mut This, *const u16) -> c_long, 141 | get_prop_name: unsafe extern "system" fn(&mut This, c_long, c_long) -> *const u16, 142 | get_prop_val: unsafe extern "system" fn(&mut This, c_long, &mut TVariant) -> bool, 143 | set_prop_val: unsafe extern "system" fn(&mut This, c_long, &mut TVariant) -> bool, 144 | is_prop_readable: unsafe extern "system" fn(&mut This, c_long) -> bool, 145 | is_prop_writable: unsafe extern "system" fn(&mut This, c_long) -> bool, 146 | get_n_methods: unsafe extern "system" fn(&mut This) -> c_long, 147 | find_method: unsafe extern "system" fn(&mut This, *const u16) -> c_long, 148 | get_method_name: unsafe extern "system" fn(&mut This, c_long, c_long) -> *const u16, 149 | get_n_params: unsafe extern "system" fn(&mut This, c_long) -> c_long, 150 | get_param_def_value: 151 | unsafe extern "system" fn(&mut This, c_long, c_long, &mut TVariant) -> bool, 152 | has_ret_val: unsafe extern "system" fn(&mut This, c_long) -> bool, 153 | call_as_proc: 154 | unsafe extern "system" fn(&mut This, c_long, *mut TVariant, c_long) -> bool, 155 | call_as_func: unsafe extern "system" fn( 156 | &mut This, 157 | c_long, 158 | &mut TVariant, 159 | *mut TVariant, 160 | c_long, 161 | ) -> bool, 162 | } 163 | 164 | unsafe extern "system" fn register_extension_as( 165 | this: &mut This, 166 | name: *mut *mut u16, 167 | ) -> bool { 168 | let component = this.get_component(); 169 | let Some(allocator) = component.memory else { 170 | return false; 171 | }; 172 | 173 | let extension_name = component.addin.register_extension_as(); 174 | 175 | let Ok(ptr) = allocator.alloc_str(extension_name.len()) else { 176 | return false; 177 | }; 178 | ptr::copy_nonoverlapping(extension_name.as_ptr(), ptr.as_ptr(), extension_name.len()); 179 | *name = ptr.as_ptr(); 180 | 181 | true 182 | } 183 | 184 | unsafe extern "system" fn get_n_props( 185 | this: &mut This, 186 | ) -> c_long { 187 | let component = this.get_component(); 188 | component.addin.get_n_props() as c_long 189 | } 190 | 191 | unsafe extern "system" fn find_prop( 192 | this: &mut This, 193 | name: *const u16, 194 | ) -> c_long { 195 | let component = this.get_component(); 196 | let name = CStr1C::from_ptr(name); 197 | match component.addin.find_prop(name) { 198 | Some(i) => i as c_long, 199 | None => -1, 200 | } 201 | } 202 | 203 | unsafe extern "system" fn get_prop_name( 204 | this: &mut This, 205 | num: c_long, 206 | alias: c_long, 207 | ) -> *const u16 { 208 | let component = this.get_component(); 209 | let Some(allocator) = component.memory else { 210 | return ptr::null(); 211 | }; 212 | let Some(prop_name) = component.addin.get_prop_name(num as usize, alias as usize) else { 213 | return ptr::null(); 214 | }; 215 | let Ok(ptr) = allocator.alloc_str(prop_name.len()) else { 216 | return ptr::null(); 217 | }; 218 | ptr::copy_nonoverlapping(prop_name.as_ptr(), ptr.as_ptr(), prop_name.len()); 219 | 220 | ptr.as_ptr() 221 | } 222 | 223 | unsafe extern "system" fn get_prop_val( 224 | component: &mut This, 225 | num: c_long, 226 | val: &mut TVariant, 227 | ) -> bool { 228 | let component = component.get_component(); 229 | let Some(mem) = component.memory else { 230 | return false; 231 | }; 232 | 233 | let mut return_value = Variant { mem, variant: val }; 234 | component 235 | .addin 236 | .get_prop_val(num as usize, &mut return_value) 237 | } 238 | 239 | unsafe extern "system" fn set_prop_val( 240 | this: &mut This, 241 | num: c_long, 242 | val: &mut TVariant, 243 | ) -> bool { 244 | let component = this.get_component(); 245 | let Some(mem) = component.memory else { 246 | return false; 247 | }; 248 | let value = Variant { mem, variant: val }; 249 | component.addin.set_prop_val(num as usize, &value) 250 | } 251 | 252 | unsafe extern "system" fn is_prop_readable( 253 | this: &mut This, 254 | num: c_long, 255 | ) -> bool { 256 | let component = this.get_component(); 257 | component.addin.is_prop_readable(num as usize) 258 | } 259 | 260 | unsafe extern "system" fn is_prop_writable( 261 | this: &mut This, 262 | num: c_long, 263 | ) -> bool { 264 | let component = this.get_component(); 265 | component.addin.is_prop_writable(num as usize) 266 | } 267 | 268 | unsafe extern "system" fn get_n_methods( 269 | this: &mut This, 270 | ) -> c_long { 271 | let component = this.get_component(); 272 | component.addin.get_n_methods() as c_long 273 | } 274 | 275 | unsafe extern "system" fn find_method( 276 | this: &mut This, 277 | name: *const u16, 278 | ) -> c_long { 279 | let component = this.get_component(); 280 | let name = CStr1C::from_ptr(name); 281 | match component.addin.find_method(name) { 282 | Some(i) => i as c_long, 283 | None => -1, 284 | } 285 | } 286 | 287 | unsafe extern "system" fn get_method_name( 288 | this: &mut This, 289 | num: c_long, 290 | alias: c_long, 291 | ) -> *const u16 { 292 | let component = this.get_component(); 293 | let Some(allocator) = component.memory else { 294 | return ptr::null(); 295 | }; 296 | let Some(method_name) = component 297 | .addin 298 | .get_method_name(num as usize, alias as usize) 299 | else { 300 | return ptr::null(); 301 | }; 302 | let Ok(ptr) = allocator.alloc_str(method_name.len()) else { 303 | return ptr::null(); 304 | }; 305 | 306 | ptr::copy_nonoverlapping(method_name.as_ptr(), ptr.as_ptr(), method_name.len()); 307 | 308 | ptr.as_ptr() 309 | } 310 | 311 | unsafe extern "system" fn get_n_params( 312 | this: &mut This, 313 | num: c_long, 314 | ) -> c_long { 315 | let component = this.get_component(); 316 | component.addin.get_n_params(num as usize) as _ 317 | } 318 | 319 | unsafe extern "system" fn get_param_def_value( 320 | this: &mut This, 321 | method_num: c_long, 322 | param_num: c_long, 323 | val: &mut TVariant, 324 | ) -> bool { 325 | let component = this.get_component(); 326 | let Some(mem) = component.memory else { 327 | return false; 328 | }; 329 | 330 | let return_value = Variant { mem, variant: val }; 331 | 332 | component 333 | .addin 334 | .get_param_def_value(method_num as usize, param_num as usize, return_value) 335 | } 336 | 337 | unsafe extern "system" fn has_ret_val( 338 | this: &mut This, 339 | method_num: c_long, 340 | ) -> bool { 341 | let component = this.get_component(); 342 | component.addin.has_ret_val(method_num as usize) 343 | } 344 | 345 | unsafe extern "system" fn call_as_proc( 346 | this: &mut This, 347 | method_num: c_long, 348 | params: *mut TVariant, 349 | size_array: c_long, 350 | ) -> bool { 351 | let component = this.get_component(); 352 | let Some(mem) = component.memory else { 353 | return false; 354 | }; 355 | 356 | let size_array = size_array as usize; 357 | 358 | let mut param_values = SmallVec::<[Variant; 8]>::new(); 359 | if size_array > 0 { 360 | for variant in from_raw_parts_mut(params, size_array) { 361 | param_values.push(Variant { mem, variant }); 362 | } 363 | } 364 | 365 | component 366 | .addin 367 | .call_as_proc(method_num as usize, &mut param_values) 368 | } 369 | 370 | unsafe extern "system" fn call_as_func( 371 | this: &mut This, 372 | method_num: c_long, 373 | ret_value: &mut TVariant, 374 | params: *mut TVariant, 375 | size_array: c_long, 376 | ) -> bool { 377 | let component = this.get_component(); 378 | let Some(mem) = component.memory else { 379 | return false; 380 | }; 381 | 382 | let size_array = size_array as usize; 383 | 384 | let mut return_value = Variant { 385 | mem, 386 | variant: ret_value, 387 | }; 388 | 389 | let mut param_values = SmallVec::<[Variant; 8]>::new(); 390 | if size_array > 0 { 391 | for variant in from_raw_parts_mut(params, size_array) { 392 | param_values.push(Variant { mem, variant }); 393 | } 394 | } 395 | 396 | component 397 | .addin 398 | .call_as_func(method_num as usize, &mut param_values, &mut return_value) 399 | } 400 | 401 | #[repr(C)] 402 | struct LocaleBaseVTable { 403 | dtor: usize, 404 | #[cfg(target_family = "unix")] 405 | dtor2: usize, 406 | set_locale: unsafe extern "system" fn(&mut This, *const u16), 407 | } 408 | 409 | unsafe extern "system" fn set_locale( 410 | this: &mut This, 411 | loc: *const u16, 412 | ) { 413 | let component = this.get_component(); 414 | let loc = CStr1C::from_ptr(loc); 415 | component.addin.set_locale(loc) 416 | } 417 | 418 | #[repr(C)] 419 | struct UserLanguageBaseVTable { 420 | dtor: usize, 421 | #[cfg(target_family = "unix")] 422 | dtor2: usize, 423 | set_user_interface_language_code: unsafe extern "system" fn(&mut This, *const u16), 424 | } 425 | 426 | unsafe extern "system" fn set_user_interface_language_code( 427 | this: &mut This, 428 | lang: *const u16, 429 | ) { 430 | let component = this.get_component(); 431 | let lang = CStr1C::from_ptr(lang); 432 | component.addin.set_user_interface_language_code(lang) 433 | } 434 | 435 | #[repr(C)] 436 | struct Component { 437 | vptr1: Box>, 438 | vptr2: Box>, 439 | vptr3: Box>, 440 | vptr4: Box>, 441 | destroy: unsafe extern "system" fn(*mut *mut Component), 442 | memory: Option<&'static MemoryManager>, 443 | addin: T, 444 | } 445 | 446 | unsafe extern "system" fn destroy(component: *mut *mut Component) { 447 | let comp = Box::from_raw(*component); 448 | drop(comp); 449 | } 450 | 451 | /// # Safety 452 | /// 453 | /// Component must be non-null. 454 | pub unsafe fn create_component(component: *mut *mut c_void, addin: T) -> c_long { 455 | let vptr1 = Box::new(InitDoneBaseVTable { 456 | dtor: 0, 457 | #[cfg(target_family = "unix")] 458 | dtor2: 0, 459 | init, 460 | set_mem_manager, 461 | get_info, 462 | done, 463 | }); 464 | 465 | let vptr2 = Box::new(LanguageExtenderBaseVTable { 466 | dtor: 0, 467 | #[cfg(target_family = "unix")] 468 | dtor2: 0, 469 | register_extension_as, 470 | get_n_props, 471 | find_prop, 472 | get_prop_name, 473 | get_prop_val, 474 | set_prop_val, 475 | is_prop_readable, 476 | is_prop_writable, 477 | get_n_methods, 478 | find_method, 479 | get_method_name, 480 | get_n_params, 481 | get_param_def_value, 482 | has_ret_val, 483 | call_as_proc, 484 | call_as_func, 485 | }); 486 | 487 | let vptr3 = Box::new(LocaleBaseVTable { 488 | dtor: 0, 489 | #[cfg(target_family = "unix")] 490 | dtor2: 0, 491 | set_locale, 492 | }); 493 | 494 | let vptr4 = Box::new(UserLanguageBaseVTable { 495 | dtor: 0, 496 | #[cfg(target_family = "unix")] 497 | dtor2: 0, 498 | set_user_interface_language_code, 499 | }); 500 | 501 | let c = Box::new(Component { 502 | vptr1, 503 | vptr2, 504 | vptr3, 505 | vptr4, 506 | destroy: destroy::, 507 | memory: None, 508 | addin, 509 | }); 510 | 511 | *component = Box::into_raw(c) as *mut c_void; 512 | 1 513 | } 514 | 515 | /// # Safety 516 | /// 517 | /// Component must be returned from `create_component`, the function must be called once for each component. 518 | pub unsafe fn destroy_component(component: *mut *mut c_void) -> c_long { 519 | #[repr(C)] 520 | struct ComponentWrapper { 521 | vptr1: usize, 522 | vptr2: usize, 523 | vptr3: usize, 524 | vptr4: usize, 525 | destroy: unsafe extern "system" fn(*mut *mut c_void), 526 | } 527 | 528 | let wrapper = *component as *mut ComponentWrapper; 529 | let wrapper = &mut *wrapper; 530 | (wrapper.destroy)(component); 531 | *component = ptr::null_mut(); 532 | 533 | 0 534 | } 535 | -------------------------------------------------------------------------------- /addin1c/src/lib.rs: -------------------------------------------------------------------------------- 1 | mod connection; 2 | mod cstr1c; 3 | mod ffi; 4 | mod macros; 5 | mod memory_manager; 6 | mod simple; 7 | mod tm; 8 | mod tvariant; 9 | mod variant; 10 | 11 | pub use connection::Connection; 12 | pub use cstr1c::{CStr1C, CString1C}; 13 | pub use ffi::{create_component, destroy_component, Addin as RawAddin}; 14 | pub use simple::{Addin as SimpleAddin, AddinResult, MethodInfo, Methods, PropInfo}; 15 | pub use tm::Tm; 16 | pub use variant::{IncompatibleTypeError, ParamValue, Variant}; 17 | 18 | pub use utf16_lit::utf16; 19 | pub use utf16_lit::utf16_null; 20 | 21 | #[repr(C)] 22 | #[derive(Debug)] 23 | pub enum AttachType { 24 | NotIsolated = 1, 25 | Isolated, 26 | Any, 27 | } 28 | -------------------------------------------------------------------------------- /addin1c/src/macros.rs: -------------------------------------------------------------------------------- 1 | /// Null terminated utf-16 static string, used for names 2 | #[macro_export] 3 | macro_rules! name { 4 | ($text:expr) => { 5 | const { unsafe { addin1c::CStr1C::from_bytes_unchecked(&addin1c::utf16_null!($text)) } } 6 | }; 7 | } 8 | 9 | /// Non null utf-16 static string, used for 1c-strings 10 | #[macro_export] 11 | macro_rules! str1c { 12 | ($text:expr) => { 13 | &addin1c::utf16!($text) 14 | }; 15 | } 16 | -------------------------------------------------------------------------------- /addin1c/src/memory_manager.rs: -------------------------------------------------------------------------------- 1 | use std::{ 2 | ffi::{c_ulong, c_void}, 3 | fmt, 4 | ptr::{self, NonNull}, 5 | }; 6 | 7 | #[repr(C)] 8 | struct MemoryManagerVTable { 9 | dtor: usize, 10 | #[cfg(target_family = "unix")] 11 | dtor2: usize, 12 | alloc_memory: unsafe extern "system" fn(&MemoryManager, *mut *mut c_void, c_ulong) -> bool, 13 | free_memory: unsafe extern "system" fn(&MemoryManager, *mut *mut c_void), 14 | } 15 | 16 | #[repr(C)] 17 | pub(crate) struct MemoryManager { 18 | vptr: &'static MemoryManagerVTable, 19 | } 20 | 21 | impl MemoryManager { 22 | pub fn alloc_blob(&self, size: usize) -> Result, AllocError> { 23 | let mut ptr = ptr::null_mut::(); 24 | unsafe { 25 | if (self.vptr.alloc_memory)(self, &mut ptr, size as c_ulong) { 26 | NonNull::new(ptr as *mut u8) 27 | } else { 28 | None 29 | } 30 | } 31 | .ok_or(AllocError(size)) 32 | } 33 | 34 | pub fn alloc_str(&self, size: usize) -> Result, AllocError> { 35 | let mut ptr = ptr::null_mut::(); 36 | unsafe { 37 | if (self.vptr.alloc_memory)(self, &mut ptr, size as c_ulong * 2) { 38 | NonNull::new(ptr as *mut u16) 39 | } else { 40 | None 41 | } 42 | } 43 | .ok_or(AllocError(size * 2)) 44 | } 45 | 46 | pub fn free_str(&self, ptr: *mut *mut u16) { 47 | unsafe { 48 | (self.vptr.free_memory)(self, ptr as _); 49 | } 50 | } 51 | 52 | pub fn free_blob(&self, ptr: *mut *mut u8) { 53 | unsafe { 54 | (self.vptr.free_memory)(self, ptr as _); 55 | } 56 | } 57 | } 58 | 59 | #[derive(Debug)] 60 | pub struct AllocError(usize); 61 | 62 | impl fmt::Display for AllocError { 63 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 64 | write!(f, "Failed to allocate {} bytes", self.0) 65 | } 66 | } 67 | 68 | impl std::error::Error for AllocError {} 69 | -------------------------------------------------------------------------------- /addin1c/src/simple.rs: -------------------------------------------------------------------------------- 1 | use core::fmt; 2 | use std::{error::Error, panic::AssertUnwindSafe}; 3 | 4 | use crate::{ 5 | ffi::{self}, 6 | CStr1C, Connection, Variant, 7 | }; 8 | 9 | #[allow(dead_code)] 10 | pub enum Methods { 11 | Method0(fn(&mut T, &mut Variant) -> AddinResult), 12 | Method1(fn(&mut T, &mut Variant, &mut Variant) -> AddinResult), 13 | Method2(fn(&mut T, &mut Variant, &mut Variant, &mut Variant) -> AddinResult), 14 | Method3(fn(&mut T, &mut Variant, &mut Variant, &mut Variant, &mut Variant) -> AddinResult), 15 | Method4( 16 | fn( 17 | &mut T, 18 | &mut Variant, 19 | &mut Variant, 20 | &mut Variant, 21 | &mut Variant, 22 | &mut Variant, 23 | ) -> AddinResult, 24 | ), 25 | Method5( 26 | fn( 27 | &mut T, 28 | &mut Variant, 29 | &mut Variant, 30 | &mut Variant, 31 | &mut Variant, 32 | &mut Variant, 33 | &mut Variant, 34 | ) -> AddinResult, 35 | ), 36 | Method6( 37 | fn( 38 | &mut T, 39 | &mut Variant, 40 | &mut Variant, 41 | &mut Variant, 42 | &mut Variant, 43 | &mut Variant, 44 | &mut Variant, 45 | &mut Variant, 46 | ) -> AddinResult, 47 | ), 48 | #[allow(clippy::type_complexity)] 49 | Method7( 50 | fn( 51 | &mut T, 52 | &mut Variant, 53 | &mut Variant, 54 | &mut Variant, 55 | &mut Variant, 56 | &mut Variant, 57 | &mut Variant, 58 | &mut Variant, 59 | &mut Variant, 60 | ) -> AddinResult, 61 | ), 62 | } 63 | 64 | #[derive(Debug)] 65 | struct ParamError {} 66 | 67 | impl fmt::Display for ParamError { 68 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 69 | write!(f, "ParamError") 70 | } 71 | } 72 | 73 | impl std::error::Error for ParamError {} 74 | 75 | pub type AddinResult = Result<(), Box>; 76 | 77 | impl Methods { 78 | fn params(&self) -> usize { 79 | match self { 80 | Methods::Method0(_) => 0, 81 | Methods::Method1(_) => 1, 82 | Methods::Method2(_) => 2, 83 | Methods::Method3(_) => 3, 84 | Methods::Method4(_) => 4, 85 | Methods::Method5(_) => 5, 86 | Methods::Method6(_) => 6, 87 | Methods::Method7(_) => 7, 88 | } 89 | } 90 | 91 | #[allow(unused_variables)] 92 | fn call(&self, addin: &mut T, params: &mut [Variant], val: &mut Variant) -> AddinResult { 93 | match self { 94 | Methods::Method0(f) => f(addin, val), 95 | Methods::Method1(f) => { 96 | let Some((p1, params)) = params.split_first_mut() else { 97 | return Err(ParamError {}.into()); 98 | }; 99 | f(addin, p1, val) 100 | } 101 | Methods::Method2(f) => { 102 | let Some((p1, params)) = params.split_first_mut() else { 103 | return Err(ParamError {}.into()); 104 | }; 105 | let Some((p2, params)) = params.split_first_mut() else { 106 | return Err(ParamError {}.into()); 107 | }; 108 | f(addin, p1, p2, val) 109 | } 110 | Methods::Method3(f) => { 111 | let Some((p1, params)) = params.split_first_mut() else { 112 | return Err(ParamError {}.into()); 113 | }; 114 | let Some((p2, params)) = params.split_first_mut() else { 115 | return Err(ParamError {}.into()); 116 | }; 117 | let Some((p3, params)) = params.split_first_mut() else { 118 | return Err(ParamError {}.into()); 119 | }; 120 | f(addin, p1, p2, p3, val) 121 | } 122 | Methods::Method4(f) => { 123 | let Some((p1, params)) = params.split_first_mut() else { 124 | return Err(ParamError {}.into()); 125 | }; 126 | let Some((p2, params)) = params.split_first_mut() else { 127 | return Err(ParamError {}.into()); 128 | }; 129 | let Some((p3, params)) = params.split_first_mut() else { 130 | return Err(ParamError {}.into()); 131 | }; 132 | let Some((p4, params)) = params.split_first_mut() else { 133 | return Err(ParamError {}.into()); 134 | }; 135 | f(addin, p1, p2, p3, p4, val) 136 | } 137 | 138 | Methods::Method5(f) => { 139 | let Some((p1, params)) = params.split_first_mut() else { 140 | return Err(ParamError {}.into()); 141 | }; 142 | let Some((p2, params)) = params.split_first_mut() else { 143 | return Err(ParamError {}.into()); 144 | }; 145 | let Some((p3, params)) = params.split_first_mut() else { 146 | return Err(ParamError {}.into()); 147 | }; 148 | let Some((p4, params)) = params.split_first_mut() else { 149 | return Err(ParamError {}.into()); 150 | }; 151 | let Some((p5, params)) = params.split_first_mut() else { 152 | return Err(ParamError {}.into()); 153 | }; 154 | f(addin, p1, p2, p3, p4, p5, val) 155 | } 156 | 157 | Methods::Method6(f) => { 158 | let Some((p1, params)) = params.split_first_mut() else { 159 | return Err(ParamError {}.into()); 160 | }; 161 | let Some((p2, params)) = params.split_first_mut() else { 162 | return Err(ParamError {}.into()); 163 | }; 164 | let Some((p3, params)) = params.split_first_mut() else { 165 | return Err(ParamError {}.into()); 166 | }; 167 | let Some((p4, params)) = params.split_first_mut() else { 168 | return Err(ParamError {}.into()); 169 | }; 170 | let Some((p5, params)) = params.split_first_mut() else { 171 | return Err(ParamError {}.into()); 172 | }; 173 | let Some((p6, params)) = params.split_first_mut() else { 174 | return Err(ParamError {}.into()); 175 | }; 176 | f(addin, p1, p2, p3, p4, p5, p6, val) 177 | } 178 | 179 | Methods::Method7(f) => { 180 | let Some((p1, params)) = params.split_first_mut() else { 181 | return Err(ParamError {}.into()); 182 | }; 183 | let Some((p2, params)) = params.split_first_mut() else { 184 | return Err(ParamError {}.into()); 185 | }; 186 | let Some((p3, params)) = params.split_first_mut() else { 187 | return Err(ParamError {}.into()); 188 | }; 189 | let Some((p4, params)) = params.split_first_mut() else { 190 | return Err(ParamError {}.into()); 191 | }; 192 | let Some((p5, params)) = params.split_first_mut() else { 193 | return Err(ParamError {}.into()); 194 | }; 195 | let Some((p6, params)) = params.split_first_mut() else { 196 | return Err(ParamError {}.into()); 197 | }; 198 | let Some((p7, params)) = params.split_first_mut() else { 199 | return Err(ParamError {}.into()); 200 | }; 201 | f(addin, p1, p2, p3, p4, p5, p6, p7, val) 202 | } 203 | } 204 | } 205 | } 206 | 207 | pub struct MethodInfo { 208 | pub name: &'static CStr1C, 209 | pub method: Methods, 210 | } 211 | 212 | pub struct PropInfo { 213 | pub name: &'static CStr1C, 214 | pub getter: Option AddinResult>, 215 | pub setter: Option AddinResult>, 216 | } 217 | 218 | #[allow(unused_variables)] 219 | pub trait Addin { 220 | fn name() -> &'static CStr1C; 221 | 222 | fn init(&mut self, interface: &'static Connection) -> bool { 223 | true 224 | } 225 | 226 | fn get_info() -> u16 { 227 | 2000 228 | } 229 | 230 | fn save_error(&mut self, err: Option>) {} 231 | 232 | fn methods() -> &'static [MethodInfo] 233 | where 234 | Self: Sized, 235 | { 236 | &[] 237 | } 238 | 239 | fn properties() -> &'static [PropInfo] 240 | where 241 | Self: Sized, 242 | { 243 | &[] 244 | } 245 | } 246 | 247 | #[allow(unused_variables)] 248 | impl ffi::Addin for T { 249 | fn register_extension_as(&mut self) -> &'static CStr1C { 250 | T::name() 251 | } 252 | 253 | fn init(&mut self, interface: &'static Connection) -> bool { 254 | self.init(interface) 255 | } 256 | 257 | fn get_info(&mut self) -> u16 { 258 | T::get_info() 259 | } 260 | 261 | fn get_n_props(&mut self) -> usize { 262 | T::properties().len() 263 | } 264 | 265 | fn find_prop(&mut self, name: &CStr1C) -> Option { 266 | T::properties().iter().position(|x| x.name == name) 267 | } 268 | 269 | fn get_prop_name(&mut self, num: usize, alias: usize) -> Option<&'static CStr1C> { 270 | T::properties().get(num).map(|x| &x.name).copied() 271 | } 272 | 273 | fn get_prop_val(&mut self, num: usize, val: &mut Variant) -> bool { 274 | let Some(property) = T::properties().get(num) else { 275 | return false; 276 | }; 277 | let Some(getter) = property.getter else { 278 | return false; 279 | }; 280 | match getter(self, val) { 281 | Ok(_) => true, 282 | Err(err) => { 283 | self.save_error(Some(err)); 284 | false 285 | } 286 | } 287 | } 288 | 289 | fn set_prop_val(&mut self, num: usize, val: &Variant) -> bool { 290 | self.save_error(None); 291 | let Some(property) = T::properties().get(num) else { 292 | return false; 293 | }; 294 | let Some(setter) = property.setter else { 295 | return false; 296 | }; 297 | match setter(self, val) { 298 | Ok(_) => true, 299 | Err(err) => { 300 | self.save_error(Some(err)); 301 | false 302 | } 303 | } 304 | } 305 | 306 | fn is_prop_readable(&mut self, num: usize) -> bool { 307 | T::properties()[num].getter.is_some() 308 | } 309 | 310 | fn is_prop_writable(&mut self, num: usize) -> bool { 311 | T::properties()[num].setter.is_some() 312 | } 313 | 314 | fn get_n_methods(&mut self) -> usize { 315 | T::methods().len() 316 | } 317 | 318 | fn find_method(&mut self, name: &CStr1C) -> Option { 319 | T::methods().iter().position(|x| x.name == name) 320 | } 321 | 322 | fn get_method_name(&mut self, num: usize, alias: usize) -> Option<&'static CStr1C> { 323 | T::methods().get(num).map(|x| &x.name).copied() 324 | } 325 | 326 | fn get_n_params(&mut self, num: usize) -> usize { 327 | let Some(info) = T::methods().get(num) else { 328 | return 0; 329 | }; 330 | info.method.params() 331 | } 332 | 333 | fn get_param_def_value(&mut self, method_num: usize, param_num: usize, value: Variant) -> bool { 334 | true 335 | } 336 | 337 | fn has_ret_val(&mut self, method_num: usize) -> bool { 338 | true 339 | } 340 | 341 | fn call_as_proc(&mut self, method_num: usize, params: &mut [Variant]) -> bool { 342 | false 343 | } 344 | 345 | fn call_as_func( 346 | &mut self, 347 | method_num: usize, 348 | params: &mut [Variant], 349 | val: &mut Variant, 350 | ) -> bool { 351 | self.save_error(None); 352 | let Some(info) = T::methods().get(method_num) else { 353 | return false; 354 | }; 355 | 356 | let result = std::panic::catch_unwind(AssertUnwindSafe(|| { 357 | match info.method.call(self, params, val) { 358 | Ok(_) => true, 359 | Err(err) => { 360 | self.save_error(Some(err)); 361 | false 362 | } 363 | } 364 | })); 365 | 366 | match result { 367 | Ok(r) => r, 368 | Err(err) => { 369 | match err.downcast::<&str>() { 370 | Ok(s) => { 371 | self.save_error(Some((*s).into())); 372 | } 373 | Err(e) => match e.downcast::() { 374 | Ok(s) => { 375 | self.save_error(Some((*s).into())); 376 | } 377 | Err(_) => self.save_error(Some("Unknown error".into())), 378 | }, 379 | }; 380 | false 381 | } 382 | } 383 | } 384 | } 385 | -------------------------------------------------------------------------------- /addin1c/src/tm.rs: -------------------------------------------------------------------------------- 1 | use std::ffi::c_int; 2 | 3 | #[repr(C)] 4 | #[derive(Clone, Copy, Default)] 5 | pub struct Tm { 6 | pub sec: c_int, // seconds after the minute - [0, 60] including leap second 7 | pub min: c_int, // minutes after the hour - [0, 59] 8 | pub hour: c_int, // hours since midnight - [0, 23] 9 | pub mday: c_int, // day of the month - [1, 31] 10 | pub mon: c_int, // months since January - [0, 11] 11 | pub year: c_int, // years since 1900 12 | pub wday: c_int, // days since Sunday - [0, 6] 13 | pub yday: c_int, // days since January 1 - [0, 365] 14 | pub isdst: c_int, // daylight savings time flag 15 | 16 | #[cfg(target_family = "unix")] 17 | pub gmtoff: std::ffi::c_long, // seconds east of UTC 18 | #[cfg(target_family = "unix")] 19 | pub zone: std::ffi::c_char, // timezone abbreviation 20 | } 21 | 22 | #[cfg(feature = "chrono")] 23 | mod chrono { 24 | use chrono::{DateTime, Datelike, NaiveDate, NaiveDateTime, Timelike, Utc}; 25 | 26 | use super::Tm; 27 | 28 | impl From for Tm { 29 | fn from(value: T) -> Self { 30 | Tm { 31 | sec: value.second() as _, 32 | min: value.minute() as _, 33 | hour: value.hour() as _, 34 | mday: value.day() as _, 35 | mon: value.month0() as _, 36 | year: value.year() - 1900, 37 | wday: value.weekday() as _, 38 | ..Tm::default() 39 | } 40 | } 41 | } 42 | 43 | impl From for DateTime { 44 | fn from(val: Tm) -> Self { 45 | Into::::into(val).and_utc() 46 | } 47 | } 48 | 49 | impl From for NaiveDateTime { 50 | fn from(val: Tm) -> Self { 51 | NaiveDate::from_ymd_opt(1900 + val.year, 1 + val.mon as u32, val.mday as _) 52 | .expect("Incorrect date") 53 | .and_hms_opt(val.hour as _, val.min as _, val.sec as _) 54 | .expect("Incorrect date") 55 | } 56 | } 57 | 58 | #[cfg(test)] 59 | mod tests { 60 | 61 | use chrono::NaiveDate; 62 | 63 | use super::Tm; 64 | 65 | #[test] 66 | fn test() { 67 | let naive_datetime = NaiveDate::from_ymd_opt(2024, 1, 1) 68 | .unwrap() 69 | .and_hms_opt(1, 2, 3) 70 | .unwrap(); 71 | 72 | let tm: Tm = naive_datetime.into(); 73 | assert_eq!(naive_datetime, tm.into()); 74 | 75 | let utc = naive_datetime.and_utc(); 76 | let tm: Tm = utc.into(); 77 | assert_eq!(utc, tm.into()); 78 | } 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /addin1c/src/tvariant.rs: -------------------------------------------------------------------------------- 1 | use crate::Tm; 2 | 3 | #[repr(C)] 4 | pub(crate) struct TVariant { 5 | pub value: VariantValue, 6 | pub elements: u32, //Dimension for an one-dimensional array in pvarVal 7 | pub vt: VariantType, 8 | } 9 | 10 | #[repr(u16)] 11 | #[allow(dead_code)] 12 | #[derive(Debug)] 13 | pub(crate) enum VariantType { 14 | Empty = 0, 15 | Null, 16 | I2, //int16_t 17 | I4, //int32_t 18 | R4, //float 19 | R8, //double 20 | Date, //DATE (double) 21 | TM, //struct tm 22 | Pstr, //struct str string 23 | Interface, //struct iface 24 | Error, //int32_t errCode 25 | Bool, //bool 26 | Variant, //struct _tVariant * 27 | I1, //int8_t 28 | Ui1, //uint8_t 29 | Ui2, //uint16_t 30 | Ui4, //uint32_t 31 | I8, //int64_t 32 | Ui8, //uint64_t 33 | Int, //int Depends on architecture 34 | Uint, //unsigned int Depends on architecture 35 | Hresult, //long hRes 36 | Pwstr, //struct wstr 37 | Blob, //means in struct str binary data contain 38 | Clsid, //UUID 39 | 40 | Undefined = 0xFFFF, 41 | } 42 | 43 | #[repr(C)] 44 | #[derive(Clone, Copy)] 45 | pub(crate) struct DataStr { 46 | pub ptr: *mut u16, 47 | pub len: u32, 48 | } 49 | 50 | #[repr(C)] 51 | #[derive(Clone, Copy)] 52 | pub(crate) struct DataBlob { 53 | pub ptr: *mut u8, 54 | pub len: u32, 55 | } 56 | 57 | #[repr(C)] 58 | pub(crate) union VariantValue { 59 | pub bool: bool, 60 | pub i32: i32, 61 | pub f64: f64, 62 | pub tm: Tm, 63 | pub data_str: DataStr, 64 | pub data_blob: DataBlob, 65 | } 66 | -------------------------------------------------------------------------------- /addin1c/src/variant.rs: -------------------------------------------------------------------------------- 1 | use std::{ 2 | fmt, 3 | slice::{from_raw_parts, from_raw_parts_mut}, 4 | }; 5 | 6 | use smallvec::SmallVec; 7 | 8 | use crate::{ 9 | memory_manager::{AllocError, MemoryManager}, 10 | tvariant::{TVariant, VariantType}, 11 | Tm, 12 | }; 13 | 14 | pub struct Variant<'a> { 15 | pub(crate) mem: &'a MemoryManager, 16 | pub(crate) variant: &'a mut TVariant, 17 | } 18 | 19 | #[allow(dead_code)] 20 | impl<'a> Variant<'a> { 21 | pub fn get(&self) -> ParamValue { 22 | ParamValue::from(self.variant as &_) 23 | } 24 | 25 | fn free_memory(&mut self) { 26 | match self.variant.vt { 27 | VariantType::Pwstr => unsafe { 28 | self.mem.free_str(&mut self.variant.value.data_str.ptr) 29 | }, 30 | VariantType::Blob => unsafe { 31 | self.mem.free_blob(&mut self.variant.value.data_blob.ptr) 32 | }, 33 | _ => (), 34 | } 35 | } 36 | pub fn get_empty(&self) -> Result<(), IncompatibleTypeError> { 37 | let ParamValue::Empty = self.get() else { 38 | return Err(IncompatibleTypeError {}); 39 | }; 40 | Ok(()) 41 | } 42 | pub fn set_empty(&mut self) { 43 | self.free_memory(); 44 | self.variant.vt = VariantType::Empty; 45 | } 46 | 47 | pub fn get_i32(&self) -> Result { 48 | let ParamValue::I32(value) = self.get() else { 49 | return Err(IncompatibleTypeError {}); 50 | }; 51 | Ok(value) 52 | } 53 | pub fn set_i32(&mut self, val: i32) { 54 | self.free_memory(); 55 | self.variant.vt = VariantType::I4; 56 | self.variant.value.i32 = val; 57 | } 58 | 59 | pub fn get_bool(&self) -> Result { 60 | let ParamValue::Bool(value) = self.get() else { 61 | return Err(IncompatibleTypeError {}); 62 | }; 63 | Ok(value) 64 | } 65 | pub fn set_bool(&mut self, val: bool) { 66 | self.free_memory(); 67 | self.variant.vt = VariantType::Bool; 68 | self.variant.value.bool = val; 69 | } 70 | 71 | pub fn get_f64(&self) -> Result { 72 | let ParamValue::F64(value) = self.get() else { 73 | return Err(IncompatibleTypeError {}); 74 | }; 75 | Ok(value) 76 | } 77 | pub fn set_f64(&mut self, val: f64) { 78 | self.free_memory(); 79 | self.variant.vt = VariantType::R8; 80 | self.variant.value.f64 = val; 81 | } 82 | 83 | pub fn get_date(&self) -> Result { 84 | let ParamValue::Date(value) = self.get() else { 85 | return Err(IncompatibleTypeError {}); 86 | }; 87 | Ok(value) 88 | } 89 | pub fn set_date(&mut self, val: Tm) { 90 | self.free_memory(); 91 | self.variant.vt = VariantType::TM; 92 | self.variant.value.tm = val; 93 | } 94 | 95 | pub fn get_str1c(&self) -> Result<&[u16], IncompatibleTypeError> { 96 | let ParamValue::Str(value) = self.get() else { 97 | return Err(IncompatibleTypeError {}); 98 | }; 99 | Ok(value) 100 | } 101 | pub fn get_string(&self) -> Result { 102 | let ParamValue::Str(value) = self.get() else { 103 | return Err(IncompatibleTypeError {}); 104 | }; 105 | let value = String::from_utf16(value).map_err(|_| IncompatibleTypeError {})?; 106 | Ok(value) 107 | } 108 | pub fn set_str1c(&mut self, val: impl IntoStr1C) -> Result<(), AllocError> { 109 | val.write(self) 110 | } 111 | 112 | pub fn get_blob(&self) -> Result<&[u8], IncompatibleTypeError> { 113 | let ParamValue::Blob(value) = self.get() else { 114 | return Err(IncompatibleTypeError {}); 115 | }; 116 | Ok(value) 117 | } 118 | pub fn set_blob(&mut self, val: &[u8]) -> Result<(), AllocError> { 119 | let ptr = self.mem.alloc_blob(val.len())?; 120 | self.free_memory(); 121 | 122 | unsafe { std::ptr::copy_nonoverlapping(val.as_ptr(), ptr.as_ptr(), val.len()) }; 123 | 124 | self.variant.vt = VariantType::Blob; 125 | self.variant.value.data_blob.ptr = ptr.as_ptr(); 126 | self.variant.value.data_blob.len = val.len() as u32; 127 | Ok(()) 128 | } 129 | 130 | pub fn alloc_str(&mut self, len: usize) -> Option<&'a mut [u16]> { 131 | let Ok(ptr) = self.mem.alloc_str(len) else { 132 | return None; 133 | }; 134 | 135 | self.free_memory(); 136 | 137 | self.variant.vt = VariantType::Pwstr; 138 | self.variant.value.data_str.ptr = ptr.as_ptr(); 139 | self.variant.value.data_str.len = len as u32; 140 | 141 | Some(unsafe { from_raw_parts_mut(ptr.as_ptr(), len) }) 142 | } 143 | 144 | pub fn alloc_blob(&mut self, len: usize) -> Option<&'a mut [u8]> { 145 | let Ok(ptr) = self.mem.alloc_blob(len) else { 146 | return None; 147 | }; 148 | self.free_memory(); 149 | 150 | self.variant.vt = VariantType::Blob; 151 | self.variant.value.data_blob.ptr = ptr.as_ptr(); 152 | self.variant.value.data_blob.len = len as u32; 153 | 154 | Some(unsafe { from_raw_parts_mut(ptr.as_ptr(), len) }) 155 | } 156 | } 157 | 158 | pub trait IntoStr1C { 159 | fn write(&self, variant: &mut Variant) -> Result<(), AllocError>; 160 | } 161 | 162 | impl IntoStr1C for &[u16] { 163 | fn write(&self, variant: &mut Variant) -> Result<(), AllocError> { 164 | let ptr = variant.mem.alloc_str(self.len())?; 165 | variant.free_memory(); 166 | 167 | unsafe { std::ptr::copy_nonoverlapping(self.as_ptr(), ptr.as_ptr(), self.len()) }; 168 | 169 | variant.variant.vt = VariantType::Pwstr; 170 | variant.variant.value.data_str.ptr = ptr.as_ptr(); 171 | variant.variant.value.data_str.len = self.len() as u32; 172 | Ok(()) 173 | } 174 | } 175 | 176 | impl IntoStr1C for &str { 177 | fn write(&self, variant: &mut Variant) -> Result<(), AllocError> { 178 | let mut buf = SmallVec::<[u16; 128]>::new(); 179 | buf.extend(self.encode_utf16()); 180 | IntoStr1C::write(&buf.as_slice(), variant) 181 | } 182 | } 183 | 184 | impl IntoStr1C for String { 185 | fn write(&self, variant: &mut Variant) -> Result<(), AllocError> { 186 | self.as_str().write(variant) 187 | } 188 | } 189 | 190 | pub enum ParamValue<'a> { 191 | Empty, 192 | Bool(bool), 193 | I32(i32), 194 | F64(f64), 195 | Date(Tm), 196 | Str(&'a [u16]), 197 | Blob(&'a [u8]), 198 | } 199 | 200 | impl<'a> From<&'a TVariant> for ParamValue<'a> { 201 | fn from(param: &'a TVariant) -> ParamValue<'a> { 202 | unsafe { 203 | match param.vt { 204 | VariantType::Empty => Self::Empty, 205 | VariantType::Bool => Self::Bool(param.value.bool), 206 | VariantType::I4 => Self::I32(param.value.i32), 207 | VariantType::R8 => Self::F64(param.value.f64), 208 | VariantType::TM => Self::Date(param.value.tm), 209 | VariantType::Pwstr => Self::Str(from_raw_parts( 210 | param.value.data_str.ptr, 211 | param.value.data_str.len as usize, 212 | )), 213 | VariantType::Blob => Self::Blob(from_raw_parts( 214 | param.value.data_blob.ptr, 215 | param.value.data_blob.len as usize, 216 | )), 217 | _ => Self::Empty, 218 | } 219 | } 220 | } 221 | } 222 | 223 | #[derive(Debug)] 224 | pub struct IncompatibleTypeError {} 225 | 226 | impl fmt::Display for IncompatibleTypeError { 227 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 228 | write!(f, "Incompatible type") 229 | } 230 | } 231 | 232 | impl std::error::Error for IncompatibleTypeError {} 233 | -------------------------------------------------------------------------------- /conf1c/Configuration.xml: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 9cd510cd-abfc-11d4-9434-004095e12fc7 7 | 29e7f5a4-dc26-408a-bd6e-083b5bf1384c 8 | 9 | 10 | 9fcd25a0-4822-11d4-9414-008048da11f9 11 | f76706d7-a451-49f0-a3b8-8c92dc9eb401 12 | 13 | 14 | e3687481-0a87-462c-a166-9f34594f9bba 15 | efad9454-c037-40e6-92f8-de6f995cb912 16 | 17 | 18 | 9de14907-ec23-4a07-96f0-85521cb6b53b 19 | 5a509aa9-2e5a-47a8-977a-8fbe609d782c 20 | 21 | 22 | 51f2d5d8-ea4d-4064-8892-82951750031e 23 | 3982e56b-1417-490c-8ac4-7672c7bffa80 24 | 25 | 26 | e68182ea-4237-4383-967f-90c1e3370bc7 27 | 862a5e5b-5378-4b7c-b567-0ab6725fc840 28 | 29 | 30 | fb282519-d103-4dd3-bc12-cb271d631dfc 31 | ed5cd24c-da69-4a84-a011-421e03c8dd71 32 | 33 | 34 | 35 | Конфигурация 36 | 37 | 38 | 39 | Version8_3_23 40 | ManagedApplication 41 | 42 | PlatformApplication 43 | 44 | Russian 45 | 46 | 47 | 48 | 49 | false 50 | false 51 | false 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | Biometrics 74 | true 75 | 76 | 77 | Location 78 | false 79 | 80 | 81 | BackgroundLocation 82 | false 83 | 84 | 85 | BluetoothPrinters 86 | false 87 | 88 | 89 | WiFiPrinters 90 | false 91 | 92 | 93 | Contacts 94 | false 95 | 96 | 97 | Calendars 98 | false 99 | 100 | 101 | PushNotifications 102 | false 103 | 104 | 105 | LocalNotifications 106 | false 107 | 108 | 109 | InAppPurchases 110 | false 111 | 112 | 113 | PersonalComputerFileExchange 114 | false 115 | 116 | 117 | Ads 118 | false 119 | 120 | 121 | NumberDialing 122 | false 123 | 124 | 125 | CallProcessing 126 | false 127 | 128 | 129 | CallLog 130 | false 131 | 132 | 133 | AutoSendSMS 134 | false 135 | 136 | 137 | ReceiveSMS 138 | false 139 | 140 | 141 | SMSLog 142 | false 143 | 144 | 145 | Camera 146 | false 147 | 148 | 149 | Microphone 150 | false 151 | 152 | 153 | MusicLibrary 154 | false 155 | 156 | 157 | PictureAndVideoLibraries 158 | false 159 | 160 | 161 | AudioPlaybackAndVibration 162 | false 163 | 164 | 165 | BackgroundAudioPlaybackAndVibration 166 | false 167 | 168 | 169 | InstallPackages 170 | false 171 | 172 | 173 | OSBackup 174 | true 175 | 176 | 177 | ApplicationUsageStatistics 178 | false 179 | 180 | 181 | BarcodeScanning 182 | false 183 | 184 | 185 | BackgroundAudioRecording 186 | false 187 | 188 | 189 | AllFilesAccess 190 | false 191 | 192 | 193 | Videoconferences 194 | false 195 | 196 | 197 | NFC 198 | false 199 | 200 | 201 | DocumentScanning 202 | false 203 | 204 | 205 | 206 | 207 | Normal 208 | 209 | 210 | Language.Русский 211 | 212 | 213 | 214 | 215 | 216 | Managed 217 | NotAutoFree 218 | DontUse 219 | DontUse 220 | Taxi 221 | DontUse 222 | Version8_3_23 223 | 224 | 225 | 226 | Русский 227 | Обработка1 228 | 229 | 230 | -------------------------------------------------------------------------------- /conf1c/DataProcessors/Обработка1.xml: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | aa584d2a-1a1f-4807-b2e6-588aef5193b8 7 | 620a7e0f-3713-4585-842f-262d1dfa18c0 8 | 9 | 10 | e6189229-5433-466b-a435-ac6f2fa36046 11 | 8606e276-c11b-4eaa-af7b-424d33285d14 12 | 13 | 14 | 15 | Обработка1 16 | 17 | 18 | true 19 | DataProcessor.Обработка1.Form.Форма 20 | 21 | false 22 | 23 | 24 | 25 | 26 |

Форма
27 | 28 | 29 | -------------------------------------------------------------------------------- /conf1c/DataProcessors/Обработка1/Forms/Форма.xml: -------------------------------------------------------------------------------- 1 |  2 | 3 |
4 | 5 | Форма 6 | 7 | 8 | ru 9 | Форма 10 | 11 | 12 | 13 | Managed 14 | false 15 | 16 | PlatformApplication 17 | MobilePlatformApplication 18 | 19 | 20 | 21 |
22 |
-------------------------------------------------------------------------------- /conf1c/DataProcessors/Обработка1/Forms/Форма/Ext/Form.xml: -------------------------------------------------------------------------------- 1 |  2 |
3 | Use 4 | 5 | 6 | ВнешнееСобытие 7 | 8 | 9 | 10 | ИмяФайла 11 | 12 | 13 | 14 | ИмяФайлаНачалоВыбора 15 | 16 | 17 | 22 | 27 | 32 | 37 | 42 | 47 | 48 | 49 | 50 | 51 | cfg:DataProcessorObject.Обработка1 52 | 53 | true 54 | 55 | 56 | 57 | <v8:item> 58 | <v8:lang>ru</v8:lang> 59 | <v8:content>Имя файла</v8:content> 60 | </v8:item> 61 | 62 | 63 | xs:string 64 | 65 | 0 66 | Variable 67 | 68 | 69 | 70 | ИмяФайла 71 | 72 | 73 | 74 | 75 | 76 | 77 | <v8:item> 78 | <v8:lang>ru</v8:lang> 79 | <v8:content>Тест2</v8:content> 80 | </v8:item> 81 | 82 | 83 | 84 | ru 85 | Тест2 86 | 87 | 88 | Тест2 89 | 90 | 91 | 92 | <v8:item> 93 | <v8:lang>ru</v8:lang> 94 | <v8:content>Тест1</v8:content> 95 | </v8:item> 96 | 97 | 98 | 99 | ru 100 | Тест1 101 | 102 | 103 | Тест1 104 | 105 | 106 | 107 | <v8:item> 108 | <v8:lang>ru</v8:lang> 109 | <v8:content>Тест3</v8:content> 110 | </v8:item> 111 | 112 | 113 | 114 | ru 115 | Тест3 116 | 117 | 118 | Тест3 119 | 120 | 121 | 122 | <v8:item> 123 | <v8:lang>ru</v8:lang> 124 | <v8:content>Тест4</v8:content> 125 | </v8:item> 126 | 127 | 128 | 129 | ru 130 | Тест4 131 | 132 | 133 | Тест4 134 | 135 | 136 | 137 | <v8:item> 138 | <v8:lang>ru</v8:lang> 139 | <v8:content>Тест5</v8:content> 140 | </v8:item> 141 | 142 | 143 | 144 | ru 145 | Тест5 146 | 147 | 148 | Тест5 149 | 150 | 151 | 152 | <v8:item> 153 | <v8:lang>ru</v8:lang> 154 | <v8:content>Тест внешнее событие</v8:content> 155 | </v8:item> 156 | 157 | 158 | 159 | ru 160 | Тест внешнее событие 161 | 162 | 163 | ТестВнешнееСобытие 164 | 165 | 166 | -------------------------------------------------------------------------------- /conf1c/DataProcessors/Обработка1/Forms/Форма/Ext/Form/Module.bsl: -------------------------------------------------------------------------------- 1 | &НаКлиенте 2 | Перем КомпонентаДляВнешнегоСобытия; 3 | 4 | &НаКлиенте 5 | Перем НачалоВызоваВнешнегоСобытия; 6 | 7 | &НаКлиенте 8 | Процедура Тест1(Команда) 9 | Тест1НаСервере(ИмяФайла); 10 | КонецПроцедуры 11 | 12 | &НаСервереБезКонтекста 13 | Процедура Тест1НаСервере(ИмяФайла) 14 | 15 | Начало = ТекущаяУниверсальнаяДатаВМиллисекундах(); 16 | 17 | Если Не ПодключитьВнешнююКомпоненту(ИмяФайла, "Test", ТипВнешнейКомпоненты.Native, ТипПодключенияВнешнейКомпоненты.НеИзолированно) Тогда 18 | Сообщить("Не удалось подключить"); 19 | Возврат; 20 | КонецЕсли; 21 | 22 | Сообщить("Подключена"); 23 | ОбъектКомпоненты = Новый ("AddIn.Test.Class1"); 24 | Test = ОбъектКомпоненты.Test; 25 | Конец = ТекущаяУниверсальнаяДатаВМиллисекундах(); 26 | Сообщить(СтрШаблон("Test: %1", Test)); 27 | Сообщить(СтрШаблон("Длительность: %1", Конец - Начало)); 28 | 29 | КонецПроцедуры 30 | 31 | 32 | &НаКлиенте 33 | Процедура Тест2(Команда) 34 | Тест2НаСервере(ИмяФайла); 35 | КонецПроцедуры 36 | 37 | &НаСервереБезКонтекста 38 | Процедура Тест2НаСервере(ИмяФайла) 39 | 40 | Начало = ТекущаяУниверсальнаяДатаВМиллисекундах(); 41 | 42 | Попытка 43 | ОбъектКомпоненты = Новый ("AddIn.Test.Class1"); 44 | Исключение 45 | Если Не ПодключитьВнешнююКомпоненту(ИмяФайла, "Test", ТипВнешнейКомпоненты.Native, ТипПодключенияВнешнейКомпоненты.НеИзолированно) Тогда 46 | ВызватьИсключение "Не удалось подключить"; 47 | КонецЕсли; 48 | ОбъектКомпоненты = Новый ("AddIn.Test.Class1"); 49 | КонецПопытки; 50 | 51 | ОбъектКомпоненты.PropI32 = 123; 52 | Если ОбъектКомпоненты.PropI32 <> 123 Тогда 53 | ВызватьИсключение "Не удалось установить значение PropI32"; 54 | КонецЕсли; 55 | 56 | ОбъектКомпоненты.PropF64 = 456.789; 57 | Если ОбъектКомпоненты.PropF64 <> 456.789 Тогда 58 | ВызватьИсключение "Не удалось установить значение PropF64"; 59 | КонецЕсли; 60 | 61 | ОбъектКомпоненты.PropBool = Истина; 62 | Если ОбъектКомпоненты.PropBool <> Истина Тогда 63 | ВызватьИсключение "Не удалось установить значение PropBool"; 64 | КонецЕсли; 65 | 66 | Date = ТекущаяДатаСеанса(); 67 | ОбъектКомпоненты.PropDate = Date; 68 | Если ОбъектКомпоненты.PropDate <> Date Тогда 69 | ВызватьИсключение "Не удалось установить значение PropDate"; 70 | КонецЕсли; 71 | 72 | ОбъектКомпоненты.PropStr = "Привет!"; 73 | Если ОбъектКомпоненты.PropStr <> "Привет!" Тогда 74 | ВызватьИсключение "Не удалось установить значение PropStr"; 75 | КонецЕсли; 76 | 77 | Blob = ПолучитьДвоичныеДанныеИзСтроки("Привет!"); 78 | ОбъектКомпоненты.PropBlob = Blob; 79 | Если ОбъектКомпоненты.PropBlob <> Blob Тогда 80 | ВызватьИсключение "Не удалось установить значение PropBlob"; 81 | КонецЕсли; 82 | 83 | Если ОбъектКомпоненты.Method1("11", "22", "33") <> "112233" Тогда 84 | ВызватьИсключение "Не удалось установить значение Method1"; 85 | КонецЕсли; 86 | 87 | ВозвращаемаяСтрока = Неопределено; 88 | ВозвращаемоеЧисло = Неопределено; 89 | Если Не ОбъектКомпоненты.Method2(ВозвращаемаяСтрока, ВозвращаемоеЧисло) 90 | Или ВозвращаемаяСтрока <> "Return value" 91 | Или ВозвращаемоеЧисло <> 1 Тогда 92 | ВызватьИсключение "Не удалось вызвать Method2"; 93 | КонецЕсли; 94 | 95 | Конец = ТекущаяУниверсальнаяДатаВМиллисекундах(); 96 | Сообщить(СтрШаблон("Длительность: %1", Конец - Начало)); 97 | 98 | КонецПроцедуры 99 | 100 | &НаКлиенте 101 | Процедура Тест3(Команда) 102 | Тест3НаСервере(ИмяФайла); 103 | КонецПроцедуры 104 | 105 | &НаСервереБезКонтекста 106 | Процедура Тест3НаСервере(ИмяФайла) 107 | 108 | Попытка 109 | ОбъектКомпоненты2 = Новый ("AddIn.Test.Class2"); 110 | Исключение 111 | Если Не ПодключитьВнешнююКомпоненту(ИмяФайла, "Test", ТипВнешнейКомпоненты.Native, ТипПодключенияВнешнейКомпоненты.НеИзолированно) Тогда 112 | ВызватьИсключение "Не удалось подключить"; 113 | КонецЕсли; 114 | ОбъектКомпоненты2 = Новый ("AddIn.Test.Class2"); 115 | КонецПопытки; 116 | 117 | Если ОбъектКомпоненты2.Method1(111) <> 222 Тогда 118 | ВызватьИсключение "Не удалось вызвать Method1"; 119 | КонецЕсли; 120 | 121 | Попытка 122 | ОбъектКомпоненты2.Method1("3213"); 123 | ВызватьИсключение "Method1 не вызвал исключение"; 124 | Исключение 125 | Если ОбъектКомпоненты2.LastError <> "Incompatible type" Тогда 126 | ВызватьИсключение "Method1 не вернул текст ошибки"; 127 | КонецЕсли; 128 | КонецПопытки; 129 | 130 | Если ОбъектКомпоненты2.Method2(111, 222) <> 333 Тогда 131 | ВызватьИсключение "Не удалось вызвать Method2"; 132 | КонецЕсли; 133 | 134 | Если ОбъектКомпоненты2.Prop1 <> 333 Тогда 135 | ВызватьИсключение "Свойство Prop1 содержит неверное значение"; 136 | КонецЕсли; 137 | 138 | Попытка 139 | ОбъектКомпоненты2.Panic1(); 140 | Исключение 141 | Если ОбъектКомпоненты2.LastError <> "Panic1" Тогда 142 | ВызватьИсключение "Panic1 не вернул текст ошибки"; 143 | КонецЕсли; 144 | КонецПопытки; 145 | 146 | Попытка 147 | ОбъектКомпоненты2.Prop1 = 2; 148 | ОбъектКомпоненты2.Panic2(); 149 | Исключение 150 | Если ОбъектКомпоненты2.LastError <> "Panic2" Тогда 151 | ВызватьИсключение "Panic2 не вернул текст ошибки"; 152 | КонецЕсли; 153 | КонецПопытки; 154 | 155 | УниверсальнаяДата1 = ТекущаяУниверсальнаяДата(); 156 | УниверсальнаяДата2 = ОбъектКомпоненты2.Utc(); 157 | Разница = УниверсальнаяДата2 - УниверсальнаяДата1; 158 | Если Разница > 1 Или Разница < -1 Тогда 159 | ВызватьИсключение "Метод Utc() вернул некорректную дату"; 160 | КонецЕсли; 161 | 162 | Попытка 163 | ОбъектКомпоненты2.MethodNoParams(); 164 | Исключение 165 | ВызватьИсключение "Не удалось вызвать MethodNoParams"; 166 | КонецПопытки; 167 | 168 | Сообщить("Тест выполнен успешно"); 169 | 170 | КонецПроцедуры 171 | 172 | &НаКлиенте 173 | Процедура Тест4(Команда) 174 | Тест4НаСервере(ИмяФайла); 175 | КонецПроцедуры 176 | 177 | &НаСервереБезКонтекста 178 | Процедура Тест4НаСервере(ИмяФайла) 179 | 180 | Попытка 181 | ОбъектКомпоненты2 = Новый ("AddIn.Test.Class2"); 182 | Исключение 183 | Если Не ПодключитьВнешнююКомпоненту(ИмяФайла, "Test", ТипВнешнейКомпоненты.Native, ТипПодключенияВнешнейКомпоненты.НеИзолированно) Тогда 184 | ВызватьИсключение "Не удалось подключить"; 185 | КонецЕсли; 186 | ОбъектКомпоненты2 = Новый ("AddIn.Test.Class2"); 187 | КонецПопытки; 188 | 189 | Начало = ТекущаяУниверсальнаяДатаВМиллисекундах(); 190 | Для К = 1 По 1000000 Цикл 191 | ОбъектКомпоненты2.Prop1 = 123; 192 | КонецЦикла; 193 | Конец = ТекущаяУниверсальнаяДатаВМиллисекундах(); 194 | Сообщить(СтрШаблон("Длительность: %1 мс", Конец - Начало)); 195 | 196 | КонецПроцедуры 197 | 198 | &НаКлиенте 199 | Процедура Тест5(Команда) 200 | Тест5НаСервере(ИмяФайла); 201 | КонецПроцедуры 202 | 203 | &НаСервереБезКонтекста 204 | Процедура Тест5НаСервере(ИмяФайла) 205 | 206 | Попытка 207 | ОбъектКомпоненты2 = Новый ("AddIn.Test.Class2"); 208 | Исключение 209 | Если Не ПодключитьВнешнююКомпоненту(ИмяФайла, "Test", ТипВнешнейКомпоненты.Native, ТипПодключенияВнешнейКомпоненты.НеИзолированно) Тогда 210 | ВызватьИсключение "Не удалось подключить"; 211 | КонецЕсли; 212 | ОбъектКомпоненты2 = Новый ("AddIn.Test.Class2"); 213 | КонецПопытки; 214 | 215 | Начало = ТекущаяУниверсальнаяДатаВМиллисекундах(); 216 | Для К = 1 По 1000000 Цикл 217 | ОбъектКомпоненты2.Method1(111); 218 | КонецЦикла; 219 | Конец = ТекущаяУниверсальнаяДатаВМиллисекундах(); 220 | Сообщить(СтрШаблон("Длительность: %1 мс", Конец - Начало)); 221 | 222 | КонецПроцедуры 223 | 224 | &НаКлиенте 225 | Асинх Процедура ТестВнешнееСобытие(Команда) 226 | 227 | Попытка 228 | КомпонентаДляВнешнегоСобытия = Новый ("AddIn.Test.Class2"); 229 | Исключение 230 | Если Не Ждать ПодключитьВнешнююКомпонентуАсинх(ИмяФайла, "Test", ТипВнешнейКомпоненты.Native, ТипПодключенияВнешнейКомпоненты.НеИзолированно) Тогда 231 | ВызватьИсключение "Не удалось подключить"; 232 | КонецЕсли; 233 | КомпонентаДляВнешнегоСобытия = Новый ("AddIn.Test.Class2"); 234 | КонецПопытки; 235 | 236 | НачалоВызоваВнешнегоСобытия = ТекущаяУниверсальнаяДатаВМиллисекундах(); 237 | Результат = Ждать КомпонентаДляВнешнегоСобытия.CallExternalEventAsync(); 238 | Сообщить(Результат.Значение); 239 | 240 | КонецПроцедуры 241 | 242 | &НаКлиенте 243 | Процедура ВнешнееСобытие(Источник, Событие, Данные) 244 | 245 | Сообщить(СтрШаблон("Прошло: %1 мс, Источник: %2, Событие: %3, Данные: %4", ТекущаяУниверсальнаяДатаВМиллисекундах() - НачалоВызоваВнешнегоСобытия, Источник, Событие, Данные)); 246 | 247 | Если Событие = "Shutdown" Тогда 248 | КомпонентаДляВнешнегоСобытия = Неопределено; 249 | Сообщить("Работа внешней компоненты завершена"); 250 | КонецЕсли; 251 | 252 | КонецПроцедуры 253 | -------------------------------------------------------------------------------- /conf1c/Ext/HomePageWorkArea.xml: -------------------------------------------------------------------------------- 1 |  2 | 3 | TwoColumnsEqualWidth 4 | 5 | 6 |
DataProcessor.Обработка1.Form.Форма
7 | 10 8 | 9 | true 10 | 11 |
12 |
13 | 14 |
-------------------------------------------------------------------------------- /conf1c/Languages/Русский.xml: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | Русский 6 | 7 | 8 | ru 9 | Русский 10 | 11 | 12 | 13 | ru 14 | 15 | 16 | -------------------------------------------------------------------------------- /example-test/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "example-test" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | [dependencies] 7 | addin1c-test = {path = "../addin1c-test"} 8 | colored = "*" 9 | -------------------------------------------------------------------------------- /example-test/README.md: -------------------------------------------------------------------------------- 1 | # example-test 2 | 3 | Тесты компоненты для использования с `valgrind`, пример запуска: 4 | ```bash 5 | cargo build && valgrind --leak-check=full --show-leak-kinds=all target/debug/example-test 6 | ``` -------------------------------------------------------------------------------- /example-test/src/main.rs: -------------------------------------------------------------------------------- 1 | use std::error::Error; 2 | 3 | use addin1c_test::{name, str1c, TestAddinLib, Variant}; 4 | 5 | use colored::Colorize; 6 | 7 | fn main() -> Result<(), Box> { 8 | let addin_lib = TestAddinLib::new(path_addin())?; 9 | let object1 = addin_lib.new_addin("Class1")?; 10 | 11 | let value = Variant::new_i32(321); 12 | object1.set_property(name!("PropI32"), &value)?; 13 | 14 | let new_value = object1.get_property(name!("PropI32"))?; 15 | assert_eq!(new_value.get_i32(), Some(321)); 16 | 17 | let str1c = "Привет из Rust!".encode_utf16().collect::>(); 18 | let value = Variant::new_str(&str1c); 19 | object1.set_property(name!("PropStr"), &value)?; 20 | 21 | let new_value = object1.get_property(name!("PropStr"))?; 22 | assert_eq!(Some(str1c.as_slice()), new_value.get_str()); 23 | 24 | let mut params = [ 25 | Variant::new_str(str1c!("11")), 26 | Variant::new_str(str1c!("22")), 27 | Variant::new_str(str1c!("33")), 28 | ]; 29 | 30 | let result = object1.call_as_func(name!("Method1"), &mut params)?; 31 | assert_eq!(result.get_str(), Some(str1c!("112233").as_slice())); 32 | 33 | println!("{}", "Test result: Ok".green()); 34 | Ok(()) 35 | } 36 | 37 | fn path_addin() -> &'static str { 38 | match (cfg!(windows), cfg!(debug_assertions)) { 39 | (true, true) => "../addin1c/target/debug/addin.dll", 40 | (true, false) => "../addin1c/target/release/addin.dll", 41 | (false, true) => "../addin1c/target/debug/libaddin.so", 42 | (false, false) => "../addin1c/target/release/libaddin.so", 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /example/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "addin" 3 | version = "0.3.0" 4 | edition = "2021" 5 | 6 | [lib] 7 | crate-type = ["cdylib"] 8 | 9 | [dependencies] 10 | addin1c = { path = "../addin1c", features = ["chrono"] } 11 | chrono = "*" 12 | -------------------------------------------------------------------------------- /example/src/addin1.rs: -------------------------------------------------------------------------------- 1 | use addin1c::{name, CStr1C, RawAddin, Tm, Variant}; 2 | 3 | const PROPS: &[&CStr1C] = &[ 4 | name!("Test"), 5 | name!("PropI32"), 6 | name!("PropF64"), 7 | name!("PropBool"), 8 | name!("PropDate"), 9 | name!("PropStr"), 10 | name!("PropBlob"), 11 | ]; 12 | 13 | const METHODS: &[&CStr1C] = &[name!("Method1"), name!("Method2")]; 14 | 15 | pub struct Addin1 { 16 | test: i32, 17 | prop_i32: i32, 18 | prop_f64: f64, 19 | prop_bool: bool, 20 | prop_date: Tm, 21 | prop_str: String, 22 | prop_blob: Vec, 23 | } 24 | 25 | impl Addin1 { 26 | pub fn new() -> Addin1 { 27 | Addin1 { 28 | test: 11111, 29 | prop_i32: 22222, 30 | prop_f64: 333.33, 31 | prop_bool: false, 32 | prop_date: Tm::default(), 33 | prop_str: String::from("00000"), 34 | prop_blob: Vec::new(), 35 | } 36 | } 37 | } 38 | 39 | impl Drop for Addin1 { 40 | fn drop(&mut self) {} 41 | } 42 | 43 | impl RawAddin for Addin1 { 44 | fn register_extension_as(&mut self) -> &'static CStr1C { 45 | name!("Class1") 46 | } 47 | 48 | fn get_n_props(&mut self) -> usize { 49 | PROPS.len() 50 | } 51 | 52 | fn find_prop(&mut self, name: &CStr1C) -> Option { 53 | PROPS.iter().position(|&x| x == name) 54 | } 55 | 56 | fn get_prop_name(&mut self, num: usize, _alias: usize) -> Option<&'static CStr1C> { 57 | PROPS.get(num).copied() 58 | } 59 | 60 | fn get_prop_val(&mut self, num: usize, val: &mut Variant) -> bool { 61 | match num { 62 | 0 => val.set_i32(self.test), 63 | 1 => val.set_i32(self.prop_i32), 64 | 2 => val.set_f64(self.prop_f64), 65 | 3 => val.set_bool(self.prop_bool), 66 | 4 => val.set_date(self.prop_date), 67 | 5 => { 68 | return val.set_str1c(self.prop_str.as_str()).is_ok(); 69 | } 70 | 6 => { 71 | return val.set_blob(self.prop_blob.as_slice()).is_ok(); 72 | } 73 | _ => return false, 74 | }; 75 | true 76 | } 77 | 78 | fn set_prop_val(&mut self, num: usize, val: &Variant) -> bool { 79 | match num { 80 | 0 => val.get_i32().is_ok_and(|x| { 81 | self.test = x; 82 | true 83 | }), 84 | 1 => val.get_i32().is_ok_and(|x| { 85 | self.prop_i32 = x; 86 | true 87 | }), 88 | 2 => val.get_f64().is_ok_and(|x| { 89 | self.prop_f64 = x; 90 | true 91 | }), 92 | 3 => val.get_bool().is_ok_and(|x| { 93 | self.prop_bool = x; 94 | true 95 | }), 96 | 4 => val.get_date().is_ok_and(|x| { 97 | self.prop_date = x; 98 | true 99 | }), 100 | 5 => val.get_string().is_ok_and(|x| { 101 | self.prop_str = x; 102 | true 103 | }), 104 | 6 => val.get_blob().is_ok_and(|x| { 105 | self.prop_blob.clear(); 106 | self.prop_blob.extend_from_slice(x); 107 | true 108 | }), 109 | _ => false, 110 | } 111 | } 112 | 113 | fn is_prop_readable(&mut self, _num: usize) -> bool { 114 | true 115 | } 116 | 117 | fn is_prop_writable(&mut self, num: usize) -> bool { 118 | matches!(num, 0..=6) 119 | } 120 | 121 | fn get_n_methods(&mut self) -> usize { 122 | METHODS.len() 123 | } 124 | 125 | fn find_method(&mut self, name: &CStr1C) -> Option { 126 | METHODS.iter().position(|&x| x == name) 127 | } 128 | 129 | fn get_method_name(&mut self, num: usize, _alias: usize) -> Option<&'static CStr1C> { 130 | METHODS.get(num).copied() 131 | } 132 | 133 | fn get_n_params(&mut self, num: usize) -> usize { 134 | match num { 135 | 0 => 3, 136 | 1 => 2, 137 | _ => 0, 138 | } 139 | } 140 | 141 | fn get_param_def_value( 142 | &mut self, 143 | _method_num: usize, 144 | _param_num: usize, 145 | _value: Variant, 146 | ) -> bool { 147 | true 148 | } 149 | 150 | fn has_ret_val(&mut self, num: usize) -> bool { 151 | matches!(num, 0 | 1) 152 | } 153 | 154 | fn call_as_proc(&mut self, _num: usize, _params: &mut [Variant]) -> bool { 155 | false 156 | } 157 | 158 | fn call_as_func( 159 | &mut self, 160 | num: usize, 161 | params: &mut [Variant], 162 | ret_value: &mut Variant, 163 | ) -> bool { 164 | match num { 165 | 0 => { 166 | let mut buf = Vec::::new(); 167 | for param in params { 168 | let Ok(x) = param.get_str1c() else { 169 | return false; 170 | }; 171 | buf.extend_from_slice(x); 172 | } 173 | ret_value.set_str1c(buf.as_slice()).is_ok() 174 | } 175 | 1 => { 176 | for (i, param) in params.iter_mut().enumerate() { 177 | let Ok(()) = param.get_empty() else { 178 | return false; 179 | }; 180 | if i == 0 { 181 | if param.set_str1c("Return value").is_err() { 182 | return false; 183 | } 184 | } else { 185 | param.set_i32(1) 186 | } 187 | } 188 | ret_value.set_bool(true); 189 | true 190 | } 191 | _ => false, 192 | } 193 | } 194 | } 195 | -------------------------------------------------------------------------------- /example/src/addin2.rs: -------------------------------------------------------------------------------- 1 | use std::{error::Error, thread, time::Duration}; 2 | 3 | use addin1c::{ 4 | cstr1c, name, AddinResult, CStr1C, CString1C, Connection, MethodInfo, Methods, PropInfo, 5 | SimpleAddin, Variant, 6 | }; 7 | use chrono::Utc; 8 | 9 | pub struct Addin2 { 10 | prop1: i32, 11 | last_error: Option>, 12 | interface: Option<&'static Connection>, 13 | } 14 | 15 | impl Addin2 { 16 | pub fn new() -> Addin2 { 17 | Addin2 { 18 | prop1: 0, 19 | last_error: None, 20 | interface: None, 21 | } 22 | } 23 | 24 | fn last_error(&mut self, value: &mut Variant) -> AddinResult { 25 | match &self.last_error { 26 | Some(err) => value 27 | .set_str1c(err.to_string().as_str()) 28 | .map_err(|e| e.into()), 29 | None => value.set_str1c("").map_err(|e| e.into()), 30 | } 31 | } 32 | 33 | fn method1(&mut self, param: &mut Variant, ret_value: &mut Variant) -> AddinResult { 34 | let value = param.get_i32()?; 35 | self.prop1 = value; 36 | ret_value.set_i32(value * 2); 37 | Ok(()) 38 | } 39 | 40 | fn method2( 41 | &mut self, 42 | param1: &mut Variant, 43 | param2: &mut Variant, 44 | ret_value: &mut Variant, 45 | ) -> AddinResult { 46 | let value1 = param1.get_i32()?; 47 | let value2 = param2.get_i32()?; 48 | self.prop1 = value1 + value2; 49 | ret_value.set_i32(self.prop1); 50 | Ok(()) 51 | } 52 | 53 | fn set_prop1(&mut self, value: &Variant) -> AddinResult { 54 | let value = value.get_i32()?; 55 | self.prop1 = value; 56 | Ok(()) 57 | } 58 | 59 | fn get_prop1(&mut self, value: &mut Variant) -> AddinResult { 60 | value.set_i32(self.prop1); 61 | Ok(()) 62 | } 63 | 64 | fn panic1(&mut self, _ret_value: &mut Variant) -> AddinResult { 65 | panic!("Panic1") 66 | } 67 | 68 | fn panic2(&mut self, _ret_value: &mut Variant) -> AddinResult { 69 | panic!("Panic{}", self.prop1) 70 | } 71 | 72 | fn method_no_params(&mut self, _ret_value: &mut Variant) -> AddinResult { 73 | Ok(()) 74 | } 75 | 76 | fn utc(&mut self, ret_value: &mut Variant) -> AddinResult { 77 | ret_value.set_date(Utc::now().into()); 78 | Ok(()) 79 | } 80 | 81 | fn call_external_event(&mut self, ret_value: &mut Variant) -> AddinResult { 82 | if let Some(interface) = self.interface { 83 | let buffer_depth1 = interface.get_event_buffer_depth(); 84 | interface.set_event_buffer_depth(100); 85 | let buffer_depth2 = interface.get_event_buffer_depth(); 86 | let result = 87 | interface.external_event(cstr1c!("Addin2"), cstr1c!("Message1"), cstr1c!("Test1")); 88 | ret_value.set_str1c(format!( 89 | "result: {result}, buffer_depth1: {buffer_depth1}, buffer_depth2: {buffer_depth2}" 90 | ))?; 91 | 92 | thread::spawn(move || { 93 | let interface = interface; 94 | for i in 2..6 { 95 | thread::sleep(Duration::from_millis(500)); 96 | interface.external_event( 97 | cstr1c!("Addin2"), 98 | CString1C::new(&format!("Message{i}")), 99 | CString1C::new(&format!("Test{i}")), 100 | ); 101 | } 102 | interface.external_event( 103 | cstr1c!("Addin2"), 104 | cstr1c!("Shutdown"), 105 | cstr1c!("Shutdown"), 106 | ); 107 | }); 108 | } else { 109 | ret_value.set_str1c("нет интерфейса")?; 110 | } 111 | Ok(()) 112 | } 113 | } 114 | 115 | impl SimpleAddin for Addin2 { 116 | fn name() -> &'static CStr1C { 117 | name!("Class2") 118 | } 119 | 120 | fn init(&mut self, interface: &'static Connection) -> bool { 121 | self.interface = Some(interface); 122 | true 123 | } 124 | 125 | fn save_error(&mut self, err: Option>) { 126 | self.last_error = err; 127 | } 128 | 129 | fn methods() -> &'static [MethodInfo] { 130 | &[ 131 | MethodInfo { 132 | name: name!("Method1"), 133 | method: Methods::Method1(Self::method1), 134 | }, 135 | MethodInfo { 136 | name: name!("Method2"), 137 | method: Methods::Method2(Self::method2), 138 | }, 139 | MethodInfo { 140 | name: name!("Panic1"), 141 | method: Methods::Method0(Self::panic1), 142 | }, 143 | MethodInfo { 144 | name: name!("Panic2"), 145 | method: Methods::Method0(Self::panic2), 146 | }, 147 | MethodInfo { 148 | name: name!("MethodNoParams"), 149 | method: Methods::Method0(Self::method_no_params), 150 | }, 151 | MethodInfo { 152 | name: name!("Utc"), 153 | method: Methods::Method0(Self::utc), 154 | }, 155 | MethodInfo { 156 | name: name!("CallExternalEvent"), 157 | method: Methods::Method0(Self::call_external_event), 158 | }, 159 | ] 160 | } 161 | 162 | fn properties() -> &'static [PropInfo] { 163 | &[ 164 | PropInfo { 165 | name: name!("Prop1"), 166 | getter: Some(Self::get_prop1), 167 | setter: Some(Self::set_prop1), 168 | }, 169 | PropInfo { 170 | name: name!("LastError"), 171 | getter: Some(Self::last_error), 172 | setter: None, 173 | }, 174 | ] 175 | } 176 | } 177 | -------------------------------------------------------------------------------- /example/src/lib.rs: -------------------------------------------------------------------------------- 1 | mod addin1; 2 | mod addin2; 3 | 4 | use std::{ 5 | ffi::{c_int, c_long, c_void}, 6 | sync::atomic::{AtomicI32, Ordering}, 7 | }; 8 | 9 | use addin1::Addin1; 10 | use addin1c::{create_component, destroy_component, name, AttachType}; 11 | use addin2::Addin2; 12 | 13 | pub static PLATFORM_CAPABILITIES: AtomicI32 = AtomicI32::new(-1); 14 | 15 | /// # Safety 16 | /// 17 | /// Component must be non-null. 18 | #[allow(non_snake_case)] 19 | #[no_mangle] 20 | pub unsafe extern "C" fn GetClassObject(name: *const u16, component: *mut *mut c_void) -> c_long { 21 | match *name as u8 { 22 | b'1' => { 23 | let addin = Addin1::new(); 24 | create_component(component, addin) 25 | } 26 | b'2' => { 27 | let addin = Addin2::new(); 28 | create_component(component, addin) 29 | } 30 | _ => 0, 31 | } 32 | } 33 | 34 | /// # Safety 35 | /// 36 | /// Component must be returned from `GetClassObject`, the function must be called once for each component. 37 | #[allow(non_snake_case)] 38 | #[no_mangle] 39 | pub unsafe extern "C" fn DestroyObject(component: *mut *mut c_void) -> c_long { 40 | destroy_component(component) 41 | } 42 | 43 | #[allow(non_snake_case)] 44 | #[no_mangle] 45 | pub extern "C" fn GetClassNames() -> *const u16 { 46 | // small strings for performance 47 | name!("1|2").as_ptr() 48 | } 49 | 50 | #[allow(non_snake_case)] 51 | #[no_mangle] 52 | pub extern "C" fn SetPlatformCapabilities(capabilities: c_int) -> c_int { 53 | PLATFORM_CAPABILITIES.store(capabilities, Ordering::Relaxed); 54 | 3 55 | } 56 | 57 | #[allow(non_snake_case)] 58 | #[no_mangle] 59 | pub extern "C" fn GetAttachType() -> AttachType { 60 | AttachType::Any 61 | } 62 | --------------------------------------------------------------------------------