Flutter Engine
The Flutter Engine
Loading...
Searching...
No Matches
ffi_dynamic_library.cc
Go to the documentation of this file.
1// Copyright (c) 2019, the Dart project authors. Please see the AUTHORS file
2// for details. All rights reserved. Use of this source code is governed by a
3// BSD-style license that can be found in the LICENSE file.
4
6
7#include "platform/globals.h"
8#if defined(DART_HOST_OS_WINDOWS)
9#include <Psapi.h>
10#include <Windows.h>
11#include <combaseapi.h>
12#include <stdio.h>
13#include <tchar.h>
14#endif
15
17#include "vm/dart_api_impl.h"
18#include "vm/exceptions.h"
20#include "vm/native_entry.h"
21#include "vm/symbols.h"
22#include "vm/uri.h"
23#include "vm/zone_text_buffer.h"
24
25#if defined(DART_HOST_OS_LINUX) || defined(DART_HOST_OS_MACOS) || \
26 defined(DART_HOST_OS_ANDROID) || defined(DART_HOST_OS_FUCHSIA)
27#include <dlfcn.h>
28#endif
29
30namespace dart {
31
32#if defined(USING_SIMULATOR) || (defined(DART_PRECOMPILER) && !defined(TESTING))
33
34DART_NORETURN static void SimulatorUnsupported() {
35#if defined(USING_SIMULATOR)
37 "Not supported on simulated architectures.");
38#else
39 Exceptions::ThrowUnsupportedError("Not supported in precompiler.");
40#endif
41}
42
43DEFINE_NATIVE_ENTRY(Ffi_dl_open, 0, 1) {
44 SimulatorUnsupported();
45}
46DEFINE_NATIVE_ENTRY(Ffi_dl_processLibrary, 0, 0) {
47 SimulatorUnsupported();
48}
49DEFINE_NATIVE_ENTRY(Ffi_dl_executableLibrary, 0, 0) {
50 SimulatorUnsupported();
51}
52DEFINE_NATIVE_ENTRY(Ffi_dl_lookup, 1, 2) {
53 SimulatorUnsupported();
54}
55DEFINE_NATIVE_ENTRY(Ffi_dl_getHandle, 0, 1) {
56 SimulatorUnsupported();
57}
58DEFINE_NATIVE_ENTRY(Ffi_dl_close, 0, 1) {
59 SimulatorUnsupported();
60}
61DEFINE_NATIVE_ENTRY(Ffi_dl_providesSymbol, 0, 2) {
62 SimulatorUnsupported();
63}
64
65DEFINE_NATIVE_ENTRY(Ffi_GetFfiNativeResolver, 1, 0) {
66 SimulatorUnsupported();
67}
68
69#else // defined(USING_SIMULATOR) || \
70 // (defined(DART_PRECOMPILER) && !defined(TESTING))
71
72// If an error occurs populates |error| (if provided) with an error message
73// (caller must free this message when it is no longer needed).
74static void* LoadDynamicLibrary(const char* library_file,
75 char** error = nullptr) {
76 char* utils_error = nullptr;
77 void* handle = Utils::LoadDynamicLibrary(library_file, &utils_error);
78 if (utils_error != nullptr) {
79 if (error != nullptr) {
81 /*use malloc*/ nullptr, "Failed to load dynamic library '%s': %s",
82 library_file != nullptr ? library_file : "<process>", utils_error);
83 }
84 free(utils_error);
85 }
86 return handle;
87}
88
89#if defined(DART_HOST_OS_WINDOWS)
90// On windows, nullptr signals trying a lookup in all loaded modules.
91const nullptr_t kWindowsDynamicLibraryProcessPtr = nullptr;
92
93void* co_task_mem_allocated = nullptr;
94
95// If an error occurs populates |error| with an error message
96// (caller must free this message when it is no longer needed).
97void* LookupSymbolInProcess(const char* symbol, char** error) {
98 // Force loading ole32.dll.
99 if (co_task_mem_allocated == nullptr) {
100 co_task_mem_allocated = CoTaskMemAlloc(sizeof(intptr_t));
101 CoTaskMemFree(co_task_mem_allocated);
102 }
103
104 HANDLE current_process =
105 OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE,
106 GetCurrentProcessId());
107 if (current_process == nullptr) {
108 *error = OS::SCreate(nullptr, "Failed to open current process.");
109 return nullptr;
110 }
111
112 HMODULE modules[1024];
113 DWORD cb_needed;
114 if (EnumProcessModules(current_process, modules, sizeof(modules),
115 &cb_needed) != 0) {
116 for (intptr_t i = 0; i < (cb_needed / sizeof(HMODULE)); i++) {
117 if (auto result =
118 reinterpret_cast<void*>(GetProcAddress(modules[i], symbol))) {
119 CloseHandle(current_process);
120 return result;
121 }
122 }
123 }
124 CloseHandle(current_process);
125
127 nullptr, // Use `malloc`.
128 "None of the loaded modules contained the requested symbol '%s'.",
129 symbol);
130 return nullptr;
131}
132#endif
133
134// If an error occurs populates |error| with an error message
135// (caller must free this message when it is no longer needed).
136static void* ResolveSymbol(void* handle, const char* symbol, char** error) {
137#if defined(DART_HOST_OS_WINDOWS)
138 if (handle == kWindowsDynamicLibraryProcessPtr) {
139 return LookupSymbolInProcess(symbol, error);
140 }
141#endif
142 return Utils::ResolveSymbolInDynamicLibrary(handle, symbol, error);
143}
144
145static bool SymbolExists(void* handle, const char* symbol) {
146 char* error = nullptr;
147#if !defined(DART_HOST_OS_WINDOWS)
149#else
150 if (handle == nullptr) {
151 LookupSymbolInProcess(symbol, &error);
152 } else {
154 }
155#endif
156 if (error != nullptr) {
157 free(error);
158 return false;
159 }
160 return true;
161}
162
163DEFINE_NATIVE_ENTRY(Ffi_dl_open, 0, 1) {
164 GET_NON_NULL_NATIVE_ARGUMENT(String, lib_path, arguments->NativeArgAt(0));
165
166 char* error = nullptr;
167 void* handle = LoadDynamicLibrary(lib_path.ToCString(), &error);
168 if (error != nullptr) {
169 const String& msg = String::Handle(String::New(error));
170 free(error);
172 }
173 return DynamicLibrary::New(handle, true);
174}
175
176DEFINE_NATIVE_ENTRY(Ffi_dl_processLibrary, 0, 0) {
177#if defined(DART_HOST_OS_LINUX) || defined(DART_HOST_OS_MACOS) || \
178 defined(DART_HOST_OS_ANDROID) || defined(DART_HOST_OS_FUCHSIA)
179 return DynamicLibrary::New(RTLD_DEFAULT, false);
180#else
181 return DynamicLibrary::New(kWindowsDynamicLibraryProcessPtr, false);
182#endif
183}
184
185DEFINE_NATIVE_ENTRY(Ffi_dl_executableLibrary, 0, 0) {
186 return DynamicLibrary::New(LoadDynamicLibrary(nullptr), false);
187}
188
189DEFINE_NATIVE_ENTRY(Ffi_dl_close, 0, 1) {
190 GET_NON_NULL_NATIVE_ARGUMENT(DynamicLibrary, dlib, arguments->NativeArgAt(0));
191 if (dlib.IsClosed()) {
192 // Already closed, nothing to do
193 } else if (!dlib.CanBeClosed()) {
194 const String& msg = String::Handle(
195 String::New("DynamicLibrary.process() and DynamicLibrary.executable() "
196 "can't be closed."));
198 } else {
199 void* handle = dlib.GetHandle();
200 char* error = nullptr;
202
203 if (error == nullptr) {
204 dlib.SetClosed(true);
205 } else {
206 const String& msg = String::Handle(String::New(error));
207 free(error);
209 }
210 }
211
212 return Object::null();
213}
214
215DEFINE_NATIVE_ENTRY(Ffi_dl_lookup, 1, 2) {
216 GET_NON_NULL_NATIVE_ARGUMENT(DynamicLibrary, dlib, arguments->NativeArgAt(0));
218 arguments->NativeArgAt(1));
219
220 if (dlib.IsClosed()) {
221 const String& msg =
222 String::Handle(String::New("Cannot lookup symbols in closed library."));
224 }
225
226 void* handle = dlib.GetHandle();
227
228 char* error = nullptr;
229 const uword pointer = reinterpret_cast<uword>(
230 ResolveSymbol(handle, argSymbolName.ToCString(), &error));
231 if (error != nullptr) {
233 "Failed to lookup symbol '%s': %s", argSymbolName.ToCString(), error));
234 free(error);
236 }
237 return Pointer::New(pointer);
238}
239
240DEFINE_NATIVE_ENTRY(Ffi_dl_getHandle, 0, 1) {
241 GET_NON_NULL_NATIVE_ARGUMENT(DynamicLibrary, dlib, arguments->NativeArgAt(0));
242
243 intptr_t handle = reinterpret_cast<intptr_t>(dlib.GetHandle());
244 return Integer::NewFromUint64(handle);
245}
246
247DEFINE_NATIVE_ENTRY(Ffi_dl_providesSymbol, 0, 2) {
248 GET_NON_NULL_NATIVE_ARGUMENT(DynamicLibrary, dlib, arguments->NativeArgAt(0));
250 arguments->NativeArgAt(1));
251
252 void* handle = dlib.GetHandle();
253 return Bool::Get(SymbolExists(handle, argSymbolName.ToCString())).ptr();
254}
255
256// nullptr if no native resolver is installed.
258 const String& lib_url_str) {
259 const Library& lib =
260 Library::Handle(Library::LookupLibrary(thread, lib_url_str));
261 if (lib.IsNull()) {
262 // It is not an error to not have a native resolver installed.
263 return nullptr;
264 }
265 return lib.ffi_native_resolver();
266}
267
268// If an error occurs populates |error| with an error message
269// (caller must free this message when it is no longer needed).
270static void* FfiResolveWithFfiNativeResolver(Thread* const thread,
271 Dart_FfiNativeResolver resolver,
272 const String& symbol,
273 intptr_t args_n,
274 char** error) {
275 auto* result = resolver(symbol.ToCString(), args_n);
276 if (result == nullptr) {
277 *error = OS::SCreate(/*use malloc*/ nullptr,
278 "Couldn't resolve function: '%s'", symbol.ToCString());
279 }
280 return result;
281}
282
283#if defined(DART_TARGET_OS_WINDOWS)
284// Replaces back slashes with forward slashes in place.
285static void ReplaceBackSlashes(char* cstr) {
286 const intptr_t length = strlen(cstr);
287 for (int i = 0; i < length; i++) {
288 cstr[i] = cstr[i] == '\\' ? '/' : cstr[i];
289 }
290}
291#endif
292
293const char* file_schema = "file://";
294const int file_schema_length = 7;
295
296// Get a file path with only forward slashes from the script path.
297static StringPtr GetPlatformScriptPath(Thread* thread) {
298 IsolateGroupSource* const source = thread->isolate_group()->source();
299
300#if defined(DART_TARGET_OS_WINDOWS)
301 // Isolate.spawnUri sets a `source` including the file schema.
302 // And on Windows we get an extra forward slash in that case.
303 const char* file_schema_slash = "file:///";
304 const int file_schema_slash_length = 8;
305 const char* path = source->script_uri;
306 if (strlen(source->script_uri) > file_schema_slash_length &&
307 strncmp(source->script_uri, file_schema_slash,
308 file_schema_slash_length) == 0) {
309 path = (source->script_uri + file_schema_slash_length);
310 }
311
312 // Replace backward slashes with forward slashes.
313 const intptr_t len = strlen(path);
314 char* path_copy = reinterpret_cast<char*>(malloc(len + 1));
315 snprintf(path_copy, len + 1, "%s", path);
316 ReplaceBackSlashes(path_copy);
318 free(path_copy);
319 return result.ptr();
320#else
321 // Isolate.spawnUri sets a `source` including the file schema.
322 if (strlen(source->script_uri) > file_schema_length &&
323 strncmp(source->script_uri, file_schema, file_schema_length) == 0) {
324 const char* path = (source->script_uri + file_schema_length);
325 return String::New(path);
326 }
327 return String::New(source->script_uri);
328#endif
329}
330
331// Array::null if asset is not in mapping or no mapping.
332static ArrayPtr GetAssetLocation(Thread* const thread, const String& asset) {
333 Zone* const zone = thread->zone();
334 auto& result = Array::Handle(zone);
335
336 const auto& native_assets_map =
337 Array::Handle(zone, GetNativeAssetsMap(thread));
338 if (!native_assets_map.IsNull()) {
339 NativeAssetsMap map(native_assets_map.ptr());
340 const auto& lookup = Object::Handle(zone, map.GetOrNull(asset));
341 if (!lookup.IsNull()) {
342 result = Array::Cast(lookup).ptr();
343 }
344 map.Release();
345 }
346 return result.ptr();
347}
348
349// String is zone allocated.
350static char* AvailableAssetsToCString(Thread* const thread) {
351 Zone* const zone = thread->zone();
352
353 const auto& native_assets_map =
354 Array::Handle(zone, GetNativeAssetsMap(thread));
355 ZoneTextBuffer buffer(zone, 1024);
356
357 if (native_assets_map.IsNull()) {
358 buffer.Printf("No available native assets.");
359 } else {
360 bool first = true;
361 buffer.Printf("Available native assets: ");
362 NativeAssetsMap map(native_assets_map.ptr());
363 NativeAssetsMap::Iterator it(&map);
364 auto& asset_id = String::Handle(zone);
365 while (it.MoveNext()) {
366 if (!first) {
367 buffer.Printf(" ,");
368 }
369 auto entry = it.Current();
370 asset_id ^= map.GetKey(entry);
371 buffer.Printf("%s", asset_id.ToCString());
372 }
373 buffer.Printf(".");
374 map.Release();
375 }
376 return buffer.buffer();
377}
378
379// If an error occurs populates |error| with an error message
380// (caller must free this message when it is no longer needed).
381//
382// The |asset_location| is formatted as follows:
383// ['<path_type>', '<path (optional)>']
384// The |asset_location| is conform to: pkg/vm/lib/native_assets/validator.dart
385static void* FfiResolveAsset(Thread* const thread,
386 const Array& asset_location,
387 const String& symbol,
388 char** error) {
389 Zone* const zone = thread->zone();
390
391 const auto& asset_type =
392 String::Cast(Object::Handle(zone, asset_location.At(0)));
393 String& path = String::Handle(zone);
394 if (asset_type.Equals(Symbols::absolute()) ||
395 asset_type.Equals(Symbols::relative()) ||
396 asset_type.Equals(Symbols::system())) {
397 path = String::RawCast(asset_location.At(1));
398 }
399 void* handle = nullptr;
400 if (asset_type.Equals(Symbols::absolute())) {
401 handle = LoadDynamicLibrary(path.ToCString(), error);
402 } else if (asset_type.Equals(Symbols::relative())) {
403 const auto& platform_script_uri = String::Handle(
404 zone,
406 "%s%s", file_schema,
407 String::Handle(zone, GetPlatformScriptPath(thread)).ToCString()));
408 const char* target_uri = nullptr;
409 char* path_cstr = path.ToMallocCString();
410#if defined(DART_TARGET_OS_WINDOWS)
411 ReplaceBackSlashes(path_cstr);
412#endif
413 const bool resolved =
414 ResolveUri(path_cstr, platform_script_uri.ToCString(), &target_uri);
415 free(path_cstr);
416 if (!resolved) {
417 *error = OS::SCreate(/*use malloc*/ nullptr,
418 "Failed to resolve '%s' relative to '%s'.",
419 path.ToCString(), platform_script_uri.ToCString());
420 } else {
421 const char* target_path = target_uri + file_schema_length;
422 handle = LoadDynamicLibrary(target_path, error);
423 }
424 } else if (asset_type.Equals(Symbols::system())) {
425 handle = LoadDynamicLibrary(path.ToCString(), error);
426 } else if (asset_type.Equals(Symbols::process())) {
427#if defined(DART_HOST_OS_LINUX) || defined(DART_HOST_OS_MACOS) || \
428 defined(DART_HOST_OS_ANDROID) || defined(DART_HOST_OS_FUCHSIA)
429 handle = RTLD_DEFAULT;
430#else
431 handle = kWindowsDynamicLibraryProcessPtr;
432#endif
433 } else if (asset_type.Equals(Symbols::executable())) {
434 handle = LoadDynamicLibrary(nullptr, error);
435 } else {
436 UNREACHABLE();
437 }
438 if (*error != nullptr) {
439 char* inner_error = *error;
440 *error = OS::SCreate(/*use malloc*/ nullptr,
441 "Failed to load dynamic library '%s': %s",
442 path.ToCString(), inner_error);
443 free(inner_error);
444 } else {
445 void* const result = ResolveSymbol(handle, symbol.ToCString(), error);
446 if (*error != nullptr) {
447 char* inner_error = *error;
448 *error = OS::SCreate(/*use malloc*/ nullptr,
449 "Failed to lookup symbol '%s': %s",
450 symbol.ToCString(), inner_error);
451 free(inner_error);
452 } else {
453 return result;
454 }
455 }
456 ASSERT(*error != nullptr);
457 return nullptr;
458}
459
460// Frees |error|.
461static void ThrowFfiResolveError(const String& symbol,
462 const String& asset,
463 char* error) {
464 const String& error_message = String::Handle(String::NewFormatted(
465 "Couldn't resolve native function '%s' in '%s' : %s.\n",
466 symbol.ToCString(), asset.ToCString(), error));
467 free(error);
468 Exceptions::ThrowArgumentError(error_message);
469}
470
471intptr_t FfiResolveInternal(const String& asset,
472 const String& symbol,
473 uintptr_t args_n,
474 char** error) {
475 Thread* thread = Thread::Current();
476 Zone* zone = thread->zone();
477
478 // Resolver resolution.
479 auto resolver = GetFfiNativeResolver(thread, asset);
480 if (resolver != nullptr) {
481 void* ffi_native_result = FfiResolveWithFfiNativeResolver(
482 thread, resolver, symbol, args_n, error);
483 return reinterpret_cast<intptr_t>(ffi_native_result);
484 }
485
486 // Native assets resolution.
487 const auto& asset_location =
488 Array::Handle(zone, GetAssetLocation(thread, asset));
489 if (!asset_location.IsNull()) {
490 void* asset_result = FfiResolveAsset(thread, asset_location, symbol, error);
491 return reinterpret_cast<intptr_t>(asset_result);
492 }
493
494 // Resolution in current process.
495#if !defined(DART_HOST_OS_WINDOWS)
497 RTLD_DEFAULT, symbol.ToCString(), error);
498#else
499 void* const result = LookupSymbolInProcess(symbol.ToCString(), error);
500#endif
501
502 if (*error != nullptr) {
503 // Process lookup failed, but the user might have tried to use native
504 // asset lookup. So augment the error message to include native assets info.
505 char* process_lookup_error = *error;
506 *error = OS::SCreate(/*use malloc*/ nullptr,
507 "No asset with id '%s' found. %s "
508 "Attempted to fallback to process lookup. %s",
509 asset.ToCString(), AvailableAssetsToCString(thread),
510 process_lookup_error);
511 free(process_lookup_error);
512 }
513
514 return reinterpret_cast<intptr_t>(result);
515}
516
517// FFI native C function pointer resolver.
518static intptr_t FfiResolve(Dart_Handle asset_handle,
519 Dart_Handle symbol_handle,
520 uintptr_t args_n) {
521 auto* const thread = Thread::Current();
522 DARTSCOPE(thread);
523 auto* const zone = thread->zone();
524 const String& asset = Api::UnwrapStringHandle(zone, asset_handle);
525 const String& symbol = Api::UnwrapStringHandle(zone, symbol_handle);
526 char* error = nullptr;
527
528 const intptr_t result = FfiResolveInternal(asset, symbol, args_n, &error);
529 if (error != nullptr) {
530 ThrowFfiResolveError(symbol, asset, error);
531 }
532 ASSERT(result != 0x0);
533 return result;
534}
535
536// Bootstrap to get the FFI Native resolver through a `native` call.
537DEFINE_NATIVE_ENTRY(Ffi_GetFfiNativeResolver, 1, 0) {
538 return Pointer::New(reinterpret_cast<intptr_t>(FfiResolve));
539}
540
541#endif // defined(USING_SIMULATOR) || \
542 // (defined(DART_PRECOMPILER) && !defined(TESTING))
543
544} // namespace dart
#define UNREACHABLE()
Definition assert.h:248
static const String & UnwrapStringHandle(const ReusableObjectHandleScope &reused, Dart_Handle object)
ObjectPtr At(intptr_t index) const
Definition object.h:10854
static const Bool & Get(bool value)
Definition object.h:10780
static DynamicLibraryPtr New(void *handle, bool canBeClosed, Heap::Space space=Heap::kNew)
Definition object.cc:25828
static DART_NORETURN void ThrowStateError(const Instance &arg)
static DART_NORETURN void ThrowUnsupportedError(const char *msg)
static DART_NORETURN void ThrowArgumentError(const Instance &arg)
static IntegerPtr NewFromUint64(uint64_t value, Heap::Space space=Heap::kNew)
Definition object.cc:23105
IsolateGroupSource * source() const
Definition isolate.h:285
static LibraryPtr LookupLibrary(Thread *thread, const String &url)
Definition object.cc:14646
Dart_FfiNativeResolver ffi_native_resolver() const
Definition object.h:5221
static char * SCreate(Zone *zone, const char *format,...) PRINTF_ATTRIBUTE(2
static ObjectPtr null()
Definition object.h:433
ObjectPtr ptr() const
Definition object.h:332
bool IsNull() const
Definition object.h:363
static Object & Handle()
Definition object.h:407
static ObjectPtr RawCast(ObjectPtr obj)
Definition object.h:325
static PointerPtr New(uword native_address, Heap::Space space=Heap::kNew)
Definition object.cc:25805
static StringPtr NewFormatted(const char *format,...) PRINTF_ATTRIBUTE(1
Definition object.cc:24083
static StringPtr New(const char *cstr, Heap::Space space=Heap::kNew)
Definition object.cc:23777
static const char * ToCString(Thread *thread, StringPtr ptr)
Definition object.cc:24205
Zone * zone() const
static Thread * Current()
Definition thread.h:361
IsolateGroup * isolate_group() const
Definition thread.h:540
static void * LoadDynamicLibrary(const char *library_path, char **error=nullptr)
Definition utils.cc:293
static void UnloadDynamicLibrary(void *library_handle, char **error=nullptr)
Definition utils.cc:352
static void * ResolveSymbolInDynamicLibrary(void *library_handle, const char *symbol, char **error=nullptr)
Definition utils.cc:326
struct _Dart_Handle * Dart_Handle
Definition dart_api.h:258
void *(* Dart_FfiNativeResolver)(const char *name, uintptr_t args_n)
Definition dart_api.h:3253
#define DARTSCOPE(thread)
#define ASSERT(E)
SkBitmap source
Definition examples.cpp:28
static const uint8_t buffer[]
const uint8_t uint32_t uint32_t GError ** error
GAsyncResult * result
size_t length
return FALSE
static ArrayPtr GetAssetLocation(Thread *const thread, const String &asset)
static Dart_FfiNativeResolver GetFfiNativeResolver(Thread *const thread, const String &lib_url_str)
bool ResolveUri(const char *ref_uri, const char *base_uri, const char **target_uri)
Definition uri.cc:424
static void * FfiResolveAsset(Thread *const thread, const Array &asset_location, const String &symbol, char **error)
void * malloc(size_t size)
Definition allocation.cc:19
static void * FfiResolveWithFfiNativeResolver(Thread *const thread, Dart_FfiNativeResolver resolver, const String &symbol, intptr_t args_n, char **error)
static char * AvailableAssetsToCString(Thread *const thread)
static void ThrowFfiResolveError(const String &symbol, const String &asset, char *error)
uintptr_t uword
Definition globals.h:501
static intptr_t FfiResolve(Dart_Handle asset_handle, Dart_Handle symbol_handle, uintptr_t args_n)
ArrayPtr GetNativeAssetsMap(Thread *thread)
static void * LoadDynamicLibrary(const char *library_file, char **error=nullptr)
const char * file_schema
const int file_schema_length
intptr_t FfiResolveInternal(const String &asset, const String &symbol, uintptr_t args_n, char **error)
static StringPtr GetPlatformScriptPath(Thread *thread)
static bool SymbolExists(void *handle, const char *symbol)
static void * ResolveSymbol(void *handle, const char *symbol, char **error)
#define DEFINE_NATIVE_ENTRY(name, type_argument_count, argument_count)
#define GET_NON_NULL_NATIVE_ARGUMENT(type, name, value)
SKWASM_EXPORT SkPath * path_copy(SkPath *path)
Definition path.cpp:20
void * HANDLE
unsigned long DWORD
HINSTANCE HMODULE