├── .gitattributes ├── .gitignore ├── .gitmodules ├── LICENSE ├── azure-pipelines.yml ├── handle_table.cpp ├── handle_table.hpp ├── host.cpp ├── host.def ├── host.h ├── host_internal.hpp ├── logger.cpp ├── logger.hpp ├── my_host.cpp ├── my_host.hpp ├── powershell.hpp ├── psh_host.sln ├── psh_host.vcxproj ├── psh_host.vcxproj.filters ├── psh_host.vcxproj.user ├── readme.md ├── release_notes.md ├── runspace.cpp ├── runspace.hpp ├── script.ps1 ├── test.ps1 ├── test ├── test.cpp ├── test.vcxproj ├── test.vcxproj.filters └── test.vcxproj.user ├── test_gcc ├── .gitignore ├── CMakeLists.txt ├── readme.md └── test.cpp ├── test_unit ├── test_unit.cpp ├── test_unit.vcxproj ├── test_unit.vcxproj.filters └── test_unit.vcxproj.user └── utils ├── cpp_wrappers.hpp ├── macros.hpp ├── managed_memory_helpers.hpp ├── memory_helpers.hpp ├── typed_handles.hpp └── zero_resetable.hpp /.gitattributes: -------------------------------------------------------------------------------- 1 | ############################################################################### 2 | # Set default behavior to automatically normalize line endings. 3 | ############################################################################### 4 | * text=auto 5 | 6 | ############################################################################### 7 | # Set default behavior for command prompt diff. 8 | # 9 | # This is need for earlier builds of msysgit that does not have it on by 10 | # default for csharp files. 11 | # Note: This is only used by command line 12 | ############################################################################### 13 | #*.cs diff=csharp 14 | 15 | ############################################################################### 16 | # Set the merge driver for project and solution files 17 | # 18 | # Merging from the command prompt will add diff markers to the files if there 19 | # are conflicts (Merging from VS is not affected by the settings below, in VS 20 | # the diff markers are never inserted). Diff markers may cause the following 21 | # file extensions to fail to load in VS. An alternative would be to treat 22 | # these files as binary and thus will always conflict and require user 23 | # intervention with every merge. To do so, just uncomment the entries below 24 | ############################################################################### 25 | #*.sln merge=binary 26 | #*.csproj merge=binary 27 | #*.vbproj merge=binary 28 | #*.vcxproj merge=binary 29 | #*.vcproj merge=binary 30 | #*.dbproj merge=binary 31 | #*.fsproj merge=binary 32 | #*.lsproj merge=binary 33 | #*.wixproj merge=binary 34 | #*.modelproj merge=binary 35 | #*.sqlproj merge=binary 36 | #*.wwaproj merge=binary 37 | 38 | ############################################################################### 39 | # behavior for image files 40 | # 41 | # image files are treated as binary by default. 42 | ############################################################################### 43 | #*.jpg binary 44 | #*.png binary 45 | #*.gif binary 46 | 47 | ############################################################################### 48 | # diff behavior for common document formats 49 | # 50 | # Convert binary document formats to text before diffing them. This feature 51 | # is only available from the command line. Turn it on by uncommenting the 52 | # entries below. 53 | ############################################################################### 54 | #*.doc diff=astextplain 55 | #*.DOC diff=astextplain 56 | #*.docx diff=astextplain 57 | #*.DOCX diff=astextplain 58 | #*.dot diff=astextplain 59 | #*.DOT diff=astextplain 60 | #*.pdf diff=astextplain 61 | #*.PDF diff=astextplain 62 | #*.rtf diff=astextplain 63 | #*.RTF diff=astextplain 64 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .vs/** 2 | x64/** 3 | .vscode/* 4 | test/x64/** 5 | test_unit/x64/** -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "native-powershell-bin"] 2 | path = native-powershell-bin 3 | url = https://github.com/KnicKnic/native-powershell-bin.git 4 | [submodule "test_unit/Catch2"] 5 | path = test_unit/Catch2 6 | url = https://github.com/catchorg/Catch2 7 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright [yyyy] [name of copyright owner] 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /azure-pipelines.yml: -------------------------------------------------------------------------------- 1 | # .NET Desktop 2 | # Build and run tests for .NET Desktop or Windows classic desktop solutions. 3 | # Add steps that publish symbols, save build artifacts, and more: 4 | # https://docs.microsoft.com/azure/devops/pipelines/apps/windows/dot-net 5 | 6 | trigger: 7 | - master 8 | 9 | pool: 10 | vmImage: 'windows-latest' 11 | 12 | variables: 13 | solution: '**/*.sln' 14 | buildPlatform: 'x64' 15 | buildConfiguration: 'Release' 16 | 17 | steps: 18 | - checkout: self 19 | submodules: recursive 20 | 21 | - task: NuGetToolInstaller@0 22 | 23 | - task: NuGetCommand@2 24 | inputs: 25 | restoreSolution: '$(solution)' 26 | 27 | - task: VSBuild@1 28 | inputs: 29 | solution: '$(solution)' 30 | platform: '$(buildPlatform)' 31 | configuration: '$(buildConfiguration)' 32 | 33 | - task: VSTest@2 34 | inputs: 35 | platform: '$(buildPlatform)' 36 | configuration: '$(buildConfiguration)' 37 | 38 | - task: CopyFiles@2 39 | inputs: 40 | sourceFolder: '$(Build.SourcesDirectory)' 41 | contents: '**/$(BuildConfiguration)/**/?(*.exe|*.dll|*.pdb)' 42 | TargetFolder: '$(Build.ArtifactStagingDirectory)' 43 | - task: PublishBuildArtifacts@1 44 | inputs: 45 | pathtoPublish: '$(Build.ArtifactStagingDirectory)' 46 | artifactName: drop 47 | -------------------------------------------------------------------------------- /handle_table.cpp: -------------------------------------------------------------------------------- 1 | #include "handle_table.hpp" 2 | 3 | 4 | /*static HandleTable() { 5 | runspaces = ; 6 | }*/ 7 | 8 | inline NativePowerShell_RunspaceHandle HandleTable::InsertRunspace(RunspaceHolder^ runspace) { 9 | return runspaces->insert(runspace); 10 | } 11 | 12 | inline RunspaceHolder^ HandleTable::GetRunspace(NativePowerShell_RunspaceHandle handle) { 13 | return runspaces->get(handle); 14 | } 15 | 16 | inline RunspaceHolder^ HandleTable::RemoveRunspace(NativePowerShell_RunspaceHandle handle) { 17 | return runspaces->remove(handle); 18 | } 19 | 20 | inline NativePowerShell_PowerShellHandle HandleTable::InsertPowershell(PowerShellHolder^ powershell) { 21 | return powershells->insert(powershell); 22 | } 23 | 24 | inline PowerShellHolder^ HandleTable::GetPowershell(NativePowerShell_PowerShellHandle handle) { 25 | return powershells->get(handle); 26 | } 27 | 28 | inline PowerShellHolder^ HandleTable::RemovePowershell(NativePowerShell_PowerShellHandle handle) { 29 | return powershells->remove(handle); 30 | } 31 | 32 | 33 | 34 | inline NativePowerShell_PowerShellObject HandleTable::InsertPSObject(PSObject^ object) { 35 | return psObjects->insert(object); 36 | } 37 | inline PSObject^ HandleTable::GetPSObject(NativePowerShell_PowerShellObject handle) { 38 | return psObjects->get(handle); 39 | } 40 | inline PSObject^ HandleTable::RemovePSObject(NativePowerShell_PowerShellObject handle) { 41 | return psObjects->get(handle); 42 | } -------------------------------------------------------------------------------- /handle_table.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "host.h" 3 | #include "runspace.hpp" 4 | #include "powershell.hpp" 5 | #include "utils/typed_handles.hpp" 6 | 7 | ref class HandleTable { 8 | 9 | private: 10 | static TypedConcurrentTable^ psObjects = gcnew TypedConcurrentTable(); 11 | static TypedConcurrentTable^ runspaces = gcnew TypedConcurrentTable(); 12 | static TypedConcurrentTable^ powershells = gcnew TypedConcurrentTable(); 13 | public: 14 | static NativePowerShell_RunspaceHandle InsertRunspace(RunspaceHolder^ runspace); 15 | static RunspaceHolder^ GetRunspace(NativePowerShell_RunspaceHandle handle); 16 | static RunspaceHolder^ RemoveRunspace(NativePowerShell_RunspaceHandle handle); 17 | static NativePowerShell_PowerShellHandle InsertPowershell(PowerShellHolder^ powershell); 18 | static PowerShellHolder^ GetPowershell(NativePowerShell_PowerShellHandle handle); 19 | static PowerShellHolder^ RemovePowershell(NativePowerShell_PowerShellHandle handle); 20 | static NativePowerShell_PowerShellObject InsertPSObject(PSObject^ object); 21 | static PSObject^ GetPSObject(NativePowerShell_PowerShellObject handle); 22 | static PSObject^ RemovePSObject(NativePowerShell_PowerShellObject handle); 23 | }; -------------------------------------------------------------------------------- /host.cpp: -------------------------------------------------------------------------------- 1 | 2 | #define WIN32_LEAN_AND_MEAN // Exclude rarely-used stuff from Windows headers 3 | // Windows Header Files 4 | #include 5 | #include 6 | #include 7 | 8 | 9 | #include "host.h" 10 | using namespace System::Management::Automation::Runspaces; 11 | using namespace System::Management::Automation; 12 | 13 | 14 | using namespace System; 15 | using namespace System::Security; 16 | using namespace System::Collections::Generic; 17 | using namespace System::Globalization; 18 | using namespace System::Management::Automation; 19 | using namespace System::Management::Automation::Host; 20 | #include "my_host.hpp" 21 | #include "utils/memory_helpers.hpp" 22 | #include "utils/managed_memory_helpers.hpp" 23 | #include "runspace.hpp" 24 | #include "powershell.hpp" 25 | #include "handle_table.hpp" 26 | 27 | #include "logger.hpp" 28 | 29 | // Globals 30 | NativePowerShell_AllocPointer NativePowerShell_AllocPointerPtr = NativePowerShell_DefaultAlloc; 31 | NativePowerShell_FreePointer NativePowerShell_FreePointerPtr = NativePowerShell_DefaultFree; 32 | 33 | const NativePowerShell_PowerShellObject EmptyNativePowerShell_PowerShellObjectHandle = (NativePowerShell_PowerShellObject)(0); 34 | 35 | unsigned char* MallocHelper(unsigned long long size) noexcept { 36 | auto ptr = (unsigned char*)malloc(size); 37 | if(ptr == nullptr){ 38 | throw L"Failed to alloc"; 39 | } 40 | return ptr; 41 | } 42 | 43 | unsigned char* NativePowerShell_DefaultAlloc(unsigned long long size){ 44 | return MallocHelper(size); 45 | } 46 | void NativePowerShell_DefaultFree(void* address){ 47 | return free(address); 48 | } 49 | 50 | void NativePowerShell_InitLibrary(NativePowerShell_AllocPointer allocPtr, NativePowerShell_FreePointer freePtr) { 51 | NativePowerShell_AllocPointerPtr = allocPtr; 52 | NativePowerShell_FreePointerPtr = freePtr; 53 | } 54 | 55 | 56 | long NativePowerShell_AddCommandSpecifyScope(NativePowerShell_PowerShellHandle handle, NativePowerShell_StringPtr command,char useLocalScope) 57 | { 58 | auto managedCommand = msclr::interop::marshal_as(command); 59 | auto powershell = HandleTable::GetPowershell(handle)->powershell; 60 | powershell->AddCommand(managedCommand, useLocalScope !=0); 61 | return 0; 62 | } 63 | long NativePowerShell_AddCommand(NativePowerShell_PowerShellHandle handle, NativePowerShell_StringPtr command) 64 | { 65 | return NativePowerShell_AddCommandSpecifyScope(handle, command, char(1)); 66 | } 67 | long NativePowerShell_AddArgument(NativePowerShell_PowerShellHandle handle, NativePowerShell_StringPtr argument) 68 | { 69 | auto managedArgument = msclr::interop::marshal_as(argument); 70 | auto powershell = HandleTable::GetPowershell(handle)->powershell; 71 | powershell->AddArgument(managedArgument); 72 | return 0; 73 | } 74 | 75 | long NativePowerShell_AddParameterString(NativePowerShell_PowerShellHandle handle, NativePowerShell_StringPtr name, NativePowerShell_StringPtr value) { 76 | 77 | auto paramName = msclr::interop::marshal_as(name); 78 | auto paramValue = msclr::interop::marshal_as(value); 79 | auto powershell = HandleTable::GetPowershell(handle)->powershell; 80 | powershell->AddParameter(paramName, paramValue); 81 | return 0; 82 | } 83 | 84 | 85 | long NativePowerShell_AddParameterObject(NativePowerShell_PowerShellHandle handle, NativePowerShell_StringPtr name, NativePowerShell_PowerShellObject object) { 86 | 87 | auto paramName = msclr::interop::marshal_as(name); 88 | auto powershell = HandleTable::GetPowershell(handle)->powershell; 89 | if (object == EmptyNativePowerShell_PowerShellObjectHandle) { 90 | powershell->AddParameter(paramName); 91 | } 92 | else { 93 | PSObject^ psObject = HandleTable::GetPSObject(object); 94 | if (psObject == nullptr) 95 | { 96 | powershell->AddParameter(paramName, nullptr); 97 | } 98 | else { 99 | powershell->AddParameter(paramName, psObject->BaseObject); 100 | } 101 | } 102 | return 0; 103 | } 104 | 105 | long NativePowerShell_AddPSObjectArgument(NativePowerShell_PowerShellHandle handle, NativePowerShell_PowerShellObject object) 106 | { 107 | PSObject^ psObject = HandleTable::GetPSObject(object); 108 | auto powershell = HandleTable::GetPowershell(handle)->powershell; 109 | if (psObject == nullptr) 110 | { 111 | powershell->AddArgument(psObject); 112 | } 113 | else { 114 | powershell->AddArgument(psObject->BaseObject); 115 | } 116 | return 0; 117 | } 118 | long NativePowerShell_AddPSObjectArguments(NativePowerShell_PowerShellHandle handle, NativePowerShell_PowerShellObject* objects, unsigned int count) 119 | { 120 | for (unsigned int i = 0; i < count; ++i) { 121 | NativePowerShell_AddPSObjectArgument(handle, objects[i]); 122 | } 123 | return 0; 124 | } 125 | 126 | long NativePowerShell_AddScript(NativePowerShell_PowerShellHandle handle, NativePowerShell_StringPtr path) { 127 | return NativePowerShell_AddScriptSpecifyScope(handle, path, char(1)); 128 | } 129 | long NativePowerShell_AddScriptSpecifyScope(NativePowerShell_PowerShellHandle handle, NativePowerShell_StringPtr path, char useLocalScope) 130 | { 131 | auto managedPath = msclr::interop::marshal_as(path); 132 | auto powershellHolder = HandleTable::GetPowershell(handle); 133 | auto powershell = powershellHolder->powershell; 134 | powershell->AddScript(managedPath, useLocalScope!=0); 135 | return 0; 136 | } 137 | NativePowerShell_PowerShellObject NativePowerShell_InvokeCommand(NativePowerShell_PowerShellHandle handle, NativePowerShell_PowerShellObject **objects, unsigned int * objectCount ) 138 | { 139 | auto powershellHolder = HandleTable::GetPowershell(handle); 140 | auto powershell = powershellHolder->powershell; 141 | auto runspaceHolder = powershellHolder->runspace; 142 | auto Logger = powershellHolder->runspace->logger; 143 | *objectCount = 0; 144 | *objects = nullptr; 145 | System::Collections::ObjectModel::Collection^ results; 146 | try { 147 | results = powershell->Invoke(); 148 | } 149 | //catch (System::Management::Automation::RuntimeException^ exception) { 150 | // Logger->LogLineError("Caught Exception of type " + exception->GetType()->ToString()); 151 | // Logger->LogLineError(exception->ToString()); 152 | // if (exception->ErrorRecord) { 153 | // Logger->LogLineError("Powershell stack trace"); 154 | // Logger->LogLineError(exception->ErrorRecord->ScriptStackTrace); 155 | // } 156 | //} 157 | catch (System::Object^ exception) { 158 | return HandleTable::InsertPSObject(gcnew PSObject(exception)); 159 | } 160 | *objectCount = (unsigned int)results->Count; 161 | *objects = (NativePowerShell_PowerShellObject*) NativePowerShell_AllocPointerPtr(results->Count * sizeof(NativePowerShell_PowerShellObject)); 162 | long long i = 0; 163 | for each (auto object in results) { 164 | (*objects)[i] = HandleTable::InsertPSObject(object); 165 | ++i; 166 | } 167 | // TODO figure out why powershell errors are not displayed in host 168 | auto errors = powershell->Streams->Error; 169 | if (!(errors == nullptr )&& errors->Count > 0) 170 | { 171 | for each(auto err in errors) 172 | { 173 | Logger->LogLineError(err->ToString()); 174 | } 175 | } 176 | return EmptyNativePowerShell_PowerShellObjectHandle; 177 | } 178 | 179 | void NativePowerShell_ClosePowerShellObject(NativePowerShell_PowerShellObject psobject) { 180 | if (psobject != EmptyNativePowerShell_PowerShellObjectHandle) { 181 | HandleTable::RemovePSObject(psobject); 182 | } 183 | } 184 | 185 | NativePowerShell_PowerShellHandle NativePowerShell_CreatePowerShell(NativePowerShell_RunspaceHandle handle) 186 | { 187 | auto runspaceHolder = HandleTable::GetRunspace(handle); 188 | auto powershell = PowerShell::Create(); 189 | auto powershellHolder = gcnew PowerShellHolder(runspaceHolder, powershell); 190 | powershell->Runspace = runspaceHolder->runspace; 191 | return HandleTable::InsertPowershell(powershellHolder); 192 | } 193 | NativePowerShell_PowerShellHandle NativePowerShell_CreatePowerShellNested(NativePowerShell_PowerShellHandle handle) 194 | { 195 | auto parentPowershell = HandleTable::GetPowershell(handle); 196 | auto powershell = parentPowershell->powershell->CreateNestedPowerShell(); 197 | auto powershellHolder = gcnew PowerShellHolder(parentPowershell->runspace, powershell); 198 | powershell->Runspace = parentPowershell->runspace->runspace; 199 | return HandleTable::InsertPowershell(powershellHolder); 200 | } 201 | 202 | 203 | 204 | void NativePowerShell_DeletePowershell(NativePowerShell_PowerShellHandle handle) 205 | { 206 | MakeUsing(HandleTable::RemovePowershell(handle))->powershell->Stop(); 207 | } 208 | 209 | 210 | /// 211 | /// Class that implements the Send-HostCommand. 212 | /// 213 | [Cmdlet(VerbsCommunications::Send, "HostCommand")] 214 | public ref class SendHostCommand : Cmdlet 215 | { 216 | public: 217 | [Parameter(Position = 0, Mandatory = true)] 218 | property System::String ^ message; 219 | 220 | [Parameter( Mandatory = false, ValueFromPipeline=true)] 221 | property array^ input; 222 | private: 223 | List^ inputObjects = gcnew List(); 224 | protected: 225 | void ProcessRecord()override 226 | { 227 | Cmdlet::ProcessRecord(); 228 | //Console::WriteLine("In Process, count: {0}, firstValue {1}, first type {2}", input->Length, input[0]->ToString(), input[0]->GetType()); 229 | 230 | //if $null is passed, then input is null. We will assume it is always 1 object(I think it is) 231 | if (input == nullptr) { 232 | inputObjects->Add(MakePSObject(nullptr)); 233 | //Console::WriteLine("got $null for input"); 234 | } 235 | else { 236 | //Console::WriteLine("got non null for input"); 237 | for each (auto obj in input) { 238 | inputObjects->Add(MakePSObject(obj)); 239 | } 240 | } 241 | } 242 | 243 | PSObject^ MakePSObject(System::Object^ obj) { 244 | if (obj == nullptr) { 245 | //return gcnew PSObject(nullptr); 246 | return nullptr; 247 | } 248 | if (obj->GetType() == PSObject::typeid) { 249 | return safe_cast(obj); 250 | } 251 | return gcnew PSObject(obj); 252 | } 253 | 254 | void EndProcessing()override 255 | { 256 | Cmdlet::EndProcessing(); 257 | 258 | MyHost^ host = safe_cast(this->CommandRuntime->Host->PrivateData->BaseObject); 259 | auto sendJsonCommand = host->runspace->sendJsonCommand; 260 | if (sendJsonCommand != nullptr) 261 | { 262 | // TODO: figure out how to differentiate between below 263 | // 264 | // by default if no input specified we get 1 nullptr object input 265 | // to clean this up, we delete all single list that is nullptr 266 | // 267 | // one problem with this approach is we cannot differentiate between 268 | // `$null | send-hostcommand -message "foo"` 269 | // `send-hostcommand -message "foo" 270 | if (inputObjects->Count == 1 && inputObjects[0] == nullptr) { 271 | inputObjects = gcnew List(); 272 | } 273 | 274 | std::vector inputObjectsC; 275 | inputObjectsC.reserve(inputObjects->Count); 276 | for each (auto obj in inputObjects) 277 | { 278 | inputObjectsC.push_back(HandleTable::InsertPSObject(obj)); 279 | } 280 | 281 | std::wstring commandInput = msclr::interop::marshal_as(message); 282 | 283 | NativePowerShell_JsonReturnValues returnValues; 284 | returnValues.count = 0; 285 | returnValues.objects = nullptr; 286 | sendJsonCommand(host->runspace->context, commandInput.c_str(),inputObjectsC.data(), inputObjectsC.size(), &returnValues); 287 | auto freeObjectList = MakeAutoDllFree(returnValues.objects); 288 | for (unsigned long i = 0; i < returnValues.count; ++i) { 289 | auto& object = returnValues.objects[i]; 290 | switch (object.type) { 291 | case NativePowerShell_PowerShellObjectTypeString: 292 | { 293 | auto outputManaged = msclr::interop::marshal_as(object.instance.string); 294 | if (object.releaseObject != char(0)) 295 | { 296 | auto freeString = MakeAutoDllFree(object.instance.string); 297 | } 298 | WriteObject(outputManaged, false); 299 | break; 300 | } 301 | case NativePowerShell_PowerShellObjectHandle: 302 | { 303 | auto psObject = HandleTable::GetPSObject(object.instance.psObject); 304 | if (psObject != nullptr && psObject->BaseObject != nullptr) 305 | { 306 | WriteObject(psObject, false); 307 | } 308 | else { 309 | WriteObject(nullptr, false); 310 | } 311 | if (object.releaseObject != char(0)) 312 | { 313 | HandleTable::RemovePSObject(object.instance.psObject); 314 | } 315 | break; 316 | } 317 | default: 318 | throw "should not hit default case"; 319 | 320 | } 321 | } 322 | for (auto& obj : inputObjectsC) { 323 | HandleTable::RemovePSObject(obj); 324 | } 325 | } 326 | } 327 | 328 | }; 329 | 330 | 331 | 332 | 333 | 334 | void SetISSEV( 335 | System::Management::Automation::Runspaces::InitialSessionStateEntryCollection^ entries, 336 | System::String^ name, 337 | System::Object^ value) 338 | { 339 | int foundIndex = 0; 340 | for each(auto entry in entries) 341 | { 342 | if (entry->Name->Equals(name, StringComparison::OrdinalIgnoreCase)) 343 | { 344 | entries->RemoveItem( foundIndex); 345 | entries->Add(gcnew SessionStateVariableEntry(entry->Name, value, entry->Description)); 346 | return; 347 | } 348 | 349 | foundIndex++; 350 | } 351 | 352 | throw gcnew System::IndexOutOfRangeException; 353 | } 354 | 355 | WSManConnectionInfo^ MakeConnectionInfo(const wchar_t* computerName, const wchar_t* username, const wchar_t* password) { 356 | 357 | auto managedComputerName = msclr::interop::marshal_as(computerName); 358 | auto uri = gcnew Uri("http://" + managedComputerName + ":5985/wsman"); 359 | //auto uri = gcnew Uri("https://" + managedComputerName + ":5986/wsman"); 360 | //auto uri = gcnew Uri("https://" + managedComputerName + "/OcsPowershell"); 361 | WSManConnectionInfo^ connection; 362 | 363 | /*if (username != nullptr) { 364 | auto managedUsername = msclr::interop::marshal_as(username); 365 | auto managedPassword = msclr::interop::marshal_as(password); 366 | 367 | SecureString^ securePass = gcnew SecureString(); 368 | for each (auto x in managedPassword) 369 | { 370 | securePass->AppendChar(x); 371 | } 372 | PSCredential^ managedCredentials = gcnew PSCredential(managedUsername, securePass); 373 | 374 | connection = gcnew WSManConnectionInfo(uri, "http://schemas.microsoft.com/powershell/Microsoft.PowerShell", managedCredentials); 375 | } 376 | else { 377 | connection = gcnew WSManConnectionInfo(uri); 378 | }*/ 379 | //connection->ComputerName = managedComputerName; 380 | 381 | if (username != nullptr) { 382 | auto managedUsername = msclr::interop::marshal_as(username); 383 | auto managedPassword = msclr::interop::marshal_as(password); 384 | 385 | SecureString^ securePass = gcnew SecureString(); 386 | for each(auto x in managedPassword) 387 | { 388 | securePass->AppendChar(x); 389 | } 390 | PSCredential^ managedCredentials = gcnew PSCredential(managedUsername, securePass); 391 | 392 | connection = gcnew WSManConnectionInfo( 393 | false, 394 | managedComputerName, 395 | 5985, 396 | msclr::interop::marshal_as(L"wsman"), 397 | msclr::interop::marshal_as(L"http://schemas.microsoft.com/powershell/Microsoft.PowerShell"), 398 | managedCredentials); 399 | } 400 | else { 401 | connection = gcnew WSManConnectionInfo(uri); 402 | } 403 | //connection->AuthenticationMechanism = AuthenticationMechanism::Negotiate; 404 | 405 | // Set the OperationTimeout property and OpenTimeout properties. 406 | // The OperationTimeout property is used to tell Windows PowerShell 407 | // how long to wait (in milliseconds) before timing out for an 408 | // operation. The OpenTimeout property is used to tell Windows 409 | // PowerShell how long to wait (in milliseconds) before timing out 410 | // while establishing a remote connection. 411 | connection->OperationTimeout = 4 * 60 * 1000; // 4 minutes. 412 | connection->OpenTimeout = 1 * 60 * 1000; // 1 minute. 413 | 414 | connection->SkipCACheck = true; 415 | connection->SkipCNCheck = true; 416 | connection->SkipRevocationCheck = true; 417 | 418 | return connection; 419 | 420 | } 421 | 422 | NativePowerShell_RunspaceHandle CreateRunspaceInternal(void* context, NativePowerShell_ReceiveJsonCommand receiveJsonCommand, PNativePowerShell_LogString_Holder BaseLogString, const wchar_t* computerName, const wchar_t* username, const wchar_t* password) 423 | { 424 | auto iss = InitialSessionState::CreateDefault(); 425 | // Add the get-proc cmdlet to the InitialSessionState object. 426 | auto ssce = gcnew SessionStateCmdletEntry("Send-HostCommand", SendHostCommand::typeid, nullptr); 427 | iss->Commands->Add(ssce); 428 | 429 | // ensure logging enabled 430 | SetISSEV(iss->Variables, "ErrorActionPreference", System::Management::Automation::ActionPreference::Continue); 431 | SetISSEV(iss->Variables, "DebugPreference", System::Management::Automation::ActionPreference::Continue); 432 | SetISSEV(iss->Variables, "WarningPreference", System::Management::Automation::ActionPreference::Continue); 433 | SetISSEV(iss->Variables, "VerbosePreference", System::Management::Automation::ActionPreference::Continue); 434 | SetISSEV(iss->Variables, "InformationPreference", System::Management::Automation::ActionPreference::Continue); 435 | 436 | 437 | auto logger = gcnew Logger(context, BaseLogString); 438 | auto holder = gcnew RunspaceHolder(context, receiveJsonCommand, logger); 439 | auto host = gcnew MyHost(holder); 440 | Runspace^ runspace; 441 | if (computerName == nullptr) { 442 | runspace = RunspaceFactory::CreateRunspace(host, iss); 443 | } 444 | else { 445 | auto connectionInfo = MakeConnectionInfo(computerName, username, password); 446 | 447 | runspace = RunspaceFactory::CreateRunspace(connectionInfo, host, gcnew TypeTable(gcnew array< String^ >(0))); 448 | //runspace = RunspaceFactory::CreateRunspace(connectionInfo); 449 | } 450 | holder->host = host; 451 | holder->runspace = runspace; 452 | runspace->Open(); 453 | return HandleTable::InsertRunspace(holder); 454 | } 455 | 456 | 457 | NativePowerShell_RunspaceHandle NativePowerShell_CreateRunspace(void * context, NativePowerShell_ReceiveJsonCommand receiveJsonCommand, PNativePowerShell_LogString_Holder BaseLogString) 458 | { 459 | return CreateRunspaceInternal(context, receiveJsonCommand, BaseLogString, nullptr, nullptr, nullptr); 460 | } 461 | 462 | NativePowerShell_RunspaceHandle NativePowerShell_CreateRemoteRunspace(void* context, PNativePowerShell_LogString_Holder BaseLogString, const wchar_t* computerName, const wchar_t* username, const wchar_t* password) 463 | { 464 | return CreateRunspaceInternal(context, nullptr, BaseLogString, computerName, username, password); 465 | } 466 | 467 | 468 | void NativePowerShell_DeleteRunspace(NativePowerShell_RunspaceHandle handle) 469 | { 470 | MakeUsing(HandleTable::RemoveRunspace(handle))->runspace->Close(); 471 | } 472 | 473 | LPCWSTR MakeHostString(System::String^ str) { 474 | std::wstring cppStr = msclr::interop::marshal_as(str); 475 | LPWSTR cStr = (LPWSTR)NativePowerShell_AllocPointerPtr((cppStr.length() + 1) * 2); 476 | std::copy(cppStr.c_str(), cppStr.c_str() + (cppStr.length() + 1),cStr); 477 | return cStr; 478 | } 479 | 480 | NativePowerShell_StringPtr NativePowerShell_GetPSObjectType(NativePowerShell_PowerShellObject handle) { 481 | auto psObject = HandleTable::GetPSObject(handle); 482 | return MakeHostString(psObject->BaseObject->GetType()->ToString()); 483 | } 484 | NativePowerShell_StringPtr NativePowerShell_GetPSObjectToString(NativePowerShell_PowerShellObject handle) { 485 | auto psObject = HandleTable::GetPSObject(handle); 486 | return MakeHostString(psObject->BaseObject->ToString()); 487 | } 488 | char NativePowerShell_IsPSObjectNullptr(NativePowerShell_PowerShellObject handle) { 489 | if (handle == EmptyNativePowerShell_PowerShellObjectHandle) { 490 | return char(1); 491 | } 492 | PSObject^ psObject = HandleTable::GetPSObject(handle); 493 | if (psObject == nullptr) { 494 | return char(1); 495 | } 496 | if (psObject->BaseObject == nullptr) { 497 | return char(1); 498 | } 499 | return char(0); 500 | } 501 | 502 | NativePowerShell_PowerShellObject NativePowerShell_AddPSObjectHandle(NativePowerShell_PowerShellObject handle) { 503 | return HandleTable::InsertPSObject(HandleTable::GetPSObject(handle)); 504 | } 505 | 506 | -------------------------------------------------------------------------------- /host.def: -------------------------------------------------------------------------------- 1 | EXPORTS 2 | NativePowerShell_CreateRunspace 3 | NativePowerShell_DeleteRunspace 4 | NativePowerShell_CreatePowerShell 5 | NativePowerShell_CreatePowerShellNested 6 | NativePowerShell_DeletePowershell 7 | NativePowerShell_AddCommand 8 | NativePowerShell_AddParameterString 9 | NativePowerShell_AddArgument 10 | NativePowerShell_InvokeCommand 11 | NativePowerShell_AddScript 12 | NativePowerShell_InitLibrary 13 | NativePowerShell_AddScriptSpecifyScope 14 | NativePowerShell_AddCommandSpecifyScope 15 | NativePowerShell_ClosePowerShellObject 16 | NativePowerShell_AddPSObjectArgument 17 | NativePowerShell_AddPSObjectArguments 18 | NativePowerShell_AddParameterObject 19 | 20 | 21 | NativePowerShell_GetPSObjectType 22 | NativePowerShell_GetPSObjectToString 23 | NativePowerShell_IsPSObjectNullptr 24 | NativePowerShell_AddPSObjectHandle 25 | NativePowerShell_CreateRemoteRunspace 26 | NativePowerShell_DefaultAlloc 27 | NativePowerShell_DefaultFree 28 | 29 | -------------------------------------------------------------------------------- /host.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | /* 4 | 5 | This header file for native-powershell (psh_host.dll) 6 | 7 | */ 8 | 9 | 10 | #ifdef __cplusplus 11 | extern "C" { 12 | #endif 13 | 14 | #define NativePowerShell_InvalidPointer (void *)0; 15 | typedef void (*NativePowerShell_FreePointer)(void*); 16 | typedef unsigned char* (*NativePowerShell_AllocPointer)(unsigned long long size); 17 | 18 | typedef void (*NativePowerShell_LogString)(void * context, const wchar_t* messages); 19 | 20 | // If Log is Null none of the logging works 21 | typedef struct NativePowerShell_LogString_Holder_ { 22 | NativePowerShell_LogString Log; 23 | NativePowerShell_LogString LogError; 24 | NativePowerShell_LogString LogWarning; 25 | NativePowerShell_LogString LogInformation; 26 | NativePowerShell_LogString LogVerbose; 27 | NativePowerShell_LogString LogDebug; 28 | NativePowerShell_LogString LogLine; 29 | NativePowerShell_LogString LogErrorLine; 30 | NativePowerShell_LogString LogWarningLine; 31 | NativePowerShell_LogString LogInformationLine; 32 | NativePowerShell_LogString LogVerboseLine; 33 | NativePowerShell_LogString LogDebugLine; 34 | }NativePowerShell_LogString_Holder, * PNativePowerShell_LogString_Holder; 35 | 36 | void NativePowerShell_InitLibrary( NativePowerShell_AllocPointer, NativePowerShell_FreePointer); 37 | 38 | unsigned char* NativePowerShell_DefaultAlloc(unsigned long long size); 39 | void NativePowerShell_DefaultFree(void* address); 40 | 41 | //typedef struct NativePowerShell_RunspaceHandle_ {} *NativePowerShell_RunspaceHandle; 42 | // typedef struct NativePowerShell_PowerShellHandle_ {} *NativePowerShell_PowerShellHandle; 43 | // typedef struct NativePowerShell_PowerShellObject_ {} *NativePowerShell_PowerShellObject; 44 | #define NativePowerShell_InvalidHandleValue 0 45 | typedef unsigned long long NativePowerShell_RunspaceHandle; 46 | typedef unsigned long long NativePowerShell_PowerShellHandle; 47 | typedef unsigned long long NativePowerShell_PowerShellObject; 48 | //typedef NativePowerShell_RunspaceHandle_d * NativePowerShell_RunspaceHandle; 49 | 50 | typedef const wchar_t* NativePowerShell_StringPtr; 51 | 52 | 53 | 54 | typedef enum NativePowerShell_PowerShellObjectType_ { NativePowerShell_PowerShellObjectTypeString, NativePowerShell_PowerShellObjectHandle }NativePowerShell_PowerShellObjectType; 55 | 56 | typedef struct NativePowerShell_GenericPowerShellObject_ { 57 | NativePowerShell_PowerShellObjectType type; 58 | union NativePowerShell_PowerShellObjectInstance { 59 | NativePowerShell_StringPtr string; 60 | NativePowerShell_PowerShellObject psObject; 61 | // continue for other ones such as UInt64... 62 | } instance; 63 | char releaseObject; // if true reciever of this object will release the instance 64 | }NativePowerShell_GenericPowerShellObject, * PNativePowerShell_GenericPowerShellObject; 65 | 66 | 67 | typedef struct NativePowerShell_JsonReturnValues_ { 68 | NativePowerShell_GenericPowerShellObject* objects; 69 | unsigned long count; 70 | }NativePowerShell_JsonReturnValues,*NativePowerShell_PJsonReturnValues; 71 | typedef void (*NativePowerShell_ReceiveJsonCommand)(void* context, const wchar_t* command, NativePowerShell_PowerShellObject * inputs, unsigned long long inputCount, NativePowerShell_JsonReturnValues* returnValues); 72 | 73 | NativePowerShell_PowerShellHandle NativePowerShell_CreatePowerShell(NativePowerShell_RunspaceHandle handle); 74 | NativePowerShell_PowerShellHandle NativePowerShell_CreatePowerShellNested(NativePowerShell_PowerShellHandle handle); 75 | 76 | 77 | void NativePowerShell_DeletePowershell(NativePowerShell_PowerShellHandle handle); 78 | 79 | 80 | NativePowerShell_RunspaceHandle NativePowerShell_CreateRunspace(void* context, NativePowerShell_ReceiveJsonCommand, PNativePowerShell_LogString_Holder); 81 | NativePowerShell_RunspaceHandle NativePowerShell_CreateRemoteRunspace(void* context, PNativePowerShell_LogString_Holder, const wchar_t* computerName, const wchar_t* username, const wchar_t * password); 82 | 83 | void NativePowerShell_DeleteRunspace(NativePowerShell_RunspaceHandle handle); 84 | 85 | 86 | long NativePowerShell_AddCommand(NativePowerShell_PowerShellHandle handle, NativePowerShell_StringPtr command); 87 | long NativePowerShell_AddCommandSpecifyScope(NativePowerShell_PowerShellHandle handle, NativePowerShell_StringPtr command, char useLocalScope); 88 | long NativePowerShell_AddParameterString(NativePowerShell_PowerShellHandle handle, NativePowerShell_StringPtr name, NativePowerShell_StringPtr value); 89 | long NativePowerShell_AddParameterObject(NativePowerShell_PowerShellHandle handle, NativePowerShell_StringPtr name, NativePowerShell_PowerShellObject object); 90 | long NativePowerShell_AddArgument(NativePowerShell_PowerShellHandle handle, NativePowerShell_StringPtr argument); 91 | long NativePowerShell_AddPSObjectArgument(NativePowerShell_PowerShellHandle handle, NativePowerShell_PowerShellObject object); 92 | long NativePowerShell_AddPSObjectArguments(NativePowerShell_PowerShellHandle handle, NativePowerShell_PowerShellObject* objects, unsigned int count); 93 | 94 | // caller is responsible for calling ClosePowerShellObject on all returned objects, as well as 95 | // calling the appropriate free routine on objects assuming it is not nullptr 96 | NativePowerShell_PowerShellObject NativePowerShell_InvokeCommand(NativePowerShell_PowerShellHandle handle, NativePowerShell_PowerShellObject** objects, unsigned int* objectCount); 97 | long NativePowerShell_AddScript(NativePowerShell_PowerShellHandle handle, NativePowerShell_StringPtr path); 98 | long NativePowerShell_AddScriptSpecifyScope(NativePowerShell_PowerShellHandle handle, NativePowerShell_StringPtr path, char useLocalScope); 99 | void NativePowerShell_ClosePowerShellObject(NativePowerShell_PowerShellObject psobject); 100 | 101 | NativePowerShell_StringPtr NativePowerShell_GetPSObjectType(NativePowerShell_PowerShellObject handle); 102 | NativePowerShell_StringPtr NativePowerShell_GetPSObjectToString(NativePowerShell_PowerShellObject handle); 103 | char NativePowerShell_IsPSObjectNullptr(NativePowerShell_PowerShellObject handle); 104 | NativePowerShell_PowerShellObject NativePowerShell_AddPSObjectHandle(NativePowerShell_PowerShellObject handle); 105 | 106 | #ifdef __cplusplus 107 | } 108 | #endif -------------------------------------------------------------------------------- /host_internal.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | ref class RunspaceHolder; 4 | ref class MyHost; -------------------------------------------------------------------------------- /logger.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include "host.h" 4 | #include "logger.hpp" 5 | 6 | 7 | void Logger::LogWrapperAddNewLine(NativePowerShell_LogString writeLine, Logger::WriteLoggerDelegate^ write, const std::wstring& log) { 8 | if (writeLine == nullptr) { 9 | std::wstring message = log + L'\n'; 10 | write(message); 11 | } 12 | else { 13 | writeLine(context, log.c_str()); 14 | } 15 | } 16 | 17 | void Logger::LogWrapper(NativePowerShell_LogString func, LPCWSTR prepend, const std::wstring& log) { 18 | if (func == nullptr) { 19 | std::wstring message; 20 | if (prepend != nullptr) 21 | message = prepend; 22 | message = message + log; 23 | if (BaseLogString != nullptr) { 24 | BaseLogString(context, message.c_str()); 25 | } 26 | } 27 | else { 28 | func(context, log.c_str()); 29 | } 30 | } 31 | 32 | void Logger::LogWarning(const std::wstring & log) { 33 | LogWrapper(LogWarningPtr, L"Warning: ", log); 34 | } 35 | void Logger::LogInformation(const std::wstring & log) { 36 | LogWrapper(LogInformationPtr, L"Information: ", log); 37 | } 38 | void Logger::LogVerbose(const std::wstring & log) { 39 | LogWrapper(LogVerbosePtr, L"Verbose: ", log); 40 | } 41 | void Logger::LogDebug(const std::wstring & log) { 42 | LogWrapper(LogDebugPtr, L"Debug: ", log); 43 | } 44 | void Logger::LogError(const std::wstring & log) { 45 | LogWrapper(LogErrorPtr, L"Error: ", log); 46 | } 47 | 48 | void Logger::Log(const std::wstring & log) { 49 | if (BaseLogString != nullptr) { 50 | BaseLogString(context, log.c_str()); 51 | } 52 | } 53 | 54 | 55 | 56 | void Logger::LogLineWarning(const std::wstring& log) { 57 | LogWrapperAddNewLine(LogWarningLinePtr, LogWarningDelegate, log); 58 | } 59 | void Logger::LogLineInformation(const std::wstring& log) { 60 | LogWrapperAddNewLine(LogInformationLinePtr, LogInformationDelegate, log); 61 | } 62 | void Logger::LogLineVerbose(const std::wstring& log) { 63 | LogWrapperAddNewLine(LogVerboseLinePtr, LogVerboseDelegate, log); 64 | } 65 | void Logger::LogLineDebug(const std::wstring& log) { 66 | LogWrapperAddNewLine(LogDebugLinePtr, LogDebugDelegate, log); 67 | } 68 | void Logger::LogLineError(const std::wstring& log) { 69 | LogWrapperAddNewLine(LogErrorLinePtr, LogErrorDelegate, log); 70 | } 71 | void Logger::LogLine(const std::wstring& log) { 72 | LogWrapperAddNewLine(BaseLogLinePtr, LogDelegate, log); 73 | } 74 | 75 | 76 | 77 | 78 | void Logger::LogWarning(System::String ^ log) { LogWarning(msclr::interop::marshal_as(log)); } 79 | void Logger::LogInformation(System::String ^ log) { LogInformation(msclr::interop::marshal_as(log)); } 80 | void Logger::LogVerbose(System::String ^ log) { LogVerbose(msclr::interop::marshal_as(log)); } 81 | void Logger::LogDebug(System::String ^ log) { LogDebug(msclr::interop::marshal_as(log)); } 82 | void Logger::LogError(System::String ^ log) { LogError(msclr::interop::marshal_as(log)); } 83 | void Logger::Log(System::String ^ log) { Log(msclr::interop::marshal_as(log)); } 84 | 85 | void Logger::LogLineWarning(System::String ^ log) { LogLineWarning(msclr::interop::marshal_as(log)); } 86 | void Logger::LogLineInformation(System::String ^ log) { LogLineInformation(msclr::interop::marshal_as(log)); } 87 | void Logger::LogLineVerbose(System::String ^ log) { LogLineVerbose(msclr::interop::marshal_as(log)); } 88 | void Logger::LogLineDebug(System::String ^ log) { LogLineDebug(msclr::interop::marshal_as(log)); } 89 | void Logger::LogLineError(System::String ^ log) { LogLineError(msclr::interop::marshal_as(log)); } 90 | void Logger::LogLine(System::String ^ log) { LogLine(msclr::interop::marshal_as(log)); } -------------------------------------------------------------------------------- /logger.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #define WIN32_LEAN_AND_MEAN // Exclude rarely-used stuff from Windows headers 3 | // Windows Header Files 4 | #include 5 | #include "host.h" 6 | #include 7 | ref class Logger { 8 | public: 9 | Logger(void * contextParam, PNativePowerShell_LogString_Holder WriteFunc) : context(contextParam) { 10 | if (WriteFunc != nullptr) { 11 | BaseLogString = WriteFunc->Log; 12 | LogWarningPtr = WriteFunc->LogWarning; 13 | LogInformationPtr = WriteFunc->LogInformation; 14 | LogVerbosePtr = WriteFunc->LogVerbose; 15 | LogDebugPtr = WriteFunc->LogDebug; 16 | LogErrorPtr = WriteFunc->LogError; 17 | BaseLogLinePtr = WriteFunc->LogLine; 18 | LogWarningLinePtr = WriteFunc->LogWarningLine; 19 | LogInformationLinePtr = WriteFunc->LogInformationLine; 20 | LogVerboseLinePtr = WriteFunc->LogVerboseLine; 21 | LogDebugLinePtr = WriteFunc->LogDebugLine; 22 | LogErrorLinePtr = WriteFunc->LogErrorLine; 23 | } 24 | LogWarningDelegate = gcnew WriteLoggerDelegate(this, &Logger::LogWarning); 25 | LogInformationDelegate = gcnew WriteLoggerDelegate(this, &Logger::LogInformation); 26 | LogVerboseDelegate = gcnew WriteLoggerDelegate(this, &Logger::LogVerbose); 27 | LogDebugDelegate = gcnew WriteLoggerDelegate(this, &Logger::LogDebug); 28 | LogErrorDelegate = gcnew WriteLoggerDelegate(this, &Logger::LogError); 29 | LogDelegate = gcnew WriteLoggerDelegate(this, &Logger::Log); 30 | }; 31 | NativePowerShell_LogString BaseLogString = nullptr; 32 | NativePowerShell_LogString LogWarningPtr = nullptr; 33 | NativePowerShell_LogString LogInformationPtr = nullptr; 34 | NativePowerShell_LogString LogVerbosePtr = nullptr; 35 | NativePowerShell_LogString LogDebugPtr = nullptr; 36 | NativePowerShell_LogString LogErrorPtr = nullptr; 37 | 38 | NativePowerShell_LogString BaseLogLinePtr = nullptr; 39 | NativePowerShell_LogString LogWarningLinePtr = nullptr; 40 | NativePowerShell_LogString LogInformationLinePtr = nullptr; 41 | NativePowerShell_LogString LogVerboseLinePtr = nullptr; 42 | NativePowerShell_LogString LogDebugLinePtr = nullptr; 43 | NativePowerShell_LogString LogErrorLinePtr = nullptr; 44 | 45 | void LogWarning(const std::wstring& log); 46 | void LogInformation(const std::wstring& log); 47 | void LogVerbose(const std::wstring& log); 48 | void LogDebug(const std::wstring& log); 49 | void LogError(const std::wstring& log); 50 | void Log(const std::wstring& log); 51 | 52 | void LogLineWarning(const std::wstring& log); 53 | void LogLineInformation(const std::wstring& log); 54 | void LogLineVerbose(const std::wstring& log); 55 | void LogLineDebug(const std::wstring& log); 56 | void LogLineError(const std::wstring& log); 57 | void LogLine(const std::wstring& log); 58 | 59 | 60 | void LogWarning(System::String^ log); 61 | void LogInformation(System::String^ log); 62 | void LogVerbose(System::String^ log); 63 | void LogDebug(System::String^ log); 64 | void LogError(System::String^ log); 65 | void Log(System::String^ log); 66 | 67 | void LogLineWarning(System::String^ log); 68 | void LogLineInformation(System::String^ log); 69 | void LogLineVerbose(System::String^ log); 70 | void LogLineDebug(System::String^ log); 71 | void LogLineError(System::String^ log); 72 | void LogLine(System::String^ log); 73 | private: 74 | void* context; 75 | delegate void WriteLoggerDelegate(const std::wstring&); 76 | 77 | WriteLoggerDelegate^ LogWarningDelegate; 78 | WriteLoggerDelegate^ LogInformationDelegate; 79 | WriteLoggerDelegate^ LogVerboseDelegate; 80 | WriteLoggerDelegate^ LogDebugDelegate; 81 | WriteLoggerDelegate^ LogErrorDelegate; 82 | WriteLoggerDelegate^ LogDelegate; 83 | 84 | void LogWrapperAddNewLine(NativePowerShell_LogString writeLine, WriteLoggerDelegate^ write, const std::wstring& log); 85 | //__inline void LogWrapperAddNewLine(NativePowerShell_LogString writeLine, void (Logger::* write)(const std::wstring&), const std::wstring& log) { 86 | // if (writeLine == nullptr) { 87 | // std::wstring message = log + L'\n'; 88 | // (this->*write)(message); 89 | // } 90 | // else { 91 | // writeLine(log.c_str()); 92 | // } 93 | //} 94 | 95 | void LogWrapper(NativePowerShell_LogString func, LPCWSTR prepend, const std::wstring& log); 96 | /*__inline void LogWrapper(NativePowerShell_LogString func, LPCWSTR prepend, const std::wstring& log) { 97 | if (func == nullptr) { 98 | std::wstring message; 99 | if (prepend != nullptr) 100 | message = prepend; 101 | message = message + log; 102 | BaseLogString(message.c_str()); 103 | } 104 | else { 105 | func(log.c_str()); 106 | } 107 | }*/ 108 | 109 | }; -------------------------------------------------------------------------------- /my_host.cpp: -------------------------------------------------------------------------------- 1 | // https://docs.microsoft.com/en-us/powershell/developer/hosting/host02-sample 2 | 3 | 4 | using namespace System; 5 | using namespace System::Collections::Generic; 6 | using namespace System::Globalization; 7 | using namespace System::Management::Automation; 8 | using namespace System::Management::Automation::Host; 9 | #include "my_host.hpp" 10 | #include "host_internal.hpp" 11 | 12 | 13 | inline void MyHostUserInterface::Write(System::String^ value) 14 | { 15 | GetLogger()->Log(value); 16 | } 17 | 18 | /// 19 | /// Writes characters to the output display of the host and specifies the 20 | /// foreground and background colors of the characters. This implementation 21 | /// ignores the colors. 22 | /// 23 | /// The color of the characters. 24 | /// The background color to use. 25 | /// The characters to be written. 26 | 27 | inline void MyHostUserInterface::Write(ConsoleColor foregroundColor, ConsoleColor backgroundColor, System::String^ value) 28 | { 29 | // Colors are ignored. 30 | GetLogger()->Log(value); 31 | } 32 | 33 | /// 34 | /// Writes a debug message to the output display of the host. 35 | /// 36 | /// The debug message that is displayed. 37 | 38 | inline void MyHostUserInterface::WriteDebugLine(System::String^ message) 39 | { 40 | GetLogger()->LogLineDebug(message); 41 | } 42 | 43 | /// 44 | /// Writes an error message to the output display of the host. 45 | /// 46 | /// The error message that is displayed. 47 | 48 | inline void MyHostUserInterface::WriteErrorLine(System::String^ message) 49 | { 50 | GetLogger()->LogLineError(message); 51 | } 52 | 53 | /// 54 | /// Writes a newline character (carriage return) 55 | /// to the output display of the host. 56 | /// 57 | 58 | inline void MyHostUserInterface::WriteLine() 59 | { 60 | GetLogger()->LogLine(L""); 61 | } 62 | 63 | /// 64 | /// Writes a line of characters to the output display of the host 65 | /// and appends a newline character(carriage return). 66 | /// 67 | /// The line to be written. 68 | 69 | inline void MyHostUserInterface::WriteLine(System::String^ message) 70 | { 71 | GetLogger()->LogLine(message); 72 | } 73 | 74 | /// 75 | /// Writes a line of characters to the output display of the host 76 | /// with foreground and background colors and appends a newline (carriage return). 77 | /// 78 | /// The foreground color of the display. 79 | /// The background color of the display. 80 | /// The line to be written. 81 | 82 | inline void MyHostUserInterface::WriteLine(ConsoleColor foregroundColor, ConsoleColor backgroundColor, System::String^ message) 83 | { 84 | GetLogger()->LogLine(message); 85 | } 86 | 87 | /// 88 | /// Writes a progress report to the output display of the host. 89 | /// 90 | /// Unique identifier of the source of the record. 91 | /// A ProgressReport object. 92 | 93 | inline void MyHostUserInterface::WriteProgress(Int64, ProgressRecord^) 94 | { 95 | } 96 | 97 | /// 98 | /// Writes a verbose message to the output display of the host. 99 | /// 100 | /// The verbose message that is displayed. 101 | 102 | inline void MyHostUserInterface::WriteVerboseLine(System::String^ message) 103 | { 104 | GetLogger()->LogLineVerbose(message); 105 | } 106 | 107 | /// 108 | /// Writes a warning message to the output display of the host. 109 | /// 110 | /// The warning message that is displayed. 111 | 112 | inline void MyHostUserInterface::WriteWarningLine(System::String^ message) 113 | { 114 | GetLogger()->LogLineWarning(message); 115 | } 116 | 117 | inline System::String^ MyHostUserInterface::ReadLine() 118 | { 119 | throw gcnew System::NotImplementedException(); 120 | // TODO: insert return statement here 121 | } 122 | 123 | inline System::Security::SecureString^ MyHostUserInterface::ReadLineAsSecureString() 124 | { 125 | throw gcnew System::NotImplementedException(); 126 | // TODO: insert return statement here 127 | } 128 | 129 | inline System::Collections::Generic::Dictionary^ MyHostUserInterface::Prompt(System::String^ caption, System::String^ message, System::Collections::ObjectModel::Collection^ descriptions) 130 | { 131 | throw gcnew System::NotImplementedException(); 132 | // TODO: insert return statement here 133 | } 134 | 135 | inline System::Management::Automation::PSCredential^ MyHostUserInterface::PromptForCredential(System::String^ caption, System::String^ message, System::String^ userName, System::String^ targetName) 136 | { 137 | throw gcnew System::NotImplementedException(); 138 | // TODO: insert return statement here 139 | } 140 | 141 | inline System::Management::Automation::PSCredential^ MyHostUserInterface::PromptForCredential(System::String^ caption, System::String^ message, System::String^ userName, System::String^ targetName, System::Management::Automation::PSCredentialTypes allowedCredentialTypes, System::Management::Automation::PSCredentialUIOptions options) 142 | { 143 | throw gcnew System::NotImplementedException(); 144 | // TODO: insert return statement here 145 | } 146 | 147 | inline int MyHostUserInterface::PromptForChoice(System::String^ caption, System::String^ message, System::Collections::ObjectModel::Collection^ choices, int defaultChoice) 148 | { 149 | throw gcnew System::NotImplementedException(); 150 | } 151 | 152 | 153 | inline void MyHost::SetShouldExit(int exitCode) 154 | { 155 | System::Console::WriteLine(System::String::Format("ERROR app tried to exit with exit code {0}", exitCode)); 156 | throw gcnew System::NotImplementedException(); 157 | } 158 | 159 | inline void MyHost::EnterNestedPrompt() 160 | { 161 | throw gcnew System::NotImplementedException(); 162 | } 163 | 164 | inline void MyHost::ExitNestedPrompt() 165 | { 166 | throw gcnew System::NotImplementedException(); 167 | } 168 | 169 | inline void MyHost::NotifyBeginApplication() 170 | { 171 | throw gcnew System::NotImplementedException(); 172 | } 173 | 174 | inline void MyHost::NotifyEndApplication() 175 | { 176 | throw gcnew System::NotImplementedException(); 177 | } 178 | 179 | inline System::Management::Automation::Host::KeyInfo MyRawUserInterface::ReadKey(System::Management::Automation::Host::ReadKeyOptions options) 180 | { 181 | return System::Management::Automation::Host::KeyInfo(); 182 | } 183 | 184 | inline void MyRawUserInterface::FlushInputBuffer() 185 | { 186 | throw gcnew System::NotImplementedException(); 187 | } 188 | 189 | inline void MyRawUserInterface::SetBufferContents(System::Management::Automation::Host::Coordinates origin, array^ contents) 190 | { 191 | throw gcnew System::NotImplementedException(); 192 | } 193 | 194 | inline void MyRawUserInterface::SetBufferContents(System::Management::Automation::Host::Rectangle rectangle, System::Management::Automation::Host::BufferCell fill) 195 | { 196 | throw gcnew System::NotImplementedException(); 197 | } 198 | 199 | inline array^ MyRawUserInterface::GetBufferContents(System::Management::Automation::Host::Rectangle rectangle) 200 | { 201 | throw gcnew System::NotImplementedException(); 202 | // TODO: insert return statement here 203 | } 204 | 205 | inline void MyRawUserInterface::ScrollBufferContents(System::Management::Automation::Host::Rectangle source, System::Management::Automation::Host::Coordinates destination, System::Management::Automation::Host::Rectangle clip, System::Management::Automation::Host::BufferCell fill) 206 | { 207 | throw gcnew System::NotImplementedException(); 208 | } 209 | 210 | inline Logger^ MyRawUserInterface::GetLogger() { 211 | return runspace->logger; 212 | } 213 | inline Logger^ MyHostUserInterface::GetLogger() { 214 | return runspace->logger; 215 | } 216 | 217 | inline Logger^ MyHost::GetLogger() { 218 | return runspace->logger; 219 | } 220 | -------------------------------------------------------------------------------- /my_host.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "runspace.hpp" 3 | #include "logger.hpp" 4 | 5 | ref class MyRawUserInterface : PSHostRawUserInterface { 6 | // Inherited via PSHostRawUserInterface 7 | private: 8 | 9 | RunspaceHolder^ runspace; 10 | public: 11 | MyRawUserInterface(RunspaceHolder^ runspaceParam) : runspace(runspaceParam) {}; 12 | Logger^ GetLogger(); 13 | 14 | virtual property System::ConsoleColor BackgroundColor { 15 | System::ConsoleColor get() override { return System::ConsoleColor::Black; }; 16 | void set(System::ConsoleColor) override { }; 17 | } 18 | virtual property System::Management::Automation::Host::Size BufferSize { 19 | System::Management::Automation::Host::Size get() override { return System::Management::Automation::Host::Size(200,1024); }; 20 | void set(System::Management::Automation::Host::Size) override { throw gcnew System::NotImplementedException(); }; 21 | } 22 | virtual property System::Management::Automation::Host::Coordinates CursorPosition { 23 | System::Management::Automation::Host::Coordinates get() override { throw gcnew System::NotImplementedException(); }; 24 | void set(System::Management::Automation::Host::Coordinates) override { throw gcnew System::NotImplementedException(); }; 25 | } 26 | virtual property int CursorSize { 27 | int get() override { throw gcnew System::NotImplementedException(); }; 28 | void set(int) override { throw gcnew System::NotImplementedException(); }; 29 | } 30 | virtual property System::ConsoleColor ForegroundColor { 31 | System::ConsoleColor get() override { return System::ConsoleColor::Black; }; 32 | void set(System::ConsoleColor) override { }; 33 | } 34 | virtual property bool KeyAvailable { 35 | bool get() override { return false; }; 36 | } 37 | virtual property System::Management::Automation::Host::Size MaxPhysicalWindowSize { 38 | System::Management::Automation::Host::Size get() override { throw gcnew System::NotImplementedException(); }; 39 | } 40 | virtual property System::Management::Automation::Host::Size MaxWindowSize { 41 | System::Management::Automation::Host::Size get() override { throw gcnew System::NotImplementedException(); }; 42 | } 43 | virtual property System::Management::Automation::Host::Coordinates WindowPosition { 44 | System::Management::Automation::Host::Coordinates get() override { throw gcnew System::NotImplementedException(); }; 45 | void set(System::Management::Automation::Host::Coordinates) override { throw gcnew System::NotImplementedException(); }; 46 | } 47 | virtual property System::Management::Automation::Host::Size WindowSize { 48 | System::Management::Automation::Host::Size get() override { throw gcnew System::NotImplementedException(); }; 49 | void set(System::Management::Automation::Host::Size) override { throw gcnew System::NotImplementedException(); }; 50 | } 51 | virtual property System::String^ WindowTitle { 52 | System::String^ get() override { return ""; }; 53 | void set(System::String^) override { }; 54 | } 55 | virtual System::Management::Automation::Host::KeyInfo ReadKey(System::Management::Automation::Host::ReadKeyOptions options) override; 56 | virtual void FlushInputBuffer() override; 57 | virtual void SetBufferContents(System::Management::Automation::Host::Coordinates origin, array^ contents) override; 58 | virtual void SetBufferContents(System::Management::Automation::Host::Rectangle rectangle, System::Management::Automation::Host::BufferCell fill) override; 59 | virtual array^ GetBufferContents(System::Management::Automation::Host::Rectangle rectangle) override; 60 | virtual void ScrollBufferContents(System::Management::Automation::Host::Rectangle source, System::Management::Automation::Host::Coordinates destination, System::Management::Automation::Host::Rectangle clip, System::Management::Automation::Host::BufferCell fill) override; 61 | }; 62 | 63 | 64 | /// 65 | /// A sample implementation of the PSHostUserInterface abstract class for 66 | /// console applications. Not all members are implemented. Those that are 67 | /// not implemented throw a NotImplementedException exception. Members that 68 | /// are implemented include those that map easily to Console APIs. 69 | /// 70 | ref class MyHostUserInterface : PSHostUserInterface 71 | { 72 | private: 73 | RunspaceHolder^ runspace; 74 | public: 75 | /// 76 | /// Writes characters to the output display of the host. 77 | /// 78 | /// The characters to be written. 79 | virtual void Write(System::String^ value)override; 80 | 81 | MyHostUserInterface(RunspaceHolder^ runspaceParam) : runspace(runspaceParam) { 82 | myRawUserInterface = gcnew MyRawUserInterface(runspaceParam); 83 | } 84 | Logger^ GetLogger(); 85 | 86 | /// 87 | /// Writes characters to the output display of the host and specifies the 88 | /// foreground and background colors of the characters. This implementation 89 | /// ignores the colors. 90 | /// 91 | /// The color of the characters. 92 | /// The background color to use. 93 | /// The characters to be written. 94 | virtual void Write( 95 | ConsoleColor foregroundColor, 96 | ConsoleColor backgroundColor, 97 | System::String^ value)override; 98 | 99 | /// 100 | /// Writes a debug message to the output display of the host. 101 | /// 102 | /// The debug message that is displayed. 103 | virtual void WriteDebugLine(System::String^ message)override; 104 | 105 | /// 106 | /// Writes an error message to the output display of the host. 107 | /// 108 | /// The error message that is displayed. 109 | virtual void WriteErrorLine(System::String^ value)override; 110 | 111 | /// 112 | /// Writes a newline character (carriage return) 113 | /// to the output display of the host. 114 | /// 115 | virtual void WriteLine()override; 116 | 117 | /// 118 | /// Writes a line of characters to the output display of the host 119 | /// and appends a newline character(carriage return). 120 | /// 121 | /// The line to be written. 122 | virtual void WriteLine(System::String^ value)override; 123 | 124 | /// 125 | /// Writes a line of characters to the output display of the host 126 | /// with foreground and background colors and appends a newline (carriage return). 127 | /// 128 | /// The foreground color of the display. 129 | /// The background color of the display. 130 | /// The line to be written. 131 | virtual void WriteLine(ConsoleColor foregroundColor, ConsoleColor backgroundColor, System::String^ value)override; 132 | 133 | /// 134 | /// Writes a progress report to the output display of the host. 135 | /// 136 | /// Unique identifier of the source of the record. 137 | /// A ProgressReport object. 138 | virtual void WriteProgress(Int64, ProgressRecord^)override; 139 | 140 | /// 141 | /// Writes a verbose message to the output display of the host. 142 | /// 143 | /// The verbose message that is displayed. 144 | virtual void WriteVerboseLine(System::String^ message)override; 145 | 146 | /// 147 | /// Writes a warning message to the output display of the host. 148 | /// 149 | /// The warning message that is displayed. 150 | virtual void WriteWarningLine(System::String^ message)override; 151 | 152 | private: 153 | MyRawUserInterface^ myRawUserInterface; 154 | public: 155 | // Inherited via PSHostUserInterface 156 | virtual property System::Management::Automation::Host::PSHostRawUserInterface^ RawUI { 157 | System::Management::Automation::Host::PSHostRawUserInterface^ get() override { return this->myRawUserInterface; } 158 | } 159 | virtual System::String^ ReadLine() override; 160 | virtual System::Security::SecureString^ ReadLineAsSecureString() override; 161 | virtual System::Collections::Generic::Dictionary^ Prompt(System::String^ caption, System::String^ message, System::Collections::ObjectModel::Collection^ descriptions) override; 162 | virtual System::Management::Automation::PSCredential^ PromptForCredential(System::String^ caption, System::String^ message, System::String^ userName, System::String^ targetName) override; 163 | virtual System::Management::Automation::PSCredential^ PromptForCredential(System::String^ caption, System::String^ message, System::String^ userName, System::String^ targetName, System::Management::Automation::PSCredentialTypes allowedCredentialTypes, System::Management::Automation::PSCredentialUIOptions options) override; 164 | virtual int PromptForChoice(System::String^ caption, System::String^ message, System::Collections::ObjectModel::Collection^ choices, int defaultChoice) override; 165 | }; 166 | 167 | 168 | ref class MyHost : PSHost 169 | { 170 | 171 | /// 172 | /// A reference to the implementation of the PSHostUserInterface 173 | /// class for this application. 174 | /// 175 | private: 176 | 177 | CultureInfo^ originalCultureInfo = 178 | System::Threading::Thread::CurrentThread->CurrentCulture; 179 | 180 | MyHostUserInterface^ myHostUserInterface; 181 | /// 182 | /// The UI culture information of the thread that created 183 | /// this object. 184 | /// 185 | CultureInfo^ originalUICultureInfo = 186 | System::Threading::Thread::CurrentThread->CurrentUICulture; 187 | 188 | /// 189 | /// The identifier of this PSHost implementation. 190 | /// 191 | Guid myId = Guid::NewGuid(); 192 | 193 | public: 194 | RunspaceHolder^ runspace; 195 | /// 196 | /// Initializes a new instance of the MyHost class. Keep 197 | /// a reference to the host application object so that it 198 | /// can be informed of when to exit. 199 | /// 200 | /// 201 | /// A reference to the host application object. 202 | /// 203 | MyHost(RunspaceHolder^ holder) : runspace(holder) { 204 | myHostUserInterface = gcnew MyHostUserInterface(holder); 205 | }; 206 | 207 | Logger^ GetLogger(); 208 | 209 | /// 210 | /// Gets a string that contains the name of this host implementation. 211 | /// Keep in mind that this string may be used by script writers to 212 | /// identify when your host is being used. 213 | /// 214 | virtual property System::String^ Name 215 | { 216 | System::String^ get() override { return "MySampleConsoleHostImplementation"; } 217 | } 218 | 219 | System::Management::Automation::PSObject^ powershellThis = gcnew System::Management::Automation::PSObject(this); 220 | 221 | virtual property System::Management::Automation::PSObject^ PrivateData 222 | { 223 | System::Management::Automation::PSObject^ get()override { return powershellThis; } 224 | }; 225 | 226 | /// 227 | /// Gets an instance of the implementation of the PSHostUserInterface 228 | /// class for this application. This instance is allocated once at startup time 229 | /// and returned every time thereafter. 230 | /// 231 | virtual property PSHostUserInterface^ UI 232 | { 233 | PSHostUserInterface^ get() override { return this->myHostUserInterface; } 234 | } 235 | 236 | 237 | /// 238 | /// Gets the culture information to use. This implementation 239 | /// returns a snapshot of the culture information of the thread 240 | /// that created this object. 241 | /// 242 | virtual property System::Globalization::CultureInfo^ CurrentCulture 243 | { 244 | System::Globalization::CultureInfo^ get() override { return this->originalCultureInfo; } 245 | } 246 | 247 | /// 248 | /// Gets the UI culture information to use. This implementation 249 | /// returns a snapshot of the UI culture information of the thread 250 | /// that created this object. 251 | /// 252 | virtual property System::Globalization::CultureInfo^ CurrentUICulture 253 | { 254 | System::Globalization::CultureInfo^ get() override { return this->originalUICultureInfo; } 255 | } 256 | 257 | //System::Management::Automation::Host::PSHost::InstanceId 258 | /// 259 | /// Gets an identifier for this host. This implementation always 260 | /// returns the GUID allocated at instantiation time. 261 | /// 262 | virtual property Guid InstanceId 263 | { 264 | Guid get() override { return this->myId; } 265 | } 266 | 267 | /// 268 | /// Gets the version object for this application. Typically this 269 | /// should match the version resource in the application. 270 | /// 271 | virtual property System::Version^ Version 272 | { 273 | System::Version^ get() override { return gcnew System::Version(1, 0, 0, 0); } 274 | } 275 | 276 | 277 | virtual void SetShouldExit(int exitCode) override; 278 | 279 | virtual void EnterNestedPrompt() override; 280 | 281 | virtual void ExitNestedPrompt() override; 282 | 283 | virtual void NotifyBeginApplication() override; 284 | 285 | virtual void NotifyEndApplication() override; 286 | 287 | }; 288 | -------------------------------------------------------------------------------- /powershell.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #pragma once 3 | #include "host.h" 4 | #include "logger.hpp" 5 | #include "runspace.hpp" 6 | using namespace System::Management::Automation; 7 | using namespace System::Management::Automation::Host; 8 | using namespace System::Management::Automation::Runspaces; 9 | ref class PowerShellHolder { 10 | public: 11 | PowerShell^ powershell; 12 | RunspaceHolder^ runspace; 13 | PowerShellHolder(RunspaceHolder^ runspaceHolder, PowerShell^ powershellParam) : runspace(runspaceHolder), powershell(powershellParam) {} 14 | ~PowerShellHolder() { 15 | delete powershell; 16 | } 17 | }; -------------------------------------------------------------------------------- /psh_host.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio Version 16 4 | VisualStudioVersion = 16.0.28803.352 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "psh_host", "psh_host.vcxproj", "{61714DE2-4807-40CD-A5B3-9168550366F9}" 7 | EndProject 8 | Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "test", "test\test.vcxproj", "{F30D011A-EDB5-4FB3-9E00-43C6833A8997}" 9 | EndProject 10 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{700B9AF1-3D71-45A2-895F-F80146986DEE}" 11 | ProjectSection(SolutionItems) = preProject 12 | readme.md = readme.md 13 | EndProjectSection 14 | EndProject 15 | Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "test_unit", "test_unit\test_unit.vcxproj", "{313CF376-B245-490B-931C-460E2B9F73B5}" 16 | EndProject 17 | Global 18 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 19 | Debug|x64 = Debug|x64 20 | Debug|x86 = Debug|x86 21 | Release|x64 = Release|x64 22 | Release|x86 = Release|x86 23 | EndGlobalSection 24 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 25 | {61714DE2-4807-40CD-A5B3-9168550366F9}.Debug|x64.ActiveCfg = Debug|x64 26 | {61714DE2-4807-40CD-A5B3-9168550366F9}.Debug|x64.Build.0 = Debug|x64 27 | {61714DE2-4807-40CD-A5B3-9168550366F9}.Debug|x86.ActiveCfg = Debug|Win32 28 | {61714DE2-4807-40CD-A5B3-9168550366F9}.Debug|x86.Build.0 = Debug|Win32 29 | {61714DE2-4807-40CD-A5B3-9168550366F9}.Release|x64.ActiveCfg = Release|x64 30 | {61714DE2-4807-40CD-A5B3-9168550366F9}.Release|x64.Build.0 = Release|x64 31 | {61714DE2-4807-40CD-A5B3-9168550366F9}.Release|x86.ActiveCfg = Release|Win32 32 | {61714DE2-4807-40CD-A5B3-9168550366F9}.Release|x86.Build.0 = Release|Win32 33 | {F30D011A-EDB5-4FB3-9E00-43C6833A8997}.Debug|x64.ActiveCfg = Debug|x64 34 | {F30D011A-EDB5-4FB3-9E00-43C6833A8997}.Debug|x64.Build.0 = Debug|x64 35 | {F30D011A-EDB5-4FB3-9E00-43C6833A8997}.Debug|x86.ActiveCfg = Debug|Win32 36 | {F30D011A-EDB5-4FB3-9E00-43C6833A8997}.Debug|x86.Build.0 = Debug|Win32 37 | {F30D011A-EDB5-4FB3-9E00-43C6833A8997}.Release|x64.ActiveCfg = Release|x64 38 | {F30D011A-EDB5-4FB3-9E00-43C6833A8997}.Release|x64.Build.0 = Release|x64 39 | {F30D011A-EDB5-4FB3-9E00-43C6833A8997}.Release|x86.ActiveCfg = Release|Win32 40 | {F30D011A-EDB5-4FB3-9E00-43C6833A8997}.Release|x86.Build.0 = Release|Win32 41 | {313CF376-B245-490B-931C-460E2B9F73B5}.Debug|x64.ActiveCfg = Debug|x64 42 | {313CF376-B245-490B-931C-460E2B9F73B5}.Debug|x64.Build.0 = Debug|x64 43 | {313CF376-B245-490B-931C-460E2B9F73B5}.Debug|x86.ActiveCfg = Debug|Win32 44 | {313CF376-B245-490B-931C-460E2B9F73B5}.Debug|x86.Build.0 = Debug|Win32 45 | {313CF376-B245-490B-931C-460E2B9F73B5}.Release|x64.ActiveCfg = Release|x64 46 | {313CF376-B245-490B-931C-460E2B9F73B5}.Release|x64.Build.0 = Release|x64 47 | {313CF376-B245-490B-931C-460E2B9F73B5}.Release|x86.ActiveCfg = Release|Win32 48 | {313CF376-B245-490B-931C-460E2B9F73B5}.Release|x86.Build.0 = Release|Win32 49 | EndGlobalSection 50 | GlobalSection(SolutionProperties) = preSolution 51 | HideSolutionNode = FALSE 52 | EndGlobalSection 53 | GlobalSection(ExtensibilityGlobals) = postSolution 54 | SolutionGuid = {93C85A09-A55F-4783-A947-508687B44FD4} 55 | EndGlobalSection 56 | EndGlobal 57 | -------------------------------------------------------------------------------- /psh_host.vcxproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Debug 6 | Win32 7 | 8 | 9 | Release 10 | Win32 11 | 12 | 13 | Debug 14 | x64 15 | 16 | 17 | Release 18 | x64 19 | 20 | 21 | 22 | 16.0 23 | {61714DE2-4807-40CD-A5B3-9168550366F9} 24 | v4.7.2 25 | ManagedCProj 26 | pshhost 27 | 10.0 28 | 29 | 30 | 31 | DynamicLibrary 32 | true 33 | v142 34 | true 35 | Unicode 36 | 37 | 38 | DynamicLibrary 39 | false 40 | v142 41 | true 42 | Unicode 43 | 44 | 45 | DynamicLibrary 46 | true 47 | v142 48 | true 49 | Unicode 50 | 51 | 52 | DynamicLibrary 53 | false 54 | v142 55 | true 56 | Unicode 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | true 78 | 79 | 80 | true 81 | 82 | 83 | false 84 | 85 | 86 | false 87 | 88 | 89 | 90 | Level3 91 | Disabled 92 | WIN32;_DEBUG;%(PreprocessorDefinitions) 93 | 94 | 95 | 96 | host.def 97 | 98 | 99 | 100 | 101 | Level3 102 | Disabled 103 | _DEBUG;%(PreprocessorDefinitions) 104 | 105 | 106 | 107 | host.def 108 | 109 | 110 | 111 | 112 | Level3 113 | WIN32;NDEBUG;%(PreprocessorDefinitions) 114 | 115 | 116 | 117 | host.def 118 | 119 | 120 | 121 | 122 | Level3 123 | NDEBUG;%(PreprocessorDefinitions) 124 | 125 | 126 | 127 | host.def 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | ..\..\Program Files (x86)\Reference Assemblies\Microsoft\WindowsPowerShell\3.0\System.Management.Automation.dll 153 | 154 | 155 | 156 | 157 | 158 | 159 | 160 | 161 | -------------------------------------------------------------------------------- /psh_host.vcxproj.filters: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | {4FC737F1-C7A5-4376-A066-2A32D752A2FF} 6 | cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx 7 | 8 | 9 | {93995380-89BD-4b04-88EB-625FBE52EBFB} 10 | h;hh;hpp;hxx;hm;inl;inc;ipp;xsd 11 | 12 | 13 | {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} 14 | rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms 15 | 16 | 17 | 18 | 19 | Source Files 20 | 21 | 22 | Source Files 23 | 24 | 25 | Source Files 26 | 27 | 28 | Source Files 29 | 30 | 31 | Source Files 32 | 33 | 34 | 35 | 36 | Header Files 37 | 38 | 39 | Header Files 40 | 41 | 42 | Header Files 43 | 44 | 45 | Header Files 46 | 47 | 48 | Header Files 49 | 50 | 51 | Header Files 52 | 53 | 54 | Header Files 55 | 56 | 57 | Header Files 58 | 59 | 60 | Header Files 61 | 62 | 63 | Header Files 64 | 65 | 66 | 67 | 68 | Source Files 69 | 70 | 71 | -------------------------------------------------------------------------------- /psh_host.vcxproj.user: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | -------------------------------------------------------------------------------- /readme.md: -------------------------------------------------------------------------------- 1 | [![Build Status](https://dev.azure.com/oneeyedelf1/powershell.native/_apis/build/status/KnicKnic.native-powershell?branchName=master)](https://dev.azure.com/oneeyedelf1/powershell.native/_build/latest?definitionId=2&branchName=master) 2 | [![GitHub commits since latest release (branch)](https://img.shields.io/github/commits-since/KnicKnic/native-powershell/latest.svg)](https://github.com/KnicKnic/native-powershell/releases/latest) 3 | 4 | # Goal 5 | Create a C Dll that can be used to execute powershell scripts and powershell commands programmatically from other languages and get the results. 6 | 7 | This is not a one way interaction like occurs via process exec. Callbacks are supported (powershell asynchronosly calling back into C) and you can keep context open / loaded across multiple invocations. 8 | 9 | I wrote this to create a golang binding (https://github.com/KnicKnic/go-powershell). Since everyone has C bindings, you can follow that to create your own language binding. 10 | 11 | 12 | ## Status 13 | This project works and is used by the [go powershell binding](https://github.com/KnicKnic/go-powershell). However I have not gotten around to fully documenting it. 14 | 15 | Someone should add a sample project, but until then I suggest you look at the test code. This code should get refactored to seperate the supporting code from the rest. [test_unit\test_unit.cpp](test_unit\test_unit.cpp) start with `TEST_CASE("test logger")` 16 | 17 | ## Non Goals - Simple Managed hosting 18 | If you want to host managed DLL's you could directly create a managed host and do it that way. This offers a very simple alternative. Write some wrappers in powershell or directly in the hosting language via powershell statements and away you go. 19 | 20 | # How 21 | ## Problem 22 | Due to needing to return from powershell arbitrary managed objects as well as exceptions. I thought how best to approach this. One thought was to use JSON, which would represent the data which you wanted to consume. You can still do this, however I don't directly give you the way to do it yourself. 23 | 24 | ## Inception 25 | What I give is a way to take the output from one invocation of powershell to the input of another. I also provide the ability to call 3 funcions of these, `GetType`, `ToString`, `==nullptr`. You might think that this is not enough power and you need more. **But you have all the power you need in the powershell provided before you**. Simply execute a powershell query on the previous results to format the data how you want to consume it. 26 | 27 | ## Exceptions 28 | I treat exceptions the same way I treat objects from above, you can get their type and string value. If you want to get the stack trace, execute a powershell query to get the stacktrace field of the exception and return it as a string. 29 | 30 | ## Logging 31 | Lastly you can initialize all the logging functions for powershell. This means not only do you get the returned objects, but you get rich leveled logging inside powershell. 32 | 33 | ## Host to Powershell 34 | From hosting layer to powershell it is all strings and previously returned objects. The thought process is this string could be whatever you want and you can just convert it in powershell. I will give some examples. 35 | 36 | ## Code overview 37 | 38 | 1. host.cpp 39 | 1. this contains the logic to execute powershell sessions and is the bulk of the project 40 | 1. host.h 41 | 1. the actual exported functions & types of this dll. 42 | 1. my_host.cpp 43 | 1. this contains a custom PSHost that allows us to implement the callbacks for logging. 44 | 1. The only purpose we went through this implementation is to get logging callbacks. 45 | 1. test_unit/test_unit.cpp 46 | 1. This contains a few unit tests. 47 | 48 | ## running the unit tests 49 | 50 | 1. open the solution 51 | 1. Ensure that the test_unit project is set to startup project 52 | 1. hit f5 53 | 54 | # Docs 55 | 56 | Generic info about writting a hosting app 57 | https://docs.microsoft.com/en-us/powershell/developer/windows-powershell 58 | 59 | writting a custom host 60 | https://docs.microsoft.com/en-us/powershell/developer/hosting/host03-sample 61 | 62 | injecting a cmdlet into your host 63 | https://docs.microsoft.com/en-us/powershell/developer/hosting/runspace10-sample 64 | 65 | shows how to change default session state parameters (this is used to change error preferences to log details to host ($DebugPreference = Continue)) 66 | https://docs.microsoft.com/en-us/powershell/developer/hosting/windows-powershell01-sample -------------------------------------------------------------------------------- /release_notes.md: -------------------------------------------------------------------------------- 1 | # v0.0.2 2 | Still Alpha as interfaces may change, however it should be easy to convert 3 | 4 | 1. Added support for adding commands using parameters by name 5 | 1. Added support for creating nested powershell commands 6 | 1. checked in x64 binaries to make project easier to consume 7 | 1. binaries checked in seperate repo, linked via submodule 8 | 9 | # v0.0.1 10 | Initial drop 11 | 12 | 1. Alpha as interfaces may change 13 | 1. Has decent tests through https://github.com/KnicKnic/go-powershell 14 | -------------------------------------------------------------------------------- /runspace.cpp: -------------------------------------------------------------------------------- 1 | #include "runspace.hpp" 2 | 3 | 4 | using namespace System::Management::Automation::Runspaces; 5 | using namespace System::Management::Automation; 6 | 7 | 8 | using namespace System; 9 | using namespace System::Collections::Generic; 10 | using namespace System::Globalization; 11 | using namespace System::Management::Automation; 12 | using namespace System::Management::Automation::Host; 13 | 14 | 15 | #include "my_host.hpp" 16 | 17 | RunspaceHolder::~RunspaceHolder() { 18 | delete runspace; 19 | delete logger; 20 | delete host; 21 | } -------------------------------------------------------------------------------- /runspace.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "host.h" 4 | #include "logger.hpp" 5 | #include "host_internal.hpp" 6 | #include "utils/typed_handles.hpp" 7 | using namespace System::Management::Automation; 8 | using namespace System::Management::Automation::Host; 9 | using namespace System::Management::Automation::Runspaces; 10 | ref class RunspaceHolder { 11 | public: 12 | Runspace^ runspace; 13 | NativePowerShell_ReceiveJsonCommand sendJsonCommand; 14 | Logger^ logger; 15 | MyHost^ host; 16 | void* context; 17 | RunspaceHolder(void * contextParam, NativePowerShell_ReceiveJsonCommand jsonCommand, Logger^ loggerParam) 18 | : sendJsonCommand(jsonCommand) 19 | , logger(loggerParam) 20 | , context(contextParam) 21 | {} 22 | ~RunspaceHolder(); 23 | }; -------------------------------------------------------------------------------- /script.ps1: -------------------------------------------------------------------------------- 1 | write-host $debugPreference, $verbosepreference,$informationPreference, $erroractionpreference, $warningpreference 2 | 3 | # $global:DebugPreference = "Continue" 4 | # $global:VerbosePreference = "Continue" 5 | # $global:InformationPreference = "Continue" 6 | # $global:ErrorActionPreference = "Continue" 7 | # $global:WarningPreference = "Continue" 8 | # write-host $debugPreference, $verbosepreference,$informationPreference, $erroractionpreference, $warningpreference 9 | 10 | function ThrowIt($message){ 11 | throw $message 12 | } 13 | send-hostcommand "yoyoyo" 14 | write-error "first error" 15 | 16 | Get-Variable * | out-string | write-host 17 | # # mkdir C:\Windows\ -ErrorAction Stop 18 | mkdir C:\Windows\ 19 | 20 | # #cmd /C "dir c:\" 21 | # $b = dir c:\ 22 | # #$b | format-table|Out-String|write-host 23 | 24 | # $f = cmd /C "dir c:\" 25 | # #write-host $f 26 | # $a = "asdf" 27 | # write-information "info" 28 | write-error "errorasdf" 29 | # write-host "one" 30 | # write-debug "two" 31 | # write-verbose "three" 32 | # write-host "four" 33 | 34 | 35 | # ThrowIt "fuzzy" -------------------------------------------------------------------------------- /test.ps1: -------------------------------------------------------------------------------- 1 | 2 | using namespace System::Management::Automation; 3 | using namespace System::Management::Automation::Host; 4 | 5 | $psCommand = [System.Management.Automation.PowerShell]::Create([System.Management.Automation.RunspaceMode]::CurrentRunspace) 6 | $null = $psCommand.AddScript(".\t3.ps1", $true) 7 | $psCommand.Invoke() 8 | -------------------------------------------------------------------------------- /test/test.cpp: -------------------------------------------------------------------------------- 1 | // test.cpp : This file contains the 'main' function. Program execution begins and ends there. 2 | // 3 | 4 | #include 5 | #include 6 | #include 7 | #include "host.h" 8 | #include "utils/macros.hpp" 9 | #include "utils/zero_resetable.hpp" 10 | #include "utils/cpp_wrappers.hpp" 11 | 12 | using namespace std; 13 | using namespace native_powershell; 14 | 15 | struct SomeContext { 16 | std::wstring LoggerContext; 17 | std::wstring CommandContext; 18 | NativePowerShell_RunspaceHandle runspace; 19 | std::optional powershell; 20 | }; 21 | 22 | extern "C" { 23 | void Logger(void* context, const wchar_t* s) 24 | { 25 | auto realContext = (SomeContext*)context; 26 | std::wcout << realContext->LoggerContext << std::wstring(s); 27 | } 28 | void Command(void* context, const wchar_t* s, NativePowerShell_PowerShellObject* input, unsigned long long inputCount, NativePowerShell_JsonReturnValues* returnValues) 29 | { 30 | 31 | input; inputCount; 32 | auto realContext = (SomeContext*)context; 33 | std::wcout << realContext->CommandContext << std::wstring(s) << L'\n'; 34 | 35 | for (size_t i = 0; i < inputCount; ++i) { 36 | // test nested creation 37 | RunScript(realContext->runspace, realContext->powershell, L"[int]11", true); 38 | auto& v = input[i]; 39 | std::wcout << L"In data processing got " << GetToString(v) << L" of type " << GetType(v) << L'\n'; 40 | } 41 | 42 | // allocate return object holders 43 | returnValues->count = 1 + inputCount; 44 | returnValues->objects = (NativePowerShell_GenericPowerShellObject*)NativePowerShell_DefaultAlloc(sizeof(*(returnValues->objects)) * returnValues->count); 45 | if (returnValues->objects == nullptr) { 46 | throw "memory allocation failed for return values in command"; 47 | } 48 | 49 | // allocate and fill out each object 50 | auto& object = returnValues->objects[0]; 51 | object.releaseObject = char(1); 52 | object.type = NativePowerShell_PowerShellObjectTypeString; 53 | object.instance.string = MallocCopy(s); 54 | 55 | for (size_t i = 0; i < inputCount; ++i) { 56 | auto& v = returnValues->objects[1 + i]; 57 | v.releaseObject = char(0); 58 | v.type = NativePowerShell_PowerShellObjectHandle; 59 | v.instance.psObject = input[i]; 60 | } 61 | 62 | return; 63 | } 64 | } 65 | 66 | 67 | int main() 68 | { 69 | SomeContext context{ L"MyLoggerContext: ", L"MyCommandContext: ", NativePowerShell_InvalidHandleValue, std::nullopt }; 70 | NativePowerShell_LogString_Holder logHolder = { 0 }; 71 | logHolder.Log = Logger; 72 | auto runspace = NativePowerShell_CreateRunspace(&context, Command, &logHolder); 73 | context.runspace = runspace; 74 | RunScript(runspace, std::nullopt, L"[int12", true); 75 | 76 | auto powershell = NativePowerShell_CreatePowerShell(runspace); 77 | //AddScriptSpecifyScope(powershell, L"c:\\code\\psh_host\\script.ps1", 1); 78 | //AddCommand(powershell, L"c:\\code\\go-net\\t3.ps1"); 79 | //AddScriptSpecifyScope(powershell, L"write-host $pwd", 0); 80 | NativePowerShell_AddScriptSpecifyScope(powershell, L"0;1;$null;dir c:\\", 1); 81 | 82 | //AddCommandSpecifyScope(powershell, L"..\\..\\go-net\\t3.ps1", 0); 83 | //AddScriptSpecifyScope(powershell, L"$a = \"asdf\"", 0); 84 | //AddArgument(powershell, L"c:\\ddddddd"); 85 | { 86 | Invoker invoke(powershell); 87 | 88 | wcout << L"examining returned objects\n"; 89 | for (unsigned int i = 0; i < invoke.count; ++i) { 90 | wcout << L"Got type: " << GetType(invoke[i]) << L"with value: " << GetToString(invoke[i]) << L'\n'; 91 | } 92 | 93 | auto powershell2 = NativePowerShell_CreatePowerShell(runspace); 94 | 95 | // note below will write to output, not return objects 96 | NativePowerShell_AddScriptSpecifyScope(powershell2, 97 | L"write-host 'about to enumerate directory';" 98 | L"write-host $args; $len = $args.length; write-host \"arg count $len\";" 99 | L"$args | ft | out-string | write-host;" 100 | L"@(1,'asdf',$null,$false) | send-hostcommand -message 'I sent the host a command' | write-host;" 101 | L"send-hostcommand -message 'I sent the host a command' | write-host", 0); 102 | NativePowerShell_AddArgument(powershell2, L"String to start"); 103 | NativePowerShell_AddPSObjectArguments(powershell2, invoke.objects, invoke.count); 104 | NativePowerShell_AddArgument(powershell2, L"String to end"); 105 | 106 | context.powershell = powershell2; 107 | Invoker invoke2(powershell2); 108 | context.powershell = std::nullopt; 109 | } 110 | NativePowerShell_DeletePowershell(powershell); 111 | 112 | powershell = NativePowerShell_CreatePowerShell(runspace); 113 | //AddScriptSpecifyScope(powershell, L"c:\\code\\psh_host\\script.ps1", 1); 114 | NativePowerShell_AddCommandSpecifyScope(powershell, L"..\\..\\go-net\\t3.ps1", 0); 115 | //AddScriptSpecifyScope(powershell, L"write-host $a", 0); 116 | 117 | //AddCommand(powershell, L"c:\\code\\go-net\\t3.ps1"); 118 | //AddArgument(powershell, L"c:\\ddddddd"); 119 | { 120 | Invoker invoke(powershell); 121 | } 122 | NativePowerShell_DeletePowershell(powershell); 123 | 124 | NativePowerShell_DeleteRunspace(runspace); 125 | std::cout << "Hello World!\n"; 126 | } 127 | 128 | // Run program: Ctrl + F5 or Debug > Start Without Debugging menu 129 | // Debug program: F5 or Debug > Start Debugging menu 130 | 131 | // Tips for Getting Started: 132 | // 1. Use the Solution Explorer window to add/manage files 133 | // 2. Use the Team Explorer window to connect to source control 134 | // 3. Use the Output window to see build output and other messages 135 | // 4. Use the Error List window to view errors 136 | // 5. Go to Project > Add New Item to create new code files, or Project > Add Existing Item to add existing code files to the project 137 | // 6. In the future, to open this project again, go to File > Open > Project and select the .sln file 138 | -------------------------------------------------------------------------------- /test/test.vcxproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Debug 6 | Win32 7 | 8 | 9 | Release 10 | Win32 11 | 12 | 13 | Debug 14 | x64 15 | 16 | 17 | Release 18 | x64 19 | 20 | 21 | 22 | 16.0 23 | {F30D011A-EDB5-4FB3-9E00-43C6833A8997} 24 | Win32Proj 25 | test 26 | 10.0 27 | 28 | 29 | 30 | Application 31 | true 32 | v142 33 | Unicode 34 | 35 | 36 | Application 37 | false 38 | v142 39 | true 40 | Unicode 41 | 42 | 43 | Application 44 | true 45 | v142 46 | Unicode 47 | 48 | 49 | Application 50 | false 51 | v142 52 | true 53 | Unicode 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | false 75 | 76 | 77 | true 78 | 79 | 80 | true 81 | 82 | 83 | false 84 | 85 | 86 | 87 | 88 | 89 | Level3 90 | MaxSpeed 91 | true 92 | true 93 | true 94 | NDEBUG;_CONSOLE;%(PreprocessorDefinitions) 95 | true 96 | ../ 97 | stdcpplatest 98 | 99 | 100 | Console 101 | true 102 | true 103 | true 104 | ..\x64\Release;%(AdditionalLibraryDirectories) 105 | false 106 | psh_host.lib;%(AdditionalDependencies) 107 | 108 | 109 | 110 | 111 | 112 | 113 | Level3 114 | Disabled 115 | true 116 | WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) 117 | true 118 | ../ 119 | stdcpplatest 120 | 121 | 122 | Console 123 | true 124 | ..\x64\Debug;%(AdditionalLibraryDirectories) 125 | 126 | 127 | 128 | 129 | 130 | 131 | Level3 132 | Disabled 133 | true 134 | _DEBUG;_CONSOLE;%(PreprocessorDefinitions) 135 | true 136 | ../ 137 | stdcpplatest 138 | 139 | 140 | Console 141 | true 142 | psh_host.lib;%(AdditionalDependencies) 143 | ..\x64\Debug;%(AdditionalLibraryDirectories) 144 | 145 | 146 | 147 | 148 | 149 | 150 | Level3 151 | MaxSpeed 152 | true 153 | true 154 | true 155 | WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) 156 | true 157 | ../ 158 | stdcpplatest 159 | 160 | 161 | Console 162 | true 163 | true 164 | true 165 | 166 | 167 | 168 | 169 | 170 | 171 | 172 | {61714de2-4807-40cd-a5b3-9168550366f9} 173 | 174 | 175 | 176 | 177 | 178 | -------------------------------------------------------------------------------- /test/test.vcxproj.filters: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | {4FC737F1-C7A5-4376-A066-2A32D752A2FF} 6 | cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx 7 | 8 | 9 | {93995380-89BD-4b04-88EB-625FBE52EBFB} 10 | h;hh;hpp;hxx;hm;inl;inc;ipp;xsd 11 | 12 | 13 | {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} 14 | rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms 15 | 16 | 17 | 18 | 19 | Source Files 20 | 21 | 22 | -------------------------------------------------------------------------------- /test/test.vcxproj.user: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | Mixed 5 | C:\Users\nmaliwa\go\src\github.com\KnicKnic\go-powershell\pkg\powershell\f.exe 6 | C:\Users\nmaliwa\go\src\github.com\KnicKnic\go-powershell\pkg\powershell\ 7 | WindowsLocalDebugger 8 | 9 | 10 | C:\Users\nmaliwa\go\src\github.com\KnicKnic\go-powershell\pkg\powershell\f.exe 11 | C:\Users\nmaliwa\go\src\github.com\KnicKnic\go-powershell\pkg\powershell\ 12 | WindowsLocalDebugger 13 | 14 | -------------------------------------------------------------------------------- /test_gcc/.gitignore: -------------------------------------------------------------------------------- 1 | ** 2 | -------------------------------------------------------------------------------- /test_gcc/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required (VERSION 2.6) 2 | project (test_gcc) 3 | set(CMAKE_CXX_STANDARD 17) 4 | set(CMAKE_CXX_STANDARD_REQUIRED on) 5 | add_executable(test_gcc test.cpp) 6 | 7 | get_filename_component(PARENT_DIR ${PROJECT_SOURCE_DIR} DIRECTORY) 8 | 9 | 10 | include_directories(${PARENT_DIR}) 11 | 12 | target_link_libraries(test_gcc ${PARENT_DIR}/x64/Release/psh_host.dll) -------------------------------------------------------------------------------- /test_gcc/readme.md: -------------------------------------------------------------------------------- 1 | 2 | # this actually worked 3 | install choco 4 | 1. install mingw 5 | 1. install cmake 6 | 7 | mkdir build 8 | cmake -B build -G "MinGW Makefiles" . 9 | cmake --build build 10 | 11 | 12 | # other things I tried that were not the answers I wanted, but may be useful in the future 13 | ## setup environment 14 | set CMAKE_C_COMPILER=gcc 15 | set CMAKE_CXX_COMPILER=g++ 16 | set CMAKE_CXX_COMPILER_ENV_VAR=CXX 17 | set CMAKE_C_COMPILER_ENV_VAR=CC 18 | set CMAKE_MAKE_PROGRAM=C:\ProgramData\chocolatey\lib\mingw\tools\install\mingw64\bin\nm.exe 19 | 20 | ## read someone else's tutorial 21 | https://www.scivision.dev/windows-gcc-gfortran-cmake-make-install/ 22 | 23 | cmake -G "MinGW Makefiles" -DCMAKE_SH="CMAKE_SH-NOTFOUND" -B bin . 24 | 25 | cmake --build . 26 | 27 | cmake --build . --target install # optional 28 | -------------------------------------------------------------------------------- /test_gcc/test.cpp: -------------------------------------------------------------------------------- 1 | // test.cpp : This file contains the 'main' function. Program execution begins and ends there. 2 | // 3 | 4 | #include 5 | #include 6 | #include 7 | #include "host.h" 8 | #include "utils/macros.hpp" 9 | #include "utils/zero_resetable.hpp" 10 | #include "utils/cpp_wrappers.hpp" 11 | 12 | using namespace std; 13 | using namespace native_powershell; 14 | 15 | struct SomeContext { 16 | std::wstring LoggerContext; 17 | std::wstring CommandContext; 18 | NativePowerShell_RunspaceHandle runspace; 19 | std::optional powershell; 20 | }; 21 | 22 | extern "C" { 23 | void Logger(void* context, const wchar_t* s) 24 | { 25 | auto realContext = (SomeContext*)context; 26 | std::wcout << realContext->LoggerContext << std::wstring(s); 27 | } 28 | void Command(void* context, const wchar_t* s, NativePowerShell_PowerShellObject* input, unsigned long long inputCount, NativePowerShell_JsonReturnValues* returnValues) 29 | { 30 | 31 | input; inputCount; 32 | auto realContext = (SomeContext*)context; 33 | std::wcout << realContext->CommandContext << std::wstring(s) << L'\n'; 34 | 35 | for (size_t i = 0; i < inputCount; ++i) { 36 | // test nested creation 37 | RunScript(realContext->runspace, realContext->powershell, L"[int]11", true); 38 | auto& v = input[i]; 39 | std::wcout << L"In data processing got " << GetToString(v) << L" of type " << GetType(v) << L'\n'; 40 | } 41 | 42 | // allocate return object holders 43 | returnValues->count = 1 + inputCount; 44 | returnValues->objects = (NativePowerShell_GenericPowerShellObject*)NativePowerShell_DefaultAlloc(sizeof(*(returnValues->objects)) * returnValues->count); 45 | if (returnValues->objects == nullptr) { 46 | throw "memory allocation failed for return values in command"; 47 | } 48 | 49 | // allocate and fill out each object 50 | auto& object = returnValues->objects[0]; 51 | object.releaseObject = char(1); 52 | object.type = NativePowerShell_PowerShellObjectTypeString; 53 | object.instance.string = MallocCopy(s); 54 | 55 | for (size_t i = 0; i < inputCount; ++i) { 56 | auto& v = returnValues->objects[1 + i]; 57 | v.releaseObject = char(0); 58 | v.type = NativePowerShell_PowerShellObjectHandle; 59 | v.instance.psObject = input[i]; 60 | } 61 | 62 | return; 63 | } 64 | } 65 | 66 | 67 | int main() 68 | { 69 | SomeContext context{ L"MyLoggerContext: ", L"MyCommandContext: ", NativePowerShell_InvalidHandleValue, std::nullopt }; 70 | NativePowerShell_LogString_Holder logHolder = { 0 }; 71 | logHolder.Log = Logger; 72 | auto runspace = NativePowerShell_CreateRunspace(&context, Command, &logHolder); 73 | context.runspace = runspace; 74 | RunScript(runspace, std::nullopt, L"[int12", true); 75 | 76 | auto powershell = NativePowerShell_CreatePowerShell(runspace); 77 | //AddScriptSpecifyScope(powershell, L"c:\\code\\psh_host\\script.ps1", 1); 78 | //AddCommand(powershell, L"c:\\code\\go-net\\t3.ps1"); 79 | //AddScriptSpecifyScope(powershell, L"write-host $pwd", 0); 80 | NativePowerShell_AddScriptSpecifyScope(powershell, L"0;1;$null;dir c:\\", 1); 81 | 82 | //AddCommandSpecifyScope(powershell, L"..\\..\\go-net\\t3.ps1", 0); 83 | //AddScriptSpecifyScope(powershell, L"$a = \"asdf\"", 0); 84 | //AddArgument(powershell, L"c:\\ddddddd"); 85 | { 86 | Invoker invoke(powershell); 87 | 88 | wcout << L"examining returned objects\n"; 89 | for (unsigned int i = 0; i < invoke.count; ++i) { 90 | wcout << L"Got type: " << GetType(invoke[i]) << L"with value: " << GetToString(invoke[i]) << L'\n'; 91 | } 92 | 93 | auto powershell2 = NativePowerShell_CreatePowerShell(runspace); 94 | 95 | // note below will write to output, not return objects 96 | NativePowerShell_AddScriptSpecifyScope(powershell2, 97 | L"write-host 'about to enumerate directory';" 98 | L"write-host $args; $len = $args.length; write-host \"arg count $len\";" 99 | L"$args | ft | out-string | write-host;" 100 | L"@(1,'asdf',$null,$false) | send-hostcommand -message 'I sent the host a command' | write-host;" 101 | L"send-hostcommand -message 'I sent the host a command' | write-host", 0); 102 | NativePowerShell_AddArgument(powershell2, L"String to start"); 103 | NativePowerShell_AddPSObjectArguments(powershell2, invoke.objects, invoke.count); 104 | NativePowerShell_AddArgument(powershell2, L"String to end"); 105 | 106 | context.powershell = powershell2; 107 | Invoker invoke2(powershell2); 108 | context.powershell = std::nullopt; 109 | } 110 | NativePowerShell_DeletePowershell(powershell); 111 | 112 | powershell = NativePowerShell_CreatePowerShell(runspace); 113 | //AddScriptSpecifyScope(powershell, L"c:\\code\\psh_host\\script.ps1", 1); 114 | NativePowerShell_AddCommandSpecifyScope(powershell, L"..\\..\\go-net\\t3.ps1", 0); 115 | //AddScriptSpecifyScope(powershell, L"write-host $a", 0); 116 | 117 | //AddCommand(powershell, L"c:\\code\\go-net\\t3.ps1"); 118 | //AddArgument(powershell, L"c:\\ddddddd"); 119 | { 120 | Invoker invoke(powershell); 121 | } 122 | NativePowerShell_DeletePowershell(powershell); 123 | 124 | NativePowerShell_DeleteRunspace(runspace); 125 | std::cout << "Hello World!\n"; 126 | } 127 | 128 | // Run program: Ctrl + F5 or Debug > Start Without Debugging menu 129 | // Debug program: F5 or Debug > Start Debugging menu 130 | 131 | // Tips for Getting Started: 132 | // 1. Use the Solution Explorer window to add/manage files 133 | // 2. Use the Team Explorer window to connect to source control 134 | // 3. Use the Output window to see build output and other messages 135 | // 4. Use the Error List window to view errors 136 | // 5. Go to Project > Add New Item to create new code files, or Project > Add Existing Item to add existing code files to the project 137 | // 6. In the future, to open this project again, go to File > Open > Project and select the .sln file 138 | -------------------------------------------------------------------------------- /test_unit/test_unit.cpp: -------------------------------------------------------------------------------- 1 | // test_unit.cpp : This file contains the 'main' function. Program execution begins and ends there. 2 | // 3 | 4 | 5 | // test.cpp : This file contains the 'main' function. Program execution begins and ends there. 6 | // 7 | 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include "host.h" 13 | #include "utils/macros.hpp" 14 | #include "utils/zero_resetable.hpp" 15 | #include "utils/cpp_wrappers.hpp" 16 | 17 | //#define CATCH_CONFIG_MAIN // This tells Catch to provide a main() - only do this in one cpp file 18 | #define CATCH_CONFIG_RUNNER // creating own main to do powershell native init 19 | #include "./Catch2/single_include/catch2/catch.hpp" 20 | 21 | using namespace std; 22 | using namespace native_powershell; 23 | 24 | 25 | 26 | struct SomeContext { 27 | std::wstring LoggerContext; 28 | std::wstring CommandContext; 29 | NativePowerShell_RunspaceHandle runspace; 30 | std::optional powershell; 31 | }; 32 | 33 | 34 | 35 | extern "C" { 36 | void Logger(void* context, const wchar_t* s) 37 | { 38 | auto realContext = (SomeContext*)context; 39 | std::wcout << realContext->LoggerContext << std::wstring(s) << L'\n'; 40 | } 41 | void Command(void* context, const wchar_t* s, NativePowerShell_PowerShellObject* input, unsigned long long inputCount, NativePowerShell_JsonReturnValues* returnValues) 42 | { 43 | 44 | input; inputCount; 45 | auto realContext = (SomeContext*)context; 46 | std::wcout << realContext->CommandContext << std::wstring(s) << L'\n'; 47 | 48 | for (size_t i = 0; i < inputCount; ++i) { 49 | // test nested creation 50 | RunScript(realContext->runspace, realContext->powershell, L"[int]11", true); 51 | auto& v = input[i]; 52 | std::wcout << L"In data processing got " << GetToString(v) << L" of type " << GetType(v) << L'\n'; 53 | } 54 | 55 | // allocate return object holders 56 | returnValues->count = 1 + inputCount; 57 | returnValues->objects = (NativePowerShell_GenericPowerShellObject*)NativePowerShell_DefaultAlloc(sizeof(*(returnValues->objects)) * returnValues->count); 58 | if (returnValues->objects == nullptr) { 59 | throw "memory allocation failed for return values in command"; 60 | } 61 | 62 | // allocate and fill out each object 63 | auto& object = returnValues->objects[0]; 64 | object.releaseObject = char(1); 65 | object.type = NativePowerShell_PowerShellObjectTypeString; 66 | object.instance.string = MallocCopy(s); 67 | 68 | for (size_t i = 0; i < inputCount; ++i) { 69 | auto& v = returnValues->objects[1 + i]; 70 | v.releaseObject = char(0); 71 | v.type = NativePowerShell_PowerShellObjectHandle; 72 | v.instance.psObject = input[i]; 73 | } 74 | 75 | return; 76 | } 77 | 78 | } 79 | 80 | TEST_CASE("nullptr | send-hostcommand") { 81 | auto command = +[](void* context, const wchar_t* s, NativePowerShell_PowerShellObject * input, unsigned long long inputCount, NativePowerShell_JsonReturnValues * returnValues) 82 | {}; 83 | 84 | auto runspace = NativePowerShell_CreateRunspace((void*)0, command, nullptr); 85 | 86 | auto powershell = NativePowerShell_CreatePowerShell(runspace); 87 | 88 | REQUIRE(0 == NativePowerShell_AddScriptSpecifyScope(powershell, L"send-hostcommand -message \"hi\"; $nullptr | send-hostcommand -message \"hi\" ", FALSE)); 89 | 90 | Invoker invoke(powershell); 91 | } 92 | 93 | // declaring at global scope to make testing for validateContext simpler 94 | unsigned long long validateContext_contextValue = 94847573123111; 95 | bool validateContext_validatedLogger = false; 96 | bool validateContext_validatedCallback = false; 97 | 98 | 99 | NativePowerShell_LogString_Holder MakeLogHolderFunction(NativePowerShell_LogString func) { 100 | NativePowerShell_LogString_Holder logHolder = { 0 }; 101 | logHolder.Log = func; 102 | return logHolder; 103 | } 104 | 105 | #define MAKE_LOG_HOLDER(SomeFunc) &(MakeLogHolderFunction(SomeFunc)) 106 | 107 | TEST_CASE("validate context") { 108 | auto logger = +[](void* context, const wchar_t* s) {validateContext_validatedLogger = true; REQUIRE(context != nullptr); REQUIRE((unsigned long long)context == validateContext_contextValue); }; 109 | auto command = +[](void* context, const wchar_t* s, NativePowerShell_PowerShellObject * input, unsigned long long inputCount, NativePowerShell_JsonReturnValues * returnValues) 110 | {validateContext_validatedCallback = true; REQUIRE(context != nullptr); REQUIRE((unsigned long long)context == validateContext_contextValue); }; 111 | 112 | auto runspace = NativePowerShell_CreateRunspace((void *)validateContext_contextValue, command, MAKE_LOG_HOLDER(logger)); 113 | 114 | auto powershell = NativePowerShell_CreatePowerShell(runspace); 115 | 116 | REQUIRE(0 == NativePowerShell_AddScriptSpecifyScope(powershell, L"write-host 5;send-hostcommand -message \"hi\" ", FALSE)); 117 | 118 | Invoker invoke(powershell); 119 | REQUIRE(validateContext_validatedLogger); 120 | REQUIRE(validateContext_validatedCallback); 121 | } 122 | 123 | 124 | TEST_CASE("test logger") { 125 | std::wstring resultString; 126 | auto logger = +[](void* context, const wchar_t* s) {std::wcout << std::wstring(s); (*((std::wstring*)(context))) += std::wstring(s); }; 127 | 128 | auto runspace = NativePowerShell_CreateRunspace((void*)&resultString, nullptr, MAKE_LOG_HOLDER(logger)); 129 | 130 | auto powershell = NativePowerShell_CreatePowerShell(runspace); 131 | 132 | REQUIRE(0 == NativePowerShell_AddScriptSpecifyScope(powershell, L"write-error a; write-debug b; write-information c; write-verbose d; write-warning e; write-error a; write-debug b; write-information c; write-verbose d; write-warning e; ", FALSE)); 133 | 134 | Invoker invoke(powershell); 135 | 136 | std::wstring strExpected(L"Debug: b\nInformation: c\nVerbose: d\nWarning: e\nDebug: b\nInformation: c\nVerbose: d\nWarning: e\nError: a\nError: a\n"); 137 | 138 | REQUIRE(resultString == strExpected); 139 | } 140 | 141 | TEST_CASE("nested runspace"){ 142 | struct SomeContext2 { 143 | bool logged; 144 | NativePowerShell_RunspaceHandle runspace; 145 | NativePowerShell_PowerShellHandle powershell; 146 | }; 147 | auto logger = +[](void* context, const wchar_t* s) {((SomeContext2*)context)->logged = true; }; 148 | 149 | auto command = +[](void* context, const wchar_t* s, NativePowerShell_PowerShellObject * input, unsigned long long inputCount, NativePowerShell_JsonReturnValues * returnValues) 150 | { 151 | auto realContext = (SomeContext2*)context; 152 | REQUIRE(realContext->logged == false); 153 | RunScript(realContext->runspace, realContext->powershell, L"write-host 15", true); 154 | 155 | REQUIRE(realContext->logged); 156 | 157 | return; 158 | }; 159 | SomeContext2 context{ false }; 160 | auto runspace = NativePowerShell_CreateRunspace(&context, command, MAKE_LOG_HOLDER(logger)); 161 | context.runspace = runspace; 162 | REQUIRE(context.runspace != 0); 163 | 164 | auto powershell = NativePowerShell_CreatePowerShell(runspace); 165 | REQUIRE(powershell != 0); 166 | context.powershell = powershell; 167 | 168 | NativePowerShell_AddScriptSpecifyScope(powershell, L"send-hostcommand -message \"hi\" ", FALSE); 169 | auto results = Invoker(powershell); 170 | REQUIRE(context.logged); 171 | REQUIRE(results.count == 0); 172 | 173 | NativePowerShell_DeletePowershell(powershell); 174 | } 175 | 176 | 177 | TEST_CASE("tests", "[native-powershell]") 178 | { 179 | SomeContext context{ L"MyLoggerContext: ", L"MyCommandContext: " }; 180 | auto runspace = NativePowerShell_CreateRunspace(&context, Command, MAKE_LOG_HOLDER(Logger)); 181 | context.runspace = runspace; 182 | RunScript(runspace, nullopt, L"[int12", true); 183 | 184 | auto powershell = NativePowerShell_CreatePowerShell(runspace); 185 | //AddScriptSpecifyScope(powershell, L"c:\\code\\psh_host\\script.ps1", 1); 186 | //AddCommand(powershell, L"c:\\code\\go-net\\t3.ps1"); 187 | //AddScriptSpecifyScope(powershell, L"write-host $pwd", 0); 188 | NativePowerShell_AddScriptSpecifyScope(powershell, L"0;1;$null;dir c:\\", 1); 189 | 190 | //AddCommandSpecifyScope(powershell, L"..\\..\\go-net\\t3.ps1", 0); 191 | //AddScriptSpecifyScope(powershell, L"$a = \"asdf\"", 0); 192 | //AddArgument(powershell, L"c:\\ddddddd"); 193 | { 194 | Invoker invoke(powershell); 195 | 196 | wcout << L"examining returned objects\n"; 197 | for (unsigned int i = 0; i < invoke.count; ++i) { 198 | wcout << L"Got type: " << GetType(invoke[i]) << L"with value: " << GetToString(invoke[i]) << L'\n'; 199 | } 200 | 201 | auto powershell2 = NativePowerShell_CreatePowerShell(runspace); 202 | 203 | // note below will write to output, not return objects 204 | NativePowerShell_AddScriptSpecifyScope(powershell2, 205 | L"write-host 'about to enumerate directory';" 206 | L"write-host $args; $len = $args.length; write-host \"arg count $len\";" 207 | L"$args | ft | out-string | write-host;" 208 | L"@(1,'asdf',$null,$false) | send-hostcommand -message 'I sent the host a command' | write-host;" 209 | L"send-hostcommand -message 'I sent the host a command' | write-host", 0); 210 | NativePowerShell_AddArgument(powershell2, L"String to start"); 211 | NativePowerShell_AddPSObjectArguments(powershell2, invoke.objects, invoke.count); 212 | NativePowerShell_AddArgument(powershell2, L"String to end"); 213 | 214 | context.powershell = powershell2; 215 | Invoker invoke2(powershell2); 216 | context.powershell = nullopt; 217 | } 218 | NativePowerShell_DeletePowershell(powershell); 219 | 220 | powershell = NativePowerShell_CreatePowerShell(runspace); 221 | //AddScriptSpecifyScope(powershell, L"c:\\code\\psh_host\\script.ps1", 1); 222 | NativePowerShell_AddCommandSpecifyScope(powershell, L"..\\..\\go-net\\t3.ps1", 0); 223 | //AddScriptSpecifyScope(powershell, L"write-host $a", 0); 224 | 225 | //AddCommand(powershell, L"c:\\code\\go-net\\t3.ps1"); 226 | //AddArgument(powershell, L"c:\\ddddddd"); 227 | { 228 | Invoker invoke(powershell); 229 | } 230 | NativePowerShell_DeletePowershell(powershell); 231 | 232 | NativePowerShell_DeleteRunspace(runspace); 233 | std::cout << "Hello World!\n"; 234 | } 235 | 236 | // 237 | //// need to uncomment test 238 | //// note that send-hostcommand does not work in remote sessions, should not accept Command 239 | //TEST_CASE("remotetest") 240 | //{ 241 | // SomeContext context{ L"MyLoggerContext: ", L"MyCommandContext: " }; 242 | // auto computer = L"computer"; 243 | // auto username = L"username"; 244 | // auto pass = L"pass"; 245 | // auto runspace = NativePowerShell_CreateRemoteRunspace(&context, Logger, computer, username, pass); 246 | // context.runspace = runspace; 247 | // RunScript(runspace, nullopt, L"[int12", true); 248 | // 249 | // auto powershell = NativePowerShell_CreatePowerShell(runspace); 250 | // //AddScriptSpecifyScope(powershell, L"c:\\code\\psh_host\\script.ps1", 1); 251 | // //AddCommand(powershell, L"c:\\code\\go-net\\t3.ps1"); 252 | // //AddScriptSpecifyScope(powershell, L"write-host $pwd", 0); 253 | // NativePowerShell_AddScriptSpecifyScope(powershell, L"hostname; 0;1;$null;dir c:\\", 1); 254 | // 255 | // //AddCommandSpecifyScope(powershell, L"..\\..\\go-net\\t3.ps1", 0); 256 | // //AddScriptSpecifyScope(powershell, L"$a = \"asdf\"", 0); 257 | // //AddArgument(powershell, L"c:\\ddddddd"); 258 | // { 259 | // Invoker invoke(powershell); 260 | // 261 | // wcout << L"examining returned objects\n"; 262 | // for (unsigned int i = 0; i < invoke.count; ++i) { 263 | // wcout << L"Got type: " << GetType(invoke[i]) << L"with value: " << GetToString(invoke[i]) << L'\n'; 264 | // } 265 | // 266 | // auto powershell2 = NativePowerShell_CreatePowerShell(runspace); 267 | // 268 | // // note below will write to output, not return objects 269 | // NativePowerShell_AddScriptSpecifyScope(powershell2, 270 | // L"write-host 'about to enumerate directory';" 271 | // L"write-host $args; $len = $args.length; write-host \"arg count $len\";" 272 | // L"$args | ft | out-string | write-host;" 273 | // L"@(1,'asdf',$null,$false) | send-hostcommand -message 'I sent the host a command' | write-host;" 274 | // L"send-hostcommand -message 'I sent the host a command' | write-host", 0); 275 | // NativePowerShell_AddArgument(powershell2, L"String to start"); 276 | // NativePowerShell_AddPSObjectArguments(powershell2, invoke.objects, invoke.count); 277 | // NativePowerShell_AddArgument(powershell2, L"String to end"); 278 | // 279 | // context.powershell = powershell2; 280 | // Invoker invoke2(powershell2); 281 | // context.powershell = nullopt; 282 | // } 283 | // NativePowerShell_DeletePowershell(powershell); 284 | // 285 | // powershell = NativePowerShell_CreatePowerShell(runspace); 286 | // //AddScriptSpecifyScope(powershell, L"c:\\code\\psh_host\\script.ps1", 1); 287 | // NativePowerShell_AddCommandSpecifyScope(powershell, L"..\\..\\go-net\\t3.ps1", 0); 288 | // //AddScriptSpecifyScope(powershell, L"write-host $a", 0); 289 | // 290 | // //AddCommand(powershell, L"c:\\code\\go-net\\t3.ps1"); 291 | // //AddArgument(powershell, L"c:\\ddddddd"); 292 | // { 293 | // Invoker invoke(powershell); 294 | // } 295 | // NativePowerShell_DeletePowershell(powershell); 296 | // 297 | // NativePowerShell_DeleteRunspace(runspace); 298 | // std::cout << "Hello World!\n"; 299 | //} 300 | 301 | int main(int argc, char* argv[]) { 302 | // global setup... 303 | 304 | int result = Catch::Session().run(argc, argv); 305 | 306 | // global clean-up... 307 | 308 | return result; 309 | } 310 | 311 | // Run program: Ctrl + F5 or Debug > Start Without Debugging menu 312 | // Debug program: F5 or Debug > Start Debugging menu 313 | 314 | // Tips for Getting Started: 315 | // 1. Use the Solution Explorer window to add/manage files 316 | // 2. Use the Team Explorer window to connect to source control 317 | // 3. Use the Output window to see build output and other messages 318 | // 4. Use the Error List window to view errors 319 | // 5. Go to Project > Add New Item to create new code files, or Project > Add Existing Item to add existing code files to the project 320 | // 6. In the future, to open this project again, go to File > Open > Project and select the .sln file 321 | 322 | 323 | 324 | -------------------------------------------------------------------------------- /test_unit/test_unit.vcxproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Debug 6 | Win32 7 | 8 | 9 | Release 10 | Win32 11 | 12 | 13 | Debug 14 | x64 15 | 16 | 17 | Release 18 | x64 19 | 20 | 21 | 22 | 16.0 23 | {313CF376-B245-490B-931C-460E2B9F73B5} 24 | Win32Proj 25 | testunit 26 | 10.0 27 | 28 | 29 | 30 | Application 31 | true 32 | v142 33 | Unicode 34 | 35 | 36 | Application 37 | false 38 | v142 39 | true 40 | Unicode 41 | 42 | 43 | Application 44 | true 45 | v142 46 | Unicode 47 | 48 | 49 | Application 50 | false 51 | v142 52 | true 53 | Unicode 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | true 75 | 76 | 77 | true 78 | 79 | 80 | false 81 | 82 | 83 | false 84 | 85 | 86 | 87 | 88 | 89 | Level3 90 | Disabled 91 | true 92 | _DEBUG;_CONSOLE;%(PreprocessorDefinitions) 93 | true 94 | ../ 95 | stdcpplatest 96 | 97 | 98 | Console 99 | true 100 | ..\x64\Debug;%(AdditionalLibraryDirectories) 101 | kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;psh_host.lib;%(AdditionalDependencies) 102 | 103 | 104 | 105 | 106 | 107 | 108 | Level3 109 | Disabled 110 | true 111 | WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) 112 | true 113 | ../ 114 | stdcpplatest 115 | 116 | 117 | Console 118 | true 119 | ..\x64\Debug;%(AdditionalLibraryDirectories) 120 | 121 | 122 | 123 | 124 | 125 | 126 | Level3 127 | MaxSpeed 128 | true 129 | true 130 | true 131 | WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) 132 | true 133 | ../ 134 | stdcpplatest 135 | 136 | 137 | Console 138 | true 139 | true 140 | true 141 | ..\x64\Release;%(AdditionalLibraryDirectories) 142 | kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;psh_host.lib;%(AdditionalDependencies) 143 | 144 | 145 | 146 | 147 | 148 | 149 | Level3 150 | MaxSpeed 151 | true 152 | true 153 | true 154 | NDEBUG;_CONSOLE;%(PreprocessorDefinitions) 155 | true 156 | ../ 157 | stdcpplatest 158 | 159 | 160 | Console 161 | true 162 | true 163 | true 164 | ..\x64\Release;%(AdditionalLibraryDirectories) 165 | kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;psh_host.lib;%(AdditionalDependencies) 166 | 167 | 168 | 169 | 170 | 171 | 172 | 173 | {61714de2-4807-40cd-a5b3-9168550366f9} 174 | 175 | 176 | 177 | 178 | 179 | -------------------------------------------------------------------------------- /test_unit/test_unit.vcxproj.filters: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | {4FC737F1-C7A5-4376-A066-2A32D752A2FF} 6 | cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx 7 | 8 | 9 | {93995380-89BD-4b04-88EB-625FBE52EBFB} 10 | h;hh;hpp;hxx;hm;inl;inc;ipp;xsd 11 | 12 | 13 | {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} 14 | rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms 15 | 16 | 17 | 18 | 19 | Source Files 20 | 21 | 22 | -------------------------------------------------------------------------------- /test_unit/test_unit.vcxproj.user: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | ManagedOnly 5 | 6 | 7 | ManagedOnly 8 | 9 | -------------------------------------------------------------------------------- /utils/cpp_wrappers.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | #include "utils/macros.hpp" 8 | #include "utils/zero_resetable.hpp" 9 | 10 | 11 | extern "C" { 12 | unsigned char* NativePowerShell_MallocWrapper(unsigned long long size) { 13 | return (unsigned char*)NativePowerShell_DefaultAlloc(size); 14 | } 15 | } 16 | 17 | namespace native_powershell{ 18 | 19 | 20 | template 21 | void FreeFunction(T* ptr) { 22 | NativePowerShell_DefaultFree((void*)ptr); 23 | } 24 | 25 | inline 26 | std::wstring GetType(NativePowerShell_PowerShellObject handle); 27 | inline 28 | std::wstring GetToString(NativePowerShell_PowerShellObject handle); 29 | 30 | inline 31 | const wchar_t* MallocCopy(const wchar_t* str) 32 | { 33 | if (str == nullptr) 34 | return nullptr; 35 | 36 | size_t s = 0; 37 | for (; str[s] != '\0'; ++s) { 38 | } 39 | ++s; 40 | auto dest = (wchar_t*)NativePowerShell_DefaultAlloc(s * sizeof(str[0])); 41 | std::copy(str, str + s, dest); 42 | return (const wchar_t*)dest; 43 | } 44 | 45 | 46 | class Invoker { 47 | public: 48 | inline 49 | Invoker(NativePowerShell_PowerShellHandle handle) { 50 | exception = NativePowerShell_InvokeCommand(handle, &objects, &count); 51 | } 52 | DENY_COPY(Invoker); 53 | DEFAULT_MOVE(Invoker); 54 | inline 55 | ~Invoker() { 56 | for (unsigned int i = 0; i < count; ++i) { 57 | if (objectsToNotClose.size() == 0 || !objectsToNotClose[i]) { 58 | NativePowerShell_ClosePowerShellObject(objects[i]); 59 | } 60 | } 61 | if (objects != nullptr) { 62 | NativePowerShell_DefaultFree(objects); 63 | count = 0; 64 | } 65 | NativePowerShell_ClosePowerShellObject(exception); 66 | } 67 | inline 68 | const bool CallFailed() const { 69 | return exception.operator!=(NativePowerShell_InvalidHandleValue); 70 | } 71 | inline 72 | NativePowerShell_PowerShellObject operator[](unsigned int i) { 73 | return objects[i]; 74 | } 75 | inline 76 | NativePowerShell_PowerShellObject* results() { 77 | return objects; 78 | } 79 | inline 80 | void DoNotCloseIndex(unsigned int i) { 81 | if (objectsToNotClose.size() == 0) { 82 | objectsToNotClose.resize(count, false); 83 | } 84 | objectsToNotClose[i] = true; 85 | } 86 | ZeroResetable< NativePowerShell_PowerShellObject*> objects; 87 | ZeroResetable< unsigned int> count; 88 | std::vector objectsToNotClose; 89 | ZeroResetable< NativePowerShell_PowerShellObject> exception; 90 | private: 91 | }; 92 | 93 | inline 94 | std::wstring CopyAndFree(const wchar_t* cStr) { 95 | if (cStr == nullptr) { 96 | throw "CopyAndFree got nullptr"; 97 | } 98 | std::wstring toRet(cStr); 99 | FreeFunction(cStr); 100 | return toRet; 101 | } 102 | 103 | inline 104 | std::wstring GetType(NativePowerShell_PowerShellObject handle) { 105 | if ('\0' == NativePowerShell_IsPSObjectNullptr(handle)) 106 | return CopyAndFree(NativePowerShell_GetPSObjectType(handle)); 107 | return L"nullptr"; 108 | } 109 | inline 110 | std::wstring GetToString(NativePowerShell_PowerShellObject handle) { 111 | if ('\0' == NativePowerShell_IsPSObjectNullptr(handle)) 112 | return CopyAndFree(NativePowerShell_GetPSObjectToString(handle)); 113 | return L"nullptr"; 114 | 115 | } 116 | 117 | inline 118 | Invoker RunScript(NativePowerShell_RunspaceHandle& runspace, std::optional parent, std::wstring command, bool useLocalScope, const std::vector& elems) { 119 | 120 | NativePowerShell_PowerShellHandle powershell; 121 | if (parent) { 122 | powershell = NativePowerShell_CreatePowerShellNested(*parent); 123 | } 124 | else { 125 | powershell = NativePowerShell_CreatePowerShell(runspace); 126 | } 127 | NativePowerShell_AddScriptSpecifyScope(powershell, command.c_str(), useLocalScope ? 1 : 0); 128 | for (auto& arg : elems) { 129 | NativePowerShell_AddArgument(powershell, arg.c_str()); 130 | } 131 | auto results = Invoker(powershell); 132 | 133 | NativePowerShell_DeletePowershell(powershell); 134 | return results; 135 | } 136 | 137 | inline 138 | Invoker RunScript(NativePowerShell_RunspaceHandle& runspace, std::optional parent, std::wstring command, bool useLocalScope) { 139 | return RunScript(runspace, parent, command, useLocalScope, std::vector({ })); 140 | } 141 | 142 | template 143 | Invoker RunScript(NativePowerShell_RunspaceHandle& runspace, std::optional parent, std::wstring command, bool useLocalScope, const std::wstring& elems, T&& ... t) { 144 | return RunScript(runspace, parent, command, useLocalScope, std::vector({ elems, std::forward(t)... })); 145 | } 146 | 147 | }; -------------------------------------------------------------------------------- /utils/macros.hpp: -------------------------------------------------------------------------------- 1 | 2 | #define DENY_COPY(T) T(T&)=delete ; T& operator=(T&) = delete; 3 | #define DEFAULT_MOVE(T) T(T&&)=default; T& operator=(T&&) = default; 4 | -------------------------------------------------------------------------------- /utils/managed_memory_helpers.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | 4 | template 5 | struct AutoDispose { 6 | gcroot t; 7 | bool freed = false; 8 | AutoDispose(const AutoDispose& obj) = delete; 9 | AutoDispose(T tParam) :t(tParam) {} 10 | AutoDispose() :freed(true) {} 11 | AutoDispose(AutoDispose&& rhs) 12 | { 13 | t = rhs.t; 14 | rhs.freed = true; 15 | } 16 | AutoDispose& operator=(AutoDispose&& p) 17 | { 18 | t = rhs.t; 19 | rhs.freed = true; 20 | } 21 | AutoDispose& operator=(AutoDispose& obj) = delete; 22 | void free() { 23 | 24 | if (!freed) { 25 | T toDelete = t; 26 | delete toDelete; 27 | freed = true; 28 | } 29 | 30 | } 31 | T operator->() { 32 | return t; 33 | } 34 | ~AutoDispose() 35 | { 36 | free(); 37 | } 38 | }; 39 | 40 | template 41 | AutoDispose MakeUsing(T t) { 42 | return AutoDispose(t); 43 | } -------------------------------------------------------------------------------- /utils/memory_helpers.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "utils/macros.hpp" 4 | #include "utils/zero_resetable.hpp" 5 | 6 | extern NativePowerShell_FreePointer NativePowerShell_FreePointerPtr; 7 | extern NativePowerShell_AllocPointer NativePowerShell_AllocPointerPtr; 8 | 9 | struct NativePowerShell_FreePointerHelper { 10 | template 11 | void operator()(T* t) { 12 | NativePowerShell_FreePointerPtr((void*)t); 13 | } 14 | }; 15 | 16 | template 17 | struct AutoDllFree { 18 | ZeroResetable t; 19 | DENY_COPY(AutoDllFree); 20 | DEFAULT_MOVE(AutoDllFree); 21 | AutoDllFree(T* tParam) { t = tParam; } 22 | AutoDllFree() :t(nullptr) {} 23 | void free() { 24 | 25 | if (t!=nullptr) { 26 | NativePowerShell_FreePointerPtr((void*)t.get()); 27 | t = nullptr; 28 | } 29 | 30 | } 31 | T* operator->() { 32 | return t; 33 | } 34 | operator T* () { 35 | return t; 36 | } 37 | T* get() { 38 | return t; 39 | } 40 | ~AutoDllFree() 41 | { 42 | free(); 43 | } 44 | }; 45 | 46 | template 47 | AutoDllFree MakeAutoDllFree(T* t) { 48 | return AutoDllFree(t); 49 | } 50 | 51 | -------------------------------------------------------------------------------- /utils/typed_handles.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | 4 | template 5 | T MakeHandle(X x) { 6 | return (T)x; 7 | } 8 | 9 | template 10 | long long GetHandle(T t) 11 | { 12 | return (long long)t; 13 | } 14 | 15 | 16 | template 17 | ref class ConcurrentTable { 18 | IndexType lastUsedIndex = 0; 19 | System::Collections::Concurrent::ConcurrentDictionary^ holder = gcnew System::Collections::Concurrent::ConcurrentDictionary(); 20 | public: 21 | ValueType get(IndexType index) { 22 | ValueType value; 23 | if (!holder->TryGetValue(index, value)) 24 | { 25 | throw "Key Not found"; 26 | } 27 | return value; 28 | } 29 | IndexType insert(ValueType value) { 30 | auto index = System::Threading::Interlocked::Increment(lastUsedIndex); 31 | holder[index] = value; 32 | return index; 33 | } 34 | ValueType remove(IndexType index) { 35 | ValueType value; 36 | if (!holder->TryRemove(index, value)) 37 | { 38 | throw "Key Not found"; 39 | } 40 | return value; 41 | } 42 | }; 43 | 44 | template 45 | ref class TypedConcurrentTable { 46 | ConcurrentTable^ holder = gcnew ConcurrentTable(); 47 | public: 48 | ValueType get(IndexType index) { 49 | return holder->get(GetHandle(index)); 50 | } 51 | IndexType insert(ValueType value) { 52 | return MakeHandle(holder->insert(value)); 53 | } 54 | ValueType remove(IndexType index) { 55 | return holder->remove(GetHandle(index)); 56 | } 57 | }; 58 | 59 | 60 | 61 | -------------------------------------------------------------------------------- /utils/zero_resetable.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | template 4 | class ZeroResetable { 5 | T var; 6 | public: 7 | ZeroResetable() : var(T{ 0 }) {} 8 | ZeroResetable(ZeroResetable&& rhs) : var(rhs.var) { 9 | rhs.var = T{ 0 }; 10 | } 11 | ZeroResetable& operator=(ZeroResetable&& rhs) { 12 | var = rhs.var; 13 | rhs.var = T{ 0 }; 14 | return *this; 15 | } 16 | ZeroResetable& operator=(T& rhs) { 17 | var = rhs; 18 | return *this; 19 | } 20 | ZeroResetable& operator=(T&& rhs) { 21 | var = rhs; 22 | return *this; 23 | } 24 | ~ZeroResetable() { 25 | var = T{ 0 }; 26 | } 27 | operator T& () { return var; } 28 | operator const T& ()const { return var; } 29 | T& get() { return var; } 30 | template 31 | auto operator[](Y i) { 32 | return var[i]; 33 | } 34 | T* operator->() { return &var; } 35 | //auto operator*() { return *var; } 36 | T* operator&() { return &var; } 37 | const bool operator!=(T t) const { return var != t; } 38 | }; 39 | --------------------------------------------------------------------------------