You've already forked example-native-api-rs
mirror of
https://github.com/medigor/example-native-api-rs.git
synced 2025-06-17 00:17:52 +02:00
init
This commit is contained in:
3
.gitignore
vendored
Normal file
3
.gitignore
vendored
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
target
|
||||||
|
Cargo.lock
|
||||||
|
.vscode
|
17
Cargo.toml
Normal file
17
Cargo.toml
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
[package]
|
||||||
|
name = "addin"
|
||||||
|
version = "0.1.0"
|
||||||
|
edition = "2021"
|
||||||
|
|
||||||
|
[lib]
|
||||||
|
crate-type = ["cdylib"]
|
||||||
|
|
||||||
|
[profile.release]
|
||||||
|
opt-level = "z" # Optimize for size.
|
||||||
|
lto = true # Enable Link Time Optimization
|
||||||
|
codegen-units = 1 # Reduce number of codegen units to increase optimizations.
|
||||||
|
panic = "abort" # Abort on panic
|
||||||
|
strip = true # Automatically strip symbols from the binary.
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
utf16_lit="2.0"
|
21
LICENSE
Normal file
21
LICENSE
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
MIT License
|
||||||
|
|
||||||
|
Copyright (c) 2022 https://github.com/medigor
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
SOFTWARE.
|
56
README.md
56
README.md
@ -1 +1,55 @@
|
|||||||
# example-native-add-rs
|
# example-native-api-rs
|
||||||
|
Пример внешней компоненты для **1С:Предприятие 8** по технологии **Native API** на языке rust
|
||||||
|
|
||||||
|
[Документция на ИТС](https://its.1c.ru/db/metod8dev#content:3221:hdoc) |
|
||||||
|
[Шаблон компоненты на c++ от Infactum](https://github.com/Infactum/addin-template)
|
||||||
|
|
||||||
|
## Преимущества по сравнению с компонентой на c++
|
||||||
|
* Преимущества самого языка rust и его экосистемы
|
||||||
|
* Для Windows не требуется msvc, собирается полностью с использованием свободных инструментов
|
||||||
|
|
||||||
|
## Обзор
|
||||||
|
Компоненты по технологии Native API предполагают разработку на языке с++, т.к. компонента должна принимать и возвращать указатели на виртуальные классы c++. Компонента для windows должна собираться только компилятором msvc, а для linux и macos подойдет gcc/clang.
|
||||||
|
Как известно, взаимодействие *rust* с *c++* из коробки не поддерживается.
|
||||||
|
|
||||||
|
Одним из вариантов было использовать [cxx](https://github.com/dtolnay/cxx) или подобные библиотеки. Это также бы потребовало использовать msvc.
|
||||||
|
|
||||||
|
Другой вариант - вручную реализовать виртуальные таблицы, именно этот вариант и реализован.
|
||||||
|
На [godbolt](https://godbolt.org/z/KM3jaWMWs) можно посмотреть, как выглядят виртуальные таблицы для разных компиляторов. Виртуальные таблицы *msvc* отличаются от *gcc*/*clang*, при этом *gcc* и *clang* используют одинаковое ABI. Виртуальные таблицы реализованы в объеме достаточном для создания компоненты.
|
||||||
|
|
||||||
|
## Описание файлов
|
||||||
|
* src/lib.rs - корень крейта, здесь располагаются экспортные функции GetClassNames и др.
|
||||||
|
* src/ffi.rs - в этом модуле всё что связано с взаимодействием, также здесь находится весь небезопасный код.
|
||||||
|
* src/addin1.rs - здесь непосредственно реализация компоненты, причем весь код безопасный.
|
||||||
|
* conf1c - конфигурация 1С (выгрузка из конфигуратора 8.3.22), минимальный тестовый код.
|
||||||
|
|
||||||
|
## Разработка
|
||||||
|
Я использую для разработки VS Code. Отлаживать и тестировать компоненту удобнее всего в файловой базе. Чтобы при нажатии F5 сразу запускалась 1С, нужно поместить в файл *.vscode/launch.json* примерно такой код:
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"version": "0.2.0",
|
||||||
|
"configurations": [
|
||||||
|
{
|
||||||
|
"type": "lldb",
|
||||||
|
"request": "launch",
|
||||||
|
"name": "Debug 1С",
|
||||||
|
"program": "путь/к/файлу/1cv8c",
|
||||||
|
"args": [
|
||||||
|
"/IBName",
|
||||||
|
"Test1"
|
||||||
|
],
|
||||||
|
"cwd": "${workspaceFolder}",
|
||||||
|
"preLaunchTask": "rust: cargo build"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
Для разработки на linux я использую виртуальную машину Hyper-V, VS Code подключается по ssh. Чтобы запуск 1С работал из ssh, нужно в конфигурацию запуска добавить:
|
||||||
|
```json
|
||||||
|
"env": {"DISPLAY": ":1"}
|
||||||
|
```
|
||||||
|
Для разработки и тестирования также подходит [Учебная версия 1С](https://online.1c.ru/catalog/free/learning.php), но версия для windows только x32.
|
||||||
|
|
||||||
|
MacOS не тестировал, думаю должно работать.
|
||||||
|
|
||||||
|
Android/iOS/веб-клиент не реализовано и планов таких нет.
|
||||||
|
11
conf1c/ConfigDumpInfo.xml
Normal file
11
conf1c/ConfigDumpInfo.xml
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<ConfigDumpInfo xmlns="http://v8.1c.ru/8.3/xcf/dumpinfo" xmlns:xen="http://v8.1c.ru/8.3/xcf/enums" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" format="Hierarchical" version="2.15">
|
||||||
|
<ConfigVersions>
|
||||||
|
<Metadata name="Configuration.Конфигурация" id="72b21170-d6cc-48ee-8426-d69353f61fd5" configVersion="a64d3ffe00e3344f821527065fd9669400000000"/>
|
||||||
|
<Metadata name="Configuration.Конфигурация.HomePageWorkArea" id="29e7f5a4-dc26-408a-bd6e-083b5bf1384c.8" configVersion="0169b7c0855a0e459f04d61506e77aef00000000"/>
|
||||||
|
<Metadata name="DataProcessor.Обработка1" id="33179d0b-730e-44f5-b20c-1af11f7f1f60" configVersion="2f45b64f61dc1b4cb42bd668cf3942df00000000"/>
|
||||||
|
<Metadata name="DataProcessor.Обработка1.Form.Форма" id="24033a67-5ef1-4265-9a88-eb7f279ce1cb" configVersion="b173f3c73e1fdd489319ecfd372edab800000000"/>
|
||||||
|
<Metadata name="DataProcessor.Обработка1.Form.Форма.Form" id="24033a67-5ef1-4265-9a88-eb7f279ce1cb.0" configVersion="7894c5dc6490f14d92e085d4d4b8847300000000"/>
|
||||||
|
<Metadata name="Language.Русский" id="e3ac9659-5250-4530-9ca6-3f4f4ef6413a" configVersion="0a61ebedb1993c4a9ed758c68a381d7d00000000"/>
|
||||||
|
</ConfigVersions>
|
||||||
|
</ConfigDumpInfo>
|
221
conf1c/Configuration.xml
Normal file
221
conf1c/Configuration.xml
Normal file
@ -0,0 +1,221 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<MetaDataObject xmlns="http://v8.1c.ru/8.3/MDClasses" xmlns:app="http://v8.1c.ru/8.2/managed-application/core" xmlns:cfg="http://v8.1c.ru/8.1/data/enterprise/current-config" xmlns:cmi="http://v8.1c.ru/8.2/managed-application/cmi" xmlns:ent="http://v8.1c.ru/8.1/data/enterprise" xmlns:lf="http://v8.1c.ru/8.2/managed-application/logform" xmlns:style="http://v8.1c.ru/8.1/data/ui/style" xmlns:sys="http://v8.1c.ru/8.1/data/ui/fonts/system" xmlns:v8="http://v8.1c.ru/8.1/data/core" xmlns:v8ui="http://v8.1c.ru/8.1/data/ui" xmlns:web="http://v8.1c.ru/8.1/data/ui/colors/web" xmlns:win="http://v8.1c.ru/8.1/data/ui/colors/windows" xmlns:xen="http://v8.1c.ru/8.3/xcf/enums" xmlns:xpr="http://v8.1c.ru/8.3/xcf/predef" xmlns:xr="http://v8.1c.ru/8.3/xcf/readable" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" version="2.15">
|
||||||
|
<Configuration uuid="72b21170-d6cc-48ee-8426-d69353f61fd5">
|
||||||
|
<InternalInfo>
|
||||||
|
<xr:ContainedObject>
|
||||||
|
<xr:ClassId>9cd510cd-abfc-11d4-9434-004095e12fc7</xr:ClassId>
|
||||||
|
<xr:ObjectId>29e7f5a4-dc26-408a-bd6e-083b5bf1384c</xr:ObjectId>
|
||||||
|
</xr:ContainedObject>
|
||||||
|
<xr:ContainedObject>
|
||||||
|
<xr:ClassId>9fcd25a0-4822-11d4-9414-008048da11f9</xr:ClassId>
|
||||||
|
<xr:ObjectId>f76706d7-a451-49f0-a3b8-8c92dc9eb401</xr:ObjectId>
|
||||||
|
</xr:ContainedObject>
|
||||||
|
<xr:ContainedObject>
|
||||||
|
<xr:ClassId>e3687481-0a87-462c-a166-9f34594f9bba</xr:ClassId>
|
||||||
|
<xr:ObjectId>efad9454-c037-40e6-92f8-de6f995cb912</xr:ObjectId>
|
||||||
|
</xr:ContainedObject>
|
||||||
|
<xr:ContainedObject>
|
||||||
|
<xr:ClassId>9de14907-ec23-4a07-96f0-85521cb6b53b</xr:ClassId>
|
||||||
|
<xr:ObjectId>5a509aa9-2e5a-47a8-977a-8fbe609d782c</xr:ObjectId>
|
||||||
|
</xr:ContainedObject>
|
||||||
|
<xr:ContainedObject>
|
||||||
|
<xr:ClassId>51f2d5d8-ea4d-4064-8892-82951750031e</xr:ClassId>
|
||||||
|
<xr:ObjectId>3982e56b-1417-490c-8ac4-7672c7bffa80</xr:ObjectId>
|
||||||
|
</xr:ContainedObject>
|
||||||
|
<xr:ContainedObject>
|
||||||
|
<xr:ClassId>e68182ea-4237-4383-967f-90c1e3370bc7</xr:ClassId>
|
||||||
|
<xr:ObjectId>862a5e5b-5378-4b7c-b567-0ab6725fc840</xr:ObjectId>
|
||||||
|
</xr:ContainedObject>
|
||||||
|
<xr:ContainedObject>
|
||||||
|
<xr:ClassId>fb282519-d103-4dd3-bc12-cb271d631dfc</xr:ClassId>
|
||||||
|
<xr:ObjectId>ed5cd24c-da69-4a84-a011-421e03c8dd71</xr:ObjectId>
|
||||||
|
</xr:ContainedObject>
|
||||||
|
</InternalInfo>
|
||||||
|
<Properties>
|
||||||
|
<Name>Конфигурация</Name>
|
||||||
|
<Synonym/>
|
||||||
|
<Comment/>
|
||||||
|
<NamePrefix/>
|
||||||
|
<ConfigurationExtensionCompatibilityMode>Version8_3_22</ConfigurationExtensionCompatibilityMode>
|
||||||
|
<DefaultRunMode>ManagedApplication</DefaultRunMode>
|
||||||
|
<UsePurposes>
|
||||||
|
<v8:Value xsi:type="app:ApplicationUsePurpose">PlatformApplication</v8:Value>
|
||||||
|
</UsePurposes>
|
||||||
|
<ScriptVariant>Russian</ScriptVariant>
|
||||||
|
<DefaultRoles/>
|
||||||
|
<Vendor/>
|
||||||
|
<Version/>
|
||||||
|
<UpdateCatalogAddress/>
|
||||||
|
<IncludeHelpInContents>false</IncludeHelpInContents>
|
||||||
|
<UseManagedFormInOrdinaryApplication>false</UseManagedFormInOrdinaryApplication>
|
||||||
|
<UseOrdinaryFormInManagedApplication>false</UseOrdinaryFormInManagedApplication>
|
||||||
|
<AdditionalFullTextSearchDictionaries/>
|
||||||
|
<CommonSettingsStorage/>
|
||||||
|
<ReportsUserSettingsStorage/>
|
||||||
|
<ReportsVariantsStorage/>
|
||||||
|
<FormDataSettingsStorage/>
|
||||||
|
<DynamicListsUserSettingsStorage/>
|
||||||
|
<URLExternalDataStorage/>
|
||||||
|
<Content/>
|
||||||
|
<DefaultReportForm/>
|
||||||
|
<DefaultReportVariantForm/>
|
||||||
|
<DefaultReportSettingsForm/>
|
||||||
|
<DefaultReportAppearanceTemplate/>
|
||||||
|
<DefaultDynamicListSettingsForm/>
|
||||||
|
<DefaultSearchForm/>
|
||||||
|
<DefaultDataHistoryChangeHistoryForm/>
|
||||||
|
<DefaultDataHistoryVersionDataForm/>
|
||||||
|
<DefaultDataHistoryVersionDifferencesForm/>
|
||||||
|
<DefaultCollaborationSystemUsersChoiceForm/>
|
||||||
|
<RequiredMobileApplicationPermissions/>
|
||||||
|
<UsedMobileApplicationFunctionalities>
|
||||||
|
<app:functionality>
|
||||||
|
<app:functionality>Biometrics</app:functionality>
|
||||||
|
<app:use>true</app:use>
|
||||||
|
</app:functionality>
|
||||||
|
<app:functionality>
|
||||||
|
<app:functionality>Location</app:functionality>
|
||||||
|
<app:use>false</app:use>
|
||||||
|
</app:functionality>
|
||||||
|
<app:functionality>
|
||||||
|
<app:functionality>BackgroundLocation</app:functionality>
|
||||||
|
<app:use>false</app:use>
|
||||||
|
</app:functionality>
|
||||||
|
<app:functionality>
|
||||||
|
<app:functionality>BluetoothPrinters</app:functionality>
|
||||||
|
<app:use>false</app:use>
|
||||||
|
</app:functionality>
|
||||||
|
<app:functionality>
|
||||||
|
<app:functionality>WiFiPrinters</app:functionality>
|
||||||
|
<app:use>false</app:use>
|
||||||
|
</app:functionality>
|
||||||
|
<app:functionality>
|
||||||
|
<app:functionality>Contacts</app:functionality>
|
||||||
|
<app:use>false</app:use>
|
||||||
|
</app:functionality>
|
||||||
|
<app:functionality>
|
||||||
|
<app:functionality>Calendars</app:functionality>
|
||||||
|
<app:use>false</app:use>
|
||||||
|
</app:functionality>
|
||||||
|
<app:functionality>
|
||||||
|
<app:functionality>PushNotifications</app:functionality>
|
||||||
|
<app:use>false</app:use>
|
||||||
|
</app:functionality>
|
||||||
|
<app:functionality>
|
||||||
|
<app:functionality>LocalNotifications</app:functionality>
|
||||||
|
<app:use>false</app:use>
|
||||||
|
</app:functionality>
|
||||||
|
<app:functionality>
|
||||||
|
<app:functionality>InAppPurchases</app:functionality>
|
||||||
|
<app:use>false</app:use>
|
||||||
|
</app:functionality>
|
||||||
|
<app:functionality>
|
||||||
|
<app:functionality>PersonalComputerFileExchange</app:functionality>
|
||||||
|
<app:use>false</app:use>
|
||||||
|
</app:functionality>
|
||||||
|
<app:functionality>
|
||||||
|
<app:functionality>Ads</app:functionality>
|
||||||
|
<app:use>false</app:use>
|
||||||
|
</app:functionality>
|
||||||
|
<app:functionality>
|
||||||
|
<app:functionality>NumberDialing</app:functionality>
|
||||||
|
<app:use>false</app:use>
|
||||||
|
</app:functionality>
|
||||||
|
<app:functionality>
|
||||||
|
<app:functionality>CallProcessing</app:functionality>
|
||||||
|
<app:use>false</app:use>
|
||||||
|
</app:functionality>
|
||||||
|
<app:functionality>
|
||||||
|
<app:functionality>CallLog</app:functionality>
|
||||||
|
<app:use>false</app:use>
|
||||||
|
</app:functionality>
|
||||||
|
<app:functionality>
|
||||||
|
<app:functionality>AutoSendSMS</app:functionality>
|
||||||
|
<app:use>false</app:use>
|
||||||
|
</app:functionality>
|
||||||
|
<app:functionality>
|
||||||
|
<app:functionality>ReceiveSMS</app:functionality>
|
||||||
|
<app:use>false</app:use>
|
||||||
|
</app:functionality>
|
||||||
|
<app:functionality>
|
||||||
|
<app:functionality>SMSLog</app:functionality>
|
||||||
|
<app:use>false</app:use>
|
||||||
|
</app:functionality>
|
||||||
|
<app:functionality>
|
||||||
|
<app:functionality>Camera</app:functionality>
|
||||||
|
<app:use>false</app:use>
|
||||||
|
</app:functionality>
|
||||||
|
<app:functionality>
|
||||||
|
<app:functionality>Microphone</app:functionality>
|
||||||
|
<app:use>false</app:use>
|
||||||
|
</app:functionality>
|
||||||
|
<app:functionality>
|
||||||
|
<app:functionality>MusicLibrary</app:functionality>
|
||||||
|
<app:use>false</app:use>
|
||||||
|
</app:functionality>
|
||||||
|
<app:functionality>
|
||||||
|
<app:functionality>PictureAndVideoLibraries</app:functionality>
|
||||||
|
<app:use>false</app:use>
|
||||||
|
</app:functionality>
|
||||||
|
<app:functionality>
|
||||||
|
<app:functionality>AudioPlaybackAndVibration</app:functionality>
|
||||||
|
<app:use>false</app:use>
|
||||||
|
</app:functionality>
|
||||||
|
<app:functionality>
|
||||||
|
<app:functionality>BackgroundAudioPlaybackAndVibration</app:functionality>
|
||||||
|
<app:use>false</app:use>
|
||||||
|
</app:functionality>
|
||||||
|
<app:functionality>
|
||||||
|
<app:functionality>InstallPackages</app:functionality>
|
||||||
|
<app:use>false</app:use>
|
||||||
|
</app:functionality>
|
||||||
|
<app:functionality>
|
||||||
|
<app:functionality>OSBackup</app:functionality>
|
||||||
|
<app:use>true</app:use>
|
||||||
|
</app:functionality>
|
||||||
|
<app:functionality>
|
||||||
|
<app:functionality>ApplicationUsageStatistics</app:functionality>
|
||||||
|
<app:use>false</app:use>
|
||||||
|
</app:functionality>
|
||||||
|
<app:functionality>
|
||||||
|
<app:functionality>BarcodeScanning</app:functionality>
|
||||||
|
<app:use>false</app:use>
|
||||||
|
</app:functionality>
|
||||||
|
<app:functionality>
|
||||||
|
<app:functionality>BackgroundAudioRecording</app:functionality>
|
||||||
|
<app:use>false</app:use>
|
||||||
|
</app:functionality>
|
||||||
|
<app:functionality>
|
||||||
|
<app:functionality>AllFilesAccess</app:functionality>
|
||||||
|
<app:use>false</app:use>
|
||||||
|
</app:functionality>
|
||||||
|
<app:functionality>
|
||||||
|
<app:functionality>Videoconferences</app:functionality>
|
||||||
|
<app:use>false</app:use>
|
||||||
|
</app:functionality>
|
||||||
|
</UsedMobileApplicationFunctionalities>
|
||||||
|
<StandaloneConfigurationRestrictionRoles/>
|
||||||
|
<MobileApplicationURLs/>
|
||||||
|
<MainClientApplicationWindowMode>Normal</MainClientApplicationWindowMode>
|
||||||
|
<DefaultInterface/>
|
||||||
|
<DefaultStyle/>
|
||||||
|
<DefaultLanguage>Language.Русский</DefaultLanguage>
|
||||||
|
<BriefInformation/>
|
||||||
|
<DetailedInformation/>
|
||||||
|
<Copyright/>
|
||||||
|
<VendorInformationAddress/>
|
||||||
|
<ConfigurationInformationAddress/>
|
||||||
|
<DataLockControlMode>Managed</DataLockControlMode>
|
||||||
|
<ObjectAutonumerationMode>NotAutoFree</ObjectAutonumerationMode>
|
||||||
|
<ModalityUseMode>DontUse</ModalityUseMode>
|
||||||
|
<SynchronousPlatformExtensionAndAddInCallUseMode>DontUse</SynchronousPlatformExtensionAndAddInCallUseMode>
|
||||||
|
<InterfaceCompatibilityMode>Taxi</InterfaceCompatibilityMode>
|
||||||
|
<CompatibilityMode>Version8_3_22</CompatibilityMode>
|
||||||
|
<DefaultConstantsForm/>
|
||||||
|
</Properties>
|
||||||
|
<ChildObjects>
|
||||||
|
<Language>Русский</Language>
|
||||||
|
<DataProcessor>Обработка1</DataProcessor>
|
||||||
|
</ChildObjects>
|
||||||
|
</Configuration>
|
||||||
|
</MetaDataObject>
|
29
conf1c/DataProcessors/Обработка1.xml
Normal file
29
conf1c/DataProcessors/Обработка1.xml
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<MetaDataObject xmlns="http://v8.1c.ru/8.3/MDClasses" xmlns:app="http://v8.1c.ru/8.2/managed-application/core" xmlns:cfg="http://v8.1c.ru/8.1/data/enterprise/current-config" xmlns:cmi="http://v8.1c.ru/8.2/managed-application/cmi" xmlns:ent="http://v8.1c.ru/8.1/data/enterprise" xmlns:lf="http://v8.1c.ru/8.2/managed-application/logform" xmlns:style="http://v8.1c.ru/8.1/data/ui/style" xmlns:sys="http://v8.1c.ru/8.1/data/ui/fonts/system" xmlns:v8="http://v8.1c.ru/8.1/data/core" xmlns:v8ui="http://v8.1c.ru/8.1/data/ui" xmlns:web="http://v8.1c.ru/8.1/data/ui/colors/web" xmlns:win="http://v8.1c.ru/8.1/data/ui/colors/windows" xmlns:xen="http://v8.1c.ru/8.3/xcf/enums" xmlns:xpr="http://v8.1c.ru/8.3/xcf/predef" xmlns:xr="http://v8.1c.ru/8.3/xcf/readable" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" version="2.15">
|
||||||
|
<DataProcessor uuid="33179d0b-730e-44f5-b20c-1af11f7f1f60">
|
||||||
|
<InternalInfo>
|
||||||
|
<xr:GeneratedType name="DataProcessorObject.Обработка1" category="Object">
|
||||||
|
<xr:TypeId>aa584d2a-1a1f-4807-b2e6-588aef5193b8</xr:TypeId>
|
||||||
|
<xr:ValueId>620a7e0f-3713-4585-842f-262d1dfa18c0</xr:ValueId>
|
||||||
|
</xr:GeneratedType>
|
||||||
|
<xr:GeneratedType name="DataProcessorManager.Обработка1" category="Manager">
|
||||||
|
<xr:TypeId>e6189229-5433-466b-a435-ac6f2fa36046</xr:TypeId>
|
||||||
|
<xr:ValueId>8606e276-c11b-4eaa-af7b-424d33285d14</xr:ValueId>
|
||||||
|
</xr:GeneratedType>
|
||||||
|
</InternalInfo>
|
||||||
|
<Properties>
|
||||||
|
<Name>Обработка1</Name>
|
||||||
|
<Synonym/>
|
||||||
|
<Comment/>
|
||||||
|
<UseStandardCommands>true</UseStandardCommands>
|
||||||
|
<DefaultForm>DataProcessor.Обработка1.Form.Форма</DefaultForm>
|
||||||
|
<AuxiliaryForm/>
|
||||||
|
<IncludeHelpInContents>false</IncludeHelpInContents>
|
||||||
|
<ExtendedPresentation/>
|
||||||
|
<Explanation/>
|
||||||
|
</Properties>
|
||||||
|
<ChildObjects>
|
||||||
|
<Form>Форма</Form>
|
||||||
|
</ChildObjects>
|
||||||
|
</DataProcessor>
|
||||||
|
</MetaDataObject>
|
22
conf1c/DataProcessors/Обработка1/Forms/Форма.xml
Normal file
22
conf1c/DataProcessors/Обработка1/Forms/Форма.xml
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<MetaDataObject xmlns="http://v8.1c.ru/8.3/MDClasses" xmlns:app="http://v8.1c.ru/8.2/managed-application/core" xmlns:cfg="http://v8.1c.ru/8.1/data/enterprise/current-config" xmlns:cmi="http://v8.1c.ru/8.2/managed-application/cmi" xmlns:ent="http://v8.1c.ru/8.1/data/enterprise" xmlns:lf="http://v8.1c.ru/8.2/managed-application/logform" xmlns:style="http://v8.1c.ru/8.1/data/ui/style" xmlns:sys="http://v8.1c.ru/8.1/data/ui/fonts/system" xmlns:v8="http://v8.1c.ru/8.1/data/core" xmlns:v8ui="http://v8.1c.ru/8.1/data/ui" xmlns:web="http://v8.1c.ru/8.1/data/ui/colors/web" xmlns:win="http://v8.1c.ru/8.1/data/ui/colors/windows" xmlns:xen="http://v8.1c.ru/8.3/xcf/enums" xmlns:xpr="http://v8.1c.ru/8.3/xcf/predef" xmlns:xr="http://v8.1c.ru/8.3/xcf/readable" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" version="2.15">
|
||||||
|
<Form uuid="24033a67-5ef1-4265-9a88-eb7f279ce1cb">
|
||||||
|
<Properties>
|
||||||
|
<Name>Форма</Name>
|
||||||
|
<Synonym>
|
||||||
|
<v8:item>
|
||||||
|
<v8:lang>ru</v8:lang>
|
||||||
|
<v8:content>Форма</v8:content>
|
||||||
|
</v8:item>
|
||||||
|
</Synonym>
|
||||||
|
<Comment/>
|
||||||
|
<FormType>Managed</FormType>
|
||||||
|
<IncludeHelpInContents>false</IncludeHelpInContents>
|
||||||
|
<UsePurposes>
|
||||||
|
<v8:Value xsi:type="app:ApplicationUsePurpose">PlatformApplication</v8:Value>
|
||||||
|
<v8:Value xsi:type="app:ApplicationUsePurpose">MobilePlatformApplication</v8:Value>
|
||||||
|
</UsePurposes>
|
||||||
|
<ExtendedPresentation/>
|
||||||
|
</Properties>
|
||||||
|
</Form>
|
||||||
|
</MetaDataObject>
|
83
conf1c/DataProcessors/Обработка1/Forms/Форма/Ext/Form.xml
Normal file
83
conf1c/DataProcessors/Обработка1/Forms/Форма/Ext/Form.xml
Normal file
@ -0,0 +1,83 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<Form xmlns="http://v8.1c.ru/8.3/xcf/logform" xmlns:app="http://v8.1c.ru/8.2/managed-application/core" xmlns:cfg="http://v8.1c.ru/8.1/data/enterprise/current-config" xmlns:dcscor="http://v8.1c.ru/8.1/data-composition-system/core" xmlns:dcssch="http://v8.1c.ru/8.1/data-composition-system/schema" xmlns:dcsset="http://v8.1c.ru/8.1/data-composition-system/settings" xmlns:ent="http://v8.1c.ru/8.1/data/enterprise" xmlns:lf="http://v8.1c.ru/8.2/managed-application/logform" xmlns:style="http://v8.1c.ru/8.1/data/ui/style" xmlns:sys="http://v8.1c.ru/8.1/data/ui/fonts/system" xmlns:v8="http://v8.1c.ru/8.1/data/core" xmlns:v8ui="http://v8.1c.ru/8.1/data/ui" xmlns:web="http://v8.1c.ru/8.1/data/ui/colors/web" xmlns:win="http://v8.1c.ru/8.1/data/ui/colors/windows" xmlns:xr="http://v8.1c.ru/8.3/xcf/readable" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" version="2.15">
|
||||||
|
<AutoSaveDataInSettings>Use</AutoSaveDataInSettings>
|
||||||
|
<AutoCommandBar name="ФормаКоманднаяПанель" id="-1"/>
|
||||||
|
<ChildItems>
|
||||||
|
<InputField name="ИмяФайла" id="19">
|
||||||
|
<DataPath>ИмяФайла</DataPath>
|
||||||
|
<ContextMenu name="ИмяФайлаКонтекстноеМеню" id="20"/>
|
||||||
|
<ExtendedTooltip name="ИмяФайлаРасширеннаяПодсказка" id="21"/>
|
||||||
|
<Events>
|
||||||
|
<Event name="StartChoice">ИмяФайлаНачалоВыбора</Event>
|
||||||
|
</Events>
|
||||||
|
</InputField>
|
||||||
|
<Button name="Тест1" id="22">
|
||||||
|
<Type>UsualButton</Type>
|
||||||
|
<CommandName>Form.Command.Тест1</CommandName>
|
||||||
|
<ExtendedTooltip name="Тест1РасширеннаяПодсказка" id="23"/>
|
||||||
|
</Button>
|
||||||
|
<Button name="Тест2" id="3">
|
||||||
|
<Type>UsualButton</Type>
|
||||||
|
<CommandName>Form.Command.Тест2</CommandName>
|
||||||
|
<ExtendedTooltip name="Тест2РасширеннаяПодсказка" id="4"/>
|
||||||
|
</Button>
|
||||||
|
</ChildItems>
|
||||||
|
<Attributes>
|
||||||
|
<Attribute name="Объект" id="1">
|
||||||
|
<Type>
|
||||||
|
<v8:Type>cfg:DataProcessorObject.Обработка1</v8:Type>
|
||||||
|
</Type>
|
||||||
|
<MainAttribute>true</MainAttribute>
|
||||||
|
</Attribute>
|
||||||
|
<Attribute name="ИмяФайла" id="2">
|
||||||
|
<Title>
|
||||||
|
<v8:item>
|
||||||
|
<v8:lang>ru</v8:lang>
|
||||||
|
<v8:content>Имя файла</v8:content>
|
||||||
|
</v8:item>
|
||||||
|
</Title>
|
||||||
|
<Type>
|
||||||
|
<v8:Type>xs:string</v8:Type>
|
||||||
|
<v8:StringQualifiers>
|
||||||
|
<v8:Length>0</v8:Length>
|
||||||
|
<v8:AllowedLength>Variable</v8:AllowedLength>
|
||||||
|
</v8:StringQualifiers>
|
||||||
|
</Type>
|
||||||
|
<Save>
|
||||||
|
<Field>ИмяФайла</Field>
|
||||||
|
</Save>
|
||||||
|
</Attribute>
|
||||||
|
</Attributes>
|
||||||
|
<Commands>
|
||||||
|
<Command name="Тест2" id="2">
|
||||||
|
<Title>
|
||||||
|
<v8:item>
|
||||||
|
<v8:lang>ru</v8:lang>
|
||||||
|
<v8:content>Тест2</v8:content>
|
||||||
|
</v8:item>
|
||||||
|
</Title>
|
||||||
|
<ToolTip>
|
||||||
|
<v8:item>
|
||||||
|
<v8:lang>ru</v8:lang>
|
||||||
|
<v8:content>Тест2</v8:content>
|
||||||
|
</v8:item>
|
||||||
|
</ToolTip>
|
||||||
|
<Action>Тест2</Action>
|
||||||
|
</Command>
|
||||||
|
<Command name="Тест1" id="1">
|
||||||
|
<Title>
|
||||||
|
<v8:item>
|
||||||
|
<v8:lang>ru</v8:lang>
|
||||||
|
<v8:content>Тест1</v8:content>
|
||||||
|
</v8:item>
|
||||||
|
</Title>
|
||||||
|
<ToolTip>
|
||||||
|
<v8:item>
|
||||||
|
<v8:lang>ru</v8:lang>
|
||||||
|
<v8:content>Тест1</v8:content>
|
||||||
|
</v8:item>
|
||||||
|
</ToolTip>
|
||||||
|
<Action>Тест1</Action>
|
||||||
|
</Command>
|
||||||
|
</Commands>
|
||||||
|
</Form>
|
@ -0,0 +1,85 @@
|
|||||||
|
|
||||||
|
&НаКлиенте
|
||||||
|
Процедура Тест1(Команда)
|
||||||
|
Тест1НаСервере(ИмяФайла);
|
||||||
|
КонецПроцедуры
|
||||||
|
|
||||||
|
&НаСервереБезКонтекста
|
||||||
|
Процедура Тест1НаСервере(ИмяФайла)
|
||||||
|
|
||||||
|
Начало = ТекущаяУниверсальнаяДатаВМиллисекундах();
|
||||||
|
|
||||||
|
Если Не ПодключитьВнешнююКомпоненту(ИмяФайла, "Test", ТипВнешнейКомпоненты.Native, ТипПодключенияВнешнейКомпоненты.НеИзолированно) Тогда
|
||||||
|
Сообщить("Не удалось подключить");
|
||||||
|
Возврат;
|
||||||
|
КонецЕсли;
|
||||||
|
|
||||||
|
Сообщить("Подключена");
|
||||||
|
ОбъектКомпоненты = Новый ("AddIn.Test.Class1");
|
||||||
|
Test = ОбъектКомпоненты.Test;
|
||||||
|
Конец = ТекущаяУниверсальнаяДатаВМиллисекундах();
|
||||||
|
Сообщить(СтрШаблон("Test: %1", Test));
|
||||||
|
Сообщить(СтрШаблон("Длительность: %1", Конец - Начало));
|
||||||
|
|
||||||
|
КонецПроцедуры
|
||||||
|
|
||||||
|
|
||||||
|
&НаКлиенте
|
||||||
|
Процедура Тест2(Команда)
|
||||||
|
Тест2НаСервере(ИмяФайла);
|
||||||
|
КонецПроцедуры
|
||||||
|
|
||||||
|
&НаСервереБезКонтекста
|
||||||
|
Процедура Тест2НаСервере(ИмяФайла)
|
||||||
|
|
||||||
|
Начало = ТекущаяУниверсальнаяДатаВМиллисекундах();
|
||||||
|
|
||||||
|
Попытка
|
||||||
|
ОбъектКомпоненты = Новый ("AddIn.Test.Class1");
|
||||||
|
Исключение
|
||||||
|
Если Не ПодключитьВнешнююКомпоненту(ИмяФайла, "Test", ТипВнешнейКомпоненты.Native, ТипПодключенияВнешнейКомпоненты.НеИзолированно) Тогда
|
||||||
|
ВызватьИсключение "Не удалось подключить";
|
||||||
|
КонецЕсли;
|
||||||
|
ОбъектКомпоненты = Новый ("AddIn.Test.Class1");
|
||||||
|
КонецПопытки;
|
||||||
|
|
||||||
|
ОбъектКомпоненты.PropI32 = 123;
|
||||||
|
Если ОбъектКомпоненты.PropI32 <> 123 Тогда
|
||||||
|
ВызватьИсключение "Не удалось установить значение PropI32";
|
||||||
|
КонецЕсли;
|
||||||
|
|
||||||
|
ОбъектКомпоненты.PropF64 = 456.789;
|
||||||
|
Если ОбъектКомпоненты.PropF64 <> 456.789 Тогда
|
||||||
|
ВызватьИсключение "Не удалось установить значение PropF64";
|
||||||
|
КонецЕсли;
|
||||||
|
|
||||||
|
ОбъектКомпоненты.PropBool = Истина;
|
||||||
|
Если ОбъектКомпоненты.PropBool <> Истина Тогда
|
||||||
|
ВызватьИсключение "Не удалось установить значение PropBool";
|
||||||
|
КонецЕсли;
|
||||||
|
|
||||||
|
Date = ТекущаяДатаСеанса();
|
||||||
|
ОбъектКомпоненты.PropDate = Date;
|
||||||
|
Если ОбъектКомпоненты.PropDate <> Date Тогда
|
||||||
|
ВызватьИсключение "Не удалось установить значение PropDate";
|
||||||
|
КонецЕсли;
|
||||||
|
|
||||||
|
ОбъектКомпоненты.PropStr = "Привет!";
|
||||||
|
Если ОбъектКомпоненты.PropStr <> "Привет!" Тогда
|
||||||
|
ВызватьИсключение "Не удалось установить значение PropStr";
|
||||||
|
КонецЕсли;
|
||||||
|
|
||||||
|
Blob = ПолучитьДвоичныеДанныеИзСтроки("Привет!");
|
||||||
|
ОбъектКомпоненты.PropBlob = Blob;
|
||||||
|
Если ОбъектКомпоненты.PropBlob <> Blob Тогда
|
||||||
|
ВызватьИсключение "Не удалось установить значение PropBlob";
|
||||||
|
КонецЕсли;
|
||||||
|
|
||||||
|
Если ОбъектКомпоненты.Method1("11", "22", "33") <> "112233" Тогда
|
||||||
|
ВызватьИсключение "Не удалось установить значение Method1";
|
||||||
|
КонецЕсли;
|
||||||
|
|
||||||
|
Конец = ТекущаяУниверсальнаяДатаВМиллисекундах();
|
||||||
|
Сообщить(СтрШаблон("Длительность: %1", Конец - Начало));
|
||||||
|
|
||||||
|
КонецПроцедуры
|
14
conf1c/Ext/HomePageWorkArea.xml
Normal file
14
conf1c/Ext/HomePageWorkArea.xml
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<HomePageWorkArea xmlns="http://v8.1c.ru/8.3/xcf/extrnprops" xmlns:xr="http://v8.1c.ru/8.3/xcf/readable" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" version="2.15">
|
||||||
|
<WorkingAreaTemplate>TwoColumnsEqualWidth</WorkingAreaTemplate>
|
||||||
|
<LeftColumn>
|
||||||
|
<Item>
|
||||||
|
<Form>DataProcessor.Обработка1.Form.Форма</Form>
|
||||||
|
<Height>10</Height>
|
||||||
|
<Visibility>
|
||||||
|
<xr:Common>true</xr:Common>
|
||||||
|
</Visibility>
|
||||||
|
</Item>
|
||||||
|
</LeftColumn>
|
||||||
|
<RightColumn/>
|
||||||
|
</HomePageWorkArea>
|
16
conf1c/Languages/Русский.xml
Normal file
16
conf1c/Languages/Русский.xml
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<MetaDataObject xmlns="http://v8.1c.ru/8.3/MDClasses" xmlns:app="http://v8.1c.ru/8.2/managed-application/core" xmlns:cfg="http://v8.1c.ru/8.1/data/enterprise/current-config" xmlns:cmi="http://v8.1c.ru/8.2/managed-application/cmi" xmlns:ent="http://v8.1c.ru/8.1/data/enterprise" xmlns:lf="http://v8.1c.ru/8.2/managed-application/logform" xmlns:style="http://v8.1c.ru/8.1/data/ui/style" xmlns:sys="http://v8.1c.ru/8.1/data/ui/fonts/system" xmlns:v8="http://v8.1c.ru/8.1/data/core" xmlns:v8ui="http://v8.1c.ru/8.1/data/ui" xmlns:web="http://v8.1c.ru/8.1/data/ui/colors/web" xmlns:win="http://v8.1c.ru/8.1/data/ui/colors/windows" xmlns:xen="http://v8.1c.ru/8.3/xcf/enums" xmlns:xpr="http://v8.1c.ru/8.3/xcf/predef" xmlns:xr="http://v8.1c.ru/8.3/xcf/readable" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" version="2.15">
|
||||||
|
<Language uuid="e3ac9659-5250-4530-9ca6-3f4f4ef6413a">
|
||||||
|
<Properties>
|
||||||
|
<Name>Русский</Name>
|
||||||
|
<Synonym>
|
||||||
|
<v8:item>
|
||||||
|
<v8:lang>ru</v8:lang>
|
||||||
|
<v8:content>Русский</v8:content>
|
||||||
|
</v8:item>
|
||||||
|
</Synonym>
|
||||||
|
<Comment/>
|
||||||
|
<LanguageCode>ru</LanguageCode>
|
||||||
|
</Properties>
|
||||||
|
</Language>
|
||||||
|
</MetaDataObject>
|
208
src/addin1.rs
Normal file
208
src/addin1.rs
Normal file
@ -0,0 +1,208 @@
|
|||||||
|
use crate::ffi::{Addin, Connection, ParamValue, ReturnValue, Tm};
|
||||||
|
use utf16_lit::utf16_null;
|
||||||
|
|
||||||
|
const PROPS: &[&[u16]] = &[
|
||||||
|
&utf16_null!("Test"),
|
||||||
|
&utf16_null!("PropI32"),
|
||||||
|
&utf16_null!("PropF64"),
|
||||||
|
&utf16_null!("PropBool"),
|
||||||
|
&utf16_null!("PropDate"),
|
||||||
|
&utf16_null!("PropStr"),
|
||||||
|
&utf16_null!("PropBlob"),
|
||||||
|
];
|
||||||
|
|
||||||
|
const METHODS: &[&[u16]] = &[&utf16_null!("Method1")];
|
||||||
|
|
||||||
|
pub struct Addin1 {
|
||||||
|
prop_i32: i32,
|
||||||
|
prop_f64: f64,
|
||||||
|
prop_bool: bool,
|
||||||
|
prop_date: Tm,
|
||||||
|
prop_str: String,
|
||||||
|
prop_blob: Vec<u8>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Addin1 {
|
||||||
|
pub fn new() -> Addin1 {
|
||||||
|
Addin1 {
|
||||||
|
prop_i32: 0,
|
||||||
|
prop_f64: 0.0,
|
||||||
|
prop_bool: false,
|
||||||
|
prop_date: Tm::default(),
|
||||||
|
prop_str: String::new(),
|
||||||
|
prop_blob: Vec::new(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Drop for Addin1 {
|
||||||
|
fn drop(&mut self) {}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Addin for Addin1 {
|
||||||
|
fn init(&mut self, _interface: &'static Connection) -> bool {
|
||||||
|
true
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_info(&mut self) -> u16 {
|
||||||
|
1000
|
||||||
|
}
|
||||||
|
|
||||||
|
fn done(&mut self) {}
|
||||||
|
|
||||||
|
fn register_extension_as(&mut self) -> &'static [u16] {
|
||||||
|
&utf16_null!("Class1")
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_n_props(&mut self) -> usize {
|
||||||
|
PROPS.len()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn find_prop(&mut self, name: &[u16]) -> Option<usize> {
|
||||||
|
PROPS.iter().position(|&x| x == name)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_prop_name(&mut self, num: usize, _alias: usize) -> Option<&'static [u16]> {
|
||||||
|
PROPS.get(num).map(|&x| x)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_prop_val(&mut self, num: usize, val: ReturnValue) -> bool {
|
||||||
|
match num {
|
||||||
|
0 => val.set_i32(111226),
|
||||||
|
1 => val.set_i32(self.prop_i32),
|
||||||
|
2 => val.set_f64(self.prop_f64),
|
||||||
|
3 => val.set_bool(self.prop_bool),
|
||||||
|
4 => val.set_date(self.prop_date),
|
||||||
|
5 => {
|
||||||
|
let s: Vec<u16> = self.prop_str.encode_utf16().collect();
|
||||||
|
val.set_str(s.as_slice());
|
||||||
|
}
|
||||||
|
6 => {
|
||||||
|
val.set_blob(self.prop_blob.as_slice());
|
||||||
|
}
|
||||||
|
_ => (),
|
||||||
|
};
|
||||||
|
true
|
||||||
|
}
|
||||||
|
|
||||||
|
fn set_prop_val(&mut self, num: usize, val: &ParamValue) -> bool {
|
||||||
|
match num {
|
||||||
|
1 => match val {
|
||||||
|
ParamValue::I32(x) => {
|
||||||
|
self.prop_i32 = *x;
|
||||||
|
true
|
||||||
|
}
|
||||||
|
_ => false,
|
||||||
|
},
|
||||||
|
2 => match val {
|
||||||
|
ParamValue::F64(x) => {
|
||||||
|
self.prop_f64 = *x;
|
||||||
|
true
|
||||||
|
}
|
||||||
|
_ => false,
|
||||||
|
},
|
||||||
|
3 => match val {
|
||||||
|
ParamValue::Bool(x) => {
|
||||||
|
self.prop_bool = *x;
|
||||||
|
true
|
||||||
|
}
|
||||||
|
_ => false,
|
||||||
|
},
|
||||||
|
4 => match val {
|
||||||
|
ParamValue::Date(x) => {
|
||||||
|
self.prop_date = *x;
|
||||||
|
true
|
||||||
|
}
|
||||||
|
_ => false,
|
||||||
|
},
|
||||||
|
5 => match val {
|
||||||
|
ParamValue::Str(x) => {
|
||||||
|
self.prop_str = String::from_utf16(x).unwrap();
|
||||||
|
true
|
||||||
|
}
|
||||||
|
_ => false,
|
||||||
|
},
|
||||||
|
6 => match val {
|
||||||
|
ParamValue::Blob(x) => {
|
||||||
|
self.prop_blob.clear();
|
||||||
|
self.prop_blob.extend_from_slice(x);
|
||||||
|
true
|
||||||
|
}
|
||||||
|
_ => false,
|
||||||
|
},
|
||||||
|
_ => false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn is_prop_readable(&mut self, _num: usize) -> bool {
|
||||||
|
true
|
||||||
|
}
|
||||||
|
|
||||||
|
fn is_prop_writable(&mut self, num: usize) -> bool {
|
||||||
|
match num {
|
||||||
|
2 => true,
|
||||||
|
3 => true,
|
||||||
|
_ => true,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_n_methods(&mut self) -> usize {
|
||||||
|
METHODS.len()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn find_method(&mut self, name: &[u16]) -> Option<usize> {
|
||||||
|
METHODS.iter().position(|&x| x == name)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_method_name(&mut self, num: usize, _alias: usize) -> Option<&'static [u16]> {
|
||||||
|
METHODS.get(num).map(|&x| x)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_n_params(&mut self, num: usize) -> usize {
|
||||||
|
match num {
|
||||||
|
0 => 3,
|
||||||
|
_ => 0,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_param_def_value(
|
||||||
|
&mut self,
|
||||||
|
_method_num: usize,
|
||||||
|
_param_num: usize,
|
||||||
|
_value: ReturnValue,
|
||||||
|
) -> bool {
|
||||||
|
true
|
||||||
|
}
|
||||||
|
|
||||||
|
fn has_ret_val(&mut self, num: usize) -> bool {
|
||||||
|
match num {
|
||||||
|
0 => true,
|
||||||
|
_ => false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn call_as_proc(&mut self, _num: usize, _params: &[ParamValue]) -> bool {
|
||||||
|
false
|
||||||
|
}
|
||||||
|
|
||||||
|
fn call_as_func(&mut self, num: usize, params: &[ParamValue], ret_value: ReturnValue) -> bool {
|
||||||
|
match num {
|
||||||
|
0 => {
|
||||||
|
let mut buf = Vec::<u16>::new();
|
||||||
|
for p in params {
|
||||||
|
match p {
|
||||||
|
ParamValue::Str(x) => buf.extend_from_slice(x),
|
||||||
|
_ => return false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ret_value.set_str(buf.as_slice());
|
||||||
|
true
|
||||||
|
}
|
||||||
|
_ => false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn set_locale(&mut self, _loc: &[u16]) {}
|
||||||
|
|
||||||
|
fn set_user_interface_language_code(&mut self, _lang: &[u16]) {}
|
||||||
|
}
|
753
src/ffi.rs
Normal file
753
src/ffi.rs
Normal file
@ -0,0 +1,753 @@
|
|||||||
|
use std::{
|
||||||
|
ffi::{c_int, c_long, c_ulong, c_void},
|
||||||
|
mem::size_of,
|
||||||
|
ptr,
|
||||||
|
slice::{from_raw_parts, from_raw_parts_mut},
|
||||||
|
};
|
||||||
|
|
||||||
|
#[repr(C)]
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub enum AttachType {
|
||||||
|
CanAttachNotIsolated = 1,
|
||||||
|
CanAttachIsolated,
|
||||||
|
CanAttachAny,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[repr(C)]
|
||||||
|
#[allow(dead_code)]
|
||||||
|
#[derive(Clone, Copy, Default)]
|
||||||
|
pub struct Tm {
|
||||||
|
pub sec: c_int, // seconds after the minute - [0, 60] including leap second
|
||||||
|
pub min: c_int, // minutes after the hour - [0, 59]
|
||||||
|
pub hour: c_int, // hours since midnight - [0, 23]
|
||||||
|
pub mday: c_int, // day of the month - [1, 31]
|
||||||
|
pub mon: c_int, // months since January - [0, 11]
|
||||||
|
pub year: c_int, // years since 1900
|
||||||
|
pub wday: c_int, // days since Sunday - [0, 6]
|
||||||
|
pub yday: c_int, // days since January 1 - [0, 365]
|
||||||
|
pub isdst: c_int, // daylight savings time flag
|
||||||
|
|
||||||
|
#[cfg(target_family = "unix")]
|
||||||
|
pub gmtoff: std::ffi::c_long, // seconds east of UTC
|
||||||
|
#[cfg(target_family = "unix")]
|
||||||
|
pub zone: std::ffi::c_char, // timezone abbreviation
|
||||||
|
}
|
||||||
|
|
||||||
|
#[allow(dead_code)]
|
||||||
|
pub struct ReturnValue<'a> {
|
||||||
|
mem: &'a MemoryManager,
|
||||||
|
variant: &'a mut TVariant,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[allow(dead_code)]
|
||||||
|
impl<'a> ReturnValue<'a> {
|
||||||
|
pub fn set_empty(self) {
|
||||||
|
self.variant.vt = VariantType::EMPTY;
|
||||||
|
}
|
||||||
|
pub fn set_i32(self, val: i32) {
|
||||||
|
self.variant.vt = VariantType::I4;
|
||||||
|
self.variant.value.i32 = val;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn set_bool(self, val: bool) {
|
||||||
|
self.variant.vt = VariantType::BOOL;
|
||||||
|
self.variant.value.bool = val;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn set_f64(self, val: f64) {
|
||||||
|
self.variant.vt = VariantType::R8;
|
||||||
|
self.variant.value.f64 = val;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn set_date(self, val: Tm) {
|
||||||
|
self.variant.vt = VariantType::TM;
|
||||||
|
self.variant.value.tm = val;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn set_str(self, val: &[u16]) {
|
||||||
|
let Some(data) = self.mem.alloc_memory::<u16>(val.len()) else {
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
|
||||||
|
data.copy_from_slice(val);
|
||||||
|
|
||||||
|
self.variant.vt = VariantType::PWSTR;
|
||||||
|
self.variant.value.data_str.ptr = data.as_ptr() as *mut u16;
|
||||||
|
self.variant.value.data_str.len = data.len() as u32;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn set_blob(self, val: &[u8]) {
|
||||||
|
let Some(data) = self.mem.alloc_memory::<u8>(val.len()) else {
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
|
||||||
|
data.copy_from_slice(val);
|
||||||
|
|
||||||
|
self.variant.vt = VariantType::BLOB;
|
||||||
|
self.variant.value.data_blob.ptr = data.as_ptr() as *mut u8;
|
||||||
|
self.variant.value.data_blob.len = data.len() as u32;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn alloc_str(self, len: usize) -> Option<&'a mut [u16]> {
|
||||||
|
let Some(data) = self.mem.alloc_memory::<u16>(len) else {
|
||||||
|
return None;
|
||||||
|
};
|
||||||
|
|
||||||
|
self.variant.vt = VariantType::PWSTR;
|
||||||
|
self.variant.value.data_str.ptr = data.as_ptr() as *mut u16;
|
||||||
|
self.variant.value.data_str.len = data.len() as u32;
|
||||||
|
|
||||||
|
Some(data)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn alloc_blob(self, len: usize) -> Option<&'a mut [u8]> {
|
||||||
|
let Some(data) = self.mem.alloc_memory::<u8>(len) else {
|
||||||
|
return None;
|
||||||
|
};
|
||||||
|
|
||||||
|
self.variant.vt = VariantType::BLOB;
|
||||||
|
self.variant.value.data_blob.ptr = data.as_ptr() as *mut u8;
|
||||||
|
self.variant.value.data_blob.len = data.len() as u32;
|
||||||
|
|
||||||
|
Some(data)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub enum ParamValue<'a> {
|
||||||
|
Empty,
|
||||||
|
Bool(bool),
|
||||||
|
I32(i32),
|
||||||
|
F64(f64),
|
||||||
|
Date(Tm),
|
||||||
|
Str(&'a [u16]),
|
||||||
|
Blob(&'a [u8]),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> From<&'a TVariant> for ParamValue<'a> {
|
||||||
|
fn from(param: &'a TVariant) -> ParamValue {
|
||||||
|
unsafe {
|
||||||
|
match param.vt {
|
||||||
|
VariantType::EMPTY => Self::Empty,
|
||||||
|
VariantType::BOOL => Self::Bool(param.value.bool),
|
||||||
|
VariantType::I4 => Self::I32(param.value.i32),
|
||||||
|
VariantType::R8 => Self::F64(param.value.f64),
|
||||||
|
VariantType::TM => Self::Date(param.value.tm),
|
||||||
|
VariantType::PWSTR => Self::Str(from_raw_parts(
|
||||||
|
param.value.data_str.ptr,
|
||||||
|
param.value.data_str.len as usize,
|
||||||
|
)),
|
||||||
|
VariantType::BLOB => Self::Blob(from_raw_parts(
|
||||||
|
param.value.data_blob.ptr,
|
||||||
|
param.value.data_blob.len as usize,
|
||||||
|
)),
|
||||||
|
_ => Self::Empty,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[repr(u16)]
|
||||||
|
#[allow(dead_code)]
|
||||||
|
enum VariantType {
|
||||||
|
EMPTY = 0,
|
||||||
|
NULL,
|
||||||
|
I2, //int16_t
|
||||||
|
I4, //int32_t
|
||||||
|
R4, //float
|
||||||
|
R8, //double
|
||||||
|
DATE, //DATE (double)
|
||||||
|
TM, //struct tm
|
||||||
|
PSTR, //struct str string
|
||||||
|
INTERFACE, //struct iface
|
||||||
|
ERROR, //int32_t errCode
|
||||||
|
BOOL, //bool
|
||||||
|
VARIANT, //struct _tVariant *
|
||||||
|
I1, //int8_t
|
||||||
|
UI1, //uint8_t
|
||||||
|
UI2, //uint16_t
|
||||||
|
UI4, //uint32_t
|
||||||
|
I8, //int64_t
|
||||||
|
UI8, //uint64_t
|
||||||
|
INT, //int Depends on architecture
|
||||||
|
UINT, //unsigned int Depends on architecture
|
||||||
|
HRESULT, //long hRes
|
||||||
|
PWSTR, //struct wstr
|
||||||
|
BLOB, //means in struct str binary data contain
|
||||||
|
CLSID, //UUID
|
||||||
|
|
||||||
|
UNDEFINED = 0xFFFF,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[repr(C)]
|
||||||
|
#[allow(dead_code)]
|
||||||
|
#[derive(Clone, Copy)]
|
||||||
|
struct DataStr {
|
||||||
|
pub ptr: *mut u16,
|
||||||
|
pub len: u32,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[repr(C)]
|
||||||
|
#[allow(dead_code)]
|
||||||
|
#[derive(Clone, Copy)]
|
||||||
|
struct DataBlob {
|
||||||
|
pub ptr: *mut u8,
|
||||||
|
pub len: u32,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[repr(C)]
|
||||||
|
union VariantValue {
|
||||||
|
pub bool: bool,
|
||||||
|
pub i32: i32,
|
||||||
|
pub f64: f64,
|
||||||
|
pub tm: Tm,
|
||||||
|
pub data_str: DataStr,
|
||||||
|
pub data_blob: DataBlob,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[repr(C)]
|
||||||
|
#[allow(dead_code)]
|
||||||
|
struct TVariant {
|
||||||
|
value: VariantValue,
|
||||||
|
elements: u32, //Dimension for an one-dimensional array in pvarVal
|
||||||
|
vt: VariantType,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub trait Addin {
|
||||||
|
fn init(&mut self, interface: &'static Connection) -> bool;
|
||||||
|
fn get_info(&mut self) -> u16;
|
||||||
|
fn done(&mut self);
|
||||||
|
fn register_extension_as(&mut self) -> &'static [u16];
|
||||||
|
fn get_n_props(&mut self) -> usize;
|
||||||
|
fn find_prop(&mut self, name: &[u16]) -> Option<usize>;
|
||||||
|
fn get_prop_name(&mut self, num: usize, alias: usize) -> Option<&'static [u16]>;
|
||||||
|
fn get_prop_val(&mut self, num: usize, val: ReturnValue) -> bool;
|
||||||
|
fn set_prop_val(&mut self, num: usize, val: &ParamValue) -> bool;
|
||||||
|
fn is_prop_readable(&mut self, num: usize) -> bool;
|
||||||
|
fn is_prop_writable(&mut self, num: usize) -> bool;
|
||||||
|
fn get_n_methods(&mut self) -> usize;
|
||||||
|
fn find_method(&mut self, name: &[u16]) -> Option<usize>;
|
||||||
|
fn get_method_name(&mut self, num: usize, alias: usize) -> Option<&'static [u16]>;
|
||||||
|
fn get_n_params(&mut self, num: usize) -> usize;
|
||||||
|
fn get_param_def_value(
|
||||||
|
&mut self,
|
||||||
|
method_num: usize,
|
||||||
|
param_num: usize,
|
||||||
|
value: ReturnValue,
|
||||||
|
) -> bool;
|
||||||
|
fn has_ret_val(&mut self, method_num: usize) -> bool;
|
||||||
|
fn call_as_proc(&mut self, method_num: usize, params: &[ParamValue]) -> bool;
|
||||||
|
fn call_as_func(&mut self, method_num: usize, params: &[ParamValue], val: ReturnValue) -> bool;
|
||||||
|
fn set_locale(&mut self, loc: &[u16]);
|
||||||
|
fn set_user_interface_language_code(&mut self, lang: &[u16]);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[repr(C)]
|
||||||
|
//#[allow(dead_code)]
|
||||||
|
struct InitDoneBaseVTable<T: Addin> {
|
||||||
|
dtor: usize,
|
||||||
|
#[cfg(target_family = "unix")]
|
||||||
|
dtor2: usize,
|
||||||
|
init: unsafe extern "system" fn(&mut InitDoneBase<T>, &'static Connection) -> bool,
|
||||||
|
set_mem_manager:
|
||||||
|
unsafe extern "system" fn(&mut InitDoneBase<T>, &'static MemoryManager) -> bool,
|
||||||
|
get_info: unsafe extern "system" fn(&mut InitDoneBase<T>) -> c_long,
|
||||||
|
done: unsafe extern "system" fn(&mut InitDoneBase<T>),
|
||||||
|
}
|
||||||
|
|
||||||
|
unsafe extern "system" fn init<T: Addin>(
|
||||||
|
component: &mut InitDoneBase<T>,
|
||||||
|
interface: &'static Connection,
|
||||||
|
) -> bool {
|
||||||
|
component.addin.init(interface)
|
||||||
|
}
|
||||||
|
|
||||||
|
unsafe extern "system" fn set_mem_manager<T: Addin>(
|
||||||
|
component: &mut InitDoneBase<T>,
|
||||||
|
mem: &'static MemoryManager,
|
||||||
|
) -> bool {
|
||||||
|
component.memory = Some(mem);
|
||||||
|
true
|
||||||
|
}
|
||||||
|
|
||||||
|
unsafe extern "system" fn get_info<T: Addin>(component: &mut InitDoneBase<T>) -> c_long {
|
||||||
|
component.addin.get_info() as c_long
|
||||||
|
}
|
||||||
|
|
||||||
|
unsafe extern "system" fn done<T: Addin>(component: &mut InitDoneBase<T>) {
|
||||||
|
component.addin.done()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[repr(C)]
|
||||||
|
#[allow(dead_code)]
|
||||||
|
struct LanguageExtenderBaseVTable<T: Addin> {
|
||||||
|
dtor: usize,
|
||||||
|
#[cfg(target_family = "unix")]
|
||||||
|
dtor2: usize,
|
||||||
|
register_extension_as:
|
||||||
|
unsafe extern "system" fn(&mut LanguageExtenderBase<T>, *mut *mut u16) -> bool,
|
||||||
|
get_n_props: unsafe extern "system" fn(&mut LanguageExtenderBase<T>) -> c_long,
|
||||||
|
find_prop: unsafe extern "system" fn(&mut LanguageExtenderBase<T>, *const u16) -> c_long,
|
||||||
|
get_prop_name:
|
||||||
|
unsafe extern "system" fn(&mut LanguageExtenderBase<T>, c_long, c_long) -> *const u16,
|
||||||
|
get_prop_val: for<'a> unsafe extern "system" fn(
|
||||||
|
&mut LanguageExtenderBase<T>,
|
||||||
|
c_long,
|
||||||
|
&'a mut TVariant,
|
||||||
|
) -> bool,
|
||||||
|
set_prop_val:
|
||||||
|
unsafe extern "system" fn(&mut LanguageExtenderBase<T>, c_long, &TVariant) -> bool,
|
||||||
|
is_prop_readable: unsafe extern "system" fn(&mut LanguageExtenderBase<T>, c_long) -> bool,
|
||||||
|
is_prop_writable: unsafe extern "system" fn(&mut LanguageExtenderBase<T>, c_long) -> bool,
|
||||||
|
get_n_methods: unsafe extern "system" fn(&mut LanguageExtenderBase<T>) -> c_long,
|
||||||
|
find_method: unsafe extern "system" fn(&mut LanguageExtenderBase<T>, *const u16) -> c_long,
|
||||||
|
get_method_name:
|
||||||
|
unsafe extern "system" fn(&mut LanguageExtenderBase<T>, c_long, c_long) -> *const u16,
|
||||||
|
get_n_params: unsafe extern "system" fn(&mut LanguageExtenderBase<T>, c_long) -> c_long,
|
||||||
|
get_param_def_value: unsafe extern "system" fn(
|
||||||
|
&mut LanguageExtenderBase<T>,
|
||||||
|
c_long,
|
||||||
|
c_long,
|
||||||
|
&mut TVariant,
|
||||||
|
) -> bool,
|
||||||
|
has_ret_val: unsafe extern "system" fn(&mut LanguageExtenderBase<T>, c_long) -> bool,
|
||||||
|
call_as_proc: unsafe extern "system" fn(
|
||||||
|
&mut LanguageExtenderBase<T>,
|
||||||
|
c_long,
|
||||||
|
*const TVariant,
|
||||||
|
c_long,
|
||||||
|
) -> bool,
|
||||||
|
call_as_func: for<'a> unsafe extern "system" fn(
|
||||||
|
&mut LanguageExtenderBase<T>,
|
||||||
|
c_long,
|
||||||
|
&mut TVariant,
|
||||||
|
*const TVariant,
|
||||||
|
c_long,
|
||||||
|
) -> bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
unsafe extern "system" fn register_extension_as<T: Addin>(
|
||||||
|
component: &mut LanguageExtenderBase<T>,
|
||||||
|
name: *mut *mut u16,
|
||||||
|
) -> bool {
|
||||||
|
let Some(allocator) = component.memory else {
|
||||||
|
return false;
|
||||||
|
};
|
||||||
|
|
||||||
|
let extension_name = component.addin.register_extension_as();
|
||||||
|
|
||||||
|
let Some(data) = allocator.alloc_memory::<u16>(extension_name.len()) else {
|
||||||
|
return false;
|
||||||
|
};
|
||||||
|
data.copy_from_slice(extension_name);
|
||||||
|
unsafe { *name = data.as_mut_ptr() };
|
||||||
|
|
||||||
|
true
|
||||||
|
}
|
||||||
|
|
||||||
|
unsafe extern "system" fn get_n_props<T: Addin>(component: &mut LanguageExtenderBase<T>) -> c_long {
|
||||||
|
component.addin.get_n_props() as c_long
|
||||||
|
}
|
||||||
|
|
||||||
|
unsafe extern "system" fn find_prop<T: Addin>(
|
||||||
|
component: &mut LanguageExtenderBase<T>,
|
||||||
|
name: *const u16,
|
||||||
|
) -> c_long {
|
||||||
|
let len = strlen(name);
|
||||||
|
let name = from_raw_parts(name, len);
|
||||||
|
match component.addin.find_prop(name) {
|
||||||
|
Some(i) => i as c_long,
|
||||||
|
None => -1,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
unsafe extern "system" fn get_prop_name<T: Addin>(
|
||||||
|
component: &mut LanguageExtenderBase<T>,
|
||||||
|
num: c_long,
|
||||||
|
alias: c_long,
|
||||||
|
) -> *const u16 {
|
||||||
|
let Some(allocator) = component.memory else {
|
||||||
|
return ptr::null();
|
||||||
|
};
|
||||||
|
let Some(prop_name) = component.addin.get_prop_name(num as usize, alias as usize) else {
|
||||||
|
return ptr::null();
|
||||||
|
};
|
||||||
|
let Some(name) = allocator.alloc_memory::<u16>(prop_name.len()) else {
|
||||||
|
return ptr::null();
|
||||||
|
};
|
||||||
|
|
||||||
|
name.copy_from_slice(prop_name);
|
||||||
|
name.as_ptr()
|
||||||
|
}
|
||||||
|
|
||||||
|
unsafe extern "system" fn get_prop_val<'a, T: Addin>(
|
||||||
|
component: &mut LanguageExtenderBase<T>,
|
||||||
|
num: c_long,
|
||||||
|
val: &'a mut TVariant,
|
||||||
|
) -> bool {
|
||||||
|
let Some(mem) = component.memory else {
|
||||||
|
return false;
|
||||||
|
};
|
||||||
|
|
||||||
|
let return_value = ReturnValue { mem, variant: val };
|
||||||
|
component.addin.get_prop_val(num as usize, return_value)
|
||||||
|
}
|
||||||
|
|
||||||
|
unsafe extern "system" fn set_prop_val<T: Addin>(
|
||||||
|
component: &mut LanguageExtenderBase<T>,
|
||||||
|
num: c_long,
|
||||||
|
val: &TVariant,
|
||||||
|
) -> bool {
|
||||||
|
let param = ParamValue::from(val);
|
||||||
|
component.addin.set_prop_val(num as usize, ¶m)
|
||||||
|
}
|
||||||
|
|
||||||
|
unsafe extern "system" fn is_prop_readable<T: Addin>(
|
||||||
|
component: &mut LanguageExtenderBase<T>,
|
||||||
|
num: c_long,
|
||||||
|
) -> bool {
|
||||||
|
component.addin.is_prop_readable(num as usize)
|
||||||
|
}
|
||||||
|
|
||||||
|
unsafe extern "system" fn is_prop_writable<T: Addin>(
|
||||||
|
component: &mut LanguageExtenderBase<T>,
|
||||||
|
num: c_long,
|
||||||
|
) -> bool {
|
||||||
|
component.addin.is_prop_writable(num as usize)
|
||||||
|
}
|
||||||
|
|
||||||
|
unsafe extern "system" fn get_n_methods<T: Addin>(
|
||||||
|
component: &mut LanguageExtenderBase<T>,
|
||||||
|
) -> c_long {
|
||||||
|
component.addin.get_n_methods() as c_long
|
||||||
|
}
|
||||||
|
|
||||||
|
unsafe extern "system" fn find_method<T: Addin>(
|
||||||
|
component: &mut LanguageExtenderBase<T>,
|
||||||
|
name: *const u16,
|
||||||
|
) -> c_long {
|
||||||
|
let len = strlen(name);
|
||||||
|
let name = from_raw_parts(name, len);
|
||||||
|
match component.addin.find_method(name) {
|
||||||
|
Some(i) => i as c_long,
|
||||||
|
None => -1,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
unsafe extern "system" fn get_method_name<T: Addin>(
|
||||||
|
component: &mut LanguageExtenderBase<T>,
|
||||||
|
num: c_long,
|
||||||
|
alias: c_long,
|
||||||
|
) -> *const u16 {
|
||||||
|
let Some(allocator) = component.memory else {
|
||||||
|
return ptr::null();
|
||||||
|
};
|
||||||
|
let Some(method_name) = component.addin.get_method_name(num as usize, alias as usize) else {
|
||||||
|
return ptr::null();
|
||||||
|
};
|
||||||
|
let Some(name) = allocator.alloc_memory::<u16>(method_name.len()) else {
|
||||||
|
return ptr::null();
|
||||||
|
};
|
||||||
|
|
||||||
|
name.copy_from_slice(method_name);
|
||||||
|
name.as_ptr()
|
||||||
|
}
|
||||||
|
|
||||||
|
unsafe extern "system" fn get_n_params<T: Addin>(
|
||||||
|
component: &mut LanguageExtenderBase<T>,
|
||||||
|
num: c_long,
|
||||||
|
) -> c_long {
|
||||||
|
component.addin.get_n_params(num as usize) as c_long
|
||||||
|
}
|
||||||
|
|
||||||
|
unsafe extern "system" fn get_param_def_value<T: Addin>(
|
||||||
|
component: &mut LanguageExtenderBase<T>,
|
||||||
|
method_num: c_long,
|
||||||
|
param_num: c_long,
|
||||||
|
val: &mut TVariant,
|
||||||
|
) -> bool {
|
||||||
|
let Some(mem) = component.memory else {
|
||||||
|
return false;
|
||||||
|
};
|
||||||
|
|
||||||
|
let return_value = ReturnValue { mem, variant: val };
|
||||||
|
|
||||||
|
component
|
||||||
|
.addin
|
||||||
|
.get_param_def_value(method_num as usize, param_num as usize, return_value)
|
||||||
|
}
|
||||||
|
|
||||||
|
unsafe extern "system" fn has_ret_val<T: Addin>(
|
||||||
|
component: &mut LanguageExtenderBase<T>,
|
||||||
|
method_num: c_long,
|
||||||
|
) -> bool {
|
||||||
|
component.addin.has_ret_val(method_num as usize)
|
||||||
|
}
|
||||||
|
|
||||||
|
unsafe extern "system" fn call_as_proc<T: Addin>(
|
||||||
|
component: &mut LanguageExtenderBase<T>,
|
||||||
|
method_num: c_long,
|
||||||
|
params: *const TVariant,
|
||||||
|
size_array: c_long,
|
||||||
|
) -> bool {
|
||||||
|
let param_values = from_raw_parts(params, size_array as usize)
|
||||||
|
.iter()
|
||||||
|
.map(|x| ParamValue::from(x))
|
||||||
|
.collect::<Vec<ParamValue>>();
|
||||||
|
|
||||||
|
component
|
||||||
|
.addin
|
||||||
|
.call_as_proc(method_num as usize, param_values.as_slice())
|
||||||
|
}
|
||||||
|
|
||||||
|
unsafe extern "system" fn call_as_func<'a, T: Addin>(
|
||||||
|
component: &mut LanguageExtenderBase<T>,
|
||||||
|
method_num: c_long,
|
||||||
|
ret_value: &'a mut TVariant,
|
||||||
|
params: *const TVariant,
|
||||||
|
size_array: c_long,
|
||||||
|
) -> bool {
|
||||||
|
let Some(mem) = component.memory else {
|
||||||
|
return false;
|
||||||
|
};
|
||||||
|
|
||||||
|
let return_value = ReturnValue {
|
||||||
|
mem,
|
||||||
|
variant: ret_value,
|
||||||
|
};
|
||||||
|
|
||||||
|
let param_values = from_raw_parts(params, size_array as usize)
|
||||||
|
.iter()
|
||||||
|
.map(|x| ParamValue::from(x))
|
||||||
|
.collect::<Vec<ParamValue>>();
|
||||||
|
|
||||||
|
component
|
||||||
|
.addin
|
||||||
|
.call_as_func(method_num as usize, param_values.as_slice(), return_value)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[repr(C)]
|
||||||
|
#[allow(dead_code)]
|
||||||
|
struct LocaleBaseVTable<T: Addin> {
|
||||||
|
dtor: usize,
|
||||||
|
#[cfg(target_family = "unix")]
|
||||||
|
dtor2: usize,
|
||||||
|
set_locale: unsafe extern "system" fn(&mut LocaleBase<T>, *const u16),
|
||||||
|
}
|
||||||
|
|
||||||
|
unsafe extern "system" fn set_locale<T: Addin>(component: &mut LocaleBase<T>, loc: *const u16) {
|
||||||
|
let len = strlen(loc);
|
||||||
|
let loc = from_raw_parts(loc, len);
|
||||||
|
component.addin.set_locale(loc)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[repr(C)]
|
||||||
|
#[allow(dead_code)]
|
||||||
|
struct UserLanguageBaseVTable<T: Addin> {
|
||||||
|
dtor: usize,
|
||||||
|
#[cfg(target_family = "unix")]
|
||||||
|
dtor2: usize,
|
||||||
|
set_user_interface_language_code:
|
||||||
|
unsafe extern "system" fn(&mut UserLanguageBase<T>, *const u16),
|
||||||
|
}
|
||||||
|
|
||||||
|
unsafe extern "system" fn set_user_interface_language_code<T: Addin>(
|
||||||
|
component: &mut UserLanguageBase<T>,
|
||||||
|
lang: *const u16,
|
||||||
|
) {
|
||||||
|
let len = strlen(lang);
|
||||||
|
let lang = from_raw_parts(lang, len);
|
||||||
|
component.addin.set_user_interface_language_code(lang)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[repr(C)]
|
||||||
|
#[allow(dead_code)]
|
||||||
|
struct ComponentBase<T: Addin> {
|
||||||
|
vptr1: Box<InitDoneBaseVTable<T>>,
|
||||||
|
vptr2: Box<LanguageExtenderBaseVTable<T>>,
|
||||||
|
vptr3: Box<LocaleBaseVTable<T>>,
|
||||||
|
vptr4: Box<UserLanguageBaseVTable<T>>,
|
||||||
|
destroy: unsafe extern "system" fn(*mut *mut ComponentBase<T>),
|
||||||
|
memory: Option<&'static MemoryManager>,
|
||||||
|
addin: T,
|
||||||
|
}
|
||||||
|
|
||||||
|
unsafe extern "system" fn destroy<T: Addin>(component: *mut *mut ComponentBase<T>) {
|
||||||
|
let component = unsafe { Box::from_raw(*component) };
|
||||||
|
drop(component);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[repr(C)]
|
||||||
|
#[allow(dead_code)]
|
||||||
|
struct InitDoneBase<T: Addin> {
|
||||||
|
_vptr1: Box<InitDoneBaseVTable<T>>,
|
||||||
|
_vptr2: Box<LanguageExtenderBaseVTable<T>>,
|
||||||
|
_vptr3: Box<LocaleBaseVTable<T>>,
|
||||||
|
_vptr4: Box<UserLanguageBaseVTable<T>>,
|
||||||
|
destroy: unsafe extern "system" fn(*mut *mut ComponentBase<T>),
|
||||||
|
memory: Option<&'static MemoryManager>,
|
||||||
|
addin: T,
|
||||||
|
}
|
||||||
|
|
||||||
|
// type InitDoneBase<T> = ComponentBase<T>;
|
||||||
|
|
||||||
|
#[repr(C)]
|
||||||
|
#[allow(dead_code)]
|
||||||
|
struct LanguageExtenderBase<T: Addin> {
|
||||||
|
_vptr2: Box<LanguageExtenderBaseVTable<T>>,
|
||||||
|
_vptr3: Box<LocaleBaseVTable<T>>,
|
||||||
|
_vptr4: Box<UserLanguageBaseVTable<T>>,
|
||||||
|
destroy: unsafe extern "system" fn(*mut *mut ComponentBase<T>),
|
||||||
|
memory: Option<&'static MemoryManager>,
|
||||||
|
addin: T,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[repr(C)]
|
||||||
|
#[allow(dead_code)]
|
||||||
|
struct LocaleBase<T: Addin> {
|
||||||
|
_vptr3: Box<LocaleBaseVTable<T>>,
|
||||||
|
_vptr4: Box<UserLanguageBaseVTable<T>>,
|
||||||
|
destroy: unsafe extern "system" fn(*mut *mut ComponentBase<T>),
|
||||||
|
memory: Option<&'static MemoryManager>,
|
||||||
|
addin: T,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[repr(C)]
|
||||||
|
#[allow(dead_code)]
|
||||||
|
struct UserLanguageBase<T: Addin> {
|
||||||
|
_vptr4: Box<UserLanguageBaseVTable<T>>,
|
||||||
|
destroy: unsafe extern "system" fn(*mut *mut ComponentBase<T>),
|
||||||
|
memory: Option<&'static MemoryManager>,
|
||||||
|
addin: T,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn create_component<T: Addin>(addin: T) -> *mut c_void {
|
||||||
|
let vptr1 = Box::new(InitDoneBaseVTable {
|
||||||
|
dtor: 0,
|
||||||
|
#[cfg(target_family = "unix")]
|
||||||
|
dtor2: 0,
|
||||||
|
init,
|
||||||
|
set_mem_manager,
|
||||||
|
get_info,
|
||||||
|
done,
|
||||||
|
});
|
||||||
|
|
||||||
|
let vptr2 = Box::new(LanguageExtenderBaseVTable {
|
||||||
|
dtor: 0,
|
||||||
|
#[cfg(target_family = "unix")]
|
||||||
|
dtor2: 0,
|
||||||
|
register_extension_as,
|
||||||
|
get_n_props,
|
||||||
|
find_prop,
|
||||||
|
get_prop_name,
|
||||||
|
get_prop_val,
|
||||||
|
set_prop_val,
|
||||||
|
is_prop_readable,
|
||||||
|
is_prop_writable,
|
||||||
|
get_n_methods,
|
||||||
|
find_method,
|
||||||
|
get_method_name,
|
||||||
|
get_n_params,
|
||||||
|
get_param_def_value,
|
||||||
|
has_ret_val,
|
||||||
|
call_as_proc,
|
||||||
|
call_as_func,
|
||||||
|
});
|
||||||
|
|
||||||
|
let vptr3 = Box::new(LocaleBaseVTable {
|
||||||
|
dtor: 0,
|
||||||
|
#[cfg(target_family = "unix")]
|
||||||
|
dtor2: 0,
|
||||||
|
set_locale,
|
||||||
|
});
|
||||||
|
|
||||||
|
let vptr4 = Box::new(UserLanguageBaseVTable {
|
||||||
|
dtor: 0,
|
||||||
|
#[cfg(target_family = "unix")]
|
||||||
|
dtor2: 0,
|
||||||
|
set_user_interface_language_code,
|
||||||
|
});
|
||||||
|
|
||||||
|
let c = Box::new(ComponentBase {
|
||||||
|
vptr1,
|
||||||
|
vptr2,
|
||||||
|
vptr3,
|
||||||
|
vptr4,
|
||||||
|
destroy: destroy::<T>,
|
||||||
|
memory: None,
|
||||||
|
addin,
|
||||||
|
});
|
||||||
|
|
||||||
|
let p = Box::leak(c);
|
||||||
|
p as *mut ComponentBase<T> as *mut c_void
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn destroy_component(component: *mut *mut c_void) {
|
||||||
|
#[repr(C)]
|
||||||
|
#[allow(dead_code)]
|
||||||
|
struct ComponentWrapper {
|
||||||
|
vptr1: Box<c_void>,
|
||||||
|
vptr2: Box<c_void>,
|
||||||
|
vptr3: Box<c_void>,
|
||||||
|
vptr4: Box<c_void>,
|
||||||
|
destroy: unsafe extern "system" fn(*mut *mut c_void),
|
||||||
|
}
|
||||||
|
|
||||||
|
unsafe {
|
||||||
|
let wrapper = *component as *mut ComponentWrapper;
|
||||||
|
let wrapper = &mut *wrapper;
|
||||||
|
(wrapper.destroy)(component);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[repr(C)]
|
||||||
|
#[allow(dead_code)]
|
||||||
|
struct MemoryManagerVTable {
|
||||||
|
dtor: usize,
|
||||||
|
#[cfg(target_family = "unix")]
|
||||||
|
dtor2: usize,
|
||||||
|
alloc_memory: unsafe extern "system" fn(&MemoryManager, *mut *mut c_void, c_ulong) -> bool,
|
||||||
|
free_memory: unsafe extern "system" fn(&MemoryManager, *mut *mut c_void),
|
||||||
|
}
|
||||||
|
|
||||||
|
#[repr(C)]
|
||||||
|
#[allow(dead_code)]
|
||||||
|
struct MemoryManager {
|
||||||
|
vptr1: &'static MemoryManagerVTable,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl MemoryManager {
|
||||||
|
pub fn alloc_memory<'a, T>(&self, size: usize) -> Option<&'a mut [T]> {
|
||||||
|
let mut data = ptr::null_mut::<c_void>();
|
||||||
|
unsafe {
|
||||||
|
if (self.vptr1.alloc_memory)(self, &mut data, (size * size_of::<T>()) as c_ulong) {
|
||||||
|
let d = from_raw_parts_mut(data as *mut T, size);
|
||||||
|
Some(d)
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[repr(C)]
|
||||||
|
#[allow(dead_code)]
|
||||||
|
struct ConnectionVTable {
|
||||||
|
dtor: usize,
|
||||||
|
#[cfg(target_family = "unix")]
|
||||||
|
dtor2: usize,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[repr(C)]
|
||||||
|
#[allow(dead_code)]
|
||||||
|
pub struct Connection {
|
||||||
|
vptr1: &'static ConnectionVTable,
|
||||||
|
}
|
||||||
|
|
||||||
|
fn strlen(s: *const u16) -> usize {
|
||||||
|
let mut i = 0;
|
||||||
|
while unsafe { *s.add(i) } != 0 {
|
||||||
|
i += 1;
|
||||||
|
}
|
||||||
|
i += 1;
|
||||||
|
i
|
||||||
|
}
|
53
src/lib.rs
Normal file
53
src/lib.rs
Normal file
@ -0,0 +1,53 @@
|
|||||||
|
mod addin1;
|
||||||
|
mod ffi;
|
||||||
|
|
||||||
|
use std::{
|
||||||
|
ffi::{c_long, c_void, c_int},
|
||||||
|
sync::atomic::{AtomicI32, Ordering},
|
||||||
|
};
|
||||||
|
|
||||||
|
use addin1::Addin1;
|
||||||
|
use ffi::{destroy_component, AttachType};
|
||||||
|
use utf16_lit::utf16_null;
|
||||||
|
|
||||||
|
use crate::ffi::create_component;
|
||||||
|
|
||||||
|
pub static mut PLATFORM_CAPABILITIES: AtomicI32 = AtomicI32::new(-1);
|
||||||
|
|
||||||
|
#[allow(non_snake_case)]
|
||||||
|
#[no_mangle]
|
||||||
|
pub extern "C" fn GetClassObject(_name: *const u16, component: *mut *mut c_void) -> c_long {
|
||||||
|
let addin = Addin1::new();
|
||||||
|
unsafe {
|
||||||
|
*component = create_component(addin);
|
||||||
|
}
|
||||||
|
1
|
||||||
|
}
|
||||||
|
|
||||||
|
#[allow(non_snake_case)]
|
||||||
|
#[no_mangle]
|
||||||
|
pub extern "C" fn DestroyObject(component: *mut *mut c_void) -> c_long {
|
||||||
|
destroy_component(component);
|
||||||
|
0
|
||||||
|
}
|
||||||
|
|
||||||
|
#[allow(non_snake_case)]
|
||||||
|
#[no_mangle]
|
||||||
|
pub extern "C" fn GetClassNames() -> *const u16 {
|
||||||
|
utf16_null!("Class1").as_ptr()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[allow(non_snake_case)]
|
||||||
|
#[no_mangle]
|
||||||
|
pub extern "C" fn SetPlatformCapabilities(capabilities: c_int) -> c_long {
|
||||||
|
unsafe {
|
||||||
|
PLATFORM_CAPABILITIES.store(capabilities, Ordering::Relaxed);
|
||||||
|
}
|
||||||
|
3
|
||||||
|
}
|
||||||
|
|
||||||
|
#[allow(non_snake_case)]
|
||||||
|
#[no_mangle]
|
||||||
|
pub extern "C" fn GetAttachType() -> AttachType {
|
||||||
|
AttachType::CanAttachAny
|
||||||
|
}
|
Reference in New Issue
Block a user