mirror of
https://github.com/medigor/example-native-api-rs.git
synced 2024-11-21 17:56:37 +02:00
init
This commit is contained in:
parent
0d60572ee6
commit
b55e50ccee
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
|
||||
}
|
Loading…
Reference in New Issue
Block a user