├── .codecov.yml ├── .editorconfig ├── .gitattributes ├── .gitignore ├── .nvimrc ├── .vscode ├── c_cpp_properties.json ├── launch.json ├── settings.json └── tasks.json ├── CMakeLists.txt ├── CONTRIBUTING.md ├── Cargo.toml ├── LICENSE ├── README.md ├── STYLE.md ├── azure-pipelines.yml ├── circle.yml ├── intercom-attributes ├── .gitignore ├── Cargo.toml ├── src │ └── lib.rs └── tests │ ├── data │ ├── .gitattributes │ ├── macro │ │ ├── com_impl.rs │ │ ├── com_impl.rs.stdout │ │ ├── com_interface.rs.skip │ │ ├── com_interface.rs.stdout │ │ ├── com_library-hooks.rs │ │ ├── com_library-hooks.rs.stdout │ │ ├── com_library.rs │ │ ├── com_library.rs.stdout │ │ ├── private_item.rs │ │ └── private_item.rs.stdout │ ├── run │ │ ├── basic-logging.rs │ │ ├── basic-logging.rs.stdout │ │ ├── confirm-run-tests.rs │ │ ├── confirm-run-tests.rs.stderr │ │ └── confirm-run-tests.rs.stdout │ └── ui │ │ ├── com_library-struct-interface.rs │ │ ├── com_library-struct-interface.rs.stderr │ │ ├── comstruct-api.rs │ │ ├── interface-basic.rs │ │ ├── interface-no-imports.rs │ │ ├── multiple-mods.rs │ │ ├── span-comclass-no-com-interface.rs │ │ ├── span-comclass-no-com-interface.rs.stderr │ │ ├── span-comclass-unknown-interface.rs │ │ ├── span-comclass-unknown-interface.rs.stderr │ │ ├── span-externtype-error.rs │ │ ├── span-externtype-error.rs.stderr │ │ ├── span-no-self-error.rs │ │ └── span-no-self-error.rs.stderr │ └── lib.rs ├── intercom-cli ├── Cargo.toml └── src │ ├── embed.rs │ ├── embed │ └── setup_configuration.rs │ ├── generators │ ├── cpp.rs │ ├── cpp_header.hbs │ ├── cpp_source.hbs │ ├── idl.hbs │ ├── idl.rs │ └── mod.rs │ ├── main.rs │ └── typelib.rs ├── intercom-common ├── Cargo.toml └── src │ ├── ast_converters.rs │ ├── attributes │ ├── com_class.rs │ ├── com_interface.rs │ ├── com_library.rs │ ├── common.rs │ ├── mod.rs │ └── type_info.rs │ ├── error.rs │ ├── guid.rs │ ├── idents.rs │ ├── lib.rs │ ├── methodinfo.rs │ ├── model │ ├── comclass.rs │ ├── cominterface.rs │ ├── comlibrary.rs │ ├── macros.rs │ └── mod.rs │ ├── prelude.rs │ ├── returnhandlers.rs │ ├── tyhandlers.rs │ └── utils.rs ├── intercom-cpp ├── intercom.hpp └── src │ ├── activator.hpp │ ├── callingconvention.hpp │ ├── classfactory.hpp │ ├── comdef.hpp │ ├── cominterop.hpp │ ├── conversions.hpp │ ├── datatypes.hpp │ ├── detail │ ├── bstr_buffer.hpp │ ├── char_buffer.hpp │ ├── declarations.hpp │ ├── dlwrapper.hpp │ ├── get_class_factory.hpp │ ├── hresult.hpp │ ├── hresult_errors.hpp │ ├── iclassfactory.hpp │ ├── msvc │ │ └── get_class_factory.hpp │ ├── posix │ │ ├── get_class_factory.hpp │ │ ├── iclassfactory.hpp │ │ └── library_index.hpp │ └── utility.hpp │ ├── error_codes.hpp │ ├── functions.hpp │ ├── guiddef.hpp │ ├── memory.hpp │ ├── msdef.hpp │ ├── msvc │ ├── conversions.hpp │ ├── datatypes.hpp │ ├── dlwrapper.hpp │ ├── guiddef.hpp │ ├── memory.hpp │ ├── utility.hpp │ └── variant.hpp │ ├── no_such_interface.hpp │ ├── posix │ ├── conversions.hpp │ ├── converter.hpp │ ├── datatypes.hpp │ ├── detail │ │ └── memory.hpp │ ├── dlwrapper.hpp │ ├── error_codes.hpp │ ├── guiddef.hpp │ ├── idispatch.hpp │ ├── isupporterrorinfo.hpp │ ├── iunknown.hpp │ ├── memory.hpp │ └── variant.hpp │ ├── raw_interface.hpp │ ├── runtime_error.hpp │ └── variant.hpp ├── intercom-fmt ├── Cargo.toml └── src │ └── main.rs ├── intercom ├── .gitignore ├── Cargo.toml ├── benches │ └── logging.rs └── src │ ├── alloc.rs │ ├── attributes.rs │ ├── classfactory.rs │ ├── combox.rs │ ├── comitf.rs │ ├── comrc.rs │ ├── error.rs │ ├── guid.rs │ ├── interfaces.rs │ ├── lib.rs │ ├── logging.rs │ ├── prelude.rs │ ├── registry.rs │ ├── runtime.rs │ ├── strings.rs │ ├── type_system.rs │ ├── typelib │ ├── from_impls.rs │ └── mod.rs │ └── variant.rs ├── rustfmt.toml ├── samples └── thumbnail_provider │ ├── Cargo.toml │ ├── README.md │ ├── association.reg │ └── src │ └── lib.rs ├── scripts ├── ci.bat ├── clang.sh ├── kcov.sh ├── test.ps1 ├── test.sh ├── vswhere license.txt └── vswhere.exe └── test ├── CMakeLists.txt ├── Cargo.toml ├── cpp-dl ├── CMakeLists.txt ├── libraries.hpp ├── main.cpp └── shared_functions.cpp ├── cpp-raw ├── .gitignore ├── CMakeLists.txt ├── alloc.cpp ├── error_info.cpp ├── interface_params.cpp ├── iunknown.cpp ├── main.cpp ├── msvc │ ├── .gitignore │ ├── cpp.sln │ ├── cpp.vcxproj │ └── os.cpp ├── nullable_parameters.cpp ├── only_interface.cpp ├── output_memory.cpp ├── primitive_tests.cpp ├── result.cpp ├── return_interfaces.cpp ├── stateful.cpp ├── strings.cpp ├── testlib.hpp ├── type_system_callbacks.cpp └── variant.cpp ├── cpp-utility ├── .gitignore ├── CMakeLists.txt ├── dummy_interface.cpp ├── dummy_interface.hpp ├── gcc │ └── test_lib.hpp ├── msvc │ └── os.cpp ├── os.hpp └── posix │ └── os.cpp ├── cpp-wrapper ├── CMakeLists.txt ├── exceptions.cpp ├── interface_wrappers.cpp ├── main.cpp ├── strings.cpp └── testlib.hpp ├── cs ├── .gitignore ├── ErrorInfoSupport.cs ├── InterfaceValueOperations.cs ├── PrimitiveOperations.cs ├── PrimitiveTests.cs ├── Properties │ └── AssemblyInfo.cs ├── ResultOperations.cs ├── SetUpFixture.cs ├── StatefulOperations.cs ├── VariantTests.cs ├── cs.csproj └── cs.sln ├── dependencies ├── .gitattributes └── catch.hpp ├── multilib ├── CMakeLists.txt ├── Cargo.toml └── src │ └── lib.rs ├── perf ├── Cargo.toml └── src │ └── main.rs ├── runpath ├── CMakeLists.txt ├── init.c └── init.h ├── rust-cmake └── CMakeLists.txt └── testlib ├── .gitignore ├── CMakeLists.txt ├── Cargo.toml └── src ├── alloc.rs ├── error_info.rs ├── interface_params.rs ├── lib.rs ├── nullable_parameters.rs ├── output_memory.rs ├── primitive.rs ├── result.rs ├── return_interfaces.rs ├── setup_configuration.rs ├── stateful.rs ├── strings.rs ├── type_system_callbacks.rs ├── unicode.rs └── variant.rs /.codecov.yml: -------------------------------------------------------------------------------- 1 | codecov: 2 | branch: master 3 | coverage: 4 | status: 5 | project: 6 | default: 7 | enabled: no 8 | patch: 9 | default: 10 | enabled: no 11 | changes: 12 | default: 13 | enabled: no 14 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | 2 | root = true 3 | 4 | # Defaults 5 | [*] 6 | insert_final_newline = true 7 | indent_style = space 8 | indent_size = 4 9 | charset = utf-8 10 | trim_trailing_whitespace = true 11 | 12 | # End of line should be LF in the repository - but we'll use gitattributes 13 | # for this instead of editorconfig. This allows Windows editors to use CRLF 14 | # instead. 15 | 16 | # For XML files we use indent size of 2 as these files often have deeper 17 | # nesting within the elements. 18 | # 19 | # ... Fine, the real reason is because that's the default for Visual Studio 20 | # project files - but the above excuse is good as well. 21 | [*.{csproj,config,manifest,vcxproj}] 22 | indent_size = 2 23 | 24 | # Visual Studio solutions are a bit special. 25 | [*.sln] 26 | charset = utf-8-bom 27 | insert_final_newline = false 28 | indent_style = tab 29 | indent_size = 4 30 | 31 | # In markdown (or some flavors of it anyway) trailing whitespace is 32 | # significant. 33 | [*.md] 34 | trim_trailing_whitespace = false 35 | 36 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | test/dependencies/* linguist-vendored 2 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | **/*.rs.bk 2 | Cargo.lock 3 | bin 4 | obj 5 | target 6 | .vs 7 | *.vcxproj.user 8 | *.csproj.user 9 | 10 | # CMake artifacts 11 | build 12 | CMakeFiles 13 | Makefile 14 | generated 15 | 16 | # Dolphin metadata 17 | .directory 18 | 19 | # CMakeTools plugin, may contain full paths 20 | .vscode/.cmaketools.json 21 | 22 | # MSVC artifacts 23 | Debug 24 | -------------------------------------------------------------------------------- /.nvimrc: -------------------------------------------------------------------------------- 1 | let g:rustfmt_autosave = 1 2 | -------------------------------------------------------------------------------- /.vscode/c_cpp_properties.json: -------------------------------------------------------------------------------- 1 | { 2 | "configurations": [ 3 | { 4 | "name": "Mac", 5 | "includePath": [ 6 | "/usr/include", 7 | "/usr/local/include", 8 | "${workspaceFolder}" 9 | ], 10 | "defines": [], 11 | "intelliSenseMode": "clang-x64", 12 | "browse": { 13 | "path": [ 14 | "/usr/include", 15 | "/usr/local/include", 16 | "${workspaceFolder}" 17 | ], 18 | "limitSymbolsToIncludedHeaders": true, 19 | "databaseFilename": "" 20 | }, 21 | "macFrameworkPath": [ 22 | "/System/Library/Frameworks", 23 | "/Library/Frameworks" 24 | ], 25 | "cStandard": "c11", 26 | "cppStandard": "c++14" 27 | }, 28 | { 29 | "name": "Linux", 30 | "includePath": [ 31 | "/usr/include", 32 | "/usr/local/include", 33 | "${workspaceFolder}", 34 | "${workspaceFolder}/intercom-cpp", 35 | "/usr/include/c++/v1" 36 | ], 37 | "defines": [], 38 | "intelliSenseMode": "clang-x64", 39 | "browse": { 40 | "path": [ 41 | "/usr/include", 42 | "/usr/local/include", 43 | "${workspaceFolder}" 44 | ], 45 | "limitSymbolsToIncludedHeaders": true, 46 | "databaseFilename": "" 47 | }, 48 | "compilerPath": "/usr/bin/gcc", 49 | "cStandard": "c11", 50 | "cppStandard": "c++14" 51 | }, 52 | { 53 | "name": "Win32", 54 | "includePath": [ 55 | "${workspaceFolder}", 56 | "C:/Program Files (x86)/Microsoft Visual Studio/2017/BuildTools/VC/Tools/MSVC/14.12.25827/include/*", 57 | "C:/Program Files (x86)/Microsoft Visual Studio/2017/BuildTools/VC/Tools/MSVC/14.12.25827/atlmfc/include/*", 58 | "C:/Program Files (x86)/Windows Kits/10/Include/10.0.16299.0/um", 59 | "C:/Program Files (x86)/Windows Kits/10/Include/10.0.16299.0/ucrt", 60 | "C:/Program Files (x86)/Windows Kits/10/Include/10.0.16299.0/shared", 61 | "C:/Program Files (x86)/Windows Kits/10/Include/10.0.16299.0/winrt", 62 | "${workspaceFolder}/intercom-cpp" 63 | ], 64 | "defines": [ 65 | "_DEBUG", 66 | "UNICODE", 67 | "_UNICODE" 68 | ], 69 | "intelliSenseMode": "msvc-x64", 70 | "browse": { 71 | "path": [ 72 | "${workspaceFolder}", 73 | "C:/Program Files (x86)/Microsoft Visual Studio/2017/BuildTools/VC/Tools/MSVC/14.12.25827/include/*", 74 | "C:/Program Files (x86)/Microsoft Visual Studio/2017/BuildTools/VC/Tools/MSVC/14.12.25827/atlmfc/include/*", 75 | "C:/Program Files (x86)/Windows Kits/10/Include/10.0.16299.0/um", 76 | "C:/Program Files (x86)/Windows Kits/10/Include/10.0.16299.0/ucrt", 77 | "C:/Program Files (x86)/Windows Kits/10/Include/10.0.16299.0/shared", 78 | "C:/Program Files (x86)/Windows Kits/10/Include/10.0.16299.0/winrt", 79 | "${workspaceFolder}/intercom-cpp" 80 | ], 81 | "limitSymbolsToIncludedHeaders": true, 82 | "databaseFilename": "" 83 | }, 84 | "cStandard": "c11", 85 | "cppStandard": "c++14" 86 | } 87 | ], 88 | "version": 4 89 | } -------------------------------------------------------------------------------- /.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | // Use IntelliSense to learn about possible attributes. 3 | // Hover to view descriptions of existing attributes. 4 | // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 5 | "version": "0.2.0", 6 | "configurations": [ 7 | { 8 | "type": "lldb", 9 | "request": "launch", 10 | "preLaunchTask": "normalizeTestExecutables", 11 | "name": "Debug Intercom", 12 | "program": "${workspaceRoot}/target/debug/intercom", 13 | "args": [ "test" ], 14 | "cwd": "${workspaceRoot}", 15 | "sourceLanguages": [ 16 | "rust" 17 | ] 18 | }, 19 | { 20 | "type": "lldb", 21 | "request": "launch", 22 | "name": "Debug Cpp-Raw", 23 | "program": "${workspaceRoot}/build/bin/cpp-raw", 24 | "cwd": "${workspaceRoot}/build/bin", 25 | "sourceLanguages": [ 26 | "rust", 27 | "cpp" 28 | ] 29 | }, 30 | { 31 | "type": "lldb", 32 | "request": "launch", 33 | "name": "Debug Cpp-Ld", 34 | "program": "${workspaceRoot}/build/bin/cpp-dl", 35 | "cwd": "${workspaceRoot}/build/bin", 36 | "sourceLanguages": [ 37 | "rust", 38 | "cpp" 39 | ] 40 | }, 41 | { 42 | "name": "(Windows) Cpp-Raw", 43 | "type": "cppvsdbg", 44 | "request": "launch", 45 | "program": "${workspaceFolder}/test/cpp-raw/msvc/x64/Debug/cpp.exe", 46 | "args": [], 47 | "stopAtEntry": false, 48 | "cwd": "${workspaceFolder}/test/cpp-raw/msvc/x64/Debug/", 49 | "environment": [], 50 | "externalConsole": true 51 | }, 52 | { 53 | "type": "lldb", 54 | "request": "launch", 55 | "name": "Generate C++ Headers", 56 | "program": "${workspaceRoot}/target/debug/intercom-utils", 57 | "args": [ 58 | "cpp", 59 | "test/testlib" 60 | ], 61 | "cwd": "${workspaceRoot}", 62 | "sourceLanguages": [ 63 | "rust", 64 | "cpp" 65 | ] 66 | }, 67 | { 68 | "type": "lldb", 69 | "request": "launch", 70 | "name": "Debug intercom-fmt", 71 | "program": "${workspaceRoot}/target/debug/intercom-fmt", 72 | "args": [ 73 | "${workspaceRoot}/intercom-attributes/tests/data/com_impl.source.rs" 74 | ], 75 | "cwd": "${workspaceRoot}/target/debug/", 76 | "sourceLanguages": [ 77 | "rust", 78 | "cpp" 79 | ] 80 | } 81 | ] 82 | } 83 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "files.trimTrailingWhitespace": true, 3 | "editor.renderWhitespace": "all", 4 | "files.associations": { 5 | "array": "c", 6 | "chrono": "c", 7 | "functional": "c", 8 | "ratio": "c", 9 | "tuple": "c", 10 | "type_traits": "c", 11 | "utility": "c", 12 | "climits": "cpp", 13 | "*.tcc": "cpp", 14 | "cctype": "cpp", 15 | "clocale": "cpp", 16 | "cmath": "cpp", 17 | "condition_variable": "cpp", 18 | "cstddef": "cpp", 19 | "cstdint": "cpp", 20 | "cstdio": "cpp", 21 | "cstdlib": "cpp", 22 | "cstring": "cpp", 23 | "ctime": "cpp", 24 | "cwchar": "cpp", 25 | "cwctype": "cpp", 26 | "exception": "cpp", 27 | "fstream": "cpp", 28 | "initializer_list": "cpp", 29 | "iomanip": "cpp", 30 | "iosfwd": "cpp", 31 | "iostream": "cpp", 32 | "istream": "cpp", 33 | "limits": "cpp", 34 | "memory": "cpp", 35 | "mutex": "cpp", 36 | "new": "cpp", 37 | "ostream": "cpp", 38 | "sstream": "cpp", 39 | "stdexcept": "cpp", 40 | "streambuf": "cpp", 41 | "system_error": "cpp", 42 | "thread": "cpp", 43 | "typeinfo": "cpp", 44 | "__config": "cpp", 45 | "atomic": "cpp", 46 | "strstream": "cpp", 47 | "codecvt": "cpp", 48 | "complex": "cpp", 49 | "csignal": "cpp", 50 | "cstdarg": "cpp", 51 | "future": "cpp", 52 | "valarray": "cpp", 53 | "*.ipp": "cpp", 54 | "__bit_reference": "cpp", 55 | "__split_buffer": "cpp", 56 | "__tree": "cpp", 57 | "iterator": "cpp", 58 | "list": "cpp", 59 | "map": "cpp", 60 | "set": "cpp", 61 | "string": "cpp", 62 | "string_view": "cpp", 63 | "vector": "cpp", 64 | "deque": "cpp", 65 | "unordered_map": "cpp", 66 | "__nullptr": "cpp", 67 | "ios": "cpp", 68 | "algorithm": "cpp", 69 | "__functional_base": "cpp", 70 | "bitset": "cpp", 71 | "locale": "cpp", 72 | "hash_map": "cpp", 73 | "unordered_set": "cpp", 74 | "numeric": "cpp", 75 | "cinttypes": "cpp", 76 | "typeindex": "cpp", 77 | "__functional_03": "cpp" 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /.vscode/tasks.json: -------------------------------------------------------------------------------- 1 | { 2 | // See https://go.microsoft.com/fwlink/?LinkId=733558 3 | // for the documentation about the tasks.json format 4 | "version": "2.0.0", 5 | "tasks": [ 6 | { 7 | "label": "normalizeTestExecutables", 8 | "type": "shell", 9 | "group": { 10 | "kind": "test", 11 | "isDefault": true 12 | }, 13 | "linux": { 14 | "command": "for f in $( cargo test --no-run --message-format=json | jq -r 'select(.profile.test == true) | .filenames[]' ); do target=$( echo $f | sed 's/-.*//g' ); mv $f $target; done" 15 | } 16 | } 17 | ] 18 | } 19 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | if( ${CMAKE_SOURCE_DIR} STREQUAL ${CMAKE_BINARY_DIR} ) 2 | message( FATAL_ERROR "In-source builds not allowed. Please make a new directory and run CMake from there. You may need to remove CMakeCache.txt." ) 3 | endif() 4 | 5 | cmake_minimum_required(VERSION 3.5) 6 | project(intercom) 7 | 8 | # The version number. 9 | set (intercom_VERSION_MAJOR 0) 10 | set (intercom_VERSION_MINOR 1) 11 | set (intercom_VERSION_PATCH 1) 12 | 13 | # C++14 support 14 | set(CMAKE_CXX_STANDARD 14) 15 | set(CMAKE_CXX_STANDARD_REQUIRED ON) 16 | 17 | # Enable warnings. 18 | # -Wall is not enabled on Windows platform due to warnings from Windows SDK headers. 19 | if(MSVC) 20 | SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -W3") 21 | else() 22 | SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall") 23 | endif() 24 | 25 | set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin) 26 | set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin) 27 | set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin) 28 | set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY_DEBUG ${CMAKE_BINARY_DIR}/bin) 29 | set(CMAKE_LIBRARY_OUTPUT_DIRECTORY_DEBUG ${CMAKE_BINARY_DIR}/bin) 30 | set(CMAKE_RUNTIME_OUTPUT_DIRECTORY_DEBUG ${CMAKE_BINARY_DIR}/bin) 31 | set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY_RELEASE ${CMAKE_BINARY_DIR}/bin) 32 | set(CMAKE_LIBRARY_OUTPUT_DIRECTORY_RELEASE ${CMAKE_BINARY_DIR}/bin) 33 | set(CMAKE_RUNTIME_OUTPUT_DIRECTORY_RELEASE ${CMAKE_BINARY_DIR}/bin) 34 | 35 | # Define output directory. 36 | # set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_SOURCE_DIR}/bin/${CMAKE_SYSTEM_PROCESSOR}) 37 | 38 | # if( WIN32 ) 39 | # set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}) 40 | # set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}) 41 | # else() 42 | # set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/lib) 43 | # set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/lib) 44 | # endif() 45 | 46 | 47 | # Use the "Debug" as the default build type. 48 | if( "${CMAKE_BUILD_TYPE}" STREQUAL "" ) 49 | message( "Build type not specified. Defaulting to \"Debug\".\n Use \"cmake -DCMAKE_BUILD_TYPE=Release ..\" to build a release version." ) 50 | set( CMAKE_BUILD_TYPE Debug ) 51 | endif() 52 | 53 | # Compile the Rust crates 54 | if( "${CMAKE_BUILD_TYPE}" STREQUAL "Release" ) 55 | add_custom_target( intercom ALL cargo build --release 56 | WORKING_DIRECTORY ${CMAKE_SOURCE_DIR} ) 57 | else() 58 | add_custom_target( intercom ALL cargo build 59 | WORKING_DIRECTORY ${CMAKE_SOURCE_DIR} ) 60 | endif() 61 | 62 | # Compile tests 63 | add_subdirectory(test) 64 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [workspace] 2 | members = [ 3 | "intercom-common", 4 | "intercom-attributes", 5 | "intercom", 6 | "intercom-cli", 7 | "intercom-fmt", 8 | ] 9 | exclude = [ "samples", "test", "intercom-expanded" ] 10 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2017 Mikko Rantanen 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. 22 | -------------------------------------------------------------------------------- /STYLE.md: -------------------------------------------------------------------------------- 1 | 2 | # Intercom style guidelines 3 | 4 | ## General 5 | 6 | ### Whitespace 7 | 8 | - Spaces inside parentheses/braces/brackets/etc. In cases where the contents 9 | contain no spaces or special characters of their own, spaces may be omitted. 10 | ```rust 11 | let v : Option< Vec > = get_result( a, b, c[0] ); 12 | // ^ ^ 13 | // Single generic --' Simple index ----' 14 | ``` 15 | 16 | - Long method signatures use chopped parameters. 17 | ```rust 18 | fn write_com_attribute( 19 | &self, 20 | attribute : &syn::Attribute, 21 | libid : &GUID, 22 | ) -> String 23 | where T: 'static 24 | { 25 | // ... 26 | } 27 | ``` 28 | 29 | - Braces pretend to use K&R style. Items (functions, traits, structs, etc.) 30 | prefer spaces on their own lines. Expressions use trailing spaces. Short 31 | items make an exception. 32 | ```rust 33 | impl MyTrait for MyStruct 34 | { 35 | fn long_method( &self ) -> Result< u32, String > 36 | { 37 | // ... 38 | } 39 | 40 | fn is_valid( &self ) -> bool { 41 | self.foo.is_some() && self.bar.len() > 0 42 | } 43 | 44 | fn has_foo( &self ) -> bool { self.foo.is_some() } 45 | } 46 | ``` 47 | 48 | ## Rust 49 | 50 | Any style items not specified in this document use style defined in 51 | [Rust code formatting RFCs](https://github.com/rust-lang-nursery/fmt-rfcs). 52 | 53 | 54 | ## Other languages 55 | 56 | Other languages default to the Rust style where applicable (spacing, 57 | indentation, etc.) unless the language strongly prefers some other style, 58 | such as using `camelCase` variable names in C#. 59 | 60 | ### C++ documentation style 61 | 62 | C++ code uses JavaDoc inspired Doxygen format for function/class/etc. 63 | documentation. The prose uses Markdown syntax similar to what Rust does. 64 | 65 | ```C++ 66 | /** 67 | * Reference counting COM pointer 68 | * 69 | * Implements the IUnknown reference counting and query interface mechanisms. 70 | * Note that `add_ref` and `release` should not be called manually for pointers 71 | * handled through the `ComPtr` type. 72 | */ 73 | class ComPtr 74 | { 75 | public: 76 | 77 | /** 78 | * Wraps an existing raw pointer into a managed ComPtr. 79 | * 80 | * @param ptr Pointer to wrap. 81 | */ 82 | ComPtr( void* ptr ); 83 | } 84 | ``` 85 | -------------------------------------------------------------------------------- /azure-pipelines.yml: -------------------------------------------------------------------------------- 1 | trigger: 2 | - master 3 | 4 | strategy: 5 | matrix: 6 | Linux_nightly: 7 | imageName: "ubuntu-20.04" 8 | rustVersion: nightly 9 | cmake_generator_opts: 10 | memcheck: valgrind --error-exitcode=1 --leak-check=full 11 | Windows_nightly: 12 | imageName: "windows-2019" 13 | rustVersion: nightly 14 | cmake_generator_opts: -DCMAKE_GENERATOR_PLATFORM=x64 15 | memcheck: 16 | Linux_stable: 17 | imageName: "ubuntu-20.04" 18 | rustVersion: stable 19 | cmake_generator_opts: 20 | memcheck: valgrind --error-exitcode=1 --leak-check=full 21 | Windows_stable: 22 | imageName: "windows-2019" 23 | rustVersion: stable 24 | cmake_generator_opts: -DCMAKE_GENERATOR_PLATFORM=x64 25 | memcheck: 26 | 27 | pool: 28 | vmImage: $(imageName) 29 | 30 | steps: 31 | - script: | 32 | curl -sSf -o rustup-init.exe https://win.rustup.rs 33 | rustup-init.exe --default-toolchain none -y --default-host x86_64-pc-windows-msvc 34 | displayName: 'Update environment (Windows)' 35 | condition: and(succeeded(), eq( variables['Agent.OS'], 'Windows_NT' )) 36 | 37 | - script: | 38 | sudo apt-get install valgrind -y 39 | displayName: 'Update environment (Linux)' 40 | condition: and(succeeded(), eq( variables['Agent.OS'], 'Linux' )) 41 | 42 | - script: | 43 | rustup -V 44 | rustup self update 45 | rustup set profile minimal 46 | rustup toolchain install $(rustVersion) -c rustfmt -c clippy 47 | rustup default $(rustVersion) 48 | cargo -V 49 | rustc -V 50 | cargo clippy -V 51 | cargo fmt -- -V 52 | displayName: 'Setup environment' 53 | 54 | - script: | 55 | cargo fmt -- --check 56 | cd test 57 | cargo fmt -- --check 58 | displayName: 'Check style' 59 | condition: and(succeeded(), eq( variables['rustVersion'], 'nightly' )) 60 | 61 | - script: | 62 | cargo clippy --all -- -D warnings 63 | displayName: 'Run build' 64 | condition: and(succeeded(), eq( variables['rustVersion'], 'nightly' )) 65 | 66 | - script: cargo test 67 | displayName: 'Run unit tests' 68 | condition: and(succeeded(), eq( variables['rustVersion'], 'nightly' )) 69 | 70 | - script: | 71 | cd test 72 | cargo clippy --all -- -D warnings 73 | displayName: 'Clippy integration tests' 74 | condition: and(succeeded(), eq( variables['rustVersion'], 'nightly' )) 75 | 76 | - script: | 77 | mkdir build 78 | cd build 79 | cmake .. $(cmake_generator_opts) 80 | cmake --build . 81 | displayName: "Build integration tests" 82 | 83 | - bash: | 84 | $(memcheck) build/bin/cpp-raw 85 | $(memcheck) build/bin/cpp-dl 86 | $(memcheck) build/bin/cpp-wrapper 87 | displayName: "Run integration tests" 88 | 89 | - script: | 90 | "C:/Program Files (x86)/Microsoft SDKs/Windows/v10.0A/bin/NETFX 4.8 Tools/tlbimp" test/target/debug/test_lib.dll /MACHINE:X64 /out:test/cs/TestLib.Interop.dll 91 | displayName: "Generate C# type library" 92 | condition: and(succeeded(), eq( variables['Agent.OS'], 'Windows_NT' )) 93 | 94 | - task: NuGetCommand@2 95 | inputs: 96 | restoreSolution: test/cs/cs.sln 97 | displayName: 'Restore NuGet packages' 98 | condition: and(succeeded(), eq( variables['Agent.OS'], 'Windows_NT' )) 99 | 100 | - task: MSBuild@1 101 | inputs: 102 | solution: test/cs/cs.sln 103 | platform: x64 104 | displayName: 'Build C# tests' 105 | condition: and(succeeded(), eq( variables['Agent.OS'], 'Windows_NT' )) 106 | 107 | - task: VSTest@2 108 | inputs: 109 | testAssemblyVer2: test/cs/bin/x64/debug/cs.dll 110 | displayName: 'Run C# tests' 111 | condition: and(succeeded(), eq( variables['Agent.OS'], 'Windows_NT' )) 112 | 113 | -------------------------------------------------------------------------------- /circle.yml: -------------------------------------------------------------------------------- 1 | 2 | version: 2 3 | 4 | jobs: 5 | build: 6 | docker: 7 | - image: rustlang/rust:nightly 8 | environment: 9 | RUSTFLAGS: -C link-dead-code 10 | 11 | steps: 12 | - checkout 13 | - run: 14 | name: "Report versions" 15 | command: | 16 | rustc -vV 17 | cargo -vV 18 | rustup -vV 19 | - run: 20 | name: "Run rustfmt" 21 | command: | 22 | rustup toolchain install nightly --allow-downgrade -c rustfmt 23 | cargo fmt -- --check 24 | - run: 25 | name: "Cargo test" 26 | command: | 27 | export RUST_BACKTRACE=1 28 | cargo test 29 | - run: 30 | name: "C++ test" 31 | command: | 32 | gcc --version 33 | g++ --version 34 | apt-get update 35 | apt-get install cmake -y 36 | cmake --version 37 | mkdir build 38 | cd build 39 | cmake .. 40 | make 41 | cd bin 42 | ./cpp-raw 43 | ./cpp-dl 44 | ./cpp-wrapper 45 | -------------------------------------------------------------------------------- /intercom-attributes/.gitignore: -------------------------------------------------------------------------------- 1 | /target/ 2 | /tests/out 3 | **/*.rs.bk 4 | Cargo.lock 5 | -------------------------------------------------------------------------------- /intercom-attributes/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "intercom-attributes" 3 | version = "0.4.0" 4 | authors = ["Mikko Rantanen "] 5 | license = "MIT" 6 | edition = "2018" 7 | repository = "https://github.com/Rantanen/intercom" 8 | description = "See 'intercom'" 9 | 10 | [lib] 11 | name = "intercom_attributes" 12 | proc-macro = true 13 | 14 | [dependencies] 15 | intercom-common = { version = "0.4", path = "../intercom-common" } 16 | syn = { version = "1.0", features = [ "full" ] } 17 | quote = { version = "1.0" } 18 | 19 | [dev-dependencies] 20 | difference = "2" 21 | term = "0.5" 22 | intercom-fmt = { version = "0.4.0", path = "../intercom-fmt" } 23 | intercom = { version = "0.4", path = "../intercom" } 24 | regex = "1.5" 25 | log = "0.4" 26 | simple_logger = { version = "1.6", default-features = false } 27 | -------------------------------------------------------------------------------- /intercom-attributes/tests/data/.gitattributes: -------------------------------------------------------------------------------- 1 | # The output references should never use autocrlf on Windows. 2 | # Our test framework ensures these are consistent. Any problems should be 3 | # fixed in the intercom-common/tests/lib.rs 4 | *.stdout binary 5 | *.stderr binary 6 | -------------------------------------------------------------------------------- /intercom-attributes/tests/data/macro/com_impl.rs: -------------------------------------------------------------------------------- 1 | extern crate intercom; 2 | use intercom::*; 3 | use std::mem::MaybeUninit; 4 | 5 | // We need the IID and Vtbl to ensure this compiles. 6 | // 7 | // Normally these are provided by the [com_interface]. 8 | #[allow(non_camel_case_types)] 9 | struct __Foo_AutomationVtbl; 10 | const IID_Foo_Automation: intercom::IID = intercom::GUID { 11 | data1: 0, 12 | data2: 0, 13 | data3: 0, 14 | data4: [0, 0, 0, 0, 0, 0, 0, 0], 15 | }; 16 | 17 | #[allow(non_camel_case_types)] 18 | struct __Foo_RawVtbl; 19 | const IID_Foo_Raw: intercom::IID = intercom::GUID { 20 | data1: 0, 21 | data2: 0, 22 | data3: 0, 23 | data4: [0, 0, 0, 0, 0, 0, 0, 0], 24 | }; 25 | 26 | fn get_intercom_interface_info_for_Foo() -> Vec 27 | { 28 | unsafe { MaybeUninit::uninit().assume_init() } 29 | } 30 | 31 | #[com_class(clsid = "{00000000-0000-0000-0000-000000000000}", Foo)] 32 | pub struct Foo; 33 | 34 | impl Foo 35 | { 36 | fn static_method(a: u16, b: i16) {} 37 | fn simple_method(&self) {} 38 | fn arg_method(&self, a: u16) {} 39 | 40 | fn simple_result_method(&self) -> u16 41 | { 42 | 0 43 | } 44 | fn com_result_method(&self) -> ComResult 45 | { 46 | Ok(0) 47 | } 48 | fn rust_result_method(&self) -> Result 49 | { 50 | Ok(0) 51 | } 52 | fn tuple_result_method(&self) -> Result<(u8, u16, u32), i32> 53 | { 54 | Ok(0) 55 | } 56 | 57 | fn string_method(&self, input: String) -> String 58 | { 59 | input 60 | } 61 | fn string_result_method(&self, input: String) -> ComResult 62 | { 63 | Ok(input) 64 | } 65 | 66 | fn complete_method(&mut self, a: u16, b: i16) -> ComResult 67 | { 68 | Ok(true) 69 | } 70 | 71 | // Should be VARIANT_BOOL in Automation interface. 72 | fn bool_method(&self, input: bool) -> ComResult 73 | { 74 | Ok(input) 75 | } 76 | 77 | fn variant_method(&self, input: Variant) -> ComResult 78 | { 79 | Ok(input) 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /intercom-attributes/tests/data/macro/com_interface.rs.skip: -------------------------------------------------------------------------------- 1 | extern crate intercom; 2 | use intercom::*; 3 | 4 | #[com_interface( 5 | com_iid = "00000000-0000-0000-0000-000000000000", 6 | raw_iid = "00000000-0000-0000-0000-000000000001")] 7 | pub trait Foo { 8 | fn static_method(a: u16, b: i16); 9 | 10 | fn simple_method(&self); 11 | 12 | fn arg_method(&self, a: u16); 13 | 14 | fn simple_result_method(&self) -> u16; 15 | fn com_result_method(&self) -> ComResult; 16 | fn rust_result_method(&self) -> Result; 17 | 18 | fn complete_method(&mut self, a: u16, b: i16) -> ComResult; 19 | 20 | fn string_method(&self, msg: String) -> String; 21 | fn comitf_method(&self, itf: ComItf) -> ComResult>; 22 | 23 | // Should be VARIANT_BOOL in Automation interface. 24 | fn bool_method(&self, input : bool) -> ComResult; 25 | 26 | fn variant_method(&self, input : Variant) -> ComResult; 27 | } 28 | -------------------------------------------------------------------------------- /intercom-attributes/tests/data/macro/com_library-hooks.rs: -------------------------------------------------------------------------------- 1 | extern crate intercom; 2 | use intercom::*; 3 | 4 | fn custom_load() -> ComResult<()> 5 | { 6 | Ok(()) 7 | } 8 | 9 | fn custom_register() -> ComResult<()> 10 | { 11 | Ok(()) 12 | } 13 | 14 | fn custom_unregister() -> ComResult<()> 15 | { 16 | Ok(()) 17 | } 18 | 19 | com_library!( 20 | libid = "00000000-0000-0000-0000-000000000000", 21 | on_load = custom_load, 22 | on_register = custom_register, 23 | on_unregister = custom_unregister, 24 | ); 25 | -------------------------------------------------------------------------------- /intercom-attributes/tests/data/macro/com_library.rs: -------------------------------------------------------------------------------- 1 | extern crate intercom; 2 | use intercom::*; 3 | use std::mem::MaybeUninit; 4 | 5 | pub mod some { 6 | pub mod path { 7 | use std::mem::MaybeUninit; 8 | pub struct Type; 9 | pub const CLSID_Type : i8 = 0i8; 10 | pub(crate) fn get_intercom_coclass_info_for_Type() -> intercom::typelib::TypeInfo { 11 | unsafe { MaybeUninit::uninit().assume_init() } 12 | } 13 | } 14 | } 15 | pub struct SimpleType; 16 | pub const CLSID_SimpleType : i8 = 0i8; 17 | 18 | pub(crate) fn get_intercom_coclass_info_for_SimpleType() -> intercom::typelib::TypeInfo { 19 | unsafe { MaybeUninit::uninit().assume_init() } 20 | } 21 | 22 | 23 | com_library!( 24 | libid = "00000000-0000-0000-0000-000000000000", 25 | class some::path::Type, 26 | class SimpleType ); 27 | -------------------------------------------------------------------------------- /intercom-attributes/tests/data/macro/private_item.rs: -------------------------------------------------------------------------------- 1 | extern crate intercom; 2 | use intercom::*; 3 | 4 | #[com_interface( 5 | com_iid = "00000000-0000-0000-0000-000000000000", 6 | raw_iid = "00000000-0000-0000-0000-000000000001" 7 | )] 8 | trait IFoo 9 | { 10 | fn trait_method(&self); 11 | } 12 | 13 | #[com_class(clsid = "00000000-0000-0000-0000-000000000000", Foo, IFoo)] 14 | struct Foo; 15 | 16 | #[com_interface( 17 | com_iid = "00000000-0000-0000-0000-000000000002", 18 | raw_iid = "00000000-0000-0000-0000-000000000003" 19 | )] 20 | impl Foo 21 | { 22 | pub fn struct_method(&self) {} 23 | } 24 | 25 | impl IFoo for Foo 26 | { 27 | fn trait_method(&self) {} 28 | } 29 | -------------------------------------------------------------------------------- /intercom-attributes/tests/data/run/basic-logging.rs: -------------------------------------------------------------------------------- 1 | extern crate intercom; 2 | extern crate log; 3 | extern crate simple_logger; 4 | 5 | use submodule::ISubInterface; 6 | 7 | #[intercom::com_class(IInterface, ISubInterface)] 8 | struct S; 9 | 10 | #[intercom::com_interface] 11 | trait IInterface 12 | { 13 | fn call(&self); 14 | } 15 | 16 | #[intercom::com_interface] 17 | trait IAnotherInterface 18 | { 19 | } 20 | 21 | impl IInterface for S 22 | { 23 | fn call(&self) 24 | { 25 | println!("Call"); 26 | } 27 | } 28 | 29 | mod submodule 30 | { 31 | use super::*; 32 | 33 | #[intercom::com_interface] 34 | pub trait ISubInterface 35 | { 36 | } 37 | 38 | impl ISubInterface for S {} 39 | } 40 | 41 | fn main() 42 | { 43 | simple_logger::init().unwrap(); 44 | 45 | log::info!("Acquire S as IInterface"); 46 | let combox = intercom::ComBox::new(S); 47 | let rc: intercom::ComRc = intercom::ComRc::from(combox); 48 | 49 | log::info!("Call IInterface::call"); 50 | rc.call(); 51 | 52 | log::info!("Query ISubInterface"); 53 | let _: intercom::ComRc = 54 | intercom::ComItf::query_interface(&rc).unwrap(); 55 | 56 | log::info!("Query IAnotherInterface"); 57 | let rc: intercom::ComResult> = 58 | intercom::ComItf::query_interface(&rc); 59 | 60 | log::info!("Cleanup"); 61 | } 62 | -------------------------------------------------------------------------------- /intercom-attributes/tests/data/run/basic-logging.rs.stdout: -------------------------------------------------------------------------------- 1 | INFO [testcrate] Acquire S as IInterface 2 | TRACE [generated::testcrate] [xxxxxxxx] S::query_interface(21495B79-90CF-33C7-7032-9BC7C31AD5F9) 3 | TRACE [generated::testcrate] [xxxxxxxx] S::query_interface(21495B79-90CF-33C7-7032-9BC7C31AD5F9) -> IInterface (Automation) [xxxxxxxx] 4 | TRACE [generated::testcrate] [xxxxxxxx] S::query_interface(C96D83CC-F4BC-3B80-6951-3692BB4FDBC7) 5 | TRACE [generated::testcrate] [xxxxxxxx] S::query_interface(C96D83CC-F4BC-3B80-6951-3692BB4FDBC7) -> IInterface (Raw) [xxxxxxxx] 6 | INFO [testcrate] Call IInterface::call 7 | TRACE [generated::testcrate] [xxxxxxxx] Calling IInterface::call 8 | TRACE [generated::testcrate] [xxxxxxxx] Calling IInterface::call, type system: Automation 9 | TRACE [generated::testcrate] [xxxxxxxx] Serving testcrate::S::call 10 | Call 11 | TRACE [generated::testcrate] [xxxxxxxx] Serving testcrate::S::call, OK 12 | INFO [testcrate] Query ISubInterface 13 | TRACE [generated::intercom::interfaces] [xxxxxxxx] Calling RawIUnknown::query_interface 14 | TRACE [generated::intercom::interfaces] [xxxxxxxx] Calling RawIUnknown::query_interface, type system: Automation 15 | TRACE [intercom::interfaces] [xxxxxxxx] Serving testcrate::S::query_interface 16 | TRACE [generated::testcrate] [xxxxxxxx] S::query_interface(4CC91049-3D9A-3498-46FF-932AEDA28A33) 17 | TRACE [generated::testcrate] [xxxxxxxx] S::query_interface(4CC91049-3D9A-3498-46FF-932AEDA28A33) -> ISubInterface (Raw) [xxxxxxxx] 18 | TRACE [generated::intercom::interfaces] [xxxxxxxx] Calling RawIUnknown::release 19 | TRACE [generated::intercom::interfaces] [xxxxxxxx] Calling RawIUnknown::release, type system: Raw 20 | TRACE [intercom::interfaces] [xxxxxxxx] Serving testcrate::S::release 21 | INFO [testcrate] Query IAnotherInterface 22 | TRACE [generated::intercom::interfaces] [xxxxxxxx] Calling RawIUnknown::query_interface 23 | TRACE [generated::intercom::interfaces] [xxxxxxxx] Calling RawIUnknown::query_interface, type system: Automation 24 | TRACE [intercom::interfaces] [xxxxxxxx] Serving testcrate::S::query_interface 25 | TRACE [generated::testcrate] [xxxxxxxx] S::query_interface(46B4908C-8685-327F-7766-42C217D7984B) 26 | TRACE [generated::testcrate] [xxxxxxxx] S::query_interface(46B4908C-8685-327F-7766-42C217D7984B) -> E_NOINTERFACE 27 | TRACE [generated::intercom::interfaces] [xxxxxxxx] Calling RawIUnknown::query_interface 28 | TRACE [generated::intercom::interfaces] [xxxxxxxx] Calling RawIUnknown::query_interface, type system: Automation 29 | TRACE [intercom::interfaces] [xxxxxxxx] Serving testcrate::S::query_interface 30 | TRACE [generated::testcrate] [xxxxxxxx] S::query_interface(25A761EE-99D6-3A40-57CF-7F6A4ACD1615) 31 | TRACE [generated::testcrate] [xxxxxxxx] S::query_interface(25A761EE-99D6-3A40-57CF-7F6A4ACD1615) -> E_NOINTERFACE 32 | INFO [testcrate] Cleanup 33 | TRACE [generated::intercom::interfaces] [xxxxxxxx] Calling RawIUnknown::release 34 | TRACE [generated::intercom::interfaces] [xxxxxxxx] Calling RawIUnknown::release, type system: Automation 35 | TRACE [intercom::interfaces] [xxxxxxxx] Serving testcrate::S::release 36 | -------------------------------------------------------------------------------- /intercom-attributes/tests/data/run/confirm-run-tests.rs: -------------------------------------------------------------------------------- 1 | fn main() 2 | { 3 | println!("stdout works"); 4 | eprintln!("stderr works"); 5 | } 6 | -------------------------------------------------------------------------------- /intercom-attributes/tests/data/run/confirm-run-tests.rs.stderr: -------------------------------------------------------------------------------- 1 | stderr works 2 | -------------------------------------------------------------------------------- /intercom-attributes/tests/data/run/confirm-run-tests.rs.stdout: -------------------------------------------------------------------------------- 1 | stdout works 2 | -------------------------------------------------------------------------------- /intercom-attributes/tests/data/ui/com_library-struct-interface.rs: -------------------------------------------------------------------------------- 1 | extern crate intercom; 2 | use intercom::prelude::*; 3 | 4 | #[com_class] 5 | struct Struct; 6 | 7 | #[com_interface] 8 | impl Struct {} 9 | 10 | com_library! { 11 | interface Struct, 12 | } 13 | -------------------------------------------------------------------------------- /intercom-attributes/tests/data/ui/com_library-struct-interface.rs.stderr: -------------------------------------------------------------------------------- 1 | error[E0404]: expected trait, found struct `Struct` 2 | --> com_library-struct-interface.rs:11:15 3 | | 4 | 11 | interface Struct, 5 | | ^^^^^^ not a trait 6 | 7 | error: aborting due to previous error 8 | 9 | For more information about this error, try `rustc --explain E0404`. 10 | -------------------------------------------------------------------------------- /intercom-attributes/tests/data/ui/comstruct-api.rs: -------------------------------------------------------------------------------- 1 | extern crate intercom; 2 | 3 | use intercom::{ComItf, ComRc, ComResult}; 4 | 5 | #[intercom::com_interface] 6 | pub trait MyInterface 7 | { 8 | fn interface_method(&self) {} 9 | } 10 | 11 | #[intercom::com_class(MyStruct, MyInterface)] 12 | pub struct MyStruct; 13 | 14 | impl MyInterface for MyStruct 15 | { 16 | fn interface_method(&self) {} 17 | } 18 | 19 | #[intercom::com_interface] 20 | impl MyStruct 21 | { 22 | fn struct_method(&self) {} 23 | } 24 | 25 | pub fn from_trait_itf(itf: &ComItf) 26 | { 27 | let _: ComRc = ComRc::from(itf); 28 | let _: ComRc = itf.to_owned(); 29 | 30 | let _: ComResult> = ComItf::query_interface(itf); 31 | } 32 | 33 | pub fn from_struct_itf(itf: &ComItf) 34 | { 35 | let _: ComRc = ComRc::from(itf); 36 | let _: ComRc = itf.to_owned(); 37 | 38 | let _: ComResult> = ComItf::query_interface(itf); 39 | } 40 | 41 | pub fn from_struct(s1: intercom::ComBox, s2: intercom::ComBox) 42 | { 43 | let _: ComRc = ComRc::from(s1); 44 | let _: ComRc = ComRc::from(s2); 45 | } 46 | 47 | pub fn from_struct_ref(s: &intercom::ComBox) 48 | { 49 | let _: ComRc = ComRc::from(s); 50 | let _: ComRc = ComRc::from(s); 51 | } 52 | 53 | pub fn pass_comrc(rc: ComRc) 54 | { 55 | from_trait_itf(&rc); 56 | } 57 | -------------------------------------------------------------------------------- /intercom-attributes/tests/data/ui/interface-basic.rs: -------------------------------------------------------------------------------- 1 | 2 | extern crate intercom; 3 | use intercom::*; 4 | 5 | #[com_interface] 6 | trait IFoo { 7 | 8 | fn arg_type(&self, input: u32); 9 | 10 | fn ret_type(&self) -> ComResult; 11 | 12 | fn all_type(&self, input: u32) -> ComResult; 13 | } 14 | 15 | -------------------------------------------------------------------------------- /intercom-attributes/tests/data/ui/interface-no-imports.rs: -------------------------------------------------------------------------------- 1 | extern crate intercom; 2 | 3 | #[intercom::com_interface] 4 | trait IFoo { 5 | 6 | fn arg_type(&self, input: u32); 7 | 8 | fn ret_type(&self) -> intercom::ComResult; 9 | 10 | fn all_type(&self, input: u32) -> intercom::ComResult; 11 | } 12 | 13 | -------------------------------------------------------------------------------- /intercom-attributes/tests/data/ui/multiple-mods.rs: -------------------------------------------------------------------------------- 1 | extern crate intercom; 2 | 3 | mod interface 4 | { 5 | 6 | #[intercom::com_interface] 7 | pub trait MyInterface 8 | { 9 | fn interface_method(&self) -> u32; 10 | } 11 | } 12 | 13 | mod class 14 | { 15 | #[intercom::com_class(MyStruct, crate::interface::MyInterface)] 16 | #[derive(Default)] 17 | pub struct MyStruct; 18 | } 19 | 20 | mod interface_impl 21 | { 22 | impl crate::interface::MyInterface for crate::class::MyStruct 23 | { 24 | fn interface_method(&self) -> u32 25 | { 26 | 0 27 | } 28 | } 29 | } 30 | 31 | mod class_impl 32 | { 33 | #[intercom::com_interface] 34 | impl crate::class::MyStruct 35 | { 36 | fn struct_method(&self) -> u32 37 | { 38 | 0 39 | } 40 | } 41 | } 42 | 43 | mod submodule 44 | { 45 | intercom::com_module!( 46 | class SubmoduleClass 47 | ); 48 | 49 | #[intercom::com_class(Self)] 50 | #[derive(Default)] 51 | pub struct SubmoduleClass; 52 | 53 | #[intercom::com_interface] 54 | impl SubmoduleClass {} 55 | } 56 | 57 | intercom::com_library!( 58 | class class::MyStruct, 59 | module submodule, 60 | ); 61 | -------------------------------------------------------------------------------- /intercom-attributes/tests/data/ui/span-comclass-no-com-interface.rs: -------------------------------------------------------------------------------- 1 | extern crate intercom; 2 | use intercom::*; 3 | 4 | trait NotComInterface 5 | { 6 | } 7 | 8 | #[com_class(NotComInterface)] 9 | pub struct S; 10 | 11 | impl NotComInterface for S {} 12 | -------------------------------------------------------------------------------- /intercom-attributes/tests/data/ui/span-comclass-no-com-interface.rs.stderr: -------------------------------------------------------------------------------- 1 | error[E0277]: the trait bound `(dyn NotComInterface + 'static): ComInterface` is not satisfied 2 | --> span-comclass-no-com-interface.rs:8:1 3 | | 4 | 8 | #[com_class(NotComInterface)] 5 | | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `ComInterface` is not implemented for `(dyn NotComInterface + 'static)` 6 | | 7 | = help: the following other types implement trait `ComInterface`: 8 | (dyn IAllocator + 'static) 9 | (dyn IClassFactory + 'static) 10 | (dyn IErrorInfo + 'static) 11 | (dyn IErrorStore + 'static) 12 | (dyn IIntercomCoClass + 'static) 13 | (dyn IIntercomInterface + 'static) 14 | (dyn IIntercomInterfaceVariant + 'static) 15 | (dyn IIntercomMethod + 'static) 16 | and 5 others 17 | = note: this error originates in the attribute macro `com_class` (in Nightly builds, run with -Z macro-backtrace for more info) 18 | 19 | error: aborting due to previous error 20 | 21 | For more information about this error, try `rustc --explain E0277`. 22 | -------------------------------------------------------------------------------- /intercom-attributes/tests/data/ui/span-comclass-unknown-interface.rs: -------------------------------------------------------------------------------- 1 | 2 | extern crate intercom; 3 | use intercom::*; 4 | 5 | #[com_class(IDoesNotExist)] 6 | struct S; 7 | -------------------------------------------------------------------------------- /intercom-attributes/tests/data/ui/span-comclass-unknown-interface.rs.stderr: -------------------------------------------------------------------------------- 1 | error[E0405]: cannot find trait `IDoesNotExist` in this scope 2 | --> span-comclass-unknown-interface.rs:5:13 3 | | 4 | 5 | #[com_class(IDoesNotExist)] 5 | | ^^^^^^^^^^^^^- help: you might be missing a type parameter: `` 6 | | | 7 | | not found in this scope 8 | 9 | error[E0405]: cannot find trait `IDoesNotExist` in this scope 10 | --> span-comclass-unknown-interface.rs:5:13 11 | | 12 | 5 | #[com_class(IDoesNotExist)] 13 | | ^^^^^^^^^^^^^ not found in this scope 14 | 15 | error[E0405]: cannot find trait `IDoesNotExist` in this scope 16 | --> span-comclass-unknown-interface.rs:5:13 17 | | 18 | 5 | #[com_class(IDoesNotExist)] 19 | | ^^^^^^^^^^^^^ - help: you might be missing a type parameter: `` 20 | | | 21 | | not found in this scope 22 | 23 | error: aborting due to 3 previous errors 24 | 25 | For more information about this error, try `rustc --explain E0405`. 26 | -------------------------------------------------------------------------------- /intercom-attributes/tests/data/ui/span-externtype-error.rs: -------------------------------------------------------------------------------- 1 | 2 | extern crate intercom; 3 | use intercom::*; 4 | 5 | // The NotExternType does not implement ExternType trait so it cannot be used 6 | // as input/output type. 7 | struct NotExternType; 8 | 9 | #[com_interface] 10 | trait IFoo { 11 | 12 | fn arg_type(&self, bad_type: NotExternType); 13 | 14 | fn ret_type(&self) -> ComResult; 15 | 16 | fn all_type(&self, bad_type: NotExternType) -> ComResult; 17 | } 18 | 19 | -------------------------------------------------------------------------------- /intercom-attributes/tests/data/ui/span-no-self-error.rs: -------------------------------------------------------------------------------- 1 | 2 | extern crate intercom; 3 | use intercom::*; 4 | 5 | // Traits that have methods with no receiver cannot be turned into trait objects, 6 | // which is needed for Intercom. 7 | 8 | #[com_interface] 9 | trait IFoo { 10 | fn arg_type(); 11 | } 12 | 13 | -------------------------------------------------------------------------------- /intercom-attributes/tests/data/ui/span-no-self-error.rs.stderr: -------------------------------------------------------------------------------- 1 | error[E0038]: the trait `IFoo` cannot be made into an object 2 | --> span-no-self-error.rs:9:7 3 | | 4 | 9 | trait IFoo { 5 | | ^^^^ `IFoo` cannot be made into an object 6 | | 7 | note: for a trait to be "object safe" it needs to allow building a vtable to allow the call to be resolvable dynamically; for more information visit 8 | --> span-no-self-error.rs:10:8 9 | | 10 | 9 | trait IFoo { 11 | | ---- this trait cannot be made into an object... 12 | 10 | fn arg_type(); 13 | | ^^^^^^^^ ...because associated function `arg_type` has no `self` parameter 14 | help: consider turning `arg_type` into a method by giving it a `&self` argument 15 | | 16 | 10 | fn arg_type(&self); 17 | | +++++ 18 | help: alternatively, consider constraining `arg_type` so it does not apply to trait objects 19 | | 20 | 10 | fn arg_type() where Self: Sized; 21 | | +++++++++++++++++ 22 | 23 | error: aborting due to previous error 24 | 25 | For more information about this error, try `rustc --explain E0038`. 26 | -------------------------------------------------------------------------------- /intercom-cli/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "intercom-cli" 3 | version = "0.4.0" 4 | edition = "2018" 5 | authors = ["Mikko Rantanen "] 6 | 7 | [dependencies] 8 | intercom = { path = "../intercom", version = "0.4" } 9 | clap = { version = "2.27.1", default-features = false } 10 | serde = "1.0" 11 | serde_derive = "1.0" 12 | failure = "0.1" 13 | libloading = "0.5" 14 | handlebars = "2.0" 15 | glob = "0.3" 16 | winapi = { version = "0.3", features = [ "winreg", "winbase" ] } 17 | env_logger = "0.7" 18 | -------------------------------------------------------------------------------- /intercom-cli/src/generators/cpp_header.hbs: -------------------------------------------------------------------------------- 1 | 2 | #ifndef INTERCOM_LIBRARY_{{lib_name}}_H 3 | #define INTERCOM_LIBRARY_{{lib_name}}_H 4 | 5 | #include 6 | #include 7 | 8 | namespace {{lib_name}} 9 | { 10 | using i8 = char; 11 | using u8 = uint8_t; 12 | using i16 = int16_t; 13 | using u16 = uint16_t; 14 | using i32 = int32_t; 15 | using u32 = uint32_t; 16 | using i64 = int64_t; 17 | using u64 = uint64_t; 18 | using f32 = float; 19 | using f64 = double; 20 | using usize = size_t; 21 | using Variant = intercom::VARIANT; 22 | using BSTR = intercom::BSTR; 23 | 24 | class Descriptor 25 | { 26 | public: 27 | static const char NAME[]; 28 | static const char WINDOWS_NAME[]; 29 | static const char POSIX_NAME[]; 30 | static const std::array< intercom::CLSID, {{coclass_count}} > CLASSES; 31 | 32 | static bool is_available(); 33 | }; 34 | 35 | namespace raw 36 | { 37 | {{~#each interfaces}} 38 | struct {{name}}; 39 | {{~/each}} 40 | 41 | {{~#each interfaces}} 42 | struct {{name}}{{#if base}} : {{base}}{{/if}} 43 | { 44 | static const intercom::IID ID; 45 | 46 | {{~#each methods}} 47 | virtual {{ret_type}} INTERCOM_CC {{name}}( 48 | {{~#each args~}} 49 | {{arg_type}} {{name}}{{#unless @last}}, {{/unless}} 50 | {{~/each~}} 51 | ) = 0; 52 | {{~/each}} 53 | }; 54 | {{~/each}} 55 | 56 | {{~#each coclasses}} 57 | class {{name}}Descriptor 58 | { 59 | public: 60 | static const intercom::CLSID ID; 61 | 62 | static const std::array INTERFACES; 63 | 64 | using Library = {{../lib_name}}::Descriptor; 65 | 66 | {{name}}Descriptor() = delete; 67 | ~{{name}}Descriptor() = delete; 68 | }; 69 | {{/each}} 70 | } 71 | } 72 | 73 | #ifdef INTERCOM_FLATTEN_DECLARATIONS 74 | {{~#each interfaces}} 75 | static constexpr intercom::IID IID_{{name}} = {{iid_struct}}; 76 | using {{name}} = {{../lib_name}}::raw::{{name}}; 77 | {{~/each}} 78 | {{~#each coclasses}} 79 | static constexpr intercom::CLSID CLSID_{{name}} = {{clsid_struct}}; 80 | {{~/each}} 81 | #endif 82 | 83 | #endif 84 | -------------------------------------------------------------------------------- /intercom-cli/src/generators/cpp_source.hbs: -------------------------------------------------------------------------------- 1 | 2 | #include "{{lib_name}}.hpp" 3 | 4 | #ifdef _MSC_VER 5 | const char {{lib_name}}::Descriptor::NAME[] = "{{lib_name}}.dll"; 6 | #else 7 | const char {{lib_name}}::Descriptor::NAME[] = "lib{{lib_name}}.so"; 8 | #endif 9 | 10 | const char {{lib_name}}::Descriptor::WINDOWS_NAME[] = "{{lib_name}}.dll"; 11 | const char {{lib_name}}::Descriptor::POSIX_NAME[] = "lib{{lib_name}}.so"; 12 | 13 | const std::array< intercom::CLSID, {{coclass_count}} > {{lib_name}}::Descriptor::CLASSES = { { 14 | {{#each coclasses}} 15 | intercom::CLSID{{clsid_struct}}{{#unless @last}},{{/unless}} 16 | {{~/each}} 17 | } }; 18 | 19 | bool {{lib_name}}::Descriptor::is_available() 20 | { 21 | // Espace the "static initialization order fiasco". 22 | // This static variable is not initialized until this method is called which 23 | // should guarantee that the ::intercom::detail::LIBRARY_INDEX has been initialized. 24 | static bool available = ::intercom::try_register_library( 25 | {{lib_name}}::Descriptor::NAME, {{lib_name}}::Descriptor::CLASSES ); 26 | 27 | return available; 28 | } 29 | {{#each interfaces}} 30 | const intercom::IID {{../../lib_name}}::raw::{{name}}::ID = {{iid_struct}}; 31 | {{~/each}} 32 | {{#each coclasses}} 33 | 34 | const intercom::CLSID {{../../lib_name}}::raw::{{name}}Descriptor::ID = {{clsid_struct}}; 35 | const std::array {{../../lib_name}}::raw::{{name}}Descriptor::INTERFACES = { { 36 | {{~#each interfaces}} 37 | {{../../lib_name}}::raw::{{this}}::ID{{#unless @last}},{{/unless}} 38 | {{~/each}} 39 | } }; 40 | {{~/each}} 41 | -------------------------------------------------------------------------------- /intercom-cli/src/generators/idl.hbs: -------------------------------------------------------------------------------- 1 | [ 2 | uuid( {{lib_id}} ) 3 | ] 4 | library {{lib_name}} 5 | { 6 | importlib("stdole2.tlb"); 7 | 8 | // Not sure if these should go somewhere else. Although this feels like as 9 | // good of a place as any for our hard coded values for now. 10 | typedef int8 i8; 11 | typedef uint8 u8; 12 | typedef int16 i16; 13 | typedef uint16 u16; 14 | typedef int32 i32; 15 | typedef uint32 u32; 16 | typedef int64 i64; 17 | typedef uint64 u64; 18 | typedef float f32; 19 | typedef double f64; 20 | typedef size_t usize; 21 | 22 | {{#each interfaces}} 23 | interface {{name}}; 24 | {{/each}} 25 | 26 | {{#each interfaces}} 27 | [ 28 | object, 29 | uuid( {{iid}} ), 30 | nonextensible, 31 | pointer_default(unique) 32 | ] 33 | interface {{name}}{{#if base}} : {{base}}{{/if}} 34 | { 35 | 36 | {{~#each methods}} 37 | [id({{idx}})] 38 | {{ret_type}} {{name}}( 39 | {{~#each args~}} 40 | [{{attributes}}] {{arg_type}} {{name}}{{#unless @last}}, {{/unless}} 41 | {{~/each~}} 42 | ); 43 | {{/each}} 44 | } 45 | {{/each}} 46 | 47 | {{#each coclasses}} 48 | [ 49 | uuid( {{clsid}} ) 50 | ] 51 | coclass {{name}} 52 | { 53 | {{#each interfaces}} 54 | interface {{this}}; 55 | {{~/each}} 56 | } 57 | {{/each}} 58 | } 59 | -------------------------------------------------------------------------------- /intercom-cli/src/generators/mod.rs: -------------------------------------------------------------------------------- 1 | //! Generators for file formats that can be derived from the intercom 2 | //! libraries. 3 | 4 | use std::collections::HashMap; 5 | 6 | use intercom::type_system::TypeSystemName; 7 | use intercom::typelib::{Interface, TypeInfo, TypeLib}; 8 | 9 | /// A common error type for all the generators. 10 | #[derive(Fail, Debug)] 11 | pub enum GeneratorError 12 | { 13 | #[fail(display = "IoError: {}", _0)] 14 | IoError(#[cause] ::std::io::Error), 15 | 16 | #[fail(display = "Invalid type library: {}", _0)] 17 | LibraryError(String), 18 | } 19 | 20 | impl From<::std::io::Error> for GeneratorError 21 | { 22 | fn from(e: ::std::io::Error) -> GeneratorError 23 | { 24 | GeneratorError::IoError(e) 25 | } 26 | } 27 | 28 | impl From for GeneratorError 29 | { 30 | fn from(s: String) -> GeneratorError 31 | { 32 | GeneratorError::LibraryError(s) 33 | } 34 | } 35 | 36 | pub struct ModelOptions 37 | { 38 | pub type_systems: Vec, 39 | } 40 | 41 | pub struct TypeSystemOptions 42 | { 43 | pub ts: TypeSystemName, 44 | pub use_full_name: bool, 45 | } 46 | 47 | pub struct LibraryContext<'a> 48 | { 49 | pub itfs_by_ref: HashMap, 50 | pub itfs_by_name: HashMap, 51 | } 52 | 53 | impl<'a> LibraryContext<'a> 54 | { 55 | fn from(lib: &'a TypeLib) -> LibraryContext<'a> 56 | { 57 | let itfs_by_name: HashMap = lib 58 | .types 59 | .iter() 60 | .filter_map(|t| match t { 61 | TypeInfo::Interface(itf) => Some(itf), 62 | _ => None, 63 | }) 64 | .map(|itf| (itf.as_ref().name.to_string(), &**(itf.as_ref()))) 65 | .collect(); 66 | let itfs_by_ref: HashMap = lib 67 | .types 68 | .iter() 69 | .filter_map(|t| match t { 70 | TypeInfo::Class(cls) => Some(cls), 71 | _ => None, 72 | }) 73 | .flat_map(|cls| &cls.as_ref().interfaces) 74 | .map(|itf_ref| { 75 | ( 76 | itf_ref.name.to_string(), 77 | itfs_by_name[itf_ref.name.as_ref()], 78 | ) 79 | }) 80 | .collect(); 81 | LibraryContext { 82 | itfs_by_name, 83 | itfs_by_ref, 84 | } 85 | } 86 | } 87 | 88 | /// Convert the Rust identifier from `snake_case` to `PascalCase` 89 | pub fn pascal_case>(input: T) -> String 90 | { 91 | let input = input.as_ref(); 92 | 93 | // Allocate the output string. We'll never increase the amount of 94 | // characters so we can reserve string buffer using the input string length. 95 | let mut output = String::new(); 96 | output.reserve(input.len()); 97 | 98 | // Process each character from the input. 99 | let mut capitalize = true; 100 | for c in input.chars() { 101 | // Check the capitalization requirement. 102 | if c == '_' { 103 | // Skip '_' but capitalize the following character. 104 | capitalize = true; 105 | } else if capitalize { 106 | // Capitalize. Add the uppercase characters. 107 | for c_up in c.to_uppercase() { 108 | output.push(c_up) 109 | } 110 | 111 | // No need to capitalize any more. 112 | capitalize = false; 113 | } else { 114 | // No need to capitalize. Just add the character as is. 115 | output.push(c); 116 | } 117 | } 118 | output 119 | } 120 | 121 | pub mod cpp; 122 | pub mod idl; 123 | -------------------------------------------------------------------------------- /intercom-cli/src/typelib.rs: -------------------------------------------------------------------------------- 1 | use intercom::{ 2 | type_system::{AutomationTypeSystem, TypeSystemName}, 3 | typelib::IIntercomTypeLib, 4 | }; 5 | use std::path::Path; 6 | 7 | #[derive(Fail, Debug)] 8 | pub enum TypeLibError 9 | { 10 | #[fail(display = "Could not acquire IntercomTypeLib: {}", _0)] 11 | AcquiringTypeLib(String), 12 | } 13 | 14 | pub fn read_typelib(path: &Path) -> Result 15 | { 16 | let lib = libloading::Library::new(path)?; 17 | let typelib = unsafe { 18 | let fn_get_type_lib: libloading::Symbol< 19 | unsafe extern "C" fn( 20 | TypeSystemName, 21 | *mut intercom::raw::RawComPtr, 22 | ) -> intercom::raw::HRESULT, 23 | > = lib.get(b"IntercomTypeLib")?; 24 | 25 | let mut ptr: intercom::raw::RawComPtr = std::ptr::null_mut(); 26 | fn_get_type_lib(TypeSystemName::Automation, &mut ptr as *mut _); 27 | 28 | let comptr = 29 | intercom::raw::InterfacePtr::::new(ptr) 30 | .ok_or_else(|| TypeLibError::AcquiringTypeLib("Null ptr".to_owned()))?; 31 | intercom::ComRc::wrap(comptr) 32 | }; 33 | 34 | Ok(intercom::typelib::TypeLib::from_comrc(&typelib)?) 35 | } 36 | -------------------------------------------------------------------------------- /intercom-common/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "intercom-common" 3 | version = "0.4.0" 4 | authors = ["Mikko Rantanen "] 5 | license = "MIT" 6 | edition = "2018" 7 | repository = "https://github.com/Rantanen/intercom" 8 | description = "See 'intercom'" 9 | 10 | [dependencies] 11 | failure = "0.1" 12 | indexmap = "1.2" 13 | proc-macro2 = "1.0" 14 | quote = "1.0" 15 | sha1 = "0.6.0" 16 | syn = { version = "1.0", features = [ "full", "extra-traits", "parsing" ] } 17 | 18 | [dev-dependencies] 19 | difference = "1.0.0" 20 | -------------------------------------------------------------------------------- /intercom-common/src/attributes/common.rs: -------------------------------------------------------------------------------- 1 | use crate::prelude::*; 2 | 3 | use std::env; 4 | use std::iter::FromIterator; 5 | 6 | /// Resolve the name of the package being compiled. 7 | pub fn lib_name() -> String 8 | { 9 | // Cargo stores the currently compiled package in the CARGO_PKG_NAME 10 | // environment variable. 11 | env::var("CARGO_PKG_NAME").expect( 12 | "Could not resolve package name. \ 13 | Ensure CARGO_PKG_NAME environment variable is defined.", 14 | ) 15 | } 16 | 17 | pub fn tokens_to_tokenstream>( 18 | original: TokenStreamNightly, 19 | tokens: T, 20 | ) -> TokenStreamNightly 21 | { 22 | TokenStreamNightly::from_iter( 23 | std::iter::once(original).chain(tokens.into_iter().map(Into::into)), 24 | ) 25 | } 26 | 27 | // These functions are left in for debugging purposes. 28 | // 29 | // They can be swapped in for the tokens_to_tokenstreams to get a printout of 30 | // span information (bad vs ok) when tracking down tokens still referring to 31 | // call site spans and thus messing the error messages. 32 | /* 33 | extern crate proc_macro; 34 | pub fn tokens_to_validated_tokenstream>( 35 | call_site: proc_macro::Span, 36 | original: TokenStreamNightly, 37 | tokens: T, 38 | ) -> TokenStreamNightly 39 | { 40 | TokenStreamNightly::from_iter( 41 | std::iter::once(original) 42 | .chain(tokens.into_iter().map(|tt| do_valid(&call_site, tt.into()))) 43 | ) 44 | } 45 | 46 | pub fn do_valid( 47 | call_site: &proc_macro::Span, 48 | ts: proc_macro::TokenStream, 49 | ) -> proc_macro::TokenStream 50 | { 51 | let mut v = vec![]; 52 | for tt in ts { 53 | let s = tt.span(); 54 | if s.start().line == call_site.start().line && 55 | s.start().column == call_site.start().column { 56 | 57 | eprint!("BAD: "); 58 | } else { 59 | eprint!("OK: "); 60 | } 61 | match tt { 62 | proc_macro::TokenTree::Group(grp) => { 63 | let (left, right) = match grp.delimiter() { 64 | proc_macro::Delimiter::Parenthesis => ( "(", ")" ), 65 | proc_macro::Delimiter::Brace => ( "{", "}" ), 66 | proc_macro::Delimiter::Bracket => ( "[", "]" ), 67 | proc_macro::Delimiter::None => ( "@", "€" ), 68 | }; 69 | eprintln!("{}", left); 70 | v.push(proc_macro::TokenTree::Group( 71 | proc_macro::Group::new(grp.delimiter(), do_valid(call_site, grp.stream())))); 72 | eprintln!("--- {}", right); 73 | }, 74 | tt => { 75 | eprintln!("{}", tt); 76 | v.push(tt) 77 | } 78 | } 79 | } 80 | proc_macro::TokenStream::from_iter(v) 81 | } 82 | */ 83 | -------------------------------------------------------------------------------- /intercom-common/src/attributes/mod.rs: -------------------------------------------------------------------------------- 1 | mod common; 2 | 3 | mod com_class; 4 | pub use self::com_class::expand_com_class; 5 | 6 | mod com_interface; 7 | pub use self::com_interface::expand_com_interface; 8 | 9 | mod com_library; 10 | pub use self::com_library::expand_com_module; 11 | 12 | mod type_info; 13 | pub use self::type_info::expand_bidirectional_type_info; 14 | pub use self::type_info::expand_derive_extern_input; 15 | pub use self::type_info::expand_derive_extern_output; 16 | pub use self::type_info::expand_derive_extern_type; 17 | -------------------------------------------------------------------------------- /intercom-common/src/error.rs: -------------------------------------------------------------------------------- 1 | pub struct MacroError 2 | { 3 | pub msg: String, 4 | } 5 | 6 | impl<'a> From<&'a str> for MacroError 7 | { 8 | fn from(m: &'a str) -> MacroError 9 | { 10 | MacroError { msg: m.to_owned() } 11 | } 12 | } 13 | 14 | impl From for MacroError 15 | { 16 | fn from(m: String) -> MacroError 17 | { 18 | MacroError { msg: m } 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /intercom-common/src/idents.rs: -------------------------------------------------------------------------------- 1 | use crate::prelude::*; 2 | use crate::tyhandlers::ModelTypeSystem; 3 | use syn::{Ident, Path}; 4 | 5 | pub trait SomeIdent 6 | { 7 | fn get_some_ident(&self) -> Option; 8 | } 9 | 10 | impl SomeIdent for Path 11 | { 12 | fn get_some_ident(&self) -> Option 13 | { 14 | self.get_ident() 15 | .cloned() 16 | .or_else(|| self.segments.last().map(|l| l.ident.clone())) 17 | } 18 | } 19 | 20 | pub fn vtable(itf: &Ident, ts: ModelTypeSystem) -> Path 21 | { 22 | let vtable_ident = format_ident!("__{}{}VTable", itf, ts); 23 | parse_quote!(#vtable_ident) 24 | } 25 | 26 | pub fn com_to_rust_method_impl(itf: &Ident, method: &Ident, ts: ModelTypeSystem) -> Ident 27 | { 28 | Ident::new(&format!("__{}_{}_{:?}", itf, method, ts), method.span()) 29 | } 30 | 31 | pub fn with_ts(ident: &Ident, ts: ModelTypeSystem) -> Ident 32 | { 33 | Ident::new(&format!("{}_{:?}", ident, ts), Span::call_site()) 34 | } 35 | 36 | pub fn clsid_path(struct_path: &Path) -> Path 37 | { 38 | let mut clsid_path = struct_path.clone(); 39 | if let Some(mut last) = clsid_path.segments.last_mut() { 40 | last.ident = clsid(&last.ident); 41 | } 42 | clsid_path 43 | } 44 | 45 | pub fn clsid(struct_name: &Ident) -> Ident 46 | { 47 | new_ident(&format!("CLSID_{}", struct_name)) 48 | } 49 | 50 | pub fn iid(itf_name: &Ident, span: Span) -> Ident 51 | { 52 | Ident::new(&format!("IID_{}", itf_name), span) 53 | } 54 | 55 | pub fn method_impl( 56 | struct_ident: &Ident, 57 | itf_ident: &Ident, 58 | method_name: TMethod, 59 | ts: ModelTypeSystem, 60 | ) -> Ident 61 | { 62 | new_ident(&format!( 63 | "__{}_{}_{}_{:?}", 64 | struct_ident, itf_ident, method_name, ts 65 | )) 66 | } 67 | 68 | fn new_ident(s: &str) -> Ident 69 | { 70 | Ident::new(s, Span::call_site()) 71 | } 72 | -------------------------------------------------------------------------------- /intercom-common/src/lib.rs: -------------------------------------------------------------------------------- 1 | #![recursion_limit = "128"] 2 | #![allow(clippy::match_bool)] 3 | 4 | #[macro_use] 5 | extern crate quote; 6 | #[macro_use] 7 | extern crate syn; 8 | #[macro_use] 9 | extern crate failure; 10 | 11 | pub mod ast_converters; 12 | pub mod attributes; 13 | pub mod error; 14 | pub mod guid; 15 | pub mod idents; 16 | pub mod methodinfo; 17 | pub mod model; 18 | pub mod prelude; 19 | pub mod returnhandlers; 20 | pub mod tyhandlers; 21 | pub mod utils; 22 | -------------------------------------------------------------------------------- /intercom-common/src/model/mod.rs: -------------------------------------------------------------------------------- 1 | //! 2 | //! COM library parse model. 3 | //! 4 | //! Defines the items constructed from the various COM attributes. 5 | //! 6 | //! Should unify COM attribute expansion and crate parsing for IDL/Manifest/etc. 7 | //! purposes in the future. 8 | //! 9 | 10 | #[derive(Fail, Debug)] 11 | #[non_exhaustive] 12 | pub enum ParseError 13 | { 14 | #[fail(display = "Parsing [com_library] failed: {}", _0)] 15 | ComLibrary(String), 16 | 17 | #[fail(display = "Parsing [com_class] item {} failed: {}", _0, _1)] 18 | ComClass(String, String), 19 | 20 | #[fail(display = "Parsing [com_interface] item {} failed: {}", _0, _1)] 21 | ComInterface(String, String), 22 | 23 | #[fail(display = "Processing crate failed: {}", _0)] 24 | ComCrate(String), 25 | 26 | #[fail(display = "Reading TOML failed: {}", _0)] 27 | CargoToml(String), 28 | } 29 | 30 | pub type ParseResult = Result; 31 | 32 | #[macro_use] 33 | mod macros; 34 | 35 | mod comlibrary; 36 | pub use self::comlibrary::*; 37 | mod comclass; 38 | pub use self::comclass::*; 39 | mod cominterface; 40 | pub use self::cominterface::*; 41 | -------------------------------------------------------------------------------- /intercom-common/src/prelude.rs: -------------------------------------------------------------------------------- 1 | extern crate proc_macro; 2 | pub use proc_macro2::{Span, TokenStream}; 3 | pub use syn::Ident; 4 | 5 | pub use self::proc_macro::TokenStream as TokenStreamNightly; 6 | -------------------------------------------------------------------------------- /intercom-cpp/intercom.hpp: -------------------------------------------------------------------------------- 1 | 2 | #include "src/callingconvention.hpp" 3 | #include "src/comdef.hpp" 4 | #include "src/classfactory.hpp" 5 | #include "src/conversions.hpp" 6 | #include "src/functions.hpp" 7 | #include "src/memory.hpp" 8 | #include "src/variant.hpp" 9 | 10 | // Exceptions. 11 | #include "src/no_such_interface.hpp" 12 | #include "src/runtime_error.hpp" 13 | -------------------------------------------------------------------------------- /intercom-cpp/src/activator.hpp: -------------------------------------------------------------------------------- 1 | 2 | #ifndef INTERCOM_CPP_ACTIVATOR_H 3 | #define INTERCOM_CPP_ACTIVATOR_H 4 | 5 | #include 6 | 7 | #include "detail/iclassfactory.hpp" 8 | #include "detail/declarations.hpp" 9 | #include "detail/get_class_factory.hpp" 10 | #include "datatypes.hpp" 11 | #include "error_codes.hpp" 12 | #include "no_such_interface.hpp" 13 | #include "raw_interface.hpp" 14 | #include "runtime_error.hpp" 15 | 16 | namespace intercom 17 | { 18 | 19 | class Activator 20 | { 21 | public: 22 | 23 | public: 24 | 25 | Activator( 26 | const char* library_name, 27 | const intercom::CLSID& classId //!< Identifies the class constructed with this activator. 28 | ) : 29 | m_classId( classId ), 30 | m_classFactory( intercom::detail::get_class_factory( library_name, classId ) ) 31 | { 32 | } 33 | 34 | Activator( 35 | const intercom::CLSID& classId //!< Identifies the class constructed with this activator. 36 | ) : 37 | m_classId( classId ), 38 | m_classFactory( intercom::detail::get_class_factory( classId ) ) 39 | { 40 | } 41 | 42 | template< typename TInterface > 43 | intercom::RawInterface create() 44 | { 45 | intercom::RawInterface< TInterface > itf; 46 | intercom::HRESULT error = m_classFactory->CreateInstance( nullptr, TInterface::ID, itf.out() ); 47 | switch( error ) 48 | { 49 | case intercom::SC_OK: 50 | break; 51 | 52 | case intercom::EC_NOINTERFACE: 53 | throw intercom::NoSuchInterface( m_classId, TInterface::ID ); 54 | 55 | // Unspecified error. 56 | default: 57 | throw intercom::RuntimeError( error, std::stringstream() << "Creating instance of class \"" 58 | << m_classId << "\" with interface \"" << TInterface::ID << "\" failed." ); 59 | } 60 | return itf; 61 | } 62 | 63 | intercom::HRESULT create( 64 | const intercom::IID& riid, 65 | void** itf 66 | ) 67 | { 68 | return m_classFactory->CreateInstance( nullptr, riid, (void**) itf ); 69 | } 70 | 71 | private: 72 | 73 | intercom::CLSID m_classId; 74 | intercom::RawInterface< IClassFactory > m_classFactory; 75 | 76 | }; 77 | 78 | } 79 | 80 | 81 | #endif 82 | -------------------------------------------------------------------------------- /intercom-cpp/src/callingconvention.hpp: -------------------------------------------------------------------------------- 1 | #ifndef INTERCOM_CPP_CALLINGCONCENTION_H 2 | #define INTERCOM_CPP_CALLINGCONCENTION_H 3 | 4 | // On Windows platform the calling convention set by COM is used. 5 | #ifdef _MSC_VER 6 | 7 | // stdcall 8 | #define INTERCOM_CC __stdcall 9 | #else 10 | 11 | // cdecl 12 | #define INTERCOM_CC 13 | 14 | #endif 15 | 16 | #endif 17 | -------------------------------------------------------------------------------- /intercom-cpp/src/classfactory.hpp: -------------------------------------------------------------------------------- 1 | #ifndef INTERCOM_CPP_CLASSFACTORY_H 2 | #define INTERCOM_CPP_CLASSFACTORY_H 3 | 4 | #include 5 | #include 6 | 7 | #include "detail/iclassfactory.hpp" 8 | 9 | #include "activator.hpp" 10 | #include "cominterop.hpp" 11 | 12 | namespace intercom 13 | { 14 | template< class TClass > 15 | class ClassFactory 16 | { 17 | public: 18 | 19 | ClassFactory() : 20 | m_initializationGuard() 21 | { 22 | } 23 | 24 | ~ClassFactory() 25 | { 26 | } 27 | 28 | /** 29 | * @brief Instantiates a new COM object. 30 | * 31 | * @param iid Identifies the interface. 32 | * @param instance Receives a pointer to specified interface of the new object. 33 | * @return intercom::HRESULT Returns SC_OK on success. 34 | */ 35 | template< typename TInterface > 36 | intercom::RawInterface create() 37 | { 38 | // Need an activator to create objects. 39 | intercom::HRESULT preparation = prepare_activator(); 40 | if( preparation != SC_OK ) 41 | { 42 | throw intercom::RuntimeError( preparation, 43 | std::stringstream() << "Preparing activator for the class factory of class \"" 44 | << TClass::ID << "\" failed." ); 45 | } 46 | 47 | return m_activator->create(); 48 | } 49 | 50 | /** 51 | * @brief Instantiates a new COM object. 52 | * 53 | * @param iid Identifies the interface. 54 | * @param instance Receives a pointer to specified interface of the new object. 55 | * @return intercom::HRESULT Returns SC_OK on success. 56 | */ 57 | intercom::HRESULT create( 58 | const intercom::IID& iid, 59 | void** instance 60 | ) 61 | { 62 | // Need an activator to create objects. 63 | intercom::HRESULT preparation = prepare_activator(); 64 | if( preparation != SC_OK ) 65 | return preparation; 66 | 67 | return m_activator->create( iid, instance ); 68 | } 69 | 70 | private: 71 | 72 | /** 73 | * @brief Ensures the activator has been initialized. Initializes the activator if not already initialized. 74 | * 75 | * @return intercom::HRESULT 76 | */ 77 | intercom::HRESULT prepare_activator() 78 | { 79 | try 80 | { 81 | std::call_once(m_initializationGuard, [&](){ 82 | this->m_activator = std::make_unique< Activator >( TClass::Library::NAME, TClass::ID ); } ); 83 | } 84 | catch( std::bad_alloc ) 85 | { 86 | return EC_OUTOFMEMORY; 87 | } 88 | catch( ... ) 89 | { 90 | return EC_FAIL; 91 | } 92 | return SC_OK; 93 | } 94 | 95 | std::once_flag m_initializationGuard; 96 | std::unique_ptr< intercom::Activator > m_activator; 97 | 98 | }; 99 | } 100 | 101 | #endif -------------------------------------------------------------------------------- /intercom-cpp/src/comdef.hpp: -------------------------------------------------------------------------------- 1 | 2 | // Includes declarations associated with COM. 3 | 4 | // Use predefined set if available. 5 | #ifdef _MSC_VER 6 | #include 7 | #include 8 | #else 9 | 10 | #include "posix/iunknown.hpp" 11 | #include "posix/isupporterrorinfo.hpp" 12 | #include "posix/idispatch.hpp" 13 | 14 | #endif 15 | -------------------------------------------------------------------------------- /intercom-cpp/src/cominterop.hpp: -------------------------------------------------------------------------------- 1 | 2 | #ifndef INTERCOM_CPP_DLLGETCLASSOBJECT 3 | #define INTERCOM_CPP_DLLGETCLASSOBJECT 4 | 5 | #ifdef _MSC_VER 6 | #include 7 | #else 8 | 9 | #include "comdef.hpp" 10 | #include "datatypes.hpp" 11 | #include "error_codes.hpp" 12 | #include "guiddef.hpp" 13 | 14 | intercom::HRESULT DllGetClassObject( 15 | intercom::REFCLSID rclsid, 16 | intercom::REFIID riid, 17 | void** ppv 18 | ); 19 | 20 | #endif 21 | 22 | #endif 23 | -------------------------------------------------------------------------------- /intercom-cpp/src/conversions.hpp: -------------------------------------------------------------------------------- 1 | 2 | 3 | #ifndef INTERCOM_CPP_STIRNGS_H 4 | #define INTERCOM_CPP_STIRNGS_H 5 | 6 | 7 | 8 | #ifdef _MSC_VER 9 | #include "msvc/conversions.hpp" 10 | #else 11 | #include "posix/conversions.hpp" 12 | #endif 13 | 14 | 15 | #endif 16 | -------------------------------------------------------------------------------- /intercom-cpp/src/datatypes.hpp: -------------------------------------------------------------------------------- 1 | 2 | #ifndef INTERCOM_CPP_DATATYPES_H 3 | #define INTERCOM_CPP_DATATYPES_H 4 | 5 | 6 | 7 | #ifdef _MSC_VER 8 | #include "msvc/datatypes.hpp" 9 | #else 10 | #include "posix/datatypes.hpp" 11 | #endif 12 | 13 | 14 | #endif 15 | -------------------------------------------------------------------------------- /intercom-cpp/src/detail/bstr_buffer.hpp: -------------------------------------------------------------------------------- 1 | 2 | #ifndef INTERCOM_CPP_DETAIL_BSTRBUFFER_H 3 | #define INTERCOM_CPP_DETAIL_BSTRBUFFER_H 4 | 5 | #include "../datatypes.hpp" 6 | #include "../memory.hpp" 7 | 8 | #include 9 | 10 | namespace intercom 11 | { 12 | namespace detail 13 | { 14 | 15 | /** 16 | * @brief Holds a BSTR string. 17 | * 18 | * A BSTR is a null terminated UTF-16 string prefixed with a length. 19 | * On Windows platform it is allocated with the function SysAllocString. 20 | * On other platforms standard libc malloc is used. 21 | */ 22 | class BstrBuffer 23 | { 24 | // Initialization methods. 25 | public: 26 | 27 | /** 28 | * @brief Initializes the Bstr from an existing string. 29 | * 30 | * @param attached The string attached to the new Bstr object. 31 | */ 32 | BstrBuffer( 33 | intercom::BSTR&& attached 34 | ) : 35 | m_value( std::forward< intercom::BSTR >( attached ) ) 36 | { 37 | attached = nullptr; 38 | } 39 | 40 | ~BstrBuffer() 41 | { 42 | intercom::free_bstr( m_value ); 43 | } 44 | 45 | /** 46 | * @brief Casts the BstrBuffer into BSTR. 47 | * 48 | * @return operator const intercom::Bstr const 49 | */ 50 | operator const intercom::BSTR() const noexcept { return m_value; } 51 | 52 | /** 53 | * @brief Returns the address of the internal buffer. 54 | * 55 | * @return intercom::BSTR* 56 | */ 57 | intercom::BSTR* operator&() { return &m_value; } 58 | 59 | /** 60 | * @brief Gets the number of characters in the buffer. 61 | * 62 | * @return size_t Returns the number of characters in the buffer. 63 | */ 64 | size_t character_count() const noexcept 65 | { 66 | uint32_t data_length_in_bytes; 67 | std::memcpy( &data_length_in_bytes, reinterpret_cast< char* >( m_value ) - 4, sizeof( uint32_t ) ); 68 | return data_length_in_bytes / sizeof( intercom::OLECHAR ); 69 | } 70 | 71 | /** 72 | * @brief Detaches the buffer. 73 | * 74 | * @return intercom::BSTR 75 | */ 76 | intercom::BSTR detach() noexcept 77 | { 78 | intercom::BSTR detached = m_value; m_value = nullptr; 79 | return detached; 80 | } 81 | 82 | // Private data. 83 | private: 84 | 85 | intercom::BSTR m_value; 86 | 87 | }; 88 | 89 | } 90 | } 91 | 92 | #endif 93 | -------------------------------------------------------------------------------- /intercom-cpp/src/detail/char_buffer.hpp: -------------------------------------------------------------------------------- 1 | 2 | #ifndef INTERCOM_CPP_DETAIL_CHARBUFFER_H 3 | #define INTERCOM_CPP_DETAIL_CHARBUFFER_H 4 | 5 | #include "../datatypes.hpp" 6 | #include "../memory.hpp" 7 | 8 | #include 9 | #include 10 | 11 | namespace intercom 12 | { 13 | namespace detail 14 | { 15 | 16 | /** 17 | * @brief Holds a string allocated with std::malloc. 18 | */ 19 | template< typename TCharType > 20 | class CharBuffer 21 | { 22 | // Initialization methods. 23 | public: 24 | 25 | /** 26 | * @brief Initializes the buffer from an existing string. 27 | * 28 | * @param attached The string attached to the new CharBuffer object. 29 | */ 30 | CharBuffer( 31 | TCharType*&& attached 32 | ) : 33 | m_value( std::forward< TCharType* >( attached ) ) 34 | { 35 | attached = nullptr; 36 | } 37 | 38 | ~CharBuffer() 39 | { 40 | std::free( m_value ); 41 | } 42 | 43 | /** 44 | * @brief Casts the buffer into appropriate string. 45 | * 46 | * @return operator const TCharType* const 47 | */ 48 | operator const TCharType*() const noexcept { return m_value; } 49 | 50 | TCharType** operator&() { return &m_value; } 51 | 52 | /** 53 | * @brief Comparsion operator for check 54 | * 55 | * @return true Returns true if no buffer is attached. 56 | * @return false Returns false when a valid buffer is attached. 57 | */ 58 | bool operator==( 59 | std::nullptr_t 60 | ) const noexcept 61 | { return m_value == nullptr; } 62 | 63 | /** 64 | * @brief Gets the number of characters in the buffer. 65 | * 66 | * @return size_t Returns the number of characters in the buffer. 67 | */ 68 | size_t character_count() const noexcept 69 | { 70 | return std::strlen( m_value ); 71 | } 72 | 73 | /** 74 | * @brief Detaches the buffer. 75 | * 76 | * @return intercom::BSTR 77 | */ 78 | TCharType* detach() noexcept 79 | { 80 | TCharType* detached = m_value; m_value = nullptr; 81 | return detached; 82 | } 83 | 84 | // Private data. 85 | private: 86 | 87 | TCharType* m_value; 88 | 89 | }; 90 | 91 | } 92 | } 93 | 94 | #endif -------------------------------------------------------------------------------- /intercom-cpp/src/detail/declarations.hpp: -------------------------------------------------------------------------------- 1 | 2 | #ifndef INTERCOM_CPP_DETAIL_DECLARATIONS_H 3 | #define INTERCOM_CPP_DETAIL_DECLARATIONS_H 4 | 5 | 6 | #include "../guiddef.hpp" 7 | #include "../datatypes.hpp" 8 | 9 | namespace intercom 10 | { 11 | namespace detail 12 | { 13 | 14 | typedef intercom::HRESULT ( *GetClassObjectFunc ) ( const intercom::CLSID&, const intercom::IID&, void** ); 15 | 16 | typedef intercom::HRESULT ( *IntercomListClassObjectsFunc ) ( size_t*, intercom::CLSID** ); 17 | 18 | } 19 | } 20 | 21 | #endif 22 | -------------------------------------------------------------------------------- /intercom-cpp/src/detail/dlwrapper.hpp: -------------------------------------------------------------------------------- 1 | 2 | #ifndef INTERCOM_CPP_DETAIL_DLWRAPPER_H 3 | #define INTERCOM_CPP_DETAIL_DLWRAPPER_H 4 | 5 | #ifdef _MSC_VER 6 | #include "../msvc/dlwrapper.hpp" 7 | namespace intercom { namespace detail { using DlWrapper = intercom::msvc::DlWrapper; } } 8 | #else 9 | 10 | #include "../posix/dlwrapper.hpp" 11 | namespace intercom { namespace detail { using DlWrapper = intercom::posix::DlWrapper; } } 12 | 13 | #endif 14 | 15 | #endif 16 | -------------------------------------------------------------------------------- /intercom-cpp/src/detail/get_class_factory.hpp: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | #ifndef INTERCOM_CPP_GETCLASSFACTORY_H 5 | #define INTERCOM_CPP_GETCLASSFACTORY_H 6 | 7 | #ifdef _MSC_VER 8 | #include "msvc/get_class_factory.hpp" 9 | #else 10 | #include "posix/get_class_factory.hpp" 11 | #endif 12 | 13 | 14 | #endif -------------------------------------------------------------------------------- /intercom-cpp/src/detail/hresult_errors.hpp: -------------------------------------------------------------------------------- 1 | 2 | #ifndef INTERCOM_CPP_DETAIL_HRESULTERRORS_H 3 | #define INTERCOM_CPP_DETAIL_HRESULTERRORS_H 4 | 5 | #include "hresult.hpp" 6 | 7 | namespace intercom 8 | { 9 | namespace detail 10 | { 11 | /** 12 | * @brief Defines the HRESULT codes used by intercom. 13 | * 14 | */ 15 | namespace hresult 16 | { 17 | static const HRESULT SC_OK = 0; 18 | static const HRESULT SC_FALSE = 1; 19 | static const HRESULT EC_NOTIMPL = null_error( 0x4001 ); 20 | static const HRESULT EC_NOINTERFACE = null_error( 0x4002 ); 21 | static const HRESULT EC_POINTER = null_error( 0x4003 ); 22 | static const HRESULT EC_ABORT = null_error( 0x4003 ); 23 | static const HRESULT EC_FAIL = null_error( 0x4005 ); 24 | static const HRESULT EC_CLASSNOTREG = itf_error( 0x0154 ); 25 | static_assert( NullError( 0x4005 ).error_code() == 0x4005, "Internal check failed: Invalid error code storage." ); 26 | 27 | static const HRESULT EC_OUTOFMEMORY = win32_error( 0x000E ); 28 | static const HRESULT EC_INVALIDARG = win32_error( 0x0057 ); 29 | } 30 | } 31 | } 32 | 33 | #endif 34 | -------------------------------------------------------------------------------- /intercom-cpp/src/detail/iclassfactory.hpp: -------------------------------------------------------------------------------- 1 | 2 | 3 | #ifndef INTERCOM_CPP_ICLASSFACTORY_H 4 | #define INTERCOM_CPP_ICLASSFACTORY_H 5 | 6 | #ifdef _MSC_VER 7 | #include 8 | 9 | namespace intercom { using IClassFactory = ::IClassFactory; } 10 | 11 | #else 12 | #include "posix/iclassfactory.hpp" 13 | #endif 14 | 15 | 16 | #endif -------------------------------------------------------------------------------- /intercom-cpp/src/detail/posix/iclassfactory.hpp: -------------------------------------------------------------------------------- 1 | #ifndef INTERCOM_CPP_POSIX_ICLASSFACTORY_H 2 | #define INTERCOM_CPP_POSIX_ICLASSFACTORY_H 3 | 4 | #include "../../callingconvention.hpp" 5 | #include "../../posix/iunknown.hpp" 6 | 7 | namespace intercom 8 | { 9 | 10 | // MIDL_INTERFACE("00000001-0000-0000-C000-000000000046") 11 | struct IClassFactory : public IUnknown 12 | { 13 | public: 14 | 15 | virtual intercom::HRESULT INTERCOM_CC CreateInstance( 16 | IUnknown *pUnkOuter, 17 | intercom::REFIID riid, 18 | void **ppvObject 19 | ) = 0; 20 | 21 | virtual intercom::HRESULT INTERCOM_CC LockServer( 22 | int fLock 23 | ) = 0; 24 | 25 | }; 26 | 27 | static const intercom::IID IID_IClassFactory = { 0x00000001, 0x0000, 0x0000, { 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x46 } }; 28 | 29 | } 30 | 31 | #endif 32 | -------------------------------------------------------------------------------- /intercom-cpp/src/detail/utility.hpp: -------------------------------------------------------------------------------- 1 | 2 | #ifndef INTERCOM_CPP_DETAIL_UTILITY_H 3 | #define INTERCOM_CPP_DETAIL_UTILITY_H 4 | 5 | 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | 12 | 13 | namespace intercom 14 | { 15 | namespace detail 16 | { 17 | 18 | /** 19 | * @brief Tests whether the machine is little endian or not. 20 | * 21 | * @return true The machine is little endian. 22 | * @return false The machine is big endian. 23 | */ 24 | inline bool is_little_endian() 25 | { 26 | static_assert( sizeof( unsigned char ) < sizeof( int ), "" ); 27 | int test_value = 1; 28 | unsigned char* asBytes = reinterpret_cast< unsigned char* >( &test_value ); 29 | return asBytes[ 0 ] == 1; 30 | } 31 | 32 | /** 33 | * @brief Writes the specified binary data into the stream as hex. 34 | * 35 | * @param stream Target stream. 36 | * @param converter The binary data to write. 37 | * @return std::ostream& Returns the stream. 38 | */ 39 | template< typename TData, typename = typename std::enable_if< std::is_unsigned< TData >::value, void >::type > 40 | inline std::ostream& write_as_hex( 41 | std::ostream& stream, 42 | TData data 43 | ) 44 | { 45 | // Determine the order in which we need to print the data. 46 | stream << std::hex << std::uppercase; 47 | unsigned char* asBytes = reinterpret_cast< unsigned char* >( &data ); 48 | if( is_little_endian() ) 49 | { 50 | // On little-endian machine the bytes are on reverse order. 51 | unsigned char* rbegin = asBytes + sizeof( TData ) - 1; 52 | unsigned char* rend = asBytes - 1; 53 | for( auto byte = rbegin; byte != rend; --byte ) 54 | { 55 | // Explicit cast required, otherwise byte is treated as unsigned char. 56 | stream << static_cast< unsigned int >( ( *byte & 0xF0 ) >> 4 ); 57 | stream << static_cast< unsigned int >( *byte & 0x0F ); 58 | } 59 | } 60 | else 61 | { 62 | unsigned char* begin = asBytes; 63 | unsigned char* end = asBytes + sizeof( TData ); 64 | for( auto byte = begin; byte != end; ++byte ) 65 | { 66 | // Explicit cast required, otherwise byte is treated as unsigned char. 67 | stream << static_cast< unsigned int >( ( *byte & 0xF0 ) >> 4 ); 68 | stream << static_cast< unsigned int >( *byte & 0x0F ); 69 | } 70 | } 71 | 72 | return stream; 73 | } 74 | 75 | //! Hashes a value to an existing hash. 76 | template< typename TValue > 77 | inline void hash_combine( 78 | size_t& seed, 79 | const TValue& value 80 | ) noexcept 81 | { 82 | // Implemenation copied from boost. 83 | std::hash< TValue > hasher; 84 | seed ^= hasher( value ) + 0x9e3779b9 + (seed<<6) + (seed>>2); 85 | } 86 | } 87 | } 88 | 89 | #endif 90 | -------------------------------------------------------------------------------- /intercom-cpp/src/error_codes.hpp: -------------------------------------------------------------------------------- 1 | 2 | #ifndef INTERCOM_CPP_ERRORCODES_H 3 | #define INTERCOM_CPP_ERRORCODES_H 4 | 5 | #include 6 | #include "detail/hresult_errors.hpp" 7 | 8 | /** 9 | * @brief Defines the error codes use by "intercom". 10 | * 11 | */ 12 | namespace intercom 13 | { 14 | static const intercom::HRESULT SC_OK = intercom::detail::hresult::SC_OK; 15 | static const intercom::HRESULT SC_FALSE = intercom::detail::hresult::SC_FALSE; 16 | static const intercom::HRESULT EC_FAIL = intercom::detail::hresult::EC_FAIL; 17 | static const intercom::HRESULT EC_NOTIMPL = intercom::detail::hresult::EC_NOTIMPL; 18 | static const intercom::HRESULT EC_NOINTERFACE = intercom::detail::hresult::EC_NOINTERFACE; 19 | static const intercom::HRESULT EC_OUTOFMEMORY = intercom::detail::hresult::EC_OUTOFMEMORY; 20 | static const intercom::HRESULT EC_INVALIDARG = intercom::detail::hresult::EC_INVALIDARG; 21 | static const intercom::HRESULT EC_POINTER = intercom::detail::hresult::EC_POINTER; 22 | static const intercom::HRESULT EC_CLASSNOTREG = intercom::detail::hresult::EC_CLASSNOTREG; 23 | static_assert( EC_FAIL == 0x80004005, "Internal check failed: Invalid error code structure." ); 24 | 25 | /** 26 | * @brief Checks whether the error represents success. 27 | * 28 | * @param error_code Specifies the error code. 29 | * @return true If the error code represents success. 30 | * @return false If the error code represents failure. 31 | */ 32 | constexpr bool succeeded( 33 | intercom::HRESULT error_code 34 | ) noexcept 35 | { 36 | // The first bit of HRESULT codes is always '1' if the error code indicates a failure. 37 | return static_cast< int32_t >( error_code ) >= 0; 38 | } 39 | 40 | /** 41 | * @brief Checks whether the error represents failure. 42 | * 43 | * @param error_code 44 | * @return true If the error code represents failure. 45 | * @return false If the error code represents success. 46 | */ 47 | constexpr bool failed( 48 | intercom::HRESULT error_code 49 | ) noexcept 50 | { 51 | // The first bit of HRESULT codes is always '1' if the error code indicates a failure. 52 | return static_cast< int32_t >( error_code ) < 0; 53 | } 54 | } 55 | 56 | // Use predefined set if available. 57 | #ifdef _MSC_VER 58 | 59 | #include 60 | static_assert( E_FAIL == intercom::EC_FAIL, "Definition of intercom errors are invalid." ); 61 | 62 | #else 63 | #include "posix/error_codes.hpp" 64 | #endif 65 | 66 | 67 | #endif 68 | -------------------------------------------------------------------------------- /intercom-cpp/src/functions.hpp: -------------------------------------------------------------------------------- 1 | #ifndef INTERCOM_CPP_FUNCTIONS_H 2 | #define INTERCOM_CPP_FUNCTIONS_H 3 | 4 | #include "guiddef.hpp" 5 | #include "datatypes.hpp" 6 | #include "raw_interface.hpp" 7 | #include "detail/get_class_factory.hpp" 8 | #include "detail/iclassfactory.hpp" 9 | 10 | namespace intercom 11 | { 12 | /** 13 | * @brief Create an object of a class. 14 | * 15 | * @param class_id Specifies the class to return. 16 | * @param riid Specifies the interface to return. 17 | * @param itf Receives a pointer to the interface. 18 | * @return intercom::HRESULT Result. 19 | */ 20 | inline intercom::HRESULT create_instance( 21 | const intercom::CLSID& class_id, 22 | const intercom::IID& riid, 23 | void** itf 24 | ) 25 | { 26 | // Locate factory for the object. 27 | intercom::RawInterface< intercom::IClassFactory > factory; 28 | intercom::HRESULT factory_result = intercom::detail::get_class_factory( 29 | class_id, &factory ); 30 | if( intercom::failed( factory_result ) ) 31 | return factory_result; 32 | 33 | return factory->CreateInstance( nullptr, riid, itf ); 34 | } 35 | 36 | /** 37 | * @brief Attempts to register a library for the "intercom". 38 | * 39 | * @param library_name The name of the library to register 40 | * @tparam TArray The classes the caller expects the library to implement. 41 | * @return Returns true if registering the library succeed and all the expected classes are availab.e. 42 | */ 43 | template< typename TArray > 44 | inline bool try_register_library( 45 | const char* library_name, 46 | TArray& expected_classes 47 | ) 48 | { 49 | return ::intercom::detail::try_register_library( library_name, 50 | expected_classes.data(), expected_classes.data() + expected_classes.size() ); 51 | } 52 | } 53 | 54 | 55 | #ifdef INTERCOM_FLATTEN_DECLARATIONS 56 | 57 | inline intercom::HRESULT CreateInstance( intercom::REFCLSID clsid, intercom::REFIID iid, void** pout ) 58 | { 59 | return intercom::create_instance( clsid, iid, pouc ); 60 | } 61 | 62 | #endif 63 | 64 | #endif 65 | -------------------------------------------------------------------------------- /intercom-cpp/src/guiddef.hpp: -------------------------------------------------------------------------------- 1 | 2 | #ifndef INTERCOM_CPP_GUIDDEF_H 3 | #define INTERCOM_CPP_GUIDDEF_H 4 | 5 | 6 | #ifdef _MSC_VER 7 | #include "msvc/guiddef.hpp" 8 | #else 9 | #include "posix/guiddef.hpp" 10 | #endif 11 | 12 | #include "detail/utility.hpp" 13 | 14 | namespace intercom 15 | { 16 | /** 17 | * @brief Writes the specified IID into a stream. 18 | * 19 | * @param stream Target stream. 20 | * @param iid IID to write to the stream. 21 | * @return std::ostream& Returns the stream. 22 | */ 23 | inline std::ostream& operator<<( 24 | std::ostream& stream, 25 | const IID& iid 26 | ) 27 | { 28 | stream << "{"; 29 | intercom::detail::write_as_hex( stream, iid.Data1 ); 30 | stream << "-"; 31 | intercom::detail::write_as_hex( stream, iid.Data2 ); 32 | stream << "-"; 33 | intercom::detail::write_as_hex( stream, iid.Data3 ); 34 | stream << "-"; 35 | intercom::detail::write_as_hex( stream, iid.Data4[0] ); 36 | intercom::detail::write_as_hex( stream, iid.Data4[1] ); 37 | stream << "-"; 38 | for( size_t d = 2; d < sizeof( iid.Data4 ); ++d ) 39 | intercom::detail::write_as_hex( stream, iid.Data4[d] ); 40 | stream << "}"; 41 | return stream; 42 | } 43 | 44 | /** 45 | * @brief Compares two interface ids. 46 | * 47 | * @param lhs Left side of the operator. 48 | * @param rhs Right side of the operator. 49 | * @return true 50 | * @return false 51 | */ 52 | inline bool operator==( 53 | const intercom::IID& lhs, 54 | const intercom::IID& rhs 55 | ) noexcept 56 | { 57 | return memcmp( &lhs, &rhs, sizeof( intercom::IID ) ) == 0; 58 | } 59 | 60 | //! Declaration for a generic hash function for intercom types. 61 | template< typename THashed > 62 | struct hash; 63 | 64 | //! Calculates a hash code for a IID. 65 | template<> 66 | struct hash 67 | { 68 | inline size_t operator()( 69 | const intercom::IID& guid 70 | ) const noexcept 71 | { 72 | size_t hash_code = 0; 73 | intercom::detail::hash_combine( hash_code, guid.Data1 ); 74 | intercom::detail::hash_combine( hash_code, guid.Data2 ); 75 | intercom::detail::hash_combine( hash_code, guid.Data3 ); 76 | for( uint8_t b : guid.Data4 ) 77 | intercom::detail::hash_combine( hash_code, b ); 78 | return hash_code; 79 | } 80 | }; 81 | } 82 | 83 | #endif 84 | -------------------------------------------------------------------------------- /intercom-cpp/src/memory.hpp: -------------------------------------------------------------------------------- 1 | 2 | 3 | #ifndef INTERCOM_CPP_MEMORY_H 4 | #define INTERCOM_CPP_MEMORY_H 5 | 6 | 7 | 8 | #ifdef _MSC_VER 9 | #include "msvc/memory.hpp" 10 | #else 11 | #include "posix/memory.hpp" 12 | #endif 13 | 14 | 15 | #endif 16 | -------------------------------------------------------------------------------- /intercom-cpp/src/msdef.hpp: -------------------------------------------------------------------------------- 1 | 2 | // Defines for miscellaneous Windows specific macros for other platforms. 3 | // Required to compile MIDL interfaces generated with midl.exe until we have cross-platform header generator. 4 | #define OUT 5 | -------------------------------------------------------------------------------- /intercom-cpp/src/msvc/datatypes.hpp: -------------------------------------------------------------------------------- 1 | #ifndef INTERCOM_CPP_MSVC_DATATYPES_H 2 | #define INTERCOM_CPP_MSVC_DATATYPES_H 3 | 4 | #include 5 | 6 | // Use predefined set if available. 7 | #include 8 | 9 | // The generated C++ headers and classes expect the data types in intercom namespace. 10 | namespace intercom 11 | { 12 | namespace _internal 13 | { 14 | template< typename TTarget, typename TSource > 15 | TTarget checked_cast( TSource source ) 16 | { 17 | if( std::numeric_limits< TTarget >::is_signed && 18 | ! std::numeric_limits< TSource >::is_signed ) 19 | { 20 | // Target signed, Source not signed. 21 | // -> No need to check the min bound. 22 | // 23 | // Min bound check would result in bad checks due to type 24 | // conversion to signed, etc. 25 | if( source > ( std::numeric_limits< TTarget >::max )() ) 26 | { 27 | _ASSERTE( false ); 28 | throw std::runtime_error( "Value out of range" ); 29 | } 30 | } 31 | else 32 | { 33 | // Every other case. 34 | if( source < ( std::numeric_limits< TTarget >::min )() ) 35 | { 36 | _ASSERTE( false ); 37 | throw std::runtime_error( "Value out of range" ); 38 | } 39 | if( source > ( std::numeric_limits< TTarget >::max )() ) 40 | { 41 | _ASSERTE( false ); 42 | throw std::runtime_error( "Value out of range" ); 43 | } 44 | } 45 | 46 | return static_cast< TTarget >( source ); 47 | } 48 | } 49 | 50 | typedef INT INT; 51 | typedef UINT UINT; 52 | typedef INT8 INT8; 53 | typedef UINT8 UINT8; 54 | typedef INT16 INT16; 55 | typedef UINT16 UINT16; 56 | typedef INT32 INT32; 57 | typedef UINT32 UINT32; 58 | typedef INT64 INT64; 59 | typedef UINT64 UINT64; 60 | 61 | typedef BOOL BOOL; 62 | typedef DWORD DWORD; 63 | typedef WORD WORD; 64 | 65 | typedef CHAR CHAR; 66 | typedef SHORT SHORT; 67 | typedef LONG LONG; 68 | typedef LONGLONG LONGLONG; 69 | typedef BYTE BYTE; 70 | typedef USHORT USHORT; 71 | typedef ULONG ULONG; 72 | typedef ULONGLONG ULONGLONG; 73 | typedef DOUBLE DOUBLE; 74 | typedef FLOAT FLOAT; 75 | 76 | typedef OLECHAR OLECHAR; 77 | typedef BSTR BSTR; 78 | 79 | typedef HRESULT HRESULT; 80 | typedef SCODE SCODE; 81 | 82 | typedef DATE DATE; 83 | typedef VARIANT_BOOL VARIANT_BOOL; 84 | typedef CURRENCY CURRENCY; 85 | 86 | typedef PVOID PVOID; 87 | 88 | //! 32-bit reference counter. unsigned long is 32-bit in Windows and 64-bit on Unix. 89 | typedef unsigned long REF_COUNT_32; 90 | } 91 | 92 | #endif 93 | -------------------------------------------------------------------------------- /intercom-cpp/src/msvc/guiddef.hpp: -------------------------------------------------------------------------------- 1 | 2 | #ifndef INTERCOM_CPP_MSVC_GUIDDEF_H 3 | #define INTERCOM_CPP_MSVC_GUIDDEF_H 4 | 5 | #include 6 | 7 | 8 | // The generated C++ headers and classes expect the IID, GUID and CLSID in intercom namespace. 9 | namespace intercom 10 | { 11 | 12 | 13 | 14 | 15 | typedef ::GUID GUID; 16 | 17 | typedef ::IID IID; 18 | typedef ::IID CLSID; 19 | } 20 | 21 | #endif 22 | -------------------------------------------------------------------------------- /intercom-cpp/src/msvc/memory.hpp: -------------------------------------------------------------------------------- 1 | 2 | #ifndef INTERCOM_CPP_POSIX_STIRNGS_H 3 | #define INTERCOM_CPP_POSIX_STIRNGS_H 4 | 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | #include "datatypes.hpp" 12 | 13 | namespace intercom 14 | { 15 | /** 16 | * @brief Allocates memory for BSTR. 17 | * 18 | * @return intercom::OLECHAR Returns an uninitialized BSTR. 19 | */ 20 | inline intercom::BSTR allocate_bstr( 21 | uint32_t character_count 22 | ) 23 | { 24 | return SysAllocStringLen( nullptr, character_count ); 25 | } 26 | 27 | /** 28 | * @brief Frees previously allocated bstr. 29 | * 30 | * @param bstr buffer to free. 31 | */ 32 | inline void free_bstr( 33 | intercom::BSTR bstr 34 | ) noexcept 35 | { 36 | SysFreeString( bstr ); 37 | } 38 | 39 | /** 40 | * @brief Reallocates a bstr. 41 | * 42 | * @param bstr 43 | * @param new_size 44 | * @return void* 45 | */ 46 | inline intercom::BSTR* realloc_bstr( 47 | intercom::BSTR* bstr, 48 | size_t new_size 49 | ) 50 | { 51 | int int_size = _internal::checked_cast< int >( new_size ); 52 | if( ! SysReAllocStringLen( OUT bstr, nullptr, int_size ) ) 53 | throw std::bad_alloc(); 54 | return bstr; 55 | } 56 | 57 | /** 58 | * @brief Gets the number of characters available within the given BSTR. 59 | * 60 | */ 61 | inline size_t get_characters_in_bstr( 62 | const intercom::BSTR bstr 63 | ) 64 | { 65 | return SysStringLen( bstr ); 66 | } 67 | 68 | /** 69 | * @brief Allocates memory for a string. 70 | * 71 | * @return intercom::OLECHAR Returns an uninitialized string with null-terminator. 72 | */ 73 | template< typename TCharType > 74 | inline TCharType* allocate_string( 75 | uint32_t character_count 76 | ) 77 | { 78 | TCharType* string = reinterpret_cast< TCharType* >( 79 | CoTaskMemAlloc( sizeof( TCharType ) * ( character_count + 1 ) ) ); 80 | string[ character_count ] = 0; 81 | 82 | return string; 83 | } 84 | 85 | /** 86 | * @brief Deallocates the string. 87 | */ 88 | template< typename TCharType > 89 | inline void free_string( 90 | TCharType* string 91 | ) 92 | { 93 | CoTaskMemFree( string ); 94 | } 95 | 96 | /** 97 | * @brief Reallocates a strng. 98 | * 99 | * @param string 100 | * @param new_size The new size fo the string buffer 101 | * @return void* 102 | */ 103 | template< typename TCharType > 104 | inline void* realloc_string( 105 | void* string, 106 | size_t new_size 107 | ) 108 | { 109 | return CoTaskMemRealloc( string, new_size ); 110 | } 111 | } 112 | 113 | #endif 114 | -------------------------------------------------------------------------------- /intercom-cpp/src/msvc/utility.hpp: -------------------------------------------------------------------------------- 1 | 2 | #ifndef INTERCOM_CPP_MSVC_UTILITY_H 3 | #define INTERCOM_CPP_MSVC_UTILITY_H 4 | 5 | 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | // Include minimal Win32 API. 12 | #ifndef WIN32_LEAN_AND_MEAN 13 | #define WIN32_LEAN_AND_MEAN 14 | #define INTERCOM_WIN32_LEAN_AND_MEAN_DEFINED 15 | #endif 16 | 17 | #ifndef VC_EXTRALEAN 18 | #define VC_EXTRALEAN 19 | #define INTERCOM_VC_EXTRALEAN_DEFINED 20 | #endif 21 | 22 | #include 23 | 24 | namespace intercom 25 | { 26 | namespace msvc 27 | { 28 | //! Attempts to get the error message for GetLastError() 29 | inline std::string get_last_error() 30 | { 31 | DWORD dwLastError = ::GetLastError(); 32 | char* buffer = nullptr; 33 | DWORD dwResult = ::FormatMessageA( FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM, 34 | nullptr, dwLastError, 0, (LPSTR) &buffer, 0, nullptr ); 35 | if( dwResult == 0 ) 36 | throw std::exception(); 37 | 38 | std::string error = buffer; 39 | ::LocalFree( buffer ); 40 | 41 | return buffer; 42 | } 43 | 44 | //! Throws an exception if last call to dl* failed. 45 | [[ noreturn ]] 46 | inline void throw_win32_error() 47 | { 48 | std::string error = get_last_error(); 49 | throw std::runtime_error( error ); 50 | } 51 | } 52 | } 53 | 54 | // Undefine WIN32_LEAN_AND_MEAN if we defined it to avoid causing problems for other developers. 55 | #ifdef INTERCOM_WIN32_LEAN_AND_MEAN_DEFINED 56 | #undef WIN32_LEAN_AND_MEAN 57 | #undef INTERCOM_WIN32_LEAN_AND_MEAN_DEFINED 58 | #endif 59 | 60 | // Undefine VC_EXTRALEAN if we defined it to avoid causing problems for other developers. 61 | #ifdef INTERCOM_VC_EXTRALEAN_DEFINED 62 | #undef VC_EXTRALEAN 63 | #undef INTERCOM_VC_EXTRALEAN_DEFINED 64 | #endif 65 | 66 | #endif -------------------------------------------------------------------------------- /intercom-cpp/src/msvc/variant.hpp: -------------------------------------------------------------------------------- 1 | 2 | #ifndef INTERCOM_CPP_MSVC_VARIANT_H 3 | #define INTERCOM_CPP_MSVC_VARIANT_H 4 | 5 | #include 6 | 7 | 8 | // The generated C++ headers and classes expect the VARIANT in intercom namespace. 9 | namespace intercom 10 | { 11 | typedef ::VARTYPE VARTYPE; 12 | typedef ::VARENUM VARENUM; 13 | typedef ::VARIANT VARIANT; 14 | 15 | typedef ::SAFEARR_BSTR SAFEARR_BSTR; 16 | typedef ::SAFEARR_UNKNOWN SAFEARR_UNKNOWN; 17 | typedef ::SAFEARR_DISPATCH SAFEARR_DISPATCH; 18 | typedef ::SAFEARR_VARIANT SAFEARR_VARIANT; 19 | typedef ::SAFEARR_HAVEIID SAFEARR_HAVEIID; 20 | 21 | typedef ::BYTE_SIZEDARR BYTE_SIZEDARR; 22 | typedef ::WORD_SIZEDARR WORD_SIZEDARR; 23 | typedef ::DWORD_SIZEDARR DWORD_SIZEDARR; 24 | 25 | typedef ::SAFEARRAYUNION SAFEARRAYUNION; 26 | typedef ::SAFEARRAYBOUND SAFEARRAYBOUND; 27 | typedef ::SAFEARRAY SAFEARRAY; 28 | 29 | using ::VT_EMPTY; 30 | using ::VT_NULL; 31 | using ::VT_I2; 32 | using ::VT_I4; 33 | using ::VT_R4; 34 | using ::VT_R8; 35 | using ::VT_CY; 36 | using ::VT_DATE; 37 | using ::VT_BSTR; 38 | using ::VT_DISPATCH; 39 | using ::VT_ERROR; 40 | using ::VT_BOOL; 41 | using ::VT_VARIANT; 42 | using ::VT_UNKNOWN; 43 | using ::VT_DECIMAL; 44 | using ::VT_I1; 45 | using ::VT_UI1; 46 | using ::VT_UI2; 47 | using ::VT_UI4; 48 | using ::VT_I8; 49 | using ::VT_UI8; 50 | using ::VT_INT; 51 | using ::VT_UINT; 52 | using ::VT_VOID; 53 | using ::VT_HRESULT; 54 | using ::VT_PTR; 55 | using ::VT_SAFEARRAY; 56 | using ::VT_CARRAY; 57 | using ::VT_USERDEFINED; 58 | using ::VT_LPSTR; 59 | using ::VT_LPWSTR; 60 | using ::VT_RECORD; 61 | using ::VT_INT_PTR; 62 | using ::VT_UINT_PTR; 63 | using ::VT_FILETIME; 64 | using ::VT_BLOB; 65 | using ::VT_STREAM; 66 | using ::VT_STORAGE; 67 | using ::VT_STREAMED_OBJECT; 68 | using ::VT_STORED_OBJECT; 69 | using ::VT_BLOB_OBJECT; 70 | using ::VT_CF; 71 | using ::VT_CLSID; 72 | using ::VT_VERSIONED_STREAM; 73 | using ::VT_BSTR_BLOB; 74 | using ::VT_VECTOR; 75 | using ::VT_ARRAY; 76 | using ::VT_BYREF; 77 | using ::VT_RESERVED; 78 | using ::VT_ILLEGAL; 79 | using ::VT_ILLEGALMASKED; 80 | using ::VT_TYPEMASK; 81 | } 82 | 83 | #endif 84 | -------------------------------------------------------------------------------- /intercom-cpp/src/no_such_interface.hpp: -------------------------------------------------------------------------------- 1 | 2 | #ifndef INTERCOM_CPP_NOSUCHINTERFACE_H 3 | #define INTERCOM_CPP_NOSUCHINTERFACE_H 4 | 5 | #include 6 | #include 7 | 8 | #include "guiddef.hpp" 9 | #include "error_codes.hpp" 10 | 11 | namespace intercom 12 | { 13 | 14 | /** 15 | * @brief Exception thrown when a COM object does not implement the requested interface. 16 | * 17 | */ 18 | class NoSuchInterface : public std::invalid_argument 19 | { 20 | public: 21 | 22 | /** 23 | * @brief Error code associated with the exception. 24 | * 25 | */ 26 | static const HRESULT ERROR_CODE = intercom::EC_NOINTERFACE; 27 | 28 | explicit NoSuchInterface( 29 | const intercom::IID& interface_id 30 | ) : 31 | std::invalid_argument( get_error_message( interface_id ) ), 32 | m_interface_id( interface_id ) 33 | { 34 | } 35 | 36 | explicit NoSuchInterface( 37 | const intercom::CLSID& class_id, 38 | const intercom::IID& interface_id 39 | ) : 40 | std::invalid_argument( get_error_message( class_id, interface_id ) ), 41 | m_interface_id( interface_id ) 42 | { 43 | } 44 | 45 | /** 46 | * @brief Returns the error associated with this exception. 47 | * 48 | * @return intercom::HRESULT Returns the error code. 49 | */ 50 | intercom::HRESULT error_code() const noexcept { return ERROR_CODE; } 51 | 52 | /** 53 | * @brief Gets the interface id associated with the error. 54 | * 55 | * @return intercom::IID interface_id cosnt 56 | */ 57 | intercom::IID interface_id() const noexcept { return m_interface_id; } 58 | 59 | private: 60 | 61 | /** 62 | * @brief Constructs error message for the exception from 63 | * 64 | * @return std::string Returns message. 65 | */ 66 | static std::string get_error_message( 67 | const intercom::IID& interface_id 68 | ) 69 | { 70 | std::stringstream fmt; 71 | fmt << "Interface \"" << interface_id << "\" not available."; 72 | return fmt.str(); 73 | } 74 | 75 | /** 76 | * @brief Constructs error message for the exception from 77 | * 78 | * @return std::string Returns message. 79 | */ 80 | static std::string get_error_message( 81 | const intercom::CLSID& class_id, 82 | const intercom::IID& interface_id 83 | ) 84 | { 85 | std::stringstream fmt; 86 | fmt << "Interface \"" << interface_id << "\" not available for the class \"" << class_id << "\"."; 87 | return fmt.str(); 88 | } 89 | 90 | private: 91 | 92 | const intercom::IID m_interface_id; 93 | }; 94 | 95 | 96 | } 97 | 98 | #endif 99 | -------------------------------------------------------------------------------- /intercom-cpp/src/posix/conversions.hpp: -------------------------------------------------------------------------------- 1 | 2 | #ifndef INTERCOM_CPP_POSIX_CONVERSIONS_H 3 | #define INTERCOM_CPP_POSIX_CONVERSIONS_H 4 | 5 | #include 6 | 7 | #include "converter.hpp" 8 | #include "memory.hpp" 9 | #include "../detail/bstr_buffer.hpp" 10 | #include "../detail/char_buffer.hpp" 11 | 12 | namespace intercom 13 | { 14 | /** 15 | * @brief Converts UTF-8 string to BSTR. 16 | * 17 | * The received BSTR must be deallocated with "intercom::free_bstr". 18 | */ 19 | inline void utf8_to_bstr( 20 | const char* utf8_string, 21 | intercom::BSTR* bstr_string 22 | ) 23 | { 24 | *bstr_string = nullptr; 25 | if( utf8_string == nullptr ) 26 | return; 27 | 28 | // Prepare output buffer. 29 | size_t input_length = strlen( utf8_string ); 30 | intercom::detail::BstrBuffer target( intercom::allocate_bstr( input_length ) ); 31 | 32 | size_t output_length = target.character_count(); 33 | intercom::posix::Converter converter( 34 | intercom::posix::Converter::Encoding::Utf8, 35 | intercom::posix::Converter::Encoding::Utf16 ); 36 | converter.convert( utf8_string, input_length, &target, &output_length, &intercom::realloc_bstr ); 37 | 38 | *bstr_string = target.detach(); 39 | } 40 | 41 | /** 42 | * @brief Converts BSTR string to UTF-8. 43 | * 44 | * The received char* must be deallocated with "free". 45 | */ 46 | inline void bstr_to_utf8( 47 | const intercom::BSTR bstr_string, 48 | char** utf8_string 49 | ) 50 | { 51 | *utf8_string = nullptr; 52 | if( bstr_string == nullptr ) 53 | return; 54 | 55 | // Prepare output buffer. 56 | size_t input_length = get_characters_in_bstr( bstr_string ); 57 | size_t output_length = input_length; 58 | intercom::detail::CharBuffer< char > target( intercom::allocate_string< char >( output_length ) ); 59 | if( target == nullptr ) 60 | throw std::bad_alloc(); 61 | 62 | intercom::posix::Converter converter( 63 | intercom::posix::Converter::Encoding::Utf16, 64 | intercom::posix::Converter::Encoding::Utf8 ); 65 | converter.convert( bstr_string, input_length, &target, &output_length, &intercom::realloc_string< char > ); 66 | 67 | *utf8_string = target.detach(); 68 | } 69 | } 70 | 71 | #endif 72 | -------------------------------------------------------------------------------- /intercom-cpp/src/posix/datatypes.hpp: -------------------------------------------------------------------------------- 1 | #ifndef INTERCOM_CPP_POSIX_DATATYPES_H 2 | #define INTERCOM_CPP_POSIX_DATATYPES_H 3 | 4 | 5 | #include 6 | 7 | // Declare data types used on Windows platform to facilitate code sharing. 8 | namespace intercom 9 | { 10 | typedef int32_t INT; 11 | typedef uint32_t UINT; 12 | typedef int8_t INT8; 13 | typedef uint8_t UINT8; 14 | typedef int16_t INT16; 15 | typedef uint16_t UINT16; 16 | typedef int32_t INT32; 17 | typedef uint32_t UINT32; 18 | typedef int64_t INT64; 19 | typedef uint64_t UINT64; 20 | 21 | typedef int BOOL; 22 | typedef uint32_t DWORD; 23 | typedef uint16_t WORD; 24 | 25 | typedef int8_t CHAR; 26 | typedef int16_t SHORT; 27 | typedef int32_t LONG; 28 | typedef int64_t LONGLONG; 29 | typedef uint8_t BYTE; 30 | typedef uint16_t USHORT; 31 | typedef uint32_t ULONG; 32 | typedef uint64_t ULONGLONG; 33 | typedef double DOUBLE; 34 | typedef float FLOAT; 35 | 36 | typedef char16_t OLECHAR; 37 | typedef OLECHAR* BSTR; 38 | 39 | typedef uint32_t HRESULT; 40 | typedef uint32_t SCODE; 41 | 42 | typedef double DATE; 43 | typedef int16_t VARIANT_BOOL; 44 | typedef int64_t CY, CURRENCY; 45 | 46 | typedef void* PVOID; 47 | 48 | //! 32-bit reference counter. unsigned long is 32-bit in Windows and 64-bit on Unix. 49 | typedef uint32_t REF_COUNT_32; 50 | 51 | struct DECIMAL { 52 | WORD wReserved; 53 | BYTE scale; 54 | BYTE sign; 55 | ULONG Hi32; 56 | ULONGLONG Lo64; 57 | }; 58 | } 59 | 60 | // Visual C++ does not declare the data types in their own namespace. 61 | // Define INTERCOM_FLATTEN_DECLARATIONS to mimic. 62 | #ifdef INTERCOM_FLATTEN_DECLARATIONS 63 | 64 | using INT8 = intercom::INT8; 65 | using UINT8 = intercom::UINT8; 66 | using INT16 = intercom::INT16; 67 | using UINT16 = intercom::UINT16; 68 | using INT32 = intercom::INT32; 69 | using UINT32 = intercom::UINT32; 70 | using INT64 = intercom::INT64; 71 | using UINT64 = intercom::UINT64; 72 | 73 | using BOOL = intercom::BOOL; 74 | using BYTE = intercom::BYTE; 75 | using ULONG = intercom::ULONG; 76 | using DWORD = intercom::DWORD; 77 | using WORD = intercom::WORD; 78 | 79 | using BSTR = intercom::BSTR; 80 | using HRESULT = intercom::HRESULT; 81 | 82 | #endif 83 | 84 | #endif 85 | -------------------------------------------------------------------------------- /intercom-cpp/src/posix/detail/memory.hpp: -------------------------------------------------------------------------------- 1 | #ifndef INTERCOM_CPP_POSIX_MEMORY_DETAIL_H 2 | #define INTERCOM_CPP_POSIX_MEMORY_DETAIL_H 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | namespace intercom 9 | { 10 | namespace posix 11 | { 12 | namespace detail 13 | { 14 | 15 | /** 16 | * @brief Sets the data length prefix for a BSTR. 17 | * 18 | * @param buffer The bstr buffer. 19 | * @param 20 | */ 21 | inline void set_bstr_data_length( 22 | void* buffer, 23 | uint32_t data_length 24 | ) noexcept 25 | { 26 | std::memcpy( buffer, &data_length, sizeof( data_length ) ); 27 | } 28 | } 29 | } 30 | } 31 | 32 | #endif -------------------------------------------------------------------------------- /intercom-cpp/src/posix/error_codes.hpp: -------------------------------------------------------------------------------- 1 | 2 | #ifndef INTERCOM_CPP_POSIX_ERRRORCODES_H 3 | #define INTERCOM_CPP_POSIX_ERRRORCODES_H 4 | 5 | #ifdef INTERCOM_FLATTEN_DECLARATIONS 6 | 7 | static const intercom::HRESULT S_OK = intercom::SC_OK; 8 | static const intercom::HRESULT E_FAIL = intercom::EC_FAIL; 9 | static const intercom::HRESULT E_NOTIMPL = intercom::EC_NOTIMPL; 10 | static const intercom::HRESULT E_NOINTERFACE = intercom::EC_NOINTERFACE; 11 | static const intercom::HRESULT E_OUTOFMEMORY = intercom::EC_OUTOFMEMORY; 12 | static const intercom::HRESULT E_INVALIDARG = intercom::EC_INVALIDARG; 13 | 14 | #endif 15 | 16 | #endif 17 | -------------------------------------------------------------------------------- /intercom-cpp/src/posix/guiddef.hpp: -------------------------------------------------------------------------------- 1 | 2 | #ifndef INTERCOM_CPP_POSIX_GUIDDEF_H 3 | #define INTERCOM_CPP_POSIX_GUIDDEF_H 4 | 5 | #include 6 | #include 7 | #include 8 | 9 | namespace intercom 10 | { 11 | 12 | typedef struct _GUID { 13 | uint32_t Data1; 14 | uint16_t Data2; 15 | uint16_t Data3; 16 | uint8_t Data4[8]; 17 | } GUID; 18 | 19 | typedef struct _IID { 20 | uint32_t Data1; 21 | uint16_t Data2; 22 | uint16_t Data3; 23 | uint8_t Data4[8]; 24 | } IID; 25 | 26 | typedef IID CLSID; 27 | typedef const IID& REFCLSID; 28 | typedef const IID& REFIID; 29 | 30 | } 31 | 32 | // Visual C++ does not declare the structs in their own namespace. 33 | // Define INTERCOM_FLATTEN_DECLARATIONS to mimic. 34 | #ifdef INTERCOM_FLATTEN_DECLARATIONS 35 | 36 | #define __IID_DEFINED__ 37 | #define CLSID_DEFINED 38 | 39 | using GUID = intercom::GUID; 40 | using IID = intercom::IID; 41 | using CLSID = intercom::IID; 42 | 43 | using REFCLSID = intercom::REFCLSID; 44 | using REFIID = intercom::REFIID; 45 | 46 | #endif 47 | 48 | #endif 49 | -------------------------------------------------------------------------------- /intercom-cpp/src/posix/idispatch.hpp: -------------------------------------------------------------------------------- 1 | 2 | #ifndef INTERCOM_CPP_POSIX_IDISPATCH_H 3 | #define INTERCOM_CPP_POSIX_IDISPATCH_H 4 | 5 | #include "../callingconvention.hpp" 6 | #include "../datatypes.hpp" 7 | #include "../error_codes.hpp" 8 | #include "../guiddef.hpp" 9 | 10 | class IDispatch; 11 | 12 | #endif 13 | -------------------------------------------------------------------------------- /intercom-cpp/src/posix/isupporterrorinfo.hpp: -------------------------------------------------------------------------------- 1 | 2 | #ifndef INTERCOM_CPP_POSIX_ISUPPORTERRORINFO_H 3 | #define INTERCOM_CPP_POSIX_ISUPPORTERRORINFO_H 4 | 5 | #include "../callingconvention.hpp" 6 | #include "../datatypes.hpp" 7 | #include "../error_codes.hpp" 8 | #include "../guiddef.hpp" 9 | 10 | #include "datatypes.hpp" 11 | 12 | // MIDL_INTERFACE("1CF2B120-547D-101B-8E65-08002B2BD119") 13 | struct IErrorInfo : public IUnknown 14 | { 15 | public: 16 | 17 | virtual intercom::HRESULT INTERCOM_CC GetGUID( 18 | intercom::GUID *pGUID) = 0; 19 | 20 | virtual intercom::HRESULT INTERCOM_CC GetSource( 21 | intercom::BSTR *pBstrSource) = 0; 22 | 23 | virtual intercom::HRESULT INTERCOM_CC GetDescription( 24 | intercom::BSTR *pBstrDescription) = 0; 25 | 26 | virtual intercom::HRESULT INTERCOM_CC GetHelpFile( 27 | intercom::BSTR *pBstrHelpFile) = 0; 28 | 29 | virtual intercom::HRESULT INTERCOM_CC GetHelpContext( 30 | intercom::DWORD *pdwHelpContext) = 0; 31 | }; 32 | 33 | static const intercom::IID IID_IErrorInfo = { 34 | 0x1CF2B120, 0x547D, 0x101B, 35 | { 0x8E, 0x65, 0x08, 0x00, 0x2B, 0x2B, 0xD1, 0x19 } }; 36 | 37 | // MIDL_INTERFACE("DF0B3D60-548F-101B-8E65-08002B2BD119" ) 38 | struct ISupportErrorInfo : public IUnknown 39 | { 40 | public: 41 | 42 | virtual intercom::HRESULT INTERCOM_CC InterfaceSupportsErrorInfo( 43 | intercom::REFIID riid 44 | ) = 0; 45 | }; 46 | 47 | static const intercom::IID IID_ISupportErrorInfo = { 0xDF0B3D60, 0x548F, 0x101B, { 0x8E, 0x65, 0x08, 0x00, 0x2B, 0x2B, 0xD1, 0x19 } }; 48 | 49 | #endif 50 | -------------------------------------------------------------------------------- /intercom-cpp/src/posix/iunknown.hpp: -------------------------------------------------------------------------------- 1 | 2 | #ifndef INTERCOM_CPP_POSIX_IUNKNOWN_H 3 | #define INTERCOM_CPP_POSIX_IUNKNOWN_H 4 | 5 | #include "../callingconvention.hpp" 6 | #include "../datatypes.hpp" 7 | #include "../error_codes.hpp" 8 | #include "../guiddef.hpp" 9 | 10 | 11 | // MIDL_INTERFACE("00000000-0000-0000-C000-000000000046") 12 | struct IUnknown 13 | { 14 | public: 15 | 16 | virtual intercom::HRESULT INTERCOM_CC QueryInterface( 17 | intercom::REFIID riid, 18 | void **ppvObject 19 | ) = 0; 20 | 21 | virtual intercom::REF_COUNT_32 INTERCOM_CC AddRef() = 0; 22 | 23 | 24 | virtual intercom::REF_COUNT_32 INTERCOM_CC Release() = 0; 25 | }; 26 | 27 | static const intercom::IID IID_IUnknown = { 0x00000000, 0x0000, 0x0000, { 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x46 } }; 28 | 29 | #endif 30 | -------------------------------------------------------------------------------- /intercom-cpp/src/runtime_error.hpp: -------------------------------------------------------------------------------- 1 | 2 | #ifndef INTERCOM_CPP_RUNTIMERROR_H 3 | #define INTERCOM_CPP_RUNTIMERROR_H 4 | 5 | #include 6 | #include 7 | #include 8 | 9 | #include "guiddef.hpp" 10 | #include "error_codes.hpp" 11 | 12 | namespace intercom 13 | { 14 | 15 | /** 16 | * @brief A generic runtime error thrown by the intercom. 17 | * 18 | */ 19 | class RuntimeError : public std::runtime_error 20 | { 21 | public: 22 | 23 | /** 24 | * @brief Initializes new RuntimeError. 25 | * 26 | */ 27 | RuntimeError( 28 | intercom::HRESULT error_code, 29 | const char* message 30 | ) : 31 | std::runtime_error( message ), 32 | m_error_code( error_code ) 33 | { 34 | } 35 | 36 | /** 37 | * @brief Initializes new RuntimeError. 38 | * 39 | */ 40 | RuntimeError( 41 | intercom::HRESULT error_code, 42 | const std::string& message 43 | ) : 44 | std::runtime_error( message ), 45 | m_error_code( error_code ) 46 | { 47 | } 48 | 49 | /** 50 | * @brief Initializes new RuntimeError. 51 | * 52 | */ 53 | RuntimeError( 54 | intercom::HRESULT error_code, 55 | std::ostream& stream 56 | ) : 57 | std::runtime_error( stream_to_string( stream ) ), 58 | m_error_code( error_code ) 59 | { 60 | } 61 | 62 | /** 63 | * @brief Returns the error associated with this exception. 64 | * 65 | * @return intercom::HRESULT Returns the error code. 66 | */ 67 | intercom::HRESULT error_code() const noexcept { return m_error_code; } 68 | 69 | private: 70 | 71 | static std::string stream_to_string( 72 | std::ostream& stream 73 | ) 74 | { 75 | stream.flush(); 76 | std::ostringstream message; 77 | message << stream.rdbuf(); 78 | return message.str(); 79 | } 80 | 81 | intercom::HRESULT m_error_code; 82 | }; 83 | 84 | } 85 | 86 | #endif 87 | -------------------------------------------------------------------------------- /intercom-cpp/src/variant.hpp: -------------------------------------------------------------------------------- 1 | 2 | #ifndef INTERCOM_CPP_VARIANT_H 3 | #define INTERCOM_CPP_VARIANT_H 4 | 5 | #ifdef _MSC_VER 6 | #include "msvc/variant.hpp" 7 | #else 8 | #include "posix/variant.hpp" 9 | #endif 10 | 11 | #endif 12 | -------------------------------------------------------------------------------- /intercom-fmt/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "intercom-fmt" 3 | version = "0.4.0" 4 | authors = ["Juha Lepola "] 5 | description = "Formats Rust code with \"rustfmt\"" 6 | 7 | [dependencies] 8 | intercom-common = { path = "../intercom-common", version = "0.4" } 9 | clap = { version = "2.27.1", default-features = false } 10 | failure = "0.1" 11 | -------------------------------------------------------------------------------- /intercom/.gitignore: -------------------------------------------------------------------------------- 1 | /target/ 2 | **/*.rs.bk 3 | Cargo.lock 4 | -------------------------------------------------------------------------------- /intercom/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "intercom" 3 | version = "0.4.0" 4 | authors = ["Mikko Rantanen "] 5 | license = "MIT" 6 | edition = "2018" 7 | repository = "https://github.com/Rantanen/intercom" 8 | description = "Utilities for writing COM visible Rust components." 9 | 10 | [lib] 11 | name = "intercom" 12 | 13 | [dependencies] 14 | intercom-attributes = { version = "0.4", path = "../intercom-attributes" } 15 | failure = "0.1" 16 | serde = { version = "1.0", optional = true } 17 | serde_derive = { version = "1.0", optional = true } 18 | handlebars = { version = "2.0", optional = true } 19 | log = { version = "0.4" } 20 | 21 | [dev-dependencies] 22 | simple_logger = { version = "1.6", default-features = false } 23 | regex = "1.5" 24 | 25 | [target.'cfg(not(windows))'.dependencies] 26 | libc = "0.2" 27 | 28 | -------------------------------------------------------------------------------- /intercom/benches/logging.rs: -------------------------------------------------------------------------------- 1 | #![feature(test)] 2 | extern crate test; 3 | 4 | extern crate intercom; 5 | extern crate simple_logger; 6 | 7 | #[intercom::com_class(IInterface)] 8 | struct S; 9 | 10 | #[intercom::com_interface] 11 | trait IInterface 12 | { 13 | fn call(&self) -> intercom::ComResult; 14 | } 15 | 16 | #[intercom::com_interface] 17 | trait IAnotherInterface {} 18 | 19 | impl IInterface for S 20 | { 21 | fn call(&self) -> intercom::ComResult 22 | { 23 | Ok("result".to_string()) 24 | } 25 | } 26 | 27 | #[bench] 28 | fn run_with_logging(bencher: &mut test::Bencher) 29 | { 30 | simple_logger::init().unwrap(); 31 | log::set_max_level(log::LevelFilter::Trace); 32 | 33 | bencher.iter(|| { 34 | let combox = test::black_box(intercom::ComBox::new(S)); 35 | let rc: intercom::ComRc = intercom::ComRc::from(combox); 36 | 37 | assert_eq!(&test::black_box(&rc).call().unwrap(), "result"); 38 | 39 | let rc: intercom::ComResult> = 40 | intercom::ComItf::query_interface(&rc); 41 | 42 | rc 43 | }); 44 | } 45 | 46 | #[bench] 47 | fn run_without_logging(bencher: &mut test::Bencher) 48 | { 49 | log::set_max_level(log::LevelFilter::Off); 50 | 51 | bencher.iter(|| { 52 | let combox = test::black_box(intercom::ComBox::new(S)); 53 | let rc: intercom::ComRc = intercom::ComRc::from(combox); 54 | 55 | assert_eq!(&test::black_box(&rc).call().unwrap(), "result"); 56 | 57 | let rc: intercom::ComResult> = 58 | intercom::ComItf::query_interface(&rc); 59 | 60 | rc 61 | }); 62 | } 63 | -------------------------------------------------------------------------------- /intercom/src/attributes.rs: -------------------------------------------------------------------------------- 1 | use crate::combox::ComBoxData; 2 | use crate::raw::RawComPtr; 3 | use crate::type_system; 4 | use crate::RawComResult; 5 | use crate::REFIID; 6 | use crate::{type_system::TypeSystem, IID}; 7 | 8 | /// Trait required by any COM coclass type. 9 | /// 10 | /// Used to specify the virtual table for the `ComBoxData`. 11 | pub trait ComClass 12 | { 13 | type VTableList: Copy; 14 | const VTABLE: Self::VTableList; 15 | fn query_interface(vtables: &Self::VTableList, riid: REFIID) -> RawComResult; 16 | fn interface_supports_error_info(riid: REFIID) -> bool; 17 | } 18 | 19 | pub trait HasInterface: ComClass {} 20 | 21 | pub trait ComClassInterface: ComClass + Sized 22 | { 23 | fn offset() -> usize; 24 | 25 | /// # Safety 26 | /// 27 | /// The `vtable` must be a valid pointer that points to the `TInterface` 28 | /// part of the `self`'s virtual table list. 29 | unsafe fn get_box<'a>(vtable: RawComPtr) -> &'a mut ComBoxData 30 | { 31 | let offset = Self::offset(); 32 | let self_ptr = (vtable as usize - offset) as *mut _; 33 | &mut *self_ptr 34 | } 35 | } 36 | 37 | pub trait ComInterfaceVTableFor: ComInterfaceVariant 38 | { 39 | const VTABLE: Self::VTable; 40 | } 41 | 42 | /// The `ComInterface` trait defines the COM interface details for a COM 43 | /// interface trait. 44 | pub trait ComInterface 45 | { 46 | /// The current interface. 47 | /// 48 | /// This associated type exists only to provide better error messages. When 49 | /// the `ComInterfaceVariant` is accessed through this type, the compiler 50 | /// will first report the missing `ComInterface` implementation if `Self` 51 | /// is not a COM interface. 52 | type TSelf: ?Sized; 53 | 54 | /// IID of the COM interface. 55 | fn iid(ts: type_system::TypeSystemName) -> Option<&'static IID>; 56 | 57 | fn iid_ts() -> &'static intercom::IID 58 | where 59 | Self: intercom::attributes::ComInterfaceVariant; 60 | 61 | /// Dereferences a `ComItf` into a `&T`. 62 | /// 63 | /// While in most cases the user crate will implement `T` for `ComItf`, 64 | /// this impl exists only in the user crate and cannot be used in generic 65 | /// contexts. For generic `ComItf` use, Intercom ipmls `Deref` 66 | /// for `ComItf` which requires this method. 67 | fn deref(com_itf: &crate::ComItf) -> &Self; 68 | } 69 | 70 | pub trait ComInterfaceVariant 71 | { 72 | type VTable: Copy + 'static; 73 | fn iid() -> &'static IID; 74 | } 75 | 76 | pub trait ComClassTypeInfo 77 | { 78 | fn gather_type_info() -> Vec; 79 | } 80 | 81 | pub trait ComInterfaceTypeInfo 82 | { 83 | fn gather_type_info() -> Vec; 84 | } 85 | -------------------------------------------------------------------------------- /intercom/src/classfactory.rs: -------------------------------------------------------------------------------- 1 | //! The class factory is an infrastructure type used by the COM clients to 2 | //! create instances of the `#[com_class(..)]`es provided by the intercom 3 | //! library. 4 | //! 5 | //! Intercom implements the class factory infrastructure automatically when the 6 | //! user specifies the `#[com_library(..)]` -attribute. 7 | 8 | use super::*; 9 | use crate::attributes; 10 | use crate::raw::RawComPtr; 11 | 12 | #[com_interface( 13 | com_iid = "00000001-0000-0000-C000-000000000046", 14 | raw_iid = "11111112-0000-0000-C000-000000000046" 15 | )] 16 | pub trait IClassFactory 17 | { 18 | /// # Safety 19 | /// 20 | /// The REFIID must be a valid IID pointer. 21 | unsafe fn create_instance(&self, outer: RawComPtr, riid: REFIID) -> ComResult; 22 | 23 | fn lock_server(&self, lock: bool) -> ComResult<()>; 24 | } 25 | 26 | #[doc(hidden)] 27 | #[com_class(IClassFactory)] 28 | pub struct ClassFactory 29 | { 30 | phantom: std::marker::PhantomData, 31 | } 32 | 33 | impl IClassFactory for ClassFactory 34 | { 35 | unsafe fn create_instance(&self, _outer: RawComPtr, riid: REFIID) -> ComResult 36 | { 37 | let instance = ComBox::new(T::default()); 38 | let mut out = std::ptr::null_mut(); 39 | let hr = ComBoxData::query_interface(instance.as_ref(), riid, &mut out); 40 | if hr == raw::S_OK { 41 | Ok(out) 42 | } else { 43 | Err(ComError::from(hr)) 44 | } 45 | } 46 | 47 | fn lock_server(&self, lock: bool) -> ComResult<()> 48 | { 49 | unsafe { 50 | if lock { 51 | ComBoxData::add_ref(ComBoxData::of(self)); 52 | } else { 53 | ComBoxData::release( 54 | ComBoxData::of(self) as *const ComBoxData as *mut ComBoxData 55 | ); 56 | } 57 | } 58 | Ok(()) 59 | } 60 | } 61 | 62 | impl ClassFactory 63 | { 64 | /// # Safety 65 | /// 66 | /// The `out` pointer must be valid for receiving the requested interface. 67 | pub unsafe fn create(riid: intercom::REFIID, out: *mut RawComPtr) 68 | -> crate::error::raw::HRESULT 69 | { 70 | let factory = Self { 71 | phantom: std::marker::PhantomData, 72 | }; 73 | 74 | intercom::ComBoxData::query_interface(intercom::ComBox::new(factory).as_mut(), riid, out) 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /intercom/src/logging.rs: -------------------------------------------------------------------------------- 1 | #[inline(always)] 2 | #[allow(unused_variables)] 3 | pub fn trace(f: FArgs) 4 | where 5 | FArgs: FnOnce(fn(&str, std::fmt::Arguments<'_>)), 6 | { 7 | if log::log_enabled!(log::Level::Trace) { 8 | f(|module, args| { 9 | log::logger().log( 10 | &log::Record::builder() 11 | .level(log::Level::Trace) 12 | .target(&format!("generated::{}", module)) 13 | .module_path(Some(module)) 14 | .args(args) 15 | .build(), 16 | ) 17 | }); 18 | } 19 | } 20 | 21 | #[inline(always)] 22 | #[allow(unused_variables)] 23 | pub fn error(f: FArgs) 24 | where 25 | FArgs: FnOnce(fn(&str, std::fmt::Arguments<'_>)), 26 | { 27 | if log::log_enabled!(log::Level::Error) { 28 | f(|module, args| { 29 | log::logger().log( 30 | &log::Record::builder() 31 | .args(args) 32 | .level(log::Level::Error) 33 | .target(&format!("generated::{}", module)) 34 | .module_path(Some(module)) 35 | .build(), 36 | ) 37 | }); 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /intercom/src/prelude.rs: -------------------------------------------------------------------------------- 1 | pub use crate::{ 2 | com_class, com_interface, com_library, com_module, ComBox, ComError, ComItf, ComRc, ComResult, 3 | }; 4 | -------------------------------------------------------------------------------- /intercom/src/runtime.rs: -------------------------------------------------------------------------------- 1 | #[cfg(windows)] 2 | mod os 3 | { 4 | 5 | #[cfg(windows)] 6 | #[link(name = "ole32")] 7 | extern "system" { 8 | #[doc(hidden)] 9 | pub fn CoInitializeEx( 10 | reserved: *const ::std::os::raw::c_void, 11 | init: u32, 12 | ) -> crate::raw::HRESULT; 13 | 14 | #[doc(hidden)] 15 | pub fn CoUninitialize(); 16 | } 17 | 18 | pub fn initialize() -> crate::raw::HRESULT 19 | { 20 | unsafe { 21 | let hr = CoInitializeEx(::std::ptr::null(), 2 /* APARTMENTTHREADED */); 22 | match hr { 23 | crate::raw::S_FALSE => crate::raw::S_OK, 24 | other => other, 25 | } 26 | } 27 | } 28 | 29 | pub fn uninitialize() 30 | { 31 | unsafe { 32 | CoUninitialize(); 33 | } 34 | } 35 | } 36 | 37 | #[cfg(not(windows))] 38 | mod os 39 | { 40 | pub fn initialize() -> crate::raw::HRESULT 41 | { 42 | crate::raw::S_OK 43 | } 44 | 45 | pub fn uninitialize() {} 46 | } 47 | 48 | pub fn initialize() -> crate::RawComResult<()> 49 | { 50 | match os::initialize() { 51 | crate::raw::S_OK => Ok(()), 52 | e => Err(e), 53 | } 54 | } 55 | 56 | pub fn uninitialize() 57 | { 58 | os::uninitialize(); 59 | } 60 | -------------------------------------------------------------------------------- /rustfmt.toml: -------------------------------------------------------------------------------- 1 | brace_style = "AlwaysNextLine" 2 | -------------------------------------------------------------------------------- /samples/thumbnail_provider/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "thumbnail_provider" 3 | version = "0.1.0" 4 | authors = ["Mikko Rantanen "] 5 | edition = "2021" 6 | 7 | [lib] 8 | crate-type = [ "cdylib" ] 9 | 10 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 11 | 12 | [dependencies] 13 | intercom = { path = "../../intercom" } 14 | winapi = { version = "0.3", features = [ "winbase", "objidlbase", "wingdi", "debugapi" ] } 15 | simple-logging = "2.0" 16 | log = "0.4" 17 | -------------------------------------------------------------------------------- /samples/thumbnail_provider/README.md: -------------------------------------------------------------------------------- 1 | # Thumbnail Provider Example 2 | 3 | ## Running the example 4 | 5 | - `cargo build` the project. 6 | - `regsvr32 target\debug\thumbnail_provider.dll` from an __elevated__ command 7 | prompt to register the thumbnail provider. 8 | - Associate the thumbnail provider with a file type. `association.reg` has 9 | example configuration for associating the provider with `*.rust` files. 10 | 11 | The provider can be unregistered with `regsvr32 /u 12 | target\debug\thumbnail_provider.dll`. Any file associations must be cleaned 13 | manually. 14 | 15 | ### Note on CLSID 16 | 17 | The CLSID (`61a6080e-0e9f-3cef-50a7-622d16145b78`) is intended to be unique. A 18 | new GUID should be generated for the CLSID if this sample is used as the basis 19 | for a new thumbnail provider. 20 | -------------------------------------------------------------------------------- /samples/thumbnail_provider/association.reg: -------------------------------------------------------------------------------- 1 | Windows Registry Editor Version 5.00 2 | 3 | [HKEY_CLASSES_ROOT\.rust] 4 | @="Rust.Example" 5 | 6 | [HKEY_CLASSES_ROOT\.rust\ShellEx] 7 | 8 | [HKEY_CLASSES_ROOT\.rust\ShellEx\{e357fccd-a995-4576-b01f-234630154e96}] 9 | @="{61A6080E-0E9F-3CEF-50A7-622D16145B78}" 10 | 11 | -------------------------------------------------------------------------------- /scripts/ci.bat: -------------------------------------------------------------------------------- 1 | setlocal enableextensions 2 | FOR /F "tokens=* delims= " %%i in ( 'scripts\vswhere -format value -property installationPath -version "[15.0,16.0)"' ) do ( set installdir=%%i ) 3 | pushd %installdir% 4 | call .\VC\Auxiliary\Build\vcvars64.bat 5 | popd 6 | 7 | echo on 8 | 9 | REM Build Intercom and the C++ test suite 10 | mkdir build 11 | pushd build 12 | if %errorlevel% neq 0 exit /b %errorlevel% 13 | 14 | cmake ".." -DCMAKE_GENERATOR_PLATFORM=x64 -DCMAKE_BUILD_TYPE=Debug 15 | if %errorlevel% neq 0 exit /b %errorlevel% 16 | 17 | cmake --build . --config Debug 18 | if %errorlevel% neq 0 exit /b %errorlevel% 19 | 20 | popd 21 | 22 | REM REM Build C# test suite 23 | pushd test\cs 24 | 25 | tlbimp ..\target\debug\test_lib.dll /MACHINE:X64 /out:TestLib.Interop.dll 26 | 27 | nuget restore 28 | msbuild /p:Platform=x64 /p:Configuration=Debug 29 | if %errorlevel% neq 0 exit /b %errorlevel% 30 | 31 | popd 32 | 33 | -------------------------------------------------------------------------------- /scripts/clang.sh: -------------------------------------------------------------------------------- 1 | export CC=clang 2 | export CXX=clang++ 3 | cmake .. 4 | 5 | -------------------------------------------------------------------------------- /scripts/kcov.sh: -------------------------------------------------------------------------------- 1 | !/bin/bash -eo pipefail 2 | # Set up the environment. We need LD_LIBRARY_PATH to include rust libs for 3 | # the rust tests to execute without cargo. 4 | # export LD_LIBRARY_PATH=/usr/local/rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib:${PWD}/build/bin/lib 5 | 6 | export KCOV=/usr/bin/kcov 7 | export KCOV_OUT=${PWD}/target/cov 8 | export KCOV_INCLUDE=${PWD} 9 | export KCOV_EXCLUDE=${PWD}/test 10 | export KCOV_FLAGS=--verify 11 | export RUN_KCOV="$KCOV --include-pattern=$KCOV_INCLUDE --exclude-pattern=$KCOV_EXCLUDE $KCOV_FLAGS $KCOV_OUT" 12 | mkdir -p $KCOV_OUT 13 | 14 | # Rust tests 15 | for file in target/debug/*-[0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f]; 16 | do 17 | ${RUN_KCOV} "$file" 18 | done 19 | 20 | for file in intercom-attributes/tests/data/*.source.rs; 21 | do 22 | CARGO_PKG_NAME="source" ${RUN_KCOV} rustc --crate-name source --crate-type lib "$file" --out-dir target/test_out -L dependency=target/debug/deps --extern intercom=target/debug/libintercom.rlib --pretty=expanded -Z unstable-options > /dev/null; 23 | done 24 | 25 | # C++ tests 26 | ( cd build/bin && ${RUN_KCOV} cpp-raw ) 27 | 28 | bash <( curl -s https://codecov.io/bash ) 29 | -------------------------------------------------------------------------------- /scripts/test.ps1: -------------------------------------------------------------------------------- 1 | pushd build\bin 2 | ./cpp-raw.exe 3 | $cpp_raw_ok = $? 4 | ./cpp-wrapper.exe 5 | $cpp_wrapper_ok = $? 6 | ./cpp-dl.exe 7 | $cpp_dl_ok = $? 8 | 9 | 10 | # Upload test results only if we're running on AppVeyor. 11 | if ( Test-Path env:APPVEYOR_JOB_ID ) { 12 | ./cpp-raw.exe -r junit > output_raw.xml 13 | $wc = New-Object 'System.Net.WebClient' 14 | $wc.UploadFile( "https://ci.appveyor.com/api/testresults/junit/$($env:APPVEYOR_JOB_ID)", (Resolve-Path .\output_raw.xml)) 15 | 16 | ./cpp-wrapper.exe -r junit > output_wrapper.xml 17 | $wc = New-Object 'System.Net.WebClient' 18 | $wc.UploadFile( "https://ci.appveyor.com/api/testresults/junit/$($env:APPVEYOR_JOB_ID)", (Resolve-Path .\output_wrapper.xml)) 19 | 20 | ./cpp-dl.exe -r junit > output_dl.xml 21 | $wc = New-Object 'System.Net.WebClient' 22 | $wc.UploadFile( "https://ci.appveyor.com/api/testresults/junit/$($env:APPVEYOR_JOB_ID)", (Resolve-Path .\output_dl.xml)) 23 | } 24 | 25 | popd 26 | 27 | pushd test\cs\bin\x64\Debug 28 | 29 | if ( Test-Path env:APPVEYOR_JOB_ID ) { 30 | vstest.console /logger:Appveyor /platform:x64 cs.dll 31 | $cs_ok = $? 32 | } else { 33 | vstest.console /platform:x64 cs.dll 34 | $cs_ok = $? 35 | } 36 | 37 | popd 38 | 39 | if( -not ( $cpp_raw_ok -and $cpp_wrapper_ok -and $cpp_dl_ok -and $cs_ok ) ) { 40 | exit -1 41 | } 42 | -------------------------------------------------------------------------------- /scripts/test.sh: -------------------------------------------------------------------------------- 1 | #! /bin/sh 2 | 3 | ( cd test/testlib && cargo build ) 4 | ( mkdir -p build ) 5 | ( cd build && rm -f bin/cpp-raw ) 6 | ( cd build && cmake .. ) 7 | ( cd build && make ) 8 | ( cd bin/x86_64 && ./cpp-raw ) 9 | -------------------------------------------------------------------------------- /scripts/vswhere license.txt: -------------------------------------------------------------------------------- 1 | vswhere is covered by the following license: 2 | The MIT License (MIT) 3 | Copyright (C) Microsoft Corporation. All rights reserved. 4 | 5 | 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: 6 | 7 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 8 | 9 | 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. 10 | -------------------------------------------------------------------------------- /scripts/vswhere.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Rantanen/intercom/e1f987d827d3da4f4785cbd95a8a464052a5920e/scripts/vswhere.exe -------------------------------------------------------------------------------- /test/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | project (test) 2 | 3 | # Helper library for ensuring RUNPATH gets set for ELF binaries. 4 | add_subdirectory(runpath) 5 | 6 | # Collection of available test libraries 7 | add_subdirectory(testlib) 8 | add_subdirectory(multilib) 9 | add_subdirectory(cpp-utility) 10 | 11 | # Collection of available tests 12 | add_subdirectory(cpp-raw) 13 | add_subdirectory(cpp-wrapper) 14 | add_subdirectory(cpp-dl) 15 | -------------------------------------------------------------------------------- /test/Cargo.toml: -------------------------------------------------------------------------------- 1 | [workspace] 2 | members = [ 3 | "testlib", 4 | "multilib", 5 | ] 6 | -------------------------------------------------------------------------------- /test/cpp-dl/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | project (cpp-dl) 2 | 3 | set (PROJECT_INCLUDE_DIR ${PROJECT_SOURCE_DIR}) 4 | set (PROJECT_SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR}) 5 | set (INTERCOM_ROOT ${CMAKE_CURRENT_SOURCE_DIR}/../..) 6 | set (TESTLIB_TARGET_DIR ${INTERCOM_ROOT}/test/target/${CMAKE_BUILD_TYPE}) 7 | 8 | if("${CMAKE_BUILD_TYPE}" STREQUAL "Release") 9 | set (TESTLIB_TARGET_DIR ${INTERCOM_ROOT}/test/target/release) 10 | else() 11 | set (TESTLIB_TARGET_DIR ${INTERCOM_ROOT}/test/target/debug) 12 | endif() 13 | 14 | # C++14 support 15 | set(CMAKE_CXX_STANDARD 14) 16 | set(CMAKE_CXX_STANDARD_REQUIRED ON) 17 | 18 | # Define source files. 19 | set(PROJECT_SRCS 20 | ${PROJECT_SOURCE_DIR}/main.cpp 21 | ${PROJECT_SOURCE_DIR}/generated/multi_lib.cpp 22 | ${PROJECT_SOURCE_DIR}/generated/test_lib.cpp 23 | ${PROJECT_SOURCE_DIR}/shared_functions.cpp 24 | ) 25 | 26 | # Specify additional platform specific paths. 27 | if( WIN32 ) 28 | set (TESTLIB_LIB ${TESTLIB_TARGET_DIR}/test_lib.dll) 29 | set (MULTILIB_LIB ${TESTLIB_TARGET_DIR}/multi_lib.dll) 30 | else() 31 | set (TESTLIB_LIB ${TESTLIB_TARGET_DIR}/libtest_lib.so) 32 | set (MULTILIB_LIB ${TESTLIB_TARGET_DIR}/libmulti_lib.so) 33 | endif() 34 | 35 | # Create the target dirs for the generated output. 36 | add_custom_command( 37 | OUTPUT ${PROJECT_SOURCE_DIR}/generated 38 | COMMAND ${CMAKE_COMMAND} -E make_directory ${PROJECT_SOURCE_DIR}/generated) 39 | 40 | # Use the intercom-cli as code generator for multi_lib.h/multi_lib.cpp 41 | add_custom_command( 42 | OUTPUT ${PROJECT_SOURCE_DIR}/generated/multi_lib.hpp ${PROJECT_SOURCE_DIR}/generated/multi_lib.cpp 43 | COMMAND cargo run cpp ${MULTILIB_LIB} --header ${PROJECT_SOURCE_DIR}/generated/multi_lib.hpp --source ${PROJECT_SOURCE_DIR}/generated/multi_lib.cpp 44 | WORKING_DIRECTORY ${INTERCOM_ROOT}/intercom-cli 45 | DEPENDS ${MULTILIB_LIB} ${PROJECT_SOURCE_DIR}/generated) 46 | 47 | # Use the intercom-cli as code generator for test_lib.h/test_lib.cpp 48 | add_custom_command( 49 | OUTPUT ${PROJECT_SOURCE_DIR}/generated/test_lib.hpp ${PROJECT_SOURCE_DIR}/generated/test_lib.cpp 50 | COMMAND cargo run cpp ${TESTLIB_LIB} --header ${PROJECT_SOURCE_DIR}/generated/test_lib.hpp --source ${PROJECT_SOURCE_DIR}/generated/test_lib.cpp 51 | WORKING_DIRECTORY ${INTERCOM_ROOT}/intercom-cli 52 | DEPENDS ${TESTLIB_LIB} ${PROJECT_SOURCE_DIR}/generated) 53 | 54 | include_directories("${PROJECT_BINARY_DIR}") 55 | include_directories("${PROJECT_INCLUDE_DIR}") 56 | 57 | # Ensure "intercom.h" is visible. 58 | include_directories("${PROJECT_INCLUDE_DIR}/../../intercom-cpp") 59 | 60 | # Compile the test executable. 61 | add_executable(${PROJECT_NAME} ${PROJECT_SRCS} ${PROJECT_COMPILER_SPECIFIC_SRC}) 62 | 63 | # Link to static dependencies 64 | target_link_libraries(${PROJECT_NAME} PRIVATE cpp-utility) 65 | 66 | # Link to test libraries. 67 | target_link_libraries(${PROJECT_NAME} PRIVATE test_lib PRIVATE multi_lib ) 68 | -------------------------------------------------------------------------------- /test/cpp-dl/libraries.hpp: -------------------------------------------------------------------------------- 1 | 2 | #pragma once 3 | 4 | #include "./generated/test_lib.hpp" 5 | #include "./generated/multi_lib.hpp" 6 | -------------------------------------------------------------------------------- /test/cpp-dl/main.cpp: -------------------------------------------------------------------------------- 1 | #define CATCH_CONFIG_MAIN 2 | #include "../dependencies/catch.hpp" 3 | -------------------------------------------------------------------------------- /test/cpp-raw/.gitignore: -------------------------------------------------------------------------------- 1 | /gcc/test_lib.cpp 2 | /gcc/test_lib.h 3 | 4 | -------------------------------------------------------------------------------- /test/cpp-raw/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | project (cpp-raw) 2 | 3 | set (PROJECT_INCLUDE_DIR ${PROJECT_SOURCE_DIR}) 4 | set (PROJECT_SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR}) 5 | set (INTERCOM_ROOT ${CMAKE_CURRENT_SOURCE_DIR}/../..) 6 | set (TESTLIB_DIR ${INTERCOM_ROOT}/test/testlib) 7 | 8 | # C++14 support 9 | set(CMAKE_CXX_STANDARD 14) 10 | set(CMAKE_CXX_STANDARD_REQUIRED ON) 11 | 12 | # Define source files. 13 | set(PROJECT_SRCS 14 | ${PROJECT_SOURCE_DIR}/error_info.cpp 15 | ${PROJECT_SOURCE_DIR}/interface_params.cpp 16 | ${PROJECT_SOURCE_DIR}/iunknown.cpp 17 | ${PROJECT_SOURCE_DIR}/main.cpp 18 | ${PROJECT_SOURCE_DIR}/primitive_tests.cpp 19 | ${PROJECT_SOURCE_DIR}/result.cpp 20 | ${PROJECT_SOURCE_DIR}/return_interfaces.cpp 21 | ${PROJECT_SOURCE_DIR}/stateful.cpp 22 | ${PROJECT_SOURCE_DIR}/strings.cpp 23 | ${PROJECT_SOURCE_DIR}/type_system_callbacks.cpp 24 | ${PROJECT_SOURCE_DIR}/variant.cpp 25 | ${PROJECT_SOURCE_DIR}/nullable_parameters.cpp 26 | ${PROJECT_SOURCE_DIR}/output_memory.cpp 27 | ${PROJECT_SOURCE_DIR}/only_interface.cpp 28 | ) 29 | 30 | include_directories("${PROJECT_BINARY_DIR}") 31 | include_directories("${PROJECT_INCLUDE_DIR}") 32 | 33 | # Ensure "intercom.hpp" is visible. 34 | include_directories("${PROJECT_INCLUDE_DIR}/../../intercom-cpp") 35 | 36 | add_executable(${PROJECT_NAME} ${PROJECT_SRCS}) 37 | 38 | # Link to static dependencies 39 | target_link_libraries(${PROJECT_NAME} PRIVATE cpp-utility) 40 | 41 | # Link to test libraries. 42 | target_link_libraries(${PROJECT_NAME} PRIVATE test_lib) 43 | -------------------------------------------------------------------------------- /test/cpp-raw/alloc.cpp: -------------------------------------------------------------------------------- 1 | #include "../cpp-utility/os.hpp" 2 | #include "../dependencies/catch.hpp" 3 | 4 | #include "testlib.hpp" 5 | 6 | TEST_CASE( "alloc" ) 7 | { 8 | // Initialize COM. 9 | InitializeRuntime(); 10 | 11 | // Get the error source interface. 12 | IAllocator_Automation* pAllocator = nullptr; 13 | HRESULT hr = CreateInstance( 14 | CLSID_Allocator, 15 | IID_IAllocator_Automation, 16 | &pAllocator ); 17 | REQUIRE( hr == S_OK ); 18 | REQUIRE( pAllocator != nullptr ); 19 | 20 | IAllocTests_Automation* pAllocTests = nullptr; 21 | hr = CreateInstance( 22 | CLSID_AllocTests, 23 | IID_IAllocTests_Automation, 24 | &pAllocTests ); 25 | REQUIRE( hr == S_OK ); 26 | REQUIRE( pAllocTests != nullptr ); 27 | 28 | SECTION( "Dealloc BSTR return value" ) 29 | { 30 | BSTR bstr = nullptr; 31 | bstr = pAllocTests->GetBstr( 123 ); 32 | 33 | REQUIRE( bstr != nullptr ); 34 | REQUIRE( bstr[ 0 ] == L'1' ); 35 | REQUIRE( bstr[ 1 ] == L'2' ); 36 | REQUIRE( bstr[ 2 ] == L'3' ); 37 | REQUIRE( bstr[ 3 ] == 0 ); 38 | 39 | // Nothing to assert after this. :< 40 | pAllocator->FreeBstr( bstr ); 41 | } 42 | 43 | SECTION( "Dealloc BSTR result" ) 44 | { 45 | BSTR bstr = nullptr; 46 | hr = pAllocTests->GetBstrResult( 999, OUT &bstr ); 47 | REQUIRE( hr == S_OK ); 48 | 49 | REQUIRE( bstr != nullptr ); 50 | REQUIRE( bstr[ 0 ] == L'9' ); 51 | REQUIRE( bstr[ 1 ] == L'9' ); 52 | REQUIRE( bstr[ 2 ] == L'9' ); 53 | REQUIRE( bstr[ 3 ] == 0 ); 54 | 55 | // Nothing to assert after this. :< 56 | pAllocator->FreeBstr( bstr ); 57 | } 58 | 59 | pAllocator->Release(); 60 | 61 | UninitializeRuntime(); 62 | } 63 | -------------------------------------------------------------------------------- /test/cpp-raw/interface_params.cpp: -------------------------------------------------------------------------------- 1 | 2 | #include "../cpp-utility/os.hpp" 3 | #include "../dependencies/catch.hpp" 4 | 5 | #define INTERCOM_FLATTEN_DECLARATIONS 6 | #include "testlib.hpp" 7 | 8 | #include 9 | 10 | class CppImplementation : public ISharedInterface_Automation 11 | { 12 | virtual unsigned int INTERCOM_CC GetValue() { return 5; } 13 | 14 | // These two are not used. 15 | virtual void INTERCOM_CC SetValue( unsigned int v ) { } 16 | virtual intercom::HRESULT INTERCOM_CC DivideBy( ISharedInterface_Automation* divisor, OUT unsigned int* result ) 17 | { return intercom::EC_NOTIMPL; } 18 | 19 | virtual intercom::HRESULT INTERCOM_CC QueryInterface( const intercom::IID& riid, void** out ) { return intercom::EC_NOTIMPL; } 20 | virtual intercom::REF_COUNT_32 INTERCOM_CC AddRef() { return 1; } 21 | virtual intercom::REF_COUNT_32 INTERCOM_CC Release() { return 1; } 22 | }; 23 | 24 | TEST_CASE( "interface_params" ) 25 | { 26 | // Initialize COM. 27 | InitializeRuntime(); 28 | 29 | // Get the first SharedImplementation object. 30 | ISharedInterface_Automation* pItf1 = nullptr; 31 | intercom::HRESULT hr = CreateInstance( 32 | CLSID_SharedImplementation, 33 | IID_ISharedInterface_Automation, 34 | &pItf1 ); 35 | REQUIRE( hr == intercom::SC_OK ); 36 | 37 | // Get the second SharedImplementation object. 38 | ISharedInterface_Automation* pItf2 = nullptr; 39 | hr = CreateInstance( 40 | CLSID_SharedImplementation, 41 | IID_ISharedInterface_Automation, 42 | &pItf2 ); 43 | REQUIRE( hr == intercom::SC_OK ); 44 | 45 | REQUIRE( pItf1 != nullptr ); 46 | REQUIRE( pItf2 != nullptr ); 47 | 48 | SECTION( "Rust CoClass can be used as a parameter." ) 49 | { 50 | pItf1->SetValue( 10 ); 51 | pItf2->SetValue( 2 ); 52 | 53 | unsigned int value = 0; 54 | hr = pItf1->DivideBy( pItf2, OUT &value ); 55 | REQUIRE( hr == intercom::SC_OK ); 56 | REQUIRE( value == 5 ); 57 | } 58 | 59 | SECTION( "C++ implementation can be used as a parameter." ) 60 | { 61 | pItf1->SetValue( 10 ); 62 | CppImplementation cppImpl; 63 | 64 | unsigned int value = 0; 65 | hr = pItf1->DivideBy( &cppImpl, OUT &value ); 66 | REQUIRE( hr == intercom::SC_OK ); 67 | REQUIRE( value == 2 ); 68 | } 69 | 70 | pItf1->Release(); 71 | pItf2->Release(); 72 | 73 | UninitializeRuntime(); 74 | } 75 | 76 | -------------------------------------------------------------------------------- /test/cpp-raw/main.cpp: -------------------------------------------------------------------------------- 1 | 2 | 3 | #define CATCH_CONFIG_MAIN 4 | #include "../dependencies/catch.hpp" 5 | -------------------------------------------------------------------------------- /test/cpp-raw/msvc/.gitignore: -------------------------------------------------------------------------------- 1 | /x64 2 | /TestLib_h.h 3 | /TestLib_i.c 4 | -------------------------------------------------------------------------------- /test/cpp-raw/msvc/cpp.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio 15 4 | VisualStudioVersion = 15.0.27004.2002 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "cpp", "cpp.vcxproj", "{E62B3920-6919-456A-933D-AEB7FD049462}" 7 | EndProject 8 | Global 9 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 10 | Debug|x64 = Debug|x64 11 | Debug|x86 = Debug|x86 12 | Release|x64 = Release|x64 13 | Release|x86 = Release|x86 14 | EndGlobalSection 15 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 16 | {E62B3920-6919-456A-933D-AEB7FD049462}.Debug|x64.ActiveCfg = Debug|x64 17 | {E62B3920-6919-456A-933D-AEB7FD049462}.Debug|x64.Build.0 = Debug|x64 18 | {E62B3920-6919-456A-933D-AEB7FD049462}.Debug|x86.ActiveCfg = Debug|Win32 19 | {E62B3920-6919-456A-933D-AEB7FD049462}.Debug|x86.Build.0 = Debug|Win32 20 | {E62B3920-6919-456A-933D-AEB7FD049462}.Release|x64.ActiveCfg = Release|x64 21 | {E62B3920-6919-456A-933D-AEB7FD049462}.Release|x64.Build.0 = Release|x64 22 | {E62B3920-6919-456A-933D-AEB7FD049462}.Release|x86.ActiveCfg = Release|Win32 23 | {E62B3920-6919-456A-933D-AEB7FD049462}.Release|x86.Build.0 = Release|Win32 24 | EndGlobalSection 25 | GlobalSection(SolutionProperties) = preSolution 26 | HideSolutionNode = FALSE 27 | EndGlobalSection 28 | GlobalSection(ExtensibilityGlobals) = postSolution 29 | SolutionGuid = {04C0E1BD-DF25-41A9-BAB9-42B5A446C993} 30 | EndGlobalSection 31 | EndGlobal 32 | -------------------------------------------------------------------------------- /test/cpp-raw/msvc/os.cpp: -------------------------------------------------------------------------------- 1 | 2 | #include "../../cpp-utility/os.hpp" 3 | 4 | #pragma comment(linker, "\"/manifestdependency:name='test_lib' type='win32' version='1.0.0.0'\"" ) 5 | 6 | void InitializeRuntime() 7 | { 8 | CoInitializeEx( nullptr, COINIT_APARTMENTTHREADED ); 9 | } 10 | 11 | void UninitializeRuntime() 12 | { 13 | CoUninitialize(); 14 | } 15 | 16 | HRESULT CreateInstance( REFCLSID clsid, REFIID iid, void** pout ) 17 | { 18 | return ::CoCreateInstance( 19 | clsid, 20 | nullptr, 21 | CLSCTX_INPROC_SERVER, 22 | iid, 23 | OUT pout ); 24 | } 25 | -------------------------------------------------------------------------------- /test/cpp-raw/only_interface.cpp: -------------------------------------------------------------------------------- 1 | 2 | #include "../cpp-utility/os.hpp" 3 | #include "../dependencies/catch.hpp" 4 | 5 | #include "testlib.hpp" 6 | 7 | TEST_CASE( "only_interface" ) 8 | { 9 | // This isn't really a runtime test. 10 | // If the types are not exported, this should fail to compile at all. 11 | SECTION( "Types are exported" ) 12 | { 13 | IOnlyInterface_Automation* pAutomation = nullptr; 14 | IOnlyInterface_Raw* pRaw = nullptr; 15 | } 16 | 17 | // We could just as well use this to ensure the auto-generated guids don't 18 | // change. 19 | SECTION( "GUIDs remain constant" ) 20 | { 21 | intercom::IID automation = { 0xfe0b0f3a, 0x7710, 0x3e69, 0x48, 0x6b, 0xd6, 0x01, 0x6b, 0xd8, 0xf8, 0x9d }; 22 | intercom::IID raw = { 0xe7de960d, 0x26be, 0x31fa, 0x5c, 0x98, 0x63, 0x0e, 0x66, 0xcc, 0x22, 0x7d }; 23 | REQUIRE( IID_IOnlyInterface_Automation == automation ); 24 | REQUIRE( IID_IOnlyInterface_Raw == raw ); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /test/cpp-raw/result.cpp: -------------------------------------------------------------------------------- 1 | #include "../cpp-utility/os.hpp" 2 | #include "../dependencies/catch.hpp" 3 | 4 | #include "testlib.hpp" 5 | 6 | TEST_CASE( "result" ) 7 | { 8 | // Initialize COM. 9 | InitializeRuntime(); 10 | 11 | // Get the IResultOperations interface. 12 | IResultOperations_Automation* pOps = nullptr; 13 | intercom::HRESULT hr = CreateInstance( 14 | CLSID_ResultOperations, 15 | IID_IResultOperations_Automation, 16 | &pOps ); 17 | 18 | REQUIRE( hr == intercom::SC_OK ); 19 | REQUIRE( pOps != nullptr ); 20 | 21 | SECTION( "HRESULT is returned with no OUT parameters" ) 22 | { 23 | REQUIRE( pOps->SOk() == intercom::SC_OK ); 24 | REQUIRE( pOps->NotImpl() == intercom::EC_NOTIMPL ); 25 | } 26 | 27 | SECTION( "Rust results are converted to [retval] and HRESULT" ) 28 | { 29 | double out = -1; 30 | 31 | SECTION( "Success yields intercom::SC_OK and [retval]" ) 32 | { 33 | // Success case. Returns intercom::SC_OK and value. 34 | REQUIRE( pOps->Sqrt( 16.0, OUT &out ) == intercom::SC_OK ); 35 | REQUIRE( out == 4.0 ); 36 | } 37 | 38 | SECTION( "Failure yields error value and resets [retval]" ) 39 | { 40 | // Fail case. Returns error and sets value to 0. 41 | intercom::HRESULT error = pOps->Sqrt( -1.0, OUT &out ); 42 | REQUIRE( error == intercom::EC_INVALIDARG ); 43 | REQUIRE( out == 0 ); 44 | } 45 | 46 | SECTION( "Tuples are converted to multiple OUT parameters" ) 47 | { 48 | uint16_t left; 49 | uint16_t right; 50 | 51 | hr = pOps->Tuple( 0xABBA0CD0, OUT &left, OUT &right ); 52 | 53 | REQUIRE( hr == intercom::SC_OK ); 54 | REQUIRE( left == 0xABBA ); 55 | REQUIRE( right == 0x0CD0 ); 56 | } 57 | } 58 | 59 | REQUIRE( pOps->Release() == 0 ); 60 | 61 | UninitializeRuntime(); 62 | } 63 | -------------------------------------------------------------------------------- /test/cpp-raw/stateful.cpp: -------------------------------------------------------------------------------- 1 | 2 | #include "../cpp-utility/os.hpp" 3 | #include "../dependencies/catch.hpp" 4 | 5 | #include "testlib.hpp" 6 | 7 | TEST_CASE( "stateful" ) 8 | { 9 | // Initialize COM. 10 | InitializeRuntime(); 11 | 12 | // Get the IResultOperations interface. 13 | IStatefulOperations_Automation* pOps = nullptr; 14 | intercom::HRESULT hr = CreateInstance( 15 | CLSID_StatefulOperations, 16 | IID_IStatefulOperations_Automation, 17 | &pOps ); 18 | 19 | REQUIRE( hr == intercom::SC_OK ); 20 | REQUIRE( pOps != nullptr ); 21 | 22 | SECTION( "State is stored" ) 23 | { 24 | pOps->PutValue( 10 ); 25 | REQUIRE( pOps->GetValue() == 10 ); 26 | pOps->PutValue( -100 ); 27 | REQUIRE( pOps->GetValue() == -100 ); 28 | pOps->PutValue( 55555 ); 29 | REQUIRE( pOps->GetValue() == 55555 ); 30 | } 31 | 32 | REQUIRE( pOps->Release() == 0 ); 33 | 34 | UninitializeRuntime(); 35 | } 36 | -------------------------------------------------------------------------------- /test/cpp-raw/testlib.hpp: -------------------------------------------------------------------------------- 1 | 2 | // Include declarations required to test "testlib". 3 | 4 | #define INTERCOM_FLATTEN_DECLARATIONS 5 | #ifdef _MSC_VER 6 | #include 7 | 8 | // Importing the DLL duplicates the __oud identifier which causes a warning. 9 | #ifndef NDEBUG 10 | #import "..\target\debug\test_lib.dll" raw_interfaces_only named_guids \ 11 | rename("__out", "__out_test_lib") 12 | #else 13 | #import "..\target\release\test_lib.dll" raw_interfaces_only named_guids \ 14 | rename("__out", "__out_test_lib") 15 | #endif 16 | using namespace TestLib; 17 | 18 | #elif __GNUC__ 19 | #include "../cpp-utility/generated/test_lib.hpp" 20 | #else 21 | #error Architecture not supported 22 | #endif 23 | -------------------------------------------------------------------------------- /test/cpp-utility/.gitignore: -------------------------------------------------------------------------------- 1 | /gcc/test_lib.cpp 2 | /gcc/test_lib.h 3 | -------------------------------------------------------------------------------- /test/cpp-utility/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | project (cpp-utility) 2 | 3 | set (PROJECT_INCLUDE_DIR ${PROJECT_SOURCE_DIR}) 4 | set (PROJECT_SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR}) 5 | set (INTERCOM_ROOT ${CMAKE_CURRENT_SOURCE_DIR}/../..) 6 | if("${CMAKE_BUILD_TYPE}" STREQUAL "Release") 7 | set (TESTLIB_TARGET_DIR ${INTERCOM_ROOT}/test/target/release) 8 | else() 9 | set (TESTLIB_TARGET_DIR ${INTERCOM_ROOT}/test/target/debug) 10 | endif() 11 | 12 | # C++14 support 13 | set(CMAKE_CXX_STANDARD 14) 14 | set(CMAKE_CXX_STANDARD_REQUIRED ON) 15 | 16 | # Define source files. 17 | set(PROJECT_SRCS 18 | ${PROJECT_SOURCE_DIR}/dummy_interface.cpp 19 | ${PROJECT_SOURCE_DIR}/generated/test_lib.cpp 20 | ) 21 | 22 | # Specify additional compiler specific helpers. 23 | if( WIN32 ) 24 | set(PROJECT_COMPILER_SPECIFIC_SRC 25 | ${PROJECT_SOURCE_DIR}/msvc/os.cpp 26 | ) 27 | set (TESTLIB_LIB ${TESTLIB_TARGET_DIR}/test_lib.dll) 28 | else() 29 | set(PROJECT_COMPILER_SPECIFIC_SRC 30 | ${PROJECT_SOURCE_DIR}/posix/os.cpp 31 | ) 32 | set (TESTLIB_LIB ${TESTLIB_TARGET_DIR}/libtest_lib.so) 33 | endif() 34 | 35 | # Use the intercom-cli as code generator for test_lib.h/test_lib.cpp 36 | add_custom_command( 37 | OUTPUT ${PROJECT_SOURCE_DIR}/generated 38 | COMMAND ${CMAKE_COMMAND} -E make_directory ${PROJECT_SOURCE_DIR}/generated) 39 | add_custom_command( 40 | OUTPUT ${PROJECT_SOURCE_DIR}/generated/test_lib.hpp ${PROJECT_SOURCE_DIR}/generated/test_lib.cpp 41 | COMMAND cargo run cpp ${TESTLIB_LIB} --all --header ${PROJECT_SOURCE_DIR}/generated/test_lib.hpp --source ${PROJECT_SOURCE_DIR}/generated/test_lib.cpp 42 | WORKING_DIRECTORY ${INTERCOM_ROOT}/intercom-cli 43 | DEPENDS ${TESTLIB_LIB} ${PROJECT_SOURCE_DIR}/generated) 44 | 45 | include_directories("${PROJECT_BINARY_DIR}") 46 | include_directories("${PROJECT_INCLUDE_DIR}") 47 | 48 | # Ensure "intercom.hpp" is visible. 49 | include_directories("${PROJECT_INCLUDE_DIR}/../../intercom-cpp") 50 | 51 | add_library(${PROJECT_NAME} STATIC ${PROJECT_SRCS} ${PROJECT_COMPILER_SPECIFIC_SRC}) 52 | 53 | # Testlib required to generate the test_lib.h and test_lib.cpp 54 | add_dependencies(${PROJECT_NAME} testlib) 55 | 56 | # Linking to "runpath" library ensures that RUNPATH is set for ELF executables. 57 | # All the test libraries built with Rust are copied into this location as well. 58 | # Forcing the RUNPATH with the help of a dummy library ensures we can 59 | # dynamically load the libraries with their name alone. 60 | target_link_libraries(${PROJECT_NAME} PUBLIC runpath) 61 | 62 | # Compile specific linking. 63 | if( NOT WIN32 ) 64 | 65 | # Link to dl, support for loading dynamic libraries. 66 | find_library(DL_LOCATION dl) 67 | target_link_libraries(${PROJECT_NAME} PUBLIC ${DL_LOCATION}) 68 | 69 | # Link to "pthread", otherwise std::call_once used by ClassFactory does not work. 70 | # See the comments around "__gthread_active" in gthr-default.h 71 | find_library(PTHREAD_LOCATION pthread) 72 | target_link_libraries(${PROJECT_NAME} PUBLIC ${PTHREAD_LOCATION}) 73 | 74 | endif() 75 | -------------------------------------------------------------------------------- /test/cpp-utility/dummy_interface.cpp: -------------------------------------------------------------------------------- 1 | 2 | #include "dummy_interface.hpp" 3 | 4 | #ifdef _MSC_VER 5 | const char cppraw::utility::DummyLibDescriptor::NAME[] = "dummy_lib.dll"; 6 | #else 7 | const char cppraw::utility::DummyLibDescriptor::NAME[] = "libdummy_lib.so"; 8 | #endif 9 | 10 | const char cppraw::utility::DummyLibDescriptor::WINDOWS_NAME[] = "dummy_lib.dll"; 11 | const char cppraw::utility::DummyLibDescriptor::POSIX_NAME[] = "libdummy_lib.so"; 12 | 13 | const intercom::IID cppraw::utility::IDummyInterface::ID = {0x12345678,0x0,0x0,{0x12,0x34,0x56,0x78,0x00,0x00,0x00,0x00}}; 14 | 15 | const intercom::CLSID cppraw::utility::DummyInterfaceDescriptor::ID = {0x12345678,0x1234,0x1234,{0x12,0x34,0x56,0x78,0x00,0x00,0x00,0x00}}; 16 | const std::array cppraw::utility::DummyInterfaceDescriptor::INTERFACES = { { 17 | cppraw::utility::IDummyInterface::ID 18 | } }; 19 | -------------------------------------------------------------------------------- /test/cpp-utility/dummy_interface.hpp: -------------------------------------------------------------------------------- 1 | 2 | #ifndef CPPRAW_DUMMYINTERFACE_H 3 | #define CPPRAW_DUMMYINTERFACE_H 4 | 5 | #include 6 | #include 7 | 8 | namespace cppraw 9 | { 10 | namespace utility 11 | { 12 | 13 | class DummyLibDescriptor 14 | { 15 | public: 16 | static const char NAME[]; 17 | static const char WINDOWS_NAME[]; 18 | static const char POSIX_NAME[]; 19 | static inline bool is_available() { return false; } 20 | }; 21 | 22 | /** 23 | * @brief An interface which does not exist in the library. 24 | * 25 | */ 26 | struct IDummyInterface : IUnknown 27 | { 28 | static const intercom::IID ID; 29 | }; 30 | 31 | class DummyInterfaceDescriptor 32 | { 33 | public: 34 | static const intercom::CLSID ID; 35 | 36 | static const std::array INTERFACES; 37 | 38 | using Library = DummyLibDescriptor; 39 | 40 | DummyInterfaceDescriptor() = delete; 41 | ~DummyInterfaceDescriptor() = delete; 42 | }; 43 | 44 | } 45 | } 46 | 47 | #endif 48 | -------------------------------------------------------------------------------- /test/cpp-utility/msvc/os.cpp: -------------------------------------------------------------------------------- 1 | 2 | #include 3 | #include 4 | #include 5 | 6 | #include "../../dependencies/catch.hpp" 7 | #include "../os.hpp" 8 | #include "../generated/test_lib.hpp" 9 | #include "../../runpath/init.h" 10 | #include "../../../intercom-cpp/src/cominterop.hpp" 11 | #include "../../../intercom-cpp/src/activator.hpp" 12 | 13 | using intercom::Activator; 14 | 15 | #pragma comment(linker, "\"/manifestdependency:name='test_lib' type='win32' version='1.0.0.0'\"" ) 16 | 17 | 18 | void InitializeRuntime() 19 | { 20 | CoInitializeEx( nullptr, COINIT_APARTMENTTHREADED ); 21 | 22 | // Ensure the library "runpath" won't get optimized away. 23 | init_runpath(); 24 | 25 | REQUIRE( test_lib::Descriptor::is_available() ); 26 | } 27 | 28 | void UninitializeRuntime() 29 | { 30 | CoUninitialize(); 31 | } 32 | 33 | -------------------------------------------------------------------------------- /test/cpp-utility/os.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | // Interface definitions. 6 | #ifdef _MSC_VER 7 | #include 8 | #else 9 | 10 | // Include declarations on non-Windows platforms. 11 | #include "../../intercom-cpp/src/msdef.hpp" 12 | 13 | #endif 14 | 15 | // Platform specific runtime initialization. 16 | void InitializeRuntime(); 17 | 18 | // Platform specific runtime uninitialization. 19 | void UninitializeRuntime(); 20 | 21 | // Create Intercom object instance. 22 | template 23 | intercom::HRESULT CreateInstance( 24 | const intercom::CLSID& clsid, 25 | const intercom::IID& iid, 26 | TInterface** pout 27 | ) 28 | { 29 | return intercom::create_instance( clsid, iid, reinterpret_cast< void** >( pout ) ); 30 | } 31 | -------------------------------------------------------------------------------- /test/cpp-utility/posix/os.cpp: -------------------------------------------------------------------------------- 1 | 2 | #include 3 | #include 4 | 5 | #include "../../dependencies/catch.hpp" 6 | #include "../os.hpp" 7 | #include "../generated/test_lib.hpp" 8 | #include "../../runpath/init.h" 9 | #include "../../../intercom-cpp/src/cominterop.hpp" 10 | #include "../../../intercom-cpp/src/activator.hpp" 11 | 12 | using intercom::Activator; 13 | 14 | 15 | void InitializeRuntime() 16 | { 17 | // Ensure the library "runpath" won't get optimized away. 18 | init_runpath(); 19 | 20 | REQUIRE( test_lib::Descriptor::is_available() ); 21 | } 22 | 23 | void UninitializeRuntime() 24 | { 25 | } 26 | -------------------------------------------------------------------------------- /test/cpp-wrapper/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | project (cpp-wrapper) 2 | 3 | set (PROJECT_INCLUDE_DIR ${PROJECT_SOURCE_DIR}) 4 | set (PROJECT_SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR}) 5 | set (INTERCOM_ROOT ${CMAKE_CURRENT_SOURCE_DIR}/../..) 6 | set (TESTLIB_DIR ${INTERCOM_ROOT}/test/testlib) 7 | 8 | # C++14 support 9 | set(CMAKE_CXX_STANDARD 14) 10 | set(CMAKE_CXX_STANDARD_REQUIRED ON) 11 | 12 | # Define source files. 13 | set(PROJECT_SRCS 14 | ${PROJECT_SOURCE_DIR}/main.cpp 15 | ${PROJECT_SOURCE_DIR}/exceptions.cpp 16 | ${PROJECT_SOURCE_DIR}/interface_wrappers.cpp 17 | ${PROJECT_SOURCE_DIR}/strings.cpp 18 | ) 19 | 20 | include_directories("${PROJECT_BINARY_DIR}") 21 | include_directories("${PROJECT_INCLUDE_DIR}") 22 | 23 | # Ensure "intercom.hpp" is visible. 24 | include_directories("${PROJECT_INCLUDE_DIR}/../../intercom-cpp") 25 | 26 | add_executable(${PROJECT_NAME} ${PROJECT_SRCS} ${PROJECT_COMPILER_SPECIFIC_SRC}) 27 | 28 | # Linking to "runpath" library ensures that RUNPATH is set for ELF executables. 29 | # All the test libraries built with Rust are copied into this location as well. 30 | # Forcing the RUNPATH with the help of a dummy library ensures we can 31 | # dynamically load the libraries with their name alone. 32 | target_link_libraries(${PROJECT_NAME} PRIVATE runpath) 33 | 34 | # Link to static dependencies 35 | target_link_libraries(${PROJECT_NAME} PRIVATE cpp-utility) 36 | 37 | # Add dynamic dependencies 38 | target_link_libraries(${PROJECT_NAME} PRIVATE test_lib) 39 | -------------------------------------------------------------------------------- /test/cpp-wrapper/exceptions.cpp: -------------------------------------------------------------------------------- 1 | 2 | 3 | #include 4 | 5 | #include "../cpp-utility/os.hpp" 6 | #include "../dependencies/catch.hpp" 7 | 8 | #include "testlib.hpp" 9 | #include "../cpp-utility/dummy_interface.hpp" 10 | 11 | TEST_CASE( "Exceptions have correct error message." ) 12 | { 13 | SECTION( "NoSuchInterface with only interface id set has correct error message." ) 14 | { 15 | try 16 | { 17 | throw intercom::NoSuchInterface( IID_IUnknown ); 18 | FAIL( "Exception not thrown." ); 19 | } 20 | catch( intercom::NoSuchInterface& ex ) 21 | { 22 | CHECK( std::string( "Interface \"{00000000-0000-0000-C000-000000000046}\" not available." ) 23 | == ex.what() ); 24 | } 25 | } 26 | 27 | SECTION( "NoSuchInterface with both interface id and class id set has correct error message." ) 28 | { 29 | try 30 | { 31 | throw intercom::NoSuchInterface( 32 | cppraw::utility::DummyInterfaceDescriptor::ID, 33 | cppraw::utility::IDummyInterface::ID ); 34 | FAIL( "Exception not thrown." ); 35 | } 36 | catch( intercom::NoSuchInterface& ex ) 37 | { 38 | CHECK( std::string( "Interface \"{12345678-0000-0000-1234-567800000000}\" not "\ 39 | "available for the class \"{12345678-1234-1234-1234-567800000000}\"." ) == ex.what() ); 40 | } 41 | } 42 | 43 | SECTION( "RuntimeError preserves error message." ) 44 | { 45 | try 46 | { 47 | throw intercom::RuntimeError( intercom::EC_FAIL, "This message is preserved." ); 48 | FAIL( "Exception not thrown." ); 49 | } 50 | catch( intercom::RuntimeError& ex ) 51 | { 52 | CHECK( std::string( "This message is preserved." ) == ex.what() ); 53 | } 54 | } 55 | } 56 | 57 | TEST_CASE( "Exception specific data is passed correctly." ) 58 | { 59 | SECTION( "NoSuchInterface preserves interface id." ) 60 | { 61 | try 62 | { 63 | throw intercom::NoSuchInterface( IID_IUnknown ); 64 | FAIL( "Exception not thrown." ); 65 | } 66 | catch( intercom::NoSuchInterface& ex ) 67 | { 68 | CHECK( IID_IUnknown == ex.interface_id() ); 69 | } 70 | } 71 | 72 | SECTION( "RuntimeError preserved its error code." ) 73 | { 74 | try 75 | { 76 | throw intercom::RuntimeError( intercom::EC_INVALIDARG, "This message is preserved." ); 77 | FAIL( "Exception not thrown." ); 78 | } 79 | catch( intercom::RuntimeError& ex ) 80 | { 81 | CHECK( intercom::EC_INVALIDARG == ex.error_code() ); 82 | } 83 | } 84 | } 85 | -------------------------------------------------------------------------------- /test/cpp-wrapper/main.cpp: -------------------------------------------------------------------------------- 1 | 2 | 3 | #define CATCH_CONFIG_MAIN 4 | #include "../dependencies/catch.hpp" 5 | -------------------------------------------------------------------------------- /test/cpp-wrapper/strings.cpp: -------------------------------------------------------------------------------- 1 | 2 | #include 3 | #include 4 | 5 | #include "../cpp-utility/os.hpp" 6 | #include "../dependencies/catch.hpp" 7 | 8 | #include "testlib.hpp" 9 | 10 | TEST_CASE( "Manipulating BSTR succeeds" ) 11 | { 12 | SECTION( "Allocationg BSTR succeeds" ) 13 | { 14 | intercom::BSTR allocated = intercom::allocate_bstr( 10 ); 15 | REQUIRE( allocated != nullptr ); 16 | 17 | REQUIRE( *( reinterpret_cast< uint32_t* >( allocated ) - 1 ) == 10 * 2 ); 18 | REQUIRE( *( allocated + 10 ) == 0 ); 19 | 20 | intercom::free_bstr( allocated ); 21 | } 22 | 23 | std::tuple< const char16_t*, const char*, const char* > test_data[] = { 24 | std::make_tuple( nullptr, nullptr, "" ), 25 | std::make_tuple( u"Test", u8"Test", "\"Test\"" ), 26 | std::make_tuple( u"\u00f6\u00e4\u00e5", u8"\u00f6\u00e4\u00e5", "Multibyte UTF-8" ), // Scandinavian letters: öäå 27 | std::make_tuple( u"\U0001F980", u8"\U0001F980", "Multibyte UTF-16" ), // Crab: 🦀 28 | }; 29 | 30 | for( const auto& tuple : test_data ) 31 | { 32 | const char16_t* utf16_text = nullptr; 33 | const char* utf8_text = nullptr; 34 | const char* description = nullptr; 35 | std::tie( utf16_text, utf8_text, description ) = tuple; 36 | 37 | size_t utf8_len = utf8_text == nullptr 38 | ? 0 39 | : strlen( utf8_text ); 40 | size_t utf16_len = utf16_text == nullptr 41 | ? 0 42 | : std::char_traits::length( utf16_text ); 43 | 44 | SECTION( std::string( "Converting UTF-8 to BSTR works: " ) + description ) 45 | { 46 | intercom::BSTR converted; 47 | intercom::utf8_to_bstr( utf8_text, &converted ); 48 | 49 | if( utf16_text != nullptr ) 50 | REQUIRE( converted != nullptr ); 51 | else 52 | REQUIRE( converted == nullptr ); 53 | 54 | REQUIRE( intercom::get_characters_in_bstr( converted ) == utf16_len ); 55 | 56 | REQUIRE( memcmp( converted, utf16_text, utf16_len * 2 ) == 0 ); 57 | 58 | intercom::free_bstr( converted ); 59 | } 60 | 61 | SECTION( std::string( "Converting BSTR to UTF-8 works: " ) + description ) 62 | { 63 | intercom::BSTR bstr = intercom::allocate_bstr( utf16_len ); 64 | memcpy( bstr, utf16_text, utf16_len * 2 ); 65 | 66 | char* converted = nullptr; 67 | intercom::bstr_to_utf8( bstr, OUT &converted ); 68 | 69 | // UTF-8 strings are always non-null. 70 | REQUIRE( converted != nullptr ); 71 | REQUIRE( strlen( converted ) == utf8_len ); 72 | 73 | // Zero length UTF-8 string is still allocated. 74 | const char* expected = utf8_text; 75 | if( utf8_len == 0 ) 76 | expected = ""; 77 | 78 | REQUIRE( memcmp( converted, expected, utf8_len ) == 0 ); 79 | 80 | intercom::free_bstr( bstr ); 81 | intercom::free_string( converted ); 82 | } 83 | } 84 | 85 | SECTION( "Attempting to free null BSTR succeeds" ) 86 | { 87 | intercom::free_bstr( nullptr ); 88 | } 89 | } 90 | -------------------------------------------------------------------------------- /test/cpp-wrapper/testlib.hpp: -------------------------------------------------------------------------------- 1 | 2 | // Include declarations required to test "testlib". 3 | #include "../cpp-utility/generated/test_lib.hpp" 4 | 5 | -------------------------------------------------------------------------------- /test/cs/.gitignore: -------------------------------------------------------------------------------- 1 | /packages 2 | *.sln.DotSettings.user 3 | TestLib.tlb 4 | TestLib.Interop.dll 5 | -------------------------------------------------------------------------------- /test/cs/InterfaceValueOperations.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | using Microsoft.VisualStudio.TestTools.UnitTesting; 7 | 8 | namespace cs 9 | { 10 | [TestClass] 11 | public class InterfaceValueOperations 12 | { 13 | [TestMethod] 14 | public void ComInterfaceAsReturnValue() 15 | { 16 | var creator = new TestLib.ClassCreator(); 17 | var root = creator.CreateRoot( 1 ); 18 | 19 | Assert.IsNotNull( root ); 20 | Assert.AreEqual( 1, root.GetId() ); 21 | } 22 | 23 | [TestMethod] 24 | public void ComInterfaceAsParameter() 25 | { 26 | var creator = new TestLib.ClassCreator(); 27 | var root = creator.CreateRoot( 1 ); 28 | 29 | Assert.IsNotNull( root ); 30 | Assert.AreEqual( 1, root.GetId() ); 31 | 32 | var child = creator.CreateChild( 2, ( TestLib.IParent_Automation ) root ); 33 | 34 | Assert.AreEqual( 2, child.GetId() ); 35 | Assert.AreEqual( 1, child.GetParentId() ); 36 | } 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /test/cs/PrimitiveOperations.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Microsoft.VisualStudio.TestTools.UnitTesting; 3 | 4 | namespace cs 5 | { 6 | [TestClass] 7 | public class PrimitiveOperations 8 | { 9 | [TestMethod] 10 | public void SupportsInt64() 11 | { 12 | var lib = new TestLib.PrimitiveOperations(); 13 | foreach (var i in new Int64[] {0, 1, 10, Int64.MaxValue, Int64.MinValue}) 14 | { 15 | Assert.AreEqual( (Int64)~( i + 1 ), lib.I64( i ) ); 16 | } 17 | } 18 | 19 | [TestMethod] 20 | public void SupportsUInt64() 21 | { 22 | var lib = new TestLib.PrimitiveOperations(); 23 | foreach (var i in new UInt64[] {0, 1, 10, UInt64.MaxValue, UInt64.MinValue}) 24 | { 25 | Assert.AreEqual( (UInt64)~( i + 1 ), lib.U64( i ) ); 26 | } 27 | } 28 | 29 | [TestMethod] 30 | public void SupportsInt32() 31 | { 32 | var lib = new TestLib.PrimitiveOperations(); 33 | foreach (var i in new Int32[] {0, 1, 10, Int32.MaxValue, Int32.MinValue}) 34 | { 35 | Assert.AreEqual( (Int32)~( i + 1 ), lib.I32( i ) ); 36 | } 37 | } 38 | 39 | [TestMethod] 40 | public void SupportsUInt32() 41 | { 42 | var lib = new TestLib.PrimitiveOperations(); 43 | foreach (var i in new UInt32[] {0, 1, 10, UInt32.MaxValue, UInt32.MinValue}) 44 | { 45 | Assert.AreEqual( (UInt32)~( i + 1 ), lib.U32( i ) ); 46 | } 47 | } 48 | 49 | 50 | [TestMethod] 51 | public void SupportsInt16() 52 | { 53 | var lib = new TestLib.PrimitiveOperations(); 54 | foreach (var i in new Int16[] {0, 1, 10, Int16.MaxValue, Int16.MinValue}) 55 | { 56 | Assert.AreEqual( (Int16)~( i + 1 ), lib.I16( i ) ); 57 | } 58 | } 59 | 60 | [TestMethod] 61 | public void SupportsUInt16() 62 | { 63 | var lib = new TestLib.PrimitiveOperations(); 64 | foreach (var i in new UInt16[] {0, 1, 10, UInt16.MaxValue, UInt16.MinValue}) 65 | { 66 | Assert.AreEqual( (UInt16)~( i + 1 ), lib.U16( i ) ); 67 | } 68 | } 69 | 70 | 71 | [TestMethod] 72 | public void SupportsSByte() 73 | { 74 | var lib = new TestLib.PrimitiveOperations(); 75 | foreach (var i in new SByte[] {0, 1, 10, SByte.MaxValue, SByte.MinValue}) 76 | { 77 | Assert.AreEqual( (SByte)~( i + 1 ), lib.I8( i ) ); 78 | } 79 | } 80 | 81 | [TestMethod] 82 | public void SupportsByte() 83 | { 84 | var lib = new TestLib.PrimitiveOperations(); 85 | foreach (var i in new Byte[] {0, 1, 10, Byte.MaxValue, Byte.MinValue}) 86 | { 87 | Assert.AreEqual( (Byte)~( i + 1 ), lib.U8( i ) ); 88 | } 89 | } 90 | } 91 | } 92 | -------------------------------------------------------------------------------- /test/cs/PrimitiveTests.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.ComponentModel; 4 | using System.Linq; 5 | using System.Text; 6 | using System.Threading; 7 | using System.Threading.Tasks; 8 | using NUnit.Framework; 9 | 10 | namespace cs 11 | { 12 | [TestFixture, Apartment( ApartmentState.STA )] 13 | class PrimitiveTests 14 | { 15 | 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /test/cs/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.CompilerServices; 3 | using System.Runtime.InteropServices; 4 | 5 | [assembly: AssemblyTitle( "cs" )] 6 | [assembly: AssemblyDescription( "" )] 7 | [assembly: AssemblyConfiguration( "" )] 8 | [assembly: AssemblyCompany( "" )] 9 | [assembly: AssemblyProduct( "cs" )] 10 | [assembly: AssemblyCopyright( "Copyright © 2017" )] 11 | [assembly: AssemblyTrademark( "" )] 12 | [assembly: AssemblyCulture( "" )] 13 | 14 | [assembly: ComVisible( false )] 15 | 16 | [assembly: Guid( "09c842ac-d779-46fd-b0cb-0640121bd2c0" )] 17 | 18 | // [assembly: AssemblyVersion("1.0.*")] 19 | [assembly: AssemblyVersion( "1.0.0.0" )] 20 | [assembly: AssemblyFileVersion( "1.0.0.0" )] 21 | -------------------------------------------------------------------------------- /test/cs/ResultOperations.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | using Microsoft.VisualStudio.TestTools.UnitTesting; 7 | 8 | namespace cs 9 | { 10 | [TestClass] 11 | public class ResultOperations 12 | { 13 | [TestMethod] 14 | public void NotImplemented() 15 | { 16 | var lib = new TestLib.ResultOperations(); 17 | Assert.ThrowsException< NotImplementedException >( () => lib.NotImpl() ); 18 | } 19 | 20 | [TestMethod] 21 | public void ArgumentException() 22 | { 23 | var lib = new TestLib.ResultOperations(); 24 | Assert.ThrowsException< ArgumentException >( () => lib.Sqrt( -1 ) ); 25 | 26 | var value = new Random().NextDouble(); 27 | Assert.AreEqual( Math.Sqrt( value ), lib.Sqrt( value ) ); 28 | } 29 | 30 | [TestMethod] 31 | public void Success() 32 | { 33 | var lib = new TestLib.ResultOperations(); 34 | lib.SOk(); 35 | } 36 | 37 | [TestMethod] 38 | public void Tuples() 39 | { 40 | var lib = new TestLib.ResultOperations(); 41 | 42 | ushort left; 43 | ushort right; 44 | lib.Tuple( 0xCAFEBABE, out left, out right ); 45 | 46 | Assert.AreEqual( 0xCAFE, left ); 47 | Assert.AreEqual( 0xBABE, right ); 48 | } 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /test/cs/SetUpFixture.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.ComponentModel; 4 | using System.IO; 5 | using System.Linq; 6 | using System.Reflection; 7 | using System.Runtime.InteropServices; 8 | using System.Security; 9 | using System.Text; 10 | using System.Threading.Tasks; 11 | using Microsoft.VisualStudio.TestTools.UnitTesting; 12 | 13 | namespace cs 14 | { 15 | [TestClass] 16 | public class SetUpFixture 17 | { 18 | private static IntPtr hActCtx; 19 | private static IntPtr cookie; 20 | 21 | [AssemblyInitialize] 22 | public static void SetUp( TestContext testContext ) 23 | { 24 | UnsafeNativeMethods.ACTCTX context = new UnsafeNativeMethods.ACTCTX(); 25 | context.cbSize = Marshal.SizeOf( typeof( UnsafeNativeMethods.ACTCTX ) ); 26 | var manifest = Path.Combine( Path.GetDirectoryName( Assembly.GetExecutingAssembly().Location ), "test_lib.dll" ); 27 | context.dwFlags = 0x008; 28 | context.lpSource = manifest; 29 | context.lpResourceName = new IntPtr( 1 ); 30 | 31 | hActCtx = UnsafeNativeMethods.CreateActCtx( ref context ); 32 | if( hActCtx == ( IntPtr ) ( -1 ) ) 33 | throw new Win32Exception( Marshal.GetLastWin32Error() ); 34 | 35 | cookie = IntPtr.Zero; 36 | if( !UnsafeNativeMethods.ActivateActCtx( hActCtx, out cookie ) ) 37 | throw new Win32Exception( Marshal.GetLastWin32Error() ); 38 | } 39 | 40 | [AssemblyCleanup] 41 | public static void TearDown() 42 | { 43 | UnsafeNativeMethods.DeactivateActCtx( 0, cookie ); 44 | UnsafeNativeMethods.ReleaseActCtx( hActCtx ); 45 | } 46 | } 47 | 48 | [SuppressUnmanagedCodeSecurity] 49 | internal static class UnsafeNativeMethods 50 | { 51 | // Activation Context API Functions 52 | [DllImport("Kernel32.dll", SetLastError = true, EntryPoint = "CreateActCtxW")] 53 | internal extern static IntPtr CreateActCtx(ref ACTCTX actctx); 54 | 55 | [DllImport("Kernel32.dll", SetLastError = true)] 56 | [return: MarshalAs(UnmanagedType.Bool)] 57 | internal static extern bool ActivateActCtx(IntPtr hActCtx, out IntPtr lpCookie); 58 | 59 | [DllImport("kernel32.dll", SetLastError = true)] 60 | [return: MarshalAs(UnmanagedType.Bool)] 61 | internal static extern bool DeactivateActCtx(int dwFlags, IntPtr lpCookie); 62 | 63 | [DllImport("Kernel32.dll", SetLastError = true)] 64 | internal static extern void ReleaseActCtx(IntPtr hActCtx); 65 | 66 | // Activation context structure 67 | [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)] 68 | internal struct ACTCTX 69 | { 70 | public int cbSize; 71 | public uint dwFlags; 72 | public string lpSource; 73 | public ushort wProcessorArchitecture; 74 | public Int16 wLangId; 75 | public string lpAssemblyDirectory; 76 | public IntPtr lpResourceName; 77 | public string lpApplicationName; 78 | public IntPtr hModule; 79 | } 80 | 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /test/cs/StatefulOperations.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | using Microsoft.VisualStudio.TestTools.UnitTesting; 7 | 8 | namespace cs 9 | { 10 | [TestClass] 11 | public class StatefulOperations 12 | { 13 | [TestMethod] 14 | public void StateIsStored() 15 | { 16 | var lib = new TestLib.StatefulOperations(); 17 | 18 | var value = new Random().Next(); 19 | lib.PutValue( value ); 20 | Assert.AreEqual( value, lib.GetValue() ); 21 | 22 | lib.PutValue( 0 ); 23 | Assert.AreEqual( 0, lib.GetValue() ); 24 | } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /test/cs/cs.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio 15 4 | VisualStudioVersion = 15.0.27004.2002 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "cs", "cs.csproj", "{09C842AC-D779-46FD-B0CB-0640121BD2C0}" 7 | EndProject 8 | Global 9 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 10 | Debug|Any CPU = Debug|Any CPU 11 | Debug|x64 = Debug|x64 12 | Release|Any CPU = Release|Any CPU 13 | Release|x64 = Release|x64 14 | EndGlobalSection 15 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 16 | {09C842AC-D779-46FD-B0CB-0640121BD2C0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 17 | {09C842AC-D779-46FD-B0CB-0640121BD2C0}.Debug|Any CPU.Build.0 = Debug|Any CPU 18 | {09C842AC-D779-46FD-B0CB-0640121BD2C0}.Debug|x64.ActiveCfg = Debug|x64 19 | {09C842AC-D779-46FD-B0CB-0640121BD2C0}.Debug|x64.Build.0 = Debug|x64 20 | {09C842AC-D779-46FD-B0CB-0640121BD2C0}.Release|Any CPU.ActiveCfg = Release|Any CPU 21 | {09C842AC-D779-46FD-B0CB-0640121BD2C0}.Release|Any CPU.Build.0 = Release|Any CPU 22 | {09C842AC-D779-46FD-B0CB-0640121BD2C0}.Release|x64.ActiveCfg = Release|x64 23 | {09C842AC-D779-46FD-B0CB-0640121BD2C0}.Release|x64.Build.0 = Release|x64 24 | EndGlobalSection 25 | GlobalSection(SolutionProperties) = preSolution 26 | HideSolutionNode = FALSE 27 | EndGlobalSection 28 | GlobalSection(ExtensibilityGlobals) = postSolution 29 | SolutionGuid = {6F22194A-A6C3-4BB4-B5C5-71DF2AA6AC0C} 30 | EndGlobalSection 31 | EndGlobal 32 | -------------------------------------------------------------------------------- /test/dependencies/.gitattributes: -------------------------------------------------------------------------------- 1 | * linguist-vendored 2 | -------------------------------------------------------------------------------- /test/multilib/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | project (multi_lib) 2 | include(ExternalProject) 3 | 4 | # Determine the name of the binary. 5 | # The binary is generated by Rust compiler so we cannot simply deduce it automatically. 6 | set(MULTILIB_BINARY_BUILD_NAME "${CMAKE_SHARED_LIBRARY_PREFIX}multi_lib${CMAKE_SHARED_LIBRARY_SUFFIX}") 7 | 8 | set(MULTILIB_WORKSPACE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/..") 9 | 10 | # Determine the parameters based on the build type. 11 | if("${CMAKE_BUILD_TYPE}" STREQUAL "Release") 12 | set(MULTILIB_BINARY_BUILD_DIRECTORY "${MULTILIB_WORKSPACE_DIR}/target/release") 13 | set(MULTILIB_BUILD_ARGUMENT_RELEASE "--release") 14 | else() 15 | set(MULTILIB_BINARY_BUILD_DIRECTORY "${MULTILIB_WORKSPACE_DIR}/target/debug") 16 | set(MULTILIB_BUILD_ARGUMENT_RELEASE "") 17 | endif() 18 | set(MULTILIB_BINARY_BUILD_PATH "${MULTILIB_BINARY_BUILD_DIRECTORY}/${MULTILIB_BINARY_BUILD_NAME}") 19 | set(MULTILIB_IMPORT_BUILD_PATH "${MULTILIB_BINARY_BUILD_PATH}.lib") 20 | 21 | set(MULTILIB_LOCATION "${CMAKE_LIBRARY_OUTPUT_DIRECTORY}/${MULTILIB_BINARY_BUILD_NAME}") 22 | set(MULTILIB_IMPORT_LOCATION "${MULTILIB_LOCATION}.lib") 23 | 24 | # Build instructions for the library. 25 | file(GLOB MULTILIB_SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/src) 26 | add_custom_command( 27 | OUTPUT ${MULTILIB_BINARY_BUILD_PATH} 28 | COMMAND "cargo" ARGS "build" ${MULTILIB_BUILD_ARGUMENT_RELEASE} 29 | WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} 30 | DEPENDS ${MULTILIB_SOURCES} 31 | ) 32 | 33 | # On Windows the DLLs are linked with the help of an import library. 34 | # Carge sets the name of the import library to ".dll.lib". 35 | if( WIN32 ) 36 | 37 | # Import library is required. 38 | add_custom_target(multilib DEPENDS ${MULTILIB_BINARY_BUILD_PATH} ${MULTILIB_IMPORT_BUILD_PATH}) 39 | 40 | add_custom_command( 41 | TARGET multilib POST_BUILD 42 | COMMAND cargo run embed-typelib ${MULTILIB_BINARY_BUILD_PATH} 43 | WORKING_DIRECTORY ${MULTILIB_WORKSPACE_DIR}/../intercom-cli 44 | ) 45 | 46 | else() 47 | 48 | # No need to depend on the import library. 49 | add_custom_target(multilib DEPENDS ${MULTILIB_BINARY_BUILD_PATH}) 50 | 51 | endif() 52 | 53 | add_custom_command( 54 | TARGET multilib POST_BUILD 55 | COMMAND ${CMAKE_COMMAND} -E copy ${MULTILIB_BINARY_BUILD_PATH} ${MULTILIB_LOCATION} 56 | DEPENDS ${MULTILIB_BINARY_BUILD_PATH} ) 57 | 58 | # Publish as a library. 59 | add_library(multi_lib SHARED IMPORTED GLOBAL) 60 | add_dependencies(multi_lib multilib) 61 | set_target_properties(multi_lib PROPERTIES IMPORTED_LOCATION "${MULTILIB_BINARY_BUILD_PATH}") 62 | set_target_properties(multi_lib PROPERTIES IMPORTED_IMPLIB "${MULTILIB_IMPORT_BUILD_PATH}") 63 | 64 | # Unfortunately Rust has a poor support for "soname". 65 | # See the following discussions 66 | # - https://github.com/rust-lang/rust/issues/37529 67 | # - https://github.com/rust-lang/cargo/issues/5045 68 | # 69 | # Without this the NEEDED path in the binaries that link to this library are wrong. 70 | # Even if the CMAKE_RUNTIME_OUTPUT_DIRECTORY is set to a custom directory the 71 | # NEEDED path to this library within the executable is still set as if the compiled exectubale 72 | # was located in the default target. e.g. CMAKE_BINARY_DIR/test// 73 | # Example: 74 | # - Wrong relative path: "../../bin/lib/libtest_lib.so => not found" 75 | # - Should have been: "./lib/libtest_lib.so" 76 | # 77 | # CMake: IMPORTED_NO_SONAME: Specifies that an IMPORTED shared library target has no "soname". 78 | # Set this property to true for an imported shared library file that has no "soname" field. 79 | # CMake may adjust generated link commands for some platforms to prevent the linker 80 | # from using the path to the library in place of its missing soname. Ignored for non-imported targets. 81 | set_target_properties(multi_lib PROPERTIES IMPORTED_NO_SONAME TRUE) 82 | -------------------------------------------------------------------------------- /test/multilib/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "multi_lib" 3 | version = "0.1.0" 4 | authors = ["Juha Lepola "] 5 | 6 | [lib] 7 | crate-type = [ "cdylib" ] 8 | 9 | [dependencies] 10 | intercom = { path = "../../intercom" } 11 | winapi = "0.2.8" 12 | env_logger = "0.7" 13 | -------------------------------------------------------------------------------- /test/multilib/src/lib.rs: -------------------------------------------------------------------------------- 1 | #![crate_type = "dylib"] 2 | 3 | extern crate intercom; 4 | use intercom::*; 5 | extern crate winapi; 6 | 7 | // Declare available COM classes. 8 | com_library!(class HelloWorld); 9 | 10 | #[com_interface] 11 | trait IHelloWorld 12 | { 13 | fn get_hello(&self) -> ComResult; 14 | } 15 | 16 | #[com_class(clsid = "{25ccb3f6-b782-4b2d-933e-54ab447da0aa}", IHelloWorld)] 17 | #[derive(Default)] 18 | pub struct HelloWorld {} 19 | 20 | impl HelloWorld 21 | { 22 | pub fn new() -> HelloWorld 23 | { 24 | HelloWorld {} 25 | } 26 | } 27 | 28 | impl IHelloWorld for HelloWorld 29 | { 30 | fn get_hello(&self) -> ComResult 31 | { 32 | Ok("Hello World!".to_string()) 33 | } 34 | } 35 | 36 | #[test] 37 | fn hello_world_returns_hello_world() 38 | { 39 | let hello = HelloWorld::new(); 40 | assert_eq!(hello.get_hello().unwrap(), "Hello World!"); 41 | } 42 | -------------------------------------------------------------------------------- /test/perf/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "intercom-perf" 3 | version = "0.1.0" 4 | authors = ["Juha Lepola "] 5 | 6 | [dependencies] 7 | rand = "0.4.2" 8 | libc = "0.2.36" 9 | -------------------------------------------------------------------------------- /test/runpath/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | project (runpath) 2 | 3 | # C++14 support 4 | set(CMAKE_CXX_STANDARD 14) 5 | set(CMAKE_CXX_STANDARD_REQUIRED ON) 6 | 7 | # Define source files. 8 | set(PROJECT_SRCS 9 | ${PROJECT_SOURCE_DIR}/init.c 10 | ) 11 | 12 | add_library(${PROJECT_NAME} SHARED ${PROJECT_SRCS}) 13 | -------------------------------------------------------------------------------- /test/runpath/init.c: -------------------------------------------------------------------------------- 1 | 2 | #include "init.h" 3 | 4 | EXPORT_API extern void init_runpath() 5 | { 6 | } 7 | -------------------------------------------------------------------------------- /test/runpath/init.h: -------------------------------------------------------------------------------- 1 | 2 | // Functions need to be explicitly exported on Windows in order to the generate import library file. 3 | #ifdef _MSC_VER 4 | #define EXPORT_API __declspec( dllexport ) 5 | #else 6 | #define EXPORT_API 7 | #endif 8 | 9 | /** 10 | * @brief Expose a symbol which other binaries can callo to ensure libpath is linked. 11 | * 12 | */ 13 | #ifdef __cplusplus 14 | extern "C" { 15 | #endif 16 | 17 | EXPORT_API void init_runpath(); 18 | 19 | #ifdef __cplusplus 20 | } 21 | #endif 22 | -------------------------------------------------------------------------------- /test/rust-cmake/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | project (rust-libs) 2 | include(ExternalProject) 3 | 4 | set(LIB_NAMES "test_lib" "multi_lib" ) 5 | 6 | set(WORKSPACE_ROOT "${CMAKE_CURRENT_SOURCE_DIR}/..") 7 | set(INTERCOM_ROOT "${WORKSPACE_ROOT}/..") 8 | 9 | if("${CMAKE_BUILD_TYPE}" STREQUAL "Release") 10 | set(BUILD_DIR "${WORKSPACE_ROOT}/target/release") 11 | set(TARGET_DIR "${CMAKE_LIBRARY_OUTPUT_DIRECTORY}/release") 12 | set(BUILD_ARGUMENTS "--release") 13 | else() 14 | set(BUILD_DIR "${WORKSPACE_ROOT}/target/debug") 15 | set(TARGET_DIR "${CMAKE_LIBRARY_OUTPUT_DIRECTORY}/debug") 16 | set(BUILD_ARGUMENTS "") 17 | endif() 18 | 19 | 20 | # Turn the lib names into binary names 21 | 22 | set(LIB_FILE_NAMES "") 23 | set(LIB_CPP_PATHS "") 24 | foreach(l ${LIB_NAMES}) 25 | LIST(APPEND LIB_FILE_NAMES "${CMAKE_SHARED_LIBRARY_PREFIX}${l}${CMAKE_SHARED_LIBRARY_SUFFIX}") 26 | LIST(APPEND LIB_CPP_PATHS "${BUILD_DIR}/${f}.cpp") 27 | endforeach() 28 | 29 | set(LIB_BUILD_PATHS "") 30 | set(LIB_TARGET_PATHS "") 31 | foreach(f ${LIB_FILE_NAMES}) 32 | LIST(APPEND LIB_BUILD_PATHS "${BUILD_DIR}/${f}") 33 | LIST(APPEND LIB_TARGET_PATHS "${TARGET_DIR}/${f}") 34 | endforeach() 35 | 36 | # Compile the binaries. 37 | add_custom_target(rust-libs-build 38 | COMMAND cargo build ${BUILD_ARGUMENTS} 39 | WORKING_DIRECTORY ${WORKSPACE_ROOT} 40 | ) 41 | 42 | foreach(p ${LIB_BUILD_PATHS}) 43 | add_custom_command( 44 | TARGET rust-libs-build POST_BUILD 45 | COMMAND cargo run ${BUILD_ARGUMENTS} embed-typelib ${p} 46 | WORKING_DIRECTORY ${INTERCOM_ROOT}/intercom-cli) 47 | endforeach() 48 | 49 | add_custom_command( 50 | TARGET rust-libs-build POST_BUILD 51 | COMMAND ${CMAKE_COMMAND} -E copy 52 | ${LIB_BUILD_PATHS} 53 | ${TARGET_DIR} 54 | DEPENDS ${LIB_BUILD_PATHS} 55 | ) 56 | 57 | if( WIN32 ) 58 | 59 | # Create the .lib file paths. 60 | set(IMPORT_BUILD_PATHS "") 61 | set(IMPORT_TARGET_PATHS "") 62 | foreach(f ${LIB_FILE_NAMES}) 63 | LIST(APPEND IMPORT_BUILD_PATHS "${BUILD_DIR}/${f}.lib") 64 | LIST(APPEND IMPORT_TARGET_PATHS "${TARGET_DIR}/${f}.lib") 65 | endforeach() 66 | 67 | # Copy the .libs to the output directory. 68 | add_custom_command( 69 | TARGET rust-libs-build POST_BUILD 70 | COMMAND ${CMAKE_COMMAND} -E copy 71 | ${IMPORT_BUILD_PATHS} 72 | ${TARGET_DIR} 73 | DEPENDS ${LIB_BUILD_PATHS} ) 74 | endif() 75 | 76 | 77 | add_library(multi_lib SHARED IMPORTED GLOBAL) 78 | add_dependencies(multi_lib rust-libs-build) 79 | set_target_properties(multi_lib PROPERTIES IMPORTED_LOCATION "${TARGET_DIR}/${CMAKE_LSHARED_LIBRARY_PREFIX}multi_lib${CMAKE_SHARED_LIBRARY_SUFFIX}") 80 | set_target_properties(multi_lib PROPERTIES IMPORTED_IMPLIB "${TARGET_DIR}/${CMAKE_LSHARED_LIBRARY_PREFIX}multi_lib${CMAKE_SHARED_LIBRARY_SUFFIX}.lib") 81 | 82 | add_library(test_lib SHARED IMPORTED GLOBAL) 83 | add_dependencies(test_lib rust-libs-build) 84 | set_target_properties(test_lib PROPERTIES IMPORTED_LOCATION "${TARGET_DIR}/${CMAKE_LSHARED_LIBRARY_PREFIX}test_lib${CMAKE_SHARED_LIBRARY_SUFFIX}") 85 | set_target_properties(test_lib PROPERTIES IMPORTED_IMPLIB "${TARGET_DIR}/${CMAKE_LSHARED_LIBRARY_PREFIX}test_lib${CMAKE_SHARED_LIBRARY_SUFFIX}.lib") 86 | -------------------------------------------------------------------------------- /test/testlib/.gitignore: -------------------------------------------------------------------------------- 1 | /TestLib.Assembly.manifest 2 | /testlib.idl 3 | -------------------------------------------------------------------------------- /test/testlib/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "test_lib" 3 | version = "0.1.0" 4 | authors = ["Mikko Rantanen "] 5 | 6 | [lib] 7 | crate-type = [ "cdylib" ] 8 | 9 | [dependencies] 10 | intercom = { path = "../../intercom" } 11 | winapi = "0.2.8" 12 | chrono = "0.4" 13 | env_logger = "0.7" 14 | 15 | -------------------------------------------------------------------------------- /test/testlib/src/alloc.rs: -------------------------------------------------------------------------------- 1 | use intercom::*; 2 | 3 | #[com_class(AllocTests)] 4 | #[derive(Default)] 5 | pub struct AllocTests; 6 | 7 | #[com_interface] 8 | impl AllocTests 9 | { 10 | pub fn get_bstr_result(&self, value: u32) -> ComResult 11 | { 12 | Ok(format!("{}", value)) 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /test/testlib/src/interface_params.rs: -------------------------------------------------------------------------------- 1 | use intercom::*; 2 | 3 | #[com_interface] 4 | pub trait ISharedInterface 5 | { 6 | fn get_value(&self) -> u32; 7 | fn set_value(&mut self, v: u32); 8 | fn divide_by(&self, divisor: &ComItf) -> ComResult; 9 | } 10 | 11 | #[com_class(ISharedInterface)] 12 | #[derive(Default)] 13 | pub struct SharedImplementation 14 | { 15 | value: u32, 16 | } 17 | 18 | impl ISharedInterface for SharedImplementation 19 | { 20 | fn get_value(&self) -> u32 21 | { 22 | self.value 23 | } 24 | fn set_value(&mut self, v: u32) 25 | { 26 | self.value = v 27 | } 28 | fn divide_by(&self, other: &ComItf) -> ComResult 29 | { 30 | let divisor = other.get_value(); 31 | match divisor { 32 | 0 => Err(ComError::E_INVALIDARG), 33 | _ => Ok(self.value / divisor), 34 | } 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /test/testlib/src/lib.rs: -------------------------------------------------------------------------------- 1 | #![crate_type = "dylib"] 2 | #![allow(clippy::match_bool)] 3 | 4 | extern crate intercom; 5 | use intercom::*; 6 | extern crate chrono; 7 | extern crate winapi; 8 | 9 | pub mod alloc; 10 | pub mod error_info; 11 | pub mod interface_params; 12 | pub mod nullable_parameters; 13 | pub mod output_memory; 14 | pub mod primitive; 15 | pub mod result; 16 | pub mod return_interfaces; 17 | pub mod stateful; 18 | pub mod strings; 19 | pub mod type_system_callbacks; 20 | pub mod unicode; 21 | pub mod variant; 22 | 23 | // Declare available COM classes. 24 | com_library! { 25 | module return_interfaces, 26 | module nullable_parameters, 27 | 28 | class primitive::PrimitiveOperations, 29 | class stateful::StatefulOperations, 30 | class result::ResultOperations, 31 | class interface_params::SharedImplementation, 32 | class error_info::ErrorTests, 33 | class alloc::AllocTests, 34 | class strings::StringTests, 35 | class type_system_callbacks::TypeSystemCaller, 36 | class variant::VariantTests, 37 | class variant::VariantImpl, 38 | class unicode::UnicodeConversion, 39 | class output_memory::OutputMemoryTests, 40 | 41 | interface IOnlyInterface, 42 | 43 | // Ensure exporting interfaces that would otherwise be included as well 44 | // does not cause problem. 45 | interface interface_params::ISharedInterface, 46 | } 47 | 48 | #[com_interface] 49 | pub trait IOnlyInterface 50 | { 51 | fn method(&self) -> ComResult; 52 | } 53 | -------------------------------------------------------------------------------- /test/testlib/src/nullable_parameters.rs: -------------------------------------------------------------------------------- 1 | use intercom::prelude::*; 2 | 3 | com_module! { 4 | class DoCallback, 5 | class NullableTests, 6 | } 7 | 8 | #[com_interface] 9 | pub trait ICallback 10 | { 11 | fn callback(&self) -> u32; 12 | } 13 | 14 | #[com_class(ICallback)] 15 | #[derive(Default)] 16 | struct DoCallback(u32); 17 | 18 | impl ICallback for DoCallback 19 | { 20 | fn callback(&self) -> u32 21 | { 22 | self.0 23 | } 24 | } 25 | 26 | #[com_interface] 27 | pub trait INullableInterface 28 | { 29 | fn nullable_parameter(&self, itf: Option<&ComItf>) -> u32; 30 | 31 | fn nonnull_parameter(&self, itf: &ComItf) -> ComResult; 32 | 33 | fn nullable_output(&self, value: u32) -> ComResult>>; 34 | 35 | fn nonnull_output(&self, value: u32) -> ComResult>; 36 | } 37 | 38 | #[com_class(Self, INullableInterface)] 39 | #[derive(Default)] 40 | pub struct NullableTests; 41 | 42 | #[com_interface] 43 | impl NullableTests 44 | { 45 | fn nullable_parameter(&self, v: u32, itf: &ComItf) -> ComResult<()> 46 | { 47 | let opt = if v == 0 { 48 | None 49 | } else { 50 | Some(ComRc::from(ComBox::new(DoCallback(v)))) 51 | }; 52 | 53 | if itf.nullable_parameter(opt.as_deref()) != v { 54 | Err(ComError::E_FAIL) 55 | } else { 56 | Ok(()) 57 | } 58 | } 59 | 60 | fn nonnull_parameter(&self, v: u32, itf: &ComItf) -> ComResult<()> 61 | { 62 | let rc = ComRc::from(ComBox::new(DoCallback(v))); 63 | 64 | if itf.nonnull_parameter(&rc)? != v { 65 | Err(ComError::E_FAIL) 66 | } else { 67 | Ok(()) 68 | } 69 | } 70 | 71 | fn nullable_output(&self, value: u32, itf: &ComItf) -> ComResult<()> 72 | { 73 | let cb = itf.nullable_output(value)?; 74 | match cb { 75 | None => { 76 | if value != 0 { 77 | Err(ComError::E_INVALIDARG) 78 | } else { 79 | Ok(()) 80 | } 81 | } 82 | Some(cb) => { 83 | if value != 0 && cb.callback() == value { 84 | Ok(()) 85 | } else { 86 | Err(ComError::E_FAIL) 87 | } 88 | } 89 | } 90 | } 91 | 92 | fn nonnull_output(&self, value: u32, itf: &ComItf) -> ComResult<()> 93 | { 94 | let cb = itf.nonnull_output(value)?; 95 | match cb.callback() == value { 96 | true => Ok(()), 97 | false => Err(ComError::E_FAIL), 98 | } 99 | } 100 | } 101 | 102 | impl INullableInterface for NullableTests 103 | { 104 | fn nullable_parameter(&self, itf: Option<&ComItf>) -> u32 105 | { 106 | if let Some(itf) = itf { 107 | itf.callback() 108 | } else { 109 | 0 110 | } 111 | } 112 | 113 | fn nonnull_parameter(&self, itf: &ComItf) -> ComResult 114 | { 115 | Ok(itf.callback()) 116 | } 117 | 118 | fn nullable_output(&self, value: u32) -> ComResult>> 119 | { 120 | match value { 121 | 0 => Ok(None), 122 | v => Ok(Some(ComRc::from(ComBox::new(DoCallback(v))))), 123 | } 124 | } 125 | 126 | fn nonnull_output(&self, value: u32) -> ComResult> 127 | { 128 | Ok(ComRc::from(ComBox::new(DoCallback(value)))) 129 | } 130 | } 131 | -------------------------------------------------------------------------------- /test/testlib/src/output_memory.rs: -------------------------------------------------------------------------------- 1 | #![allow(clippy::type_complexity)] 2 | 3 | use intercom::prelude::*; 4 | use intercom::type_system::{ExternOutput, ExternType, TypeSystem}; 5 | use intercom::IUnknown; 6 | use std::ffi::c_void; 7 | 8 | #[com_class(IOutputMemoryTests)] 9 | #[derive(Default)] 10 | pub struct OutputMemoryTests; 11 | 12 | #[com_interface] 13 | pub trait IOutputMemoryTests 14 | { 15 | fn succeed( 16 | &self, 17 | input: &ComItf, 18 | ) -> ComResult<(ComRc, ComRc)>; 19 | 20 | fn fail( 21 | &self, 22 | input: &ComItf, 23 | ) -> ComResult<(ComRc, FailingType, ComRc)>; 24 | 25 | fn call_succeed( 26 | &self, 27 | itf: &ComItf, 28 | input: &ComItf, 29 | ) -> ComResult<()>; 30 | 31 | fn call_fail( 32 | &self, 33 | itf: &ComItf, 34 | input: &ComItf, 35 | ) -> ComResult<()>; 36 | } 37 | 38 | impl IOutputMemoryTests for OutputMemoryTests 39 | { 40 | fn succeed( 41 | &self, 42 | input: &ComItf, 43 | ) -> ComResult<(ComRc, ComRc)> 44 | { 45 | Ok((input.into(), input.into())) 46 | } 47 | 48 | fn fail( 49 | &self, 50 | input: &ComItf, 51 | ) -> ComResult<(ComRc, FailingType, ComRc)> 52 | { 53 | Ok((input.into(), FailingType, input.into())) 54 | } 55 | 56 | fn call_succeed( 57 | &self, 58 | itf: &ComItf, 59 | input: &ComItf, 60 | ) -> ComResult<()> 61 | { 62 | itf.succeed(input).map(|_| ()) 63 | } 64 | 65 | fn call_fail( 66 | &self, 67 | itf: &ComItf, 68 | input: &ComItf, 69 | ) -> ComResult<()> 70 | { 71 | itf.fail(input).map(|_| ()) 72 | } 73 | } 74 | 75 | /// A type that fails all conversions. 76 | pub struct FailingType; 77 | impl ExternType for FailingType 78 | { 79 | type ForeignType = *mut c_void; 80 | } 81 | 82 | unsafe impl ExternOutput for FailingType 83 | { 84 | fn into_foreign_output(self) -> ComResult 85 | { 86 | Err(ComError::E_FAIL) 87 | } 88 | 89 | unsafe fn from_foreign_output(_: Self::ForeignType) -> ComResult 90 | { 91 | Err(ComError::E_FAIL) 92 | } 93 | } 94 | -------------------------------------------------------------------------------- /test/testlib/src/primitive.rs: -------------------------------------------------------------------------------- 1 | use intercom::*; 2 | 3 | #[com_class(clsid = "{12341234-1234-1234-1234-123412340001}", PrimitiveOperations)] 4 | #[derive(Default)] 5 | pub struct PrimitiveOperations {} 6 | 7 | #[com_interface( 8 | com_iid = "{12341234-1234-1234-1234-123412340002}", 9 | raw_iid = "{12341234-1234-1234-1234-123412340003}" 10 | )] 11 | impl PrimitiveOperations 12 | { 13 | pub fn i8(&self, v: i8) -> i8 14 | { 15 | !(v.wrapping_add(1)) 16 | } 17 | pub fn u8(&self, v: u8) -> u8 18 | { 19 | !(v.wrapping_add(1)) 20 | } 21 | 22 | pub fn u16(&self, v: u16) -> u16 23 | { 24 | !(v.wrapping_add(1)) 25 | } 26 | pub fn i16(&self, v: i16) -> i16 27 | { 28 | !(v.wrapping_add(1)) 29 | } 30 | 31 | pub fn i32(&self, v: i32) -> i32 32 | { 33 | !(v.wrapping_add(1)) 34 | } 35 | pub fn u32(&self, v: u32) -> u32 36 | { 37 | !(v.wrapping_add(1)) 38 | } 39 | 40 | pub fn i64(&self, v: i64) -> i64 41 | { 42 | !(v.wrapping_add(1)) 43 | } 44 | pub fn u64(&self, v: u64) -> u64 45 | { 46 | !(v.wrapping_add(1)) 47 | } 48 | 49 | pub fn f64(&self, v: f64) -> f64 50 | { 51 | 1f64 / v 52 | } 53 | pub fn f32(&self, v: f32) -> f32 54 | { 55 | 1f32 / v 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /test/testlib/src/result.rs: -------------------------------------------------------------------------------- 1 | use intercom::*; 2 | use std::convert::TryFrom; 3 | 4 | #[com_class(ResultOperations)] 5 | #[derive(Default)] 6 | pub struct ResultOperations {} 7 | 8 | #[com_interface] 9 | impl ResultOperations 10 | { 11 | pub fn s_ok(&mut self) -> raw::HRESULT 12 | { 13 | raw::S_OK 14 | } 15 | 16 | pub fn not_impl(&mut self) -> raw::HRESULT 17 | { 18 | raw::E_NOTIMPL 19 | } 20 | 21 | pub fn sqrt(&mut self, value: f64) -> ComResult 22 | { 23 | if value < 0.0 { 24 | return Err(ComError::E_INVALIDARG); 25 | } 26 | Ok(value.sqrt()) 27 | } 28 | 29 | pub fn tuple(&self, value: u32) -> ComResult<(u16, u16)> 30 | { 31 | let first = u16::try_from((value & 0xffff_0000) >> 16).unwrap(); 32 | let second = u16::try_from(value & 0xffff).unwrap(); 33 | 34 | Ok((first, second)) 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /test/testlib/src/return_interfaces.rs: -------------------------------------------------------------------------------- 1 | use intercom::*; 2 | 3 | com_module!( 4 | class RefCountOperations, 5 | class ClassCreator, 6 | class CreatedClass, 7 | ); 8 | 9 | #[com_interface] 10 | trait IRefCount 11 | { 12 | fn get_ref_count(&self) -> u32; 13 | } 14 | 15 | #[com_class(ClassCreator)] 16 | #[derive(Default)] 17 | pub struct ClassCreator {} 18 | 19 | #[com_interface] 20 | impl ClassCreator 21 | { 22 | pub fn create_root(&self, id: i32) -> ComResult> 23 | { 24 | Ok(ComRc::from(&ComBox::new(CreatedClass::new_with_id(id)))) 25 | } 26 | 27 | pub fn create_child( 28 | &self, 29 | id: i32, 30 | parent: &ComItf, 31 | ) -> ComResult> 32 | { 33 | Ok(ComRc::from(&ComBox::new(CreatedClass::new_child( 34 | id, 35 | parent.get_id(), 36 | )))) 37 | } 38 | } 39 | 40 | #[com_class(CreatedClass, IParent, IRefCount)] 41 | #[derive(Default)] 42 | pub struct CreatedClass 43 | { 44 | id: i32, 45 | parent: i32, 46 | } 47 | 48 | #[com_interface] 49 | impl CreatedClass 50 | { 51 | pub fn new_with_id(id: i32) -> CreatedClass 52 | { 53 | CreatedClass { id, parent: 0 } 54 | } 55 | pub fn new_child(id: i32, parent: i32) -> CreatedClass 56 | { 57 | CreatedClass { id, parent } 58 | } 59 | 60 | pub fn get_id(&self) -> ComResult 61 | { 62 | Ok(self.id) 63 | } 64 | pub fn get_parent_id(&self) -> ComResult 65 | { 66 | Ok(self.parent) 67 | } 68 | } 69 | 70 | impl IRefCount for CreatedClass 71 | { 72 | fn get_ref_count(&self) -> u32 73 | { 74 | let combox = unsafe { ComBoxData::of(self) }; 75 | combox.get_ref_count() 76 | } 77 | } 78 | 79 | #[com_interface] 80 | pub trait IParent 81 | { 82 | fn get_id(&self) -> i32; 83 | } 84 | 85 | impl IParent for CreatedClass 86 | { 87 | fn get_id(&self) -> i32 88 | { 89 | self.id 90 | } 91 | } 92 | 93 | #[com_class(RefCountOperations)] 94 | #[derive(Default)] 95 | pub struct RefCountOperations {} 96 | 97 | #[com_interface] 98 | impl RefCountOperations 99 | { 100 | pub fn get_new(&self) -> ComResult> 101 | { 102 | Ok(ComRc::from(&ComBox::::default())) 103 | } 104 | 105 | pub fn get_ref_count(&self) -> u32 106 | { 107 | let combox = unsafe { ComBoxData::of(self) }; 108 | combox.get_ref_count() 109 | } 110 | } 111 | -------------------------------------------------------------------------------- /test/testlib/src/setup_configuration.rs: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Rantanen/intercom/e1f987d827d3da4f4785cbd95a8a464052a5920e/test/testlib/src/setup_configuration.rs -------------------------------------------------------------------------------- /test/testlib/src/stateful.rs: -------------------------------------------------------------------------------- 1 | use intercom::*; 2 | 3 | #[com_class(StatefulOperations)] 4 | pub struct StatefulOperations 5 | { 6 | state: i32, 7 | } 8 | 9 | impl Default for StatefulOperations 10 | { 11 | fn default() -> Self 12 | { 13 | StatefulOperations { state: 0xABBACD } 14 | } 15 | } 16 | 17 | #[com_interface] 18 | impl StatefulOperations 19 | { 20 | pub fn put_value(&mut self, v: i32) 21 | { 22 | self.state = v; 23 | } 24 | pub fn get_value(&mut self) -> i32 25 | { 26 | self.state 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /test/testlib/src/strings.rs: -------------------------------------------------------------------------------- 1 | use intercom::*; 2 | 3 | pub static STRING_DATA: &[&str] = &["", "Test", "öäå", "\u{1F980}"]; 4 | 5 | #[com_interface] 6 | pub trait IStringTests 7 | { 8 | fn string_to_index(&self, s: &str) -> ComResult; 9 | 10 | fn index_to_string(&self, i: u32) -> ComResult; 11 | 12 | fn bstr_parameter(&self, s: &BStr, ptr: usize) -> ComResult<()>; 13 | 14 | fn bstring_parameter(&self, s: BString) -> ComResult<()>; 15 | 16 | fn bstring_return_value(&self) -> ComResult<(BString, usize)>; 17 | 18 | fn cstr_parameter(&self, s: &CStr, ptr: usize) -> ComResult<()>; 19 | 20 | fn cstring_parameter(&self, s: CString) -> ComResult<()>; 21 | 22 | fn cstring_return_value(&self) -> ComResult<(CString, usize)>; 23 | 24 | fn invalid_string(&self, s: &str) -> ComResult<()>; 25 | } 26 | 27 | #[com_class(IStringTests)] 28 | #[derive(Default)] 29 | pub struct StringTests; 30 | 31 | impl StringTests 32 | { 33 | pub fn new() -> StringTests 34 | { 35 | StringTests 36 | } 37 | } 38 | 39 | impl IStringTests for StringTests 40 | { 41 | fn string_to_index(&self, s: &str) -> ComResult 42 | { 43 | for (idx, &candidate) in STRING_DATA.iter().enumerate() { 44 | if s == candidate { 45 | return Ok(idx as u32); 46 | } 47 | } 48 | 49 | Err(ComError::E_FAIL) 50 | } 51 | 52 | fn index_to_string(&self, i: u32) -> ComResult 53 | { 54 | STRING_DATA 55 | .get(i as usize) 56 | .map(|s| s.to_string()) 57 | .ok_or(ComError::E_FAIL) 58 | } 59 | 60 | fn bstr_parameter(&self, s: &BStr, ptr: usize) -> ComResult<()> 61 | { 62 | let string = s.to_string().map_err(|_| ComError::E_INVALIDARG)?; 63 | 64 | if string != "\u{1F600}" { 65 | return Err(ComError::E_FAIL); 66 | } 67 | 68 | if s.as_ptr() as usize == ptr { 69 | Ok(()) 70 | } else { 71 | Err(ComError::E_POINTER) 72 | } 73 | } 74 | 75 | fn bstring_parameter(&self, s: BString) -> ComResult<()> 76 | { 77 | let string = s.to_string().map_err(|_| ComError::E_INVALIDARG)?; 78 | 79 | if string != "\u{1F600}" { 80 | Err(ComError::E_FAIL) 81 | } else { 82 | Ok(()) 83 | } 84 | } 85 | 86 | fn bstring_return_value(&self) -> ComResult<(BString, usize)> 87 | { 88 | let bs: BString = BString::from("\u{1F600}"); 89 | let ptr = bs.as_ptr() as usize; 90 | 91 | Ok((bs, ptr)) 92 | } 93 | 94 | fn cstr_parameter(&self, s: &CStr, ptr: usize) -> ComResult<()> 95 | { 96 | if s.to_string_lossy() != "\u{1F600}" { 97 | return Err(ComError::E_FAIL); 98 | } 99 | 100 | if s.as_ptr() as usize == ptr { 101 | Ok(()) 102 | } else { 103 | Err(ComError::E_POINTER) 104 | } 105 | } 106 | 107 | fn cstring_parameter(&self, s: CString) -> ComResult<()> 108 | { 109 | if s.to_string_lossy() != "\u{1F600}" { 110 | Err(ComError::E_FAIL) 111 | } else { 112 | Ok(()) 113 | } 114 | } 115 | 116 | fn cstring_return_value(&self) -> ComResult<(CString, usize)> 117 | { 118 | let bs: CString = CString::new("\u{1F600}").unwrap(); 119 | let ptr = bs.as_ptr() as usize; 120 | 121 | Ok((bs, ptr)) 122 | } 123 | 124 | fn invalid_string(&self, s: &str) -> ComResult<()> 125 | { 126 | // Don't do any validation here. 127 | // Intercom should do validation automatically. 128 | println!("String parameter was not invalid: {}", s); 129 | 130 | // Caller expects E_INVALIDARG, use E_FAIL to indicate something 131 | // went wrong. 132 | Err(ComError::E_FAIL) 133 | } 134 | } 135 | -------------------------------------------------------------------------------- /test/testlib/src/type_system_callbacks.rs: -------------------------------------------------------------------------------- 1 | use intercom::*; 2 | use strings::{IStringTests, STRING_DATA}; 3 | 4 | #[com_class(TypeSystemCaller)] 5 | #[derive(Default)] 6 | pub struct TypeSystemCaller; 7 | 8 | #[com_interface] 9 | impl TypeSystemCaller 10 | { 11 | pub fn new() -> Self 12 | { 13 | TypeSystemCaller 14 | } 15 | 16 | pub fn call_string(&self, i: u32, callback: &ComItf) -> ComResult<()> 17 | { 18 | let actual = callback.string_to_index(STRING_DATA[i as usize])?; 19 | if actual == i { 20 | Ok(()) 21 | } else { 22 | Err(ComError::E_FAIL) 23 | } 24 | } 25 | 26 | fn receive_string(&self, i: u32, callback: &ComItf) -> ComResult<()> 27 | { 28 | let actual = callback.index_to_string(i)?; 29 | let expected = STRING_DATA[i as usize]; 30 | 31 | if actual == expected { 32 | Ok(()) 33 | } else { 34 | Err(ComError::E_FAIL) 35 | } 36 | } 37 | 38 | fn pass_bstr(&self, callback: &ComItf) -> ComResult<()> 39 | { 40 | let bstr = BString::from("\u{1F4A9}"); 41 | callback.bstr_parameter(&bstr, bstr.as_ptr() as usize) 42 | } 43 | 44 | fn pass_bstring(&self, callback: &ComItf) -> ComResult<()> 45 | { 46 | let bstr = BString::from("\u{1F4A9}"); 47 | callback.bstring_parameter(bstr) 48 | } 49 | 50 | fn receive_bstring(&self, callback: &ComItf) -> ComResult<()> 51 | { 52 | let (bstr, ptr) = callback.bstring_return_value()?; 53 | 54 | if bstr.to_string().unwrap() != "\u{1F4A9}" { 55 | return Err(ComError::E_FAIL); 56 | } 57 | 58 | if bstr.as_ptr() as usize != ptr { 59 | return Err(ComError::E_POINTER); 60 | } 61 | 62 | Ok(()) 63 | } 64 | 65 | fn pass_cstr(&self, callback: &ComItf) -> ComResult<()> 66 | { 67 | let cstr = CString::new("\u{1F4A9}").unwrap(); 68 | callback.cstr_parameter(&cstr, cstr.as_ptr() as usize) 69 | } 70 | 71 | fn pass_cstring(&self, callback: &ComItf) -> ComResult<()> 72 | { 73 | let cstr = CString::new("\u{1F4A9}").unwrap(); 74 | callback.cstring_parameter(cstr) 75 | } 76 | 77 | fn receive_cstring(&self, callback: &ComItf) -> ComResult<()> 78 | { 79 | let (cstr, ptr) = callback.cstring_return_value()?; 80 | 81 | if cstr.to_string_lossy() != "\u{1F4A9}" { 82 | return Err(ComError::E_FAIL); 83 | } 84 | 85 | if cstr.as_ptr() as usize != ptr { 86 | return Err(ComError::E_POINTER); 87 | } 88 | 89 | Ok(()) 90 | } 91 | } 92 | -------------------------------------------------------------------------------- /test/testlib/src/unicode.rs: -------------------------------------------------------------------------------- 1 | use intercom::*; 2 | use std::os::raw::c_char; 3 | 4 | #[com_class(UnicodeConversion)] 5 | #[derive(Default)] 6 | pub struct UnicodeConversion; 7 | 8 | #[com_interface] 9 | impl UnicodeConversion 10 | { 11 | fn utf8_to_utf16(&self, input: *const c_char) -> ComResult<*mut u16> 12 | { 13 | if input.is_null() { 14 | return Ok(std::ptr::null_mut()); 15 | } 16 | 17 | let cstr = unsafe { CStr::from_ptr(input) }; 18 | let rust_str = cstr.to_str().map_err(|_| ComError::E_INVALIDARG)?; 19 | 20 | let utf16: Vec<_> = rust_str.encode_utf16().collect(); 21 | let buffer_len = (utf16.len() + 1) * 2; 22 | 23 | unsafe { 24 | let buffer = intercom::alloc::allocate((utf16.len() + 1) * 2) as *mut u16; 25 | std::ptr::copy_nonoverlapping( 26 | utf16.as_ptr() as *const c_char, 27 | buffer as *mut c_char, 28 | buffer_len, 29 | ); 30 | Ok(buffer) 31 | } 32 | } 33 | 34 | fn utf16_to_utf8(&self, input: *const u16) -> ComResult<*mut c_char> 35 | { 36 | if input.is_null() { 37 | return Ok(std::ptr::null_mut()); 38 | } 39 | 40 | let slice = unsafe { 41 | // Find the first zero byte. 42 | let mut len = 0usize; 43 | while *input.add(len) != 0 { 44 | len += 1; 45 | } 46 | 47 | std::slice::from_raw_parts(input, len) 48 | }; 49 | 50 | let s = String::from_utf16(slice).map_err(|_| ComError::E_INVALIDARG)?; 51 | let cstring = CString::new(s).map_err(|_| ComError::E_INVALIDARG)?; 52 | let utf8bytes = cstring.to_bytes(); 53 | 54 | unsafe { 55 | let buffer = intercom::alloc::allocate(utf8bytes.len() + 1) as *mut c_char; 56 | std::ptr::copy_nonoverlapping( 57 | utf8bytes.as_ptr() as *const c_char, 58 | buffer as *mut c_char, 59 | utf8bytes.len() + 1, 60 | ); 61 | Ok(buffer) 62 | } 63 | } 64 | } 65 | --------------------------------------------------------------------------------