-
Notifications
You must be signed in to change notification settings - Fork 47
/
marshal.h
executable file
·414 lines (348 loc) · 15.4 KB
/
marshal.h
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
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
/***
*marshal.h
*
* Copyright (c) Microsoft Corporation. All rights reserved.
*
*Purpose: Marshalling classes
*
* [Public]
*
****/
#pragma once
#ifndef _INC_MSCLR_MARSHAL
#define _INC_MSCLR_MARSHAL
// #using is not used in the Windows build.
#if !defined _CRT_WINDOWS && !defined UNDOCKED_WINDOWS_UCRT
#using <system.dll>
#endif
#include <Windows.h>
#include <errno.h>
#include <memory>
#include <sal.h>
#include <stdlib.h>
#include <string.h>
#include <type_traits>
#include <vcclr.h>
#pragma comment(lib, "comsupp.lib")
#pragma comment(lib, "oleaut32.lib")
#pragma warning(push)
#pragma warning(error : 4996)
namespace msclr{
namespace interop{
namespace details
{
#define _EXCEPTION_GREATER_THAN_INT_MAX "Size of string exceeds INT_MAX."
#define _EXCEPTION_NULLPTR "NULLPTR is not supported for this conversion."
#define _EXCEPTION_MB2WC "Conversion from MultiByte to WideChar failed. Please check the content of the string and/or locale settings."
#define _EXCEPTION_WC2MB "Conversion from WideChar to MultiByte failed. Please check the content of the string and/or locale settings."
// helper class to allocate/deallocate a buffer with new[]/delete[]
template <class T>
class char_buffer
{
public:
explicit char_buffer(size_t _size)
{
_ptr = new T[_size];
}
~char_buffer()
{
delete [] _ptr;
}
T *get() const
{
return _ptr;
}
T* release()
{
T *_ret_value = _ptr;
_ptr = NULL;
return _ret_value;
}
private:
// no copy constructor nor operator=
char_buffer(const char_buffer&);
char_buffer& operator=(const char_buffer&);
T *_ptr;
};
inline _Check_return_ size_t GetAnsiStringSize(System::String^ _str)
{
cli::pin_ptr<const wchar_t> _pinned_ptr = PtrToStringChars(_str);
auto _size = static_cast<size_t>(
::WideCharToMultiByte(CP_THREAD_ACP, WC_NO_BEST_FIT_CHARS, _pinned_ptr,
_str->Length, NULL, 0, NULL,NULL));
if (_size == 0 && _str->Length != 0)
{
throw gcnew System::ArgumentException(_EXCEPTION_WC2MB);
}
// adding 1 for terminating nul
_size+=1;
return _size;
}
inline void WriteAnsiString(_Out_writes_all_(_size) _Post_z_ char* _buf, size_t _size, System::String^ _str)
{
cli::pin_ptr<const wchar_t> _pinned_ptr = PtrToStringChars(_str);
//checking for overflow
if (_size > INT_MAX)
{
// this should never happen if _size was returned by GetAnsiStringSize()
throw gcnew System::ArgumentOutOfRangeException(_EXCEPTION_GREATER_THAN_INT_MAX);
}
const auto _written = static_cast<size_t>(::WideCharToMultiByte(CP_THREAD_ACP, WC_NO_BEST_FIT_CHARS,
_pinned_ptr, _str->Length, _buf, static_cast<int>(_size), NULL, NULL));
if( _written >= _size || (_written == 0 && _size != 1)) // allowing empty string
{
throw gcnew System::ArgumentException(_EXCEPTION_WC2MB);
}
_buf[_written] = '\0';
}
inline _Check_return_ size_t GetUnicodeStringSize(_In_reads_z_(_count+1) const char* _str, size_t _count)
{
if (_count > INT_MAX)
{
throw gcnew System::ArgumentOutOfRangeException(_EXCEPTION_GREATER_THAN_INT_MAX);
}
auto _size = static_cast<size_t>(::MultiByteToWideChar(CP_THREAD_ACP, 0, _str, static_cast<int>(_count), NULL, 0));
if (_size == 0 && _count != 0)
{
throw gcnew System::ArgumentException(_EXCEPTION_MB2WC);
}
//adding 1 for terminating nul
_size+=1;
return _size;
}
inline void WriteUnicodeString(_Out_writes_all_(_size) _Post_z_ wchar_t* _dest, size_t _size, _In_reads_bytes_(_count)const char* _src, size_t _count)
{
//checking for overflow
if (_size > INT_MAX || _count > INT_MAX)
{
throw gcnew System::ArgumentOutOfRangeException(_EXCEPTION_GREATER_THAN_INT_MAX);
}
size_t _written = static_cast<size_t>(::MultiByteToWideChar(CP_THREAD_ACP, 0, _src,
static_cast<int>(_count), _dest, static_cast<int>(_size)));
if( _written >= _size || (_written == 0 && _size != 1)) // allowing empty string
{
throw gcnew System::ArgumentException(_EXCEPTION_MB2WC);
}
_dest[_written] = L'\0';
}
inline System::String^ InternalAnsiToStringHelper(_In_reads_z_(_count+1)const char* _src, size_t _count)
{
const size_t _size = details::GetUnicodeStringSize(_src, _count);
if (_size > INT_MAX || _size <=0 )
{
throw gcnew System::ArgumentOutOfRangeException(_EXCEPTION_GREATER_THAN_INT_MAX);
}
details::char_buffer<wchar_t> _wchar_buf(_size);
if (_wchar_buf.get() == NULL)
{
throw gcnew System::InsufficientMemoryException();
}
details::WriteUnicodeString(_wchar_buf.get(), _size, _src, _count);
return gcnew System::String(_wchar_buf.get(), 0, static_cast<int>(_size)-1);
}
inline System::String^ InternalUnicodeToStringHelper(_In_reads_z_(_count+1)const wchar_t* _src, size_t _count)
{
if (_count > INT_MAX)
{
throw gcnew System::ArgumentOutOfRangeException(_EXCEPTION_GREATER_THAN_INT_MAX);
}
return gcnew System::String(_src, 0 ,static_cast<int>(_count));
}
} // namespace details
template<class _To_Type, class _From_Type>
ref class context_node;
template <class _To_Type, class _From_Type,
bool _Needs_Context = context_node<_To_Type,_From_Type>::_Needs_Context>
class error_reporting_helper;
template <class _To_Type, class _From_Type>
class error_reporting_helper<_To_Type, _From_Type, /* _Needs_Context */ false>
{
public:
__declspec(deprecated("This conversion is not supported by the library or the header file needed for this conversion is not included. Please refer to the documentation on 'How to: Extend the Marshaling Library' for adding your own marshaling method."))
static _To_Type marshal_as(const _From_Type&) = delete;
};
template <class _To_Type, class _From_Type>
class error_reporting_helper<_To_Type, _From_Type, /* _Needs_Context */ true>
{
public:
__declspec(deprecated("This conversion requires a marshal_context. Please use a marshal_context for this conversion."))
static _To_Type marshal_as(const _From_Type&) = delete;
};
template <class _To_Type, class _From_Type>
inline _To_Type _Marshal_as_impl(::std::false_type, const _From_Type& _from_object)
{
return error_reporting_helper<_To_Type, _From_Type>::marshal_as(_from_object);
}
template <class _From_Type>
inline System::String^ _Marshal_as_impl(::std::true_type, const char* _from_object)
{
if (!_from_object)
{
return nullptr;
}
return details::InternalAnsiToStringHelper(_from_object, strlen(_from_object));
}
template <class _From_Type>
inline System::String^ _Marshal_as_impl(::std::true_type, const wchar_t* _from_object)
{
if (!_from_object)
{
return nullptr;
}
return gcnew System::String(_from_object);
}
template <class _To_Type, class _From_Type>
inline _To_Type marshal_as(const _From_Type& _from_object)
{
using _Decayed = ::std::decay_t<_From_Type>;
return _Marshal_as_impl<_To_Type>(typename ::std::conjunction<
::std::is_same<System::String^, _To_Type>,
::std::disjunction<
::std::is_same<char*, _Decayed>,
::std::is_same<const char*, _Decayed>,
// Note: BSTR is actually a typedef for wchar_t*. As a result, BSTR will
// get accepted as a null terminated character type sequence here.
::std::is_same<wchar_t*, _Decayed>,
::std::is_same<const wchar_t*, _Decayed>
>
>::type{}, _from_object);
}
ref class context_node_base
{
public:
static const bool _Needs_Context= true;
};
template<class _To_Type, class _From_Type>
ref class context_node : public context_node_base
{
public:
static const bool _Needs_Context= false;
};
template<>
ref class context_node<const char*, System::String^> :
public context_node_base
{
private:
char* _ptr;
public:
context_node(_Outref_result_maybenull_ _Post_z_ const char*& _to_object, System::String^ _from_object)
{
_ptr = NULL;
if (_from_object == nullptr)
{
_to_object =nullptr;
return;
}
size_t _size = details::GetAnsiStringSize(_from_object);
details::char_buffer<char> _char_buf(_size);
if (_char_buf.get() == NULL)
{
throw gcnew System::InsufficientMemoryException();
}
details::WriteAnsiString(_char_buf.get(), _size, _from_object);
_ptr = _char_buf.release();
_to_object = _ptr;
}
~context_node()
{
this->!context_node();
}
protected:
!context_node()
{
delete [] _ptr;
}
};
template<>
ref class context_node<const wchar_t*, System::String^> :
public context_node_base
{
private:
System::IntPtr _ip;
public:
context_node(_Outref_ _Post_z_ const wchar_t*& _to_object, System::String^ _from_object)
{
_ip = System::Runtime::InteropServices::Marshal::StringToHGlobalUni (_from_object);
_to_object = static_cast<wchar_t*>(_ip.ToPointer());
}
~context_node()
{
this->!context_node();
}
protected:
!context_node()
{
if(_ip != System::IntPtr::Zero)
System::Runtime::InteropServices::Marshal::FreeHGlobal(_ip);
}
};
ref class marshal_context
{
internal:
//The list of objects that need to be cleaned up. Basically all those that have a destructor
System::Collections::Generic::LinkedList <Object^> _clean_up_list;
template<class _To_Type, class _From_Type, bool _Needs_Context>
ref class internal_marshaler;
template<class _To_Type, class _From_Type>
ref class internal_marshaler<_To_Type, _From_Type, true>
{
public:
// This is a supported context conversion.
// Convert and add it to the list of cleanup
static inline _To_Type marshal_as(const _From_Type& _from, System::Collections::Generic::LinkedList<Object^>% _clean_up_list)
{
typedef context_node<_To_Type, _From_Type> _cn;
_To_Type _to_object;
//Create a context node and assign marshalling result to the 'to' variable
_cn^ _obj = gcnew _cn(_to_object, _from);
_clean_up_list.AddLast(_obj);
return _to_object;
}
};
template<class _To_Type, class _From_Type>
ref class internal_marshaler<_To_Type, _From_Type, false>
{
public:
// This is not a supported context conversion.
// This may be a context-free conversion or an unsupported conversion.
// Both cases will be handled by the marshal_as object.
static inline _To_Type marshal_as(const _From_Type& _from, System::Collections::Generic::LinkedList<Object^>% _clean_up_list)
{
(void)_clean_up_list;
return ::msclr::interop::marshal_as<_To_Type, _From_Type>(_from);
}
};
public:
template<class _To_Type, class _From_Type>
inline _To_Type marshal_as(const _From_Type& _from)
{
using _Decayed = ::std::decay_t<_From_Type>;
using _From_adjusted =
::std::conditional_t<::std::is_same_v<_Decayed, const char*>
|| ::std::is_same_v<_Decayed, const wchar_t*>,
_Decayed, _From_Type>;
typedef context_node<_To_Type, _From_adjusted> _cn;
return internal_marshaler<_To_Type, _From_adjusted, _cn::_Needs_Context>::marshal_as(_from, _clean_up_list);
}
template<class _To_Type, class _From_Type>
inline _To_Type marshal_as(_From_Type^ _from)
{
typedef context_node<_To_Type, _From_Type^> _cn;
return internal_marshaler<_To_Type, _From_Type^, _cn::_Needs_Context>::marshal_as(_from, _clean_up_list);
}
template<class _To_Type>
inline _To_Type marshal_as(System::IntPtr _from)
{
return ::msclr::interop::marshal_as<_To_Type>(_from);
}
marshal_context() {}
~marshal_context()
{
for each(Object^ _obj in _clean_up_list)
delete _obj;
}
};
} //namespace interop
} //namespace msclr
#pragma warning(pop) // error:4996
#endif /* _INC_MSCLR_MARSHAL */