forked from ropnop/go-clr
-
Notifications
You must be signed in to change notification settings - Fork 12
/
iclrmetahost.go
187 lines (169 loc) · 6.96 KB
/
iclrmetahost.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
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
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
// +build windows
package clr
import (
"fmt"
"syscall"
"unsafe"
"golang.org/x/sys/windows"
)
// Couldnt have done any of this without this SO answer I stumbled on:
// https://stackoverflow.com/questions/37781676/how-to-use-com-component-object-model-in-golang
//ICLRMetaHost Interface from metahost.h
type ICLRMetaHost struct {
vtbl *ICLRMetaHostVtbl
}
// ICLRMetaHostVtbl provides methods that return a specific version of the common language runtime (CLR)
// based on its version number, list all installed CLRs, list all runtimes that are loaded in a specified
// process, discover the CLR version used to compile an assembly, exit a process with a clean runtime
// shutdown, and query legacy API binding.
// https://docs.microsoft.com/en-us/dotnet/framework/unmanaged-api/hosting/iclrmetahost-interface
type ICLRMetaHostVtbl struct {
QueryInterface uintptr
AddRef uintptr
Release uintptr
// GetRuntime gets the ICLRRuntimeInfo interface that corresponds to a particular CLR version.
// This method supersedes the CorBindToRuntimeEx function used with the STARTUP_LOADER_SAFEMODE flag.
GetRuntime uintptr
// GetVersionFromFile gets the assembly's original .NET Framework compilation version (stored in the metadata),
// given its file path. This method supersedes GetFileVersion.
GetVersionFromFile uintptr
// EnumerateInstalledRuntimes returns an enumeration that contains a valid ICLRRuntimeInfo interface
// pointer for each CLR version that is installed on a computer.
EnumerateInstalledRuntimes uintptr
// EnumerateLoadedRuntimes returns an enumeration that contains a valid ICLRRuntimeInfo interface
// pointer for each CLR that is loaded in a given process. This method supersedes GetVersionFromProcess.
EnumerateLoadedRuntimes uintptr
// RequestRuntimeLoadedNotification guarantees a callback to the specified function pointer when a
// CLR version is first loaded, but not yet started. This method supersedes LockClrVersion
RequestRuntimeLoadedNotification uintptr
// QueryLegacyV2RuntimeBinding returns an interface that represents a runtime to which legacy activation policy
// has been bound, for example by using the useLegacyV2RuntimeActivationPolicy attribute on the <startup> Element
// configuration file entry, by direct use of the legacy activation APIs, or by calling the
// ICLRRuntimeInfo::BindAsLegacyV2Runtime method.
QueryLegacyV2RuntimeBinding uintptr
// ExitProcess attempts to shut down all loaded runtimes gracefully and then terminates the process.
ExitProcess uintptr
}
// CLRCreateInstance provides one of three interfaces: ICLRMetaHost, ICLRMetaHostPolicy, or ICLRDebugging.
// HRESULT CLRCreateInstance(
// [in] REFCLSID clsid,
// [in] REFIID riid,
// [out] LPVOID * ppInterface
// );
// https://docs.microsoft.com/en-us/dotnet/framework/unmanaged-api/hosting/clrcreateinstance-function
func CLRCreateInstance(clsid, riid windows.GUID) (ppInterface *ICLRMetaHost, err error) {
debugPrint("Entering into iclrmetahost.CLRCreateInstance()...")
if clsid != CLSID_CLRMetaHost {
err = fmt.Errorf("the input Class ID (CLSID) is not supported: %s", clsid)
return
}
modMSCoree := syscall.MustLoadDLL("mscoree.dll")
procCLRCreateInstance := modMSCoree.MustFindProc("CLRCreateInstance")
// For some reason this procedure call returns "The specified procedure could not be found." even though it works
hr, _, err := procCLRCreateInstance.Call(
uintptr(unsafe.Pointer(&clsid)),
uintptr(unsafe.Pointer(&riid)),
uintptr(unsafe.Pointer(&ppInterface)),
)
if err != nil {
// TODO Figure out why "The specified procedure could not be found." is returned even though everything works fine?
debugPrint(fmt.Sprintf("the mscoree!CLRCreateInstance function returned an error:\r\n%s", err))
}
if hr != S_OK {
err = fmt.Errorf("the mscoree!CLRCreateInstance function returned a non-zero HRESULT: 0x%x", hr)
return
}
err = nil
return
}
func (obj *ICLRMetaHost) QueryInterface(riid windows.GUID, ppvObject unsafe.Pointer) error {
debugPrint("Entering into icorruntimehost.QueryInterface()...")
hr, _, err := syscall.Syscall(
obj.vtbl.QueryInterface,
3,
uintptr(unsafe.Pointer(obj)),
uintptr(unsafe.Pointer(&riid)), // A reference to the interface identifier (IID) of the interface being queried for.
uintptr(ppvObject),
)
if err != syscall.Errno(0) {
return fmt.Errorf("the IUknown::QueryInterface method returned an error:\r\n%s", err)
}
if hr != S_OK {
return fmt.Errorf("the IUknown::QueryInterface method method returned a non-zero HRESULT: 0x%x", hr)
}
return nil
}
func (obj *ICLRMetaHost) AddRef() uintptr {
ret, _, _ := syscall.Syscall(
obj.vtbl.AddRef,
1,
uintptr(unsafe.Pointer(obj)),
0,
0)
return ret
}
func (obj *ICLRMetaHost) Release() uintptr {
ret, _, _ := syscall.Syscall(
obj.vtbl.Release,
1,
uintptr(unsafe.Pointer(obj)),
0,
0)
return ret
}
// EnumerateInstalledRuntimes returns an enumeration that contains a valid ICLRRuntimeInfo interface for each
// version of the common language runtime (CLR) that is installed on a computer.
// HRESULT EnumerateInstalledRuntimes (
// [out, retval] IEnumUnknown **ppEnumerator);
// https://docs.microsoft.com/en-us/dotnet/framework/unmanaged-api/hosting/iclrmetahost-enumerateinstalledruntimes-method
func (obj *ICLRMetaHost) EnumerateInstalledRuntimes() (ppEnumerator *IEnumUnknown, err error) {
debugPrint("Entering into iclrmetahost.EnumerateInstalledRuntimes()...")
hr, _, err := syscall.Syscall(
obj.vtbl.EnumerateInstalledRuntimes,
2,
uintptr(unsafe.Pointer(obj)),
uintptr(unsafe.Pointer(&ppEnumerator)),
0,
)
if err != syscall.Errno(0) {
err = fmt.Errorf("there was an error calling the ICLRMetaHost::EnumerateInstalledRuntimes method:\r\n%s", err)
return
}
if hr != S_OK {
err = fmt.Errorf("the ICLRMetaHost::EnumerateInstalledRuntimes method returned a non-zero HRESULT: 0x%x", hr)
return
}
err = nil
return
}
// GetRuntime gets the ICLRRuntimeInfo interface that corresponds to a particular version of the common language runtime (CLR).
// This method supersedes the CorBindToRuntimeEx function used with the STARTUP_LOADER_SAFEMODE flag.
// HRESULT GetRuntime (
// [in] LPCWSTR pwzVersion,
// [in] REFIID riid,
// [out,iid_is(riid), retval] LPVOID *ppRuntime
// );
// https://docs.microsoft.com/en-us/dotnet/framework/unmanaged-api/hosting/iclrmetahost-getruntime-method
func (obj *ICLRMetaHost) GetRuntime(pwzVersion *uint16, riid windows.GUID) (ppRuntime *ICLRRuntimeInfo, err error) {
debugPrint("Entering into iclrmetahost.GetRuntime()...")
hr, _, err := syscall.Syscall6(
obj.vtbl.GetRuntime,
4,
uintptr(unsafe.Pointer(obj)),
uintptr(unsafe.Pointer(pwzVersion)),
uintptr(unsafe.Pointer(&IID_ICLRRuntimeInfo)),
uintptr(unsafe.Pointer(&ppRuntime)),
0,
0,
)
if err != syscall.Errno(0) {
err = fmt.Errorf("there was an error calling the ICLRMetaHost::GetRuntime method:\r\n%s", err)
return
}
if hr != S_OK {
err = fmt.Errorf("the ICLRMetaHost::GetRuntime method returned a non-zero HRESULT: 0x%x", hr)
return
}
err = nil
return
}