source: trunk/src/kernel32/wprocess.cpp@ 9487

Last change on this file since 9487 was 9487, checked in by sandervl, 23 years ago

16 bits loader command line changes

File size: 88.2 KB
Line 
1/* $Id: wprocess.cpp,v 1.163 2002-12-11 17:12:59 sandervl Exp $ */
2
3/*
4 * Win32 process functions
5 *
6 * Copyright 1998-2000 Sander van Leeuwen (sandervl@xs4all.nl)
7 * Copyright 2000 knut st. osmundsen (knut.stange.osmundsen@mynd.no)
8 *
9 * NOTE: Even though Odin32 OS/2 apps don't switch FS selectors,
10 * we still allocate a TEB to store misc information.
11 *
12 * TODO: What happens when a dll is first loaded as LOAD_LIBRARY_AS_DATAFILE
13 * and then for real? (first one not freed of course)
14 *
15 * Project Odin Software License can be found in LICENSE.TXT
16 *
17 */
18#include <odin.h>
19#include <odinwrap.h>
20#include <os2win.h>
21#include <stdio.h>
22#include <stdlib.h>
23#include <string.h>
24
25#include <unicode.h>
26#include "windllbase.h"
27#include "winexebase.h"
28#include "windllpeldr.h"
29#include "winexepeldr.h"
30#include "windlllx.h"
31#include <vmutex.h>
32#include <handlemanager.h>
33
34#include "odin32validate.h"
35#include "exceptutil.h"
36#include "asmutil.h"
37#include "oslibdos.h"
38#include "oslibmisc.h"
39#include "oslibdebug.h"
40#include "hmcomm.h"
41
42#include "console.h"
43#include "wincon.h"
44#include "cio.h"
45#include "versionos2.h" /*PLF Wed 98-03-18 02:36:51*/
46#include <wprocess.h>
47#include "mmap.h"
48#include "initterm.h"
49
50#define DBG_LOCALLOG DBG_wprocess
51#include "dbglocal.h"
52
53#ifdef PROFILE
54#include <perfview.h>
55#include <profiler.h>
56#endif /* PROFILE */
57
58
59ODINDEBUGCHANNEL(KERNEL32-WPROCESS)
60
61
62/*******************************************************************************
63* Global Variables *
64*******************************************************************************/
65BOOL fIsOS2Image = FALSE; /* TRUE -> Odin32 OS/2 application (not converted!) */
66 /* FALSE -> otherwise */
67BOOL fExitProcess = FALSE;
68
69//Commandlines
70PCSTR pszCmdLineA; /* ASCII/ANSII commandline. */
71PCWSTR pszCmdLineW; /* Unicode commandline. */
72
73//Process database
74PDB ProcessPDB = {0};
75ENVDB ProcessENVDB = {0};
76CONCTRLDATA ProcessConCtrlData = {0};
77STARTUPINFOA StartupInfo = {0};
78CHAR unknownPDBData[16] = {0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0 ,0 ,0};
79USHORT ProcessTIBSel = 0;
80DWORD *TIBFlatPtr = 0;
81
82//list of thread database structures
83static TEB *threadList = 0;
84static VMutex threadListMutex;
85
86//TODO: This should not be here: (need to rearrange NTDLL; kernel32 can't depend on ntdll)
87BOOLEAN (* WINAPI RtlAllocateAndInitializeSid) ( PSID_IDENTIFIER_AUTHORITY pIdentifierAuthority,
88 BYTE nSubAuthorityCount,
89 DWORD nSubAuthority0,
90 DWORD nSubAuthority1,
91 DWORD nSubAuthority2,
92 DWORD nSubAuthority3,
93 DWORD nSubAuthority4,
94 DWORD nSubAuthority5,
95 DWORD nSubAuthority6,
96 DWORD nSubAuthority7,
97 PSID *pSid);
98static HINSTANCE hInstNTDll = 0;
99
100//******************************************************************************
101//******************************************************************************
102TEB *WIN32API GetThreadTEB()
103{
104 if(TIBFlatPtr == NULL) {
105 return 0;
106 }
107 return (TEB *)*TIBFlatPtr;
108}
109//******************************************************************************
110//******************************************************************************
111TEB *WIN32API GetTEBFromThreadId(ULONG threadId)
112{
113 TEB *teb = threadList;
114
115 threadListMutex.enter();
116 while(teb) {
117 if(teb->o.odin.threadId == threadId) {
118 break;
119 }
120 teb = teb->o.odin.next;
121 }
122 threadListMutex.leave();
123 return teb;
124}
125//******************************************************************************
126//******************************************************************************
127TEB *WIN32API GetTEBFromThreadHandle(HANDLE hThread)
128{
129 TEB *teb = threadList;
130
131 threadListMutex.enter();
132 while(teb) {
133 if(teb->o.odin.hThread == hThread) {
134 break;
135 }
136 teb = teb->o.odin.next;
137 }
138 threadListMutex.leave();
139 return teb;
140}
141//******************************************************************************
142//Allocate TEB structure for new thread
143//******************************************************************************
144TEB *WIN32API CreateTEB(HANDLE hThread, DWORD dwThreadId)
145{
146 USHORT tibsel;
147 TEB *winteb;
148
149 if(OSLibAllocSel(sizeof(TEB), &tibsel) == FALSE)
150 {
151 dprintf(("InitializeTIB: selector alloc failed!!"));
152 DebugInt3();
153 return NULL;
154 }
155 winteb = (TEB *)OSLibSelToFlat(tibsel);
156 if(winteb == NULL)
157 {
158 dprintf(("InitializeTIB: DosSelToFlat failed!!"));
159 DebugInt3();
160 return NULL;
161 }
162 memset(winteb, 0, sizeof(TEB));
163
164 threadListMutex.enter();
165 TEB *teblast = threadList;
166 if(!teblast) {
167 threadList = winteb;
168 winteb->o.odin.next = NULL;
169 }
170 else {
171 while(teblast->o.odin.next) {
172 teblast = teblast->o.odin.next;
173 }
174 teblast->o.odin.next = winteb;
175 }
176 threadListMutex.leave();
177
178 winteb->except = (PVOID)-1; /* 00 Head of exception handling chain */
179 winteb->htask16 = (USHORT)OSLibGetPIB(PIB_TASKHNDL); /* 0c Win16 task handle */
180 winteb->stack_sel = getSS(); /* 0e 16-bit stack selector */
181 winteb->self = winteb; /* 18 Pointer to this structure */
182 winteb->flags = TEBF_WIN32; /* 1c Flags */
183 winteb->queue = 0; /* 28 Message queue */
184 winteb->tls_ptr = &winteb->tls_array[0]; /* 2c Pointer to TLS array */
185 winteb->process = &ProcessPDB; /* 30 owning process (used by NT3.51 applets)*/
186 winteb->delta_priority = THREAD_PRIORITY_NORMAL;
187 winteb->process = &ProcessPDB;
188
189 //store selector of new TEB
190 winteb->teb_sel = tibsel;
191
192 winteb->o.odin.hThread = hThread;
193 winteb->o.odin.threadId = dwThreadId;
194
195 // Event semaphore (auto-reset) to signal message post to MsgWaitForMultipleObjects
196 winteb->o.odin.hPostMsgEvent = CreateEventA(NULL, FALSE, FALSE, NULL);
197
198 return winteb;
199}
200//******************************************************************************
201// Set up the TIB selector and memory for the main thread
202//******************************************************************************
203TEB *WIN32API InitializeMainThread()
204{
205 HANDLE hThreadMain;
206 TEB *teb;
207
208 //Allocate one dword to store the flat address of our TEB
209 dprintf(("InitializeMainThread Process handle %x, id %x", GetCurrentProcess(), GetCurrentProcessId()));
210
211 TIBFlatPtr = (DWORD *)OSLibAllocThreadLocalMemory(1);
212 if(TIBFlatPtr == 0) {
213 dprintf(("InitializeTIB: local thread memory alloc failed!!"));
214 DebugInt3();
215 return NULL;
216 }
217 //SvL: This doesn't really create a thread, but only sets up the
218 // handle of thread 0
219 hThreadMain = HMCreateThread(NULL, 0, 0, 0, 0, 0, TRUE);
220
221 //create and initialize TEB
222 teb = CreateTEB(hThreadMain, GetCurrentThreadId());
223 if(teb == NULL || InitializeThread(teb, TRUE) == FALSE) {
224 DebugInt3();
225 return NULL;
226 }
227
228 ProcessTIBSel = teb->teb_sel;
229
230 //todo initialize PDB during process creation
231 //todo: initialize TLS array if required
232 //TLS in executable always TLS index 0?
233//// ProcessPDB.exit_code = 0x103; /* STILL_ACTIVE */
234 ProcessPDB.threads = 1;
235 ProcessPDB.running_threads = 1;
236 ProcessPDB.ring0_threads = 1;
237 ProcessPDB.system_heap = GetProcessHeap();
238 ProcessPDB.parent = 0;
239 ProcessPDB.group = &ProcessPDB;
240 ProcessPDB.priority = 8; /* Normal */
241 ProcessPDB.heap = ProcessPDB.system_heap; /* will be changed later on */
242 ProcessPDB.next = NULL;
243 ProcessPDB.winver = 0xffff; /* to be determined */
244 ProcessPDB.server_pid = (void *)GetCurrentProcessId();
245 ProcessPDB.tls_bits[0] = 0; //all tls slots are free
246 ProcessPDB.tls_bits[1] = 0;
247
248 GetSystemTime(&ProcessPDB.creationTime);
249
250 /* Initialize the critical section */
251 InitializeCriticalSection(&ProcessPDB.crit_section );
252
253 //initialize the environment db entry.
254 ProcessPDB.env_db = &ProcessENVDB;
255 ProcessENVDB.startup_info = &StartupInfo;
256 ProcessENVDB.environ = GetEnvironmentStringsA();
257 ProcessENVDB.cmd_line = (CHAR*)(void*)pszCmdLineA;
258 ProcessENVDB.cmd_lineW = (WCHAR*)(void*)pszCmdLineW;
259 ProcessENVDB.break_handlers = &ProcessConCtrlData;
260 ProcessConCtrlData.fIgnoreCtrlC = FALSE; /* TODO! Should be inherited from parent. */
261 ProcessConCtrlData.pHead = ProcessConCtrlData.pTail = (PCONCTRL)malloc(sizeof(CONCTRL));
262 ProcessConCtrlData.pHead->pfnHandler = (void*)DefaultConsoleCtrlHandler;
263 ProcessConCtrlData.pHead->pNext = ProcessConCtrlData.pHead->pPrev = NULL;
264 ProcessConCtrlData.pHead->flFlags = ODIN32_CONCTRL_FLAGS_INIT;
265 InitializeCriticalSection(&ProcessENVDB.section);
266
267// ProcessPDB.startup_info = &StartupInfo;
268 ProcessPDB.unknown10 = (PVOID)&unknownPDBData[0];
269 StartupInfo.cb = sizeof(StartupInfo);
270 ProcessENVDB.hStdin = StartupInfo.hStdInput = GetStdHandle(STD_INPUT_HANDLE);
271 ProcessENVDB.hStdout = StartupInfo.hStdOutput = GetStdHandle(STD_OUTPUT_HANDLE);
272 ProcessENVDB.hStderr = StartupInfo.hStdError = GetStdHandle(STD_ERROR_HANDLE);
273
274 return teb;
275}
276//******************************************************************************
277// Set up the TEB structure of the CURRENT (!) thread
278//******************************************************************************
279BOOL WIN32API InitializeThread(TEB *winteb, BOOL fMainThread)
280{
281 //store TEB address in thread locale memory for easy retrieval
282 *TIBFlatPtr = (DWORD)winteb;
283
284//// winteb->exit_code = 0x103; /* STILL_ACTIVE */
285
286 winteb->stack_top = (PVOID)OSLibGetTIB(TIB_STACKTOP); /* 04 Top of thread stack */
287 winteb->stack_top = (PVOID)(((ULONG)winteb->stack_top + 0xFFF) & ~0xFFF);
288 //round to next page (OS/2 doesn't return a nice rounded value)
289 winteb->stack_low = (PVOID)OSLibGetTIB(TIB_STACKLOW); /* 08 Stack low-water mark */
290 //round to page boundary (OS/2 doesn't return a nice rounded value)
291 winteb->stack_low = (PVOID)((ULONG)winteb->stack_low & ~0xFFF);
292
293 winteb->o.odin.OrgTIBSel = GetFS();
294 winteb->o.odin.pWsockData = NULL;
295#ifdef DEBUG
296 winteb->o.odin.dbgCallDepth = 0;
297#endif
298 winteb->o.odin.pMessageBuffer = NULL;
299 winteb->o.odin.lcid = GetUserDefaultLCID();
300
301 if(OSLibGetPIB(PIB_TASKTYPE) == TASKTYPE_PM)
302 {
303 winteb->flags = 0; //todo gui
304 }
305 else winteb->flags = 0; //todo textmode
306
307 //Initialize thread security objects (TODO: Not complete)
308 if(hInstNTDll == 0) {
309 hInstNTDll = LoadLibraryA("NTDLL.DLL");
310 *(ULONG *)&RtlAllocateAndInitializeSid = (ULONG)GetProcAddress(hInstNTDll, "RtlAllocateAndInitializeSid");
311 if(RtlAllocateAndInitializeSid == NULL) {
312 DebugInt3();
313 }
314 }
315 SID_IDENTIFIER_AUTHORITY sidIdAuth = {0};
316 winteb->o.odin.threadinfo.dwType = SECTYPE_PROCESS | SECTYPE_INITIALIZED;
317
318 if (NULL != RtlAllocateAndInitializeSid) {
319 RtlAllocateAndInitializeSid(&sidIdAuth, 1, 0, 0, 0, 0, 0, 0, 0, 0, &winteb->o.odin.threadinfo.SidUser.User.Sid);
320 }
321 else DebugInt3();
322
323 winteb->o.odin.threadinfo.SidUser.User.Attributes = 0; //?????????
324
325 winteb->o.odin.threadinfo.pTokenGroups = (TOKEN_GROUPS*)malloc(sizeof(TOKEN_GROUPS));
326 winteb->o.odin.threadinfo.pTokenGroups->GroupCount = 1;
327
328 if (NULL != RtlAllocateAndInitializeSid) {
329 RtlAllocateAndInitializeSid(&sidIdAuth, 1, 0, 0, 0, 0, 0, 0, 0, 0, &winteb->o.odin.threadinfo.PrimaryGroup.PrimaryGroup);
330 }
331 else DebugInt3();
332
333 winteb->o.odin.threadinfo.pTokenGroups->Groups[0].Sid = winteb->o.odin.threadinfo.PrimaryGroup.PrimaryGroup;
334 winteb->o.odin.threadinfo.pTokenGroups->Groups[0].Attributes = 0; //????
335// pPrivilegeSet = NULL;
336// pTokenPrivileges= NULL;
337// TokenOwner = {0};
338// DefaultDACL = {0};
339// TokenSource = {0};
340 winteb->o.odin.threadinfo.TokenType = TokenPrimary;
341
342 dprintf(("InitializeTIB setup TEB with selector %x", winteb->teb_sel));
343 dprintf(("InitializeTIB: FS(%x):[0] = %x", GetFS(), QueryExceptionChain()));
344 return TRUE;
345}
346//******************************************************************************
347// Destroy the TIB selector and memory for the current thread
348//******************************************************************************
349void WIN32API DestroyTEB(TEB *winteb)
350{
351 SHORT orgtibsel;
352
353 dprintf(("DestroyTIB: FS = %x", GetFS()));
354 dprintf(("DestroyTIB: FS:[0] = %x", QueryExceptionChain()));
355
356 orgtibsel = winteb->o.odin.OrgTIBSel;
357
358 dprintf(("DestroyTIB: OSLibFreeSel %x", winteb->teb_sel));
359
360 threadListMutex.enter();
361 TEB *curteb = threadList;
362 if(curteb == winteb) {
363 threadList = winteb->o.odin.next;
364 }
365 else {
366 while(curteb->o.odin.next != winteb) {
367 curteb = curteb->o.odin.next;
368 if(curteb == NULL) {
369 dprintf(("DestroyTIB: couldn't find teb %x", winteb));
370 DebugInt3();
371 break;
372 }
373 }
374 if(curteb) {
375 curteb->o.odin.next = winteb->o.odin.next;
376 }
377 }
378 threadListMutex.leave();
379
380 // free allocated memory for security structures
381 free( winteb->o.odin.threadinfo.pTokenGroups );
382
383 // free PostMessage event semaphore
384 if(winteb->o.odin.hPostMsgEvent) {
385 CloseHandle(winteb->o.odin.hPostMsgEvent);
386 }
387
388#ifdef DEBUG
389 if (winteb->o.odin.arrstrCallStack != NULL)
390 free( winteb->o.odin.arrstrCallStack );
391#endif
392
393 //Restore our original FS selector
394 SetFS(orgtibsel);
395
396 //And free our own
397 OSLibFreeSel(winteb->teb_sel);
398
399 *TIBFlatPtr = 0;
400
401 dprintf(("DestroyTIB: FS(%x):[0] = %x", GetFS(), QueryExceptionChain()));
402 return;
403}
404//******************************************************************************
405//******************************************************************************
406ULONG WIN32API GetProcessTIBSel()
407{
408 if(fExitProcess) {
409 return 0;
410 }
411 return ProcessTIBSel;
412}
413/******************************************************************************/
414/******************************************************************************/
415void SetPDBInstance(HINSTANCE hInstance)
416{
417 ProcessPDB.hInstance = hInstance;
418}
419/******************************************************************************/
420/******************************************************************************/
421void WIN32API RestoreOS2TIB()
422{
423 SHORT orgtibsel;
424 TEB *winteb;
425
426 //If we're running an Odin32 OS/2 application (not converted!), then we
427 //we don't switch FS selectors
428 if(fIsOS2Image) {
429 return;
430 }
431
432 winteb = (TEB *)*TIBFlatPtr;
433 if(winteb) {
434 orgtibsel = winteb->o.odin.OrgTIBSel;
435
436 //Restore our original FS selector
437 SetFS(orgtibsel);
438 }
439}
440/******************************************************************************/
441//Switch to WIN32 TIB (FS selector)
442//NOTE: This is not done for Odin32 applications (LX), unless
443// fForceSwitch is TRUE)
444/******************************************************************************/
445USHORT WIN32API SetWin32TIB(BOOL fForceSwitch)
446{
447 SHORT win32tibsel;
448 TEB *winteb;
449
450 //If we're running an Odin32 OS/2 application (not converted!), then we
451 //we don't switch FS selectors
452 if(fIsOS2Image && !fForceSwitch) {
453 return GetFS();
454 }
455
456 winteb = (TEB *)*TIBFlatPtr;
457 if(winteb) {
458 win32tibsel = winteb->teb_sel;
459
460 //Restore our win32 FS selector
461 return SetReturnFS(win32tibsel);
462 }
463 else {
464 return GetFS();
465 }
466 // nested calls are OK, OS2ToWinCallback for instance
467 //else DebugInt3();
468
469 return GetFS();
470}
471//******************************************************************************
472//******************************************************************************
473//#define DEBUG_HEAPSTATE
474#ifdef DEBUG_HEAPSTATE
475char *pszHeapDump = NULL;
476char *pszHeapDumpStart = NULL;
477
478int _LNK_CONV callback_function(const void *pentry, size_t sz, int useflag, int status,
479 const char *filename, size_t line)
480{
481 if (_HEAPOK != status) {
482// dprintf(("status is not _HEAPOK."));
483 return 1;
484 }
485 if (_USEDENTRY == useflag && sz && filename && line && pszHeapDump) {
486 sprintf(pszHeapDump, "allocated %08x %u at %s %d\n", pentry, sz, filename, line);
487 pszHeapDump += strlen(pszHeapDump);
488 }
489
490 return 0;
491}
492//******************************************************************************
493//******************************************************************************
494#endif
495VOID WIN32API ExitProcess(DWORD exitcode)
496{
497 HANDLE hThread = GetCurrentThread();
498 TEB *teb;
499
500 dprintf(("KERNEL32: ExitProcess %d\n", exitcode));
501 dprintf(("KERNEL32: ExitProcess FS = %x\n", GetFS()));
502
503 fExitProcess = TRUE;
504
505 SetOS2ExceptionChain(-1);
506
507 HMDeviceCommClass::CloseOverlappedIOHandlers();
508
509 //detach all dlls (LIFO order) before really unloading them; this
510 //should take care of circular dependencies (crash while accessing
511 //memory of a dll that has just been freed)
512 dprintf(("********************************************"));
513 dprintf(("**** Detach process from all dlls -- START"));
514 Win32DllBase::detachProcessFromAllDlls();
515 dprintf(("**** Detach process from all dlls -- END"));
516 dprintf(("********************************************"));
517
518 if(WinExe) {
519 delete(WinExe);
520 WinExe = NULL;
521 }
522
523 //Note: Needs to be done after deleting WinExe (destruction of exe + dll objects)
524 //Flush and delete all open memory mapped files
525 Win32MemMap::deleteAll();
526
527 //SvL: We must make sure no threads are still suspended (with SuspendThread)
528 // OS/2 seems to be unable to terminate the process otherwise (exitlist hang)
529 threadListMutex.enter();
530 teb = threadList;
531 while(teb) {
532 if(teb->o.odin.hThread != hThread && teb->o.odin.dwSuspend > 0) {
533 //kill any threads that are suspended; dangerous, but so is calling
534 //SuspendThread; we assume the app knew what it was doing
535 TerminateThread(teb->o.odin.hThread, 0);
536 ResumeThread(teb->o.odin.hThread);
537 }
538 teb = teb->o.odin.next;
539 }
540 threadListMutex.leave();
541
542#ifdef DEBUG_HEAPSTATE
543 pszHeapDumpStart = pszHeapDump = (char *)malloc(10*1024*1024);
544 _heap_walk(callback_function);
545 dprintf((pszHeapDumpStart));
546 free(pszHeapDumpStart);
547#endif
548
549#ifdef PROFILE
550 // Note: after this point we do not expect any more Win32-API calls,
551 // so this is probably the best time to dump the gathered profiling
552 // information
553 PerfView_Write();
554 ProfilerWrite();
555 ProfilerTerminate();
556#endif /* PROFILE */
557
558 //Restore original OS/2 TIB selector
559 teb = GetThreadTEB();
560 if(teb) DestroyTEB(teb);
561 SetExceptionChain((ULONG)-1);
562
563 //avoid crashes since win32 & OS/2 exception handler aren't identical
564 //(terminate process generates two exceptions)
565 /* @@@PH 1998/02/12 Added Console Support */
566 if (iConsoleIsActive())
567 iConsoleWaitClose();
568
569 O32_ExitProcess(exitcode);
570}
571//******************************************************************************
572//******************************************************************************
573BOOL WIN32API FreeLibrary(HINSTANCE hinstance)
574{
575 Win32DllBase *winmod;
576 BOOL rc;
577
578 SetLastError(ERROR_SUCCESS);
579 //SvL: Ignore FreeLibary for executable
580 if(WinExe && hinstance == WinExe->getInstanceHandle()) {
581 return TRUE;
582 }
583
584 winmod = Win32DllBase::findModule(hinstance);
585 if(winmod) {
586 dprintf(("FreeLibrary %s", winmod->getName()));
587 //Only free it when the nrDynamicLibRef != 0
588 //This prevent problems after ExitProcess:
589 //i.e. dll A is referenced by our exe and loaded with LoadLibrary by dll B
590 // During ExitProcess it's unloaded once (before dll B), dll B calls
591 // FreeLibrary, but our exe also has a reference -> unloaded too many times
592 if(winmod->isDynamicLib()) {
593 winmod->decDynamicLib();
594 winmod->Release();
595 }
596 else {
597 dprintf(("Skipping dynamic unload as nrDynamicLibRef == 0"));
598 }
599 return(TRUE);
600 }
601 dprintf(("WARNING: KERNEL32: FreeLibrary %s %x NOT FOUND!", OSLibGetDllName(hinstance), hinstance));
602 return(TRUE);
603}
604/*****************************************************************************
605 * Name : VOID WIN32API FreeLibraryAndExitThread
606 * Purpose : The FreeLibraryAndExitThread function decrements the reference
607 * count of a loaded dynamic-link library (DLL) by one, and then
608 * calls ExitThread to terminate the calling thread.
609 * The function does not return.
610 *
611 * The FreeLibraryAndExitThread function gives threads that are
612 * created and executed within a dynamic-link library an opportunity
613 * to safely unload the DLL and terminate themselves.
614 * Parameters:
615 * Variables :
616 * Result :
617 * Remark :
618 *****************************************************************************/
619VOID WIN32API FreeLibraryAndExitThread( HMODULE hLibModule, DWORD dwExitCode)
620{
621
622 dprintf(("KERNEL32: FreeLibraryAndExitThread(%08x,%08x)", hLibModule, dwExitCode));
623 FreeLibrary(hLibModule);
624 ExitThread(dwExitCode);
625}
626/******************************************************************************/
627/******************************************************************************/
628/**
629 * LoadLibraryA can be used to map a DLL module into the calling process's
630 * addressspace. It returns a handle that can be used with GetProcAddress to
631 * get addresses of exported entry points (functions and variables).
632 *
633 * LoadLibraryA can also be used to map executable (.exe) modules into the
634 * address to access resources in the module. However, LoadLibrary can't be
635 * used to run an executable (.exe) module.
636 *
637 * @returns Handle to the library which was loaded.
638 * @param lpszLibFile Pointer to zero ASCII string giving the name of the
639 * executable image (either a Dll or an Exe) which is to be
640 * loaded.
641 *
642 * If no extention is specified the default .DLL extention is
643 * appended to the name. End the filename with an '.' if the
644 * file does not have an extention (and don't want the .DLL
645 * appended).
646 *
647 * If no path is specified, this API will use the Odin32
648 * standard search strategy to find the file. This strategy
649 * is described in the method Win32ImageBase::findDLL.
650 *
651 * This API likes to have backslashes (\), but will probably
652 * accept forward slashes too. Win32 SDK docs says that it
653 * should not contain forward slashes.
654 *
655 * Win32 SDK docs adds:
656 * "The name specified is the file name of the module and
657 * is not related to the name stored in the library module
658 * itself, as specified by the LIBRARY keyword in the
659 * module-definition (.def) file."
660 *
661 * @sketch Call LoadLibraryExA with flags set to 0.
662 * @status Odin32 Completely Implemented.
663 * @author Sander van Leeuwen (sandervl@xs4all.nl)
664 * knut st. osmundsen (knut.stange.osmundsen@pmsc.no)
665 * @remark Forwards to LoadLibraryExA.
666 */
667HINSTANCE WIN32API LoadLibraryA(LPCTSTR lpszLibFile)
668{
669 HINSTANCE hDll;
670
671 dprintf(("KERNEL32: LoadLibraryA(%s) --> LoadLibraryExA(lpszLibFile, 0, 0)",
672 lpszLibFile));
673 hDll = LoadLibraryExA(lpszLibFile, 0, 0);
674 dprintf(("KERNEL32: LoadLibraryA(%s) returns 0x%x",
675 lpszLibFile, hDll));
676 return hDll;
677}
678
679
680/**
681 * LoadLibraryW can be used to map a DLL module into the calling process's
682 * addressspace. It returns a handle that can be used with GetProcAddress to
683 * get addresses of exported entry points (functions and variables).
684 *
685 * LoadLibraryW can also be used to map executable (.exe) modules into the
686 * address to access resources in the module. However, LoadLibrary can't be
687 * used to run an executable (.exe) module.
688 *
689 * @returns Handle to the library which was loaded.
690 * @param lpszLibFile Pointer to Unicode string giving the name of
691 * the executable image (either a Dll or an Exe) which is to
692 * be loaded.
693 *
694 * If no extention is specified the default .DLL extention is
695 * appended to the name. End the filename with an '.' if the
696 * file does not have an extention (and don't want the .DLL
697 * appended).
698 *
699 * If no path is specified, this API will use the Odin32
700 * standard search strategy to find the file. This strategy
701 * is described in the method Win32ImageBase::findDLL.
702 *
703 * This API likes to have backslashes (\), but will probably
704 * accept forward slashes too. Win32 SDK docs says that it
705 * should not contain forward slashes.
706 *
707 * Win32 SDK docs adds:
708 * "The name specified is the file name of the module and
709 * is not related to the name stored in the library module
710 * itself, as specified by the LIBRARY keyword in the
711 * module-definition (.def) file."
712 *
713 * @sketch Convert Unicode name to ascii.
714 * Call LoadLibraryExA with flags set to 0.
715 * free ascii string.
716 * @status Odin32 Completely Implemented.
717 * @author Sander van Leeuwen (sandervl@xs4all.nl)
718 * knut st. osmundsen (knut.stange.osmundsen@pmsc.no)
719 * @remark Forwards to LoadLibraryExA.
720 */
721HINSTANCE WIN32API LoadLibraryW(LPCWSTR lpszLibFile)
722{
723 char * pszAsciiLibFile;
724 HINSTANCE hDll;
725
726 pszAsciiLibFile = UnicodeToAsciiString(lpszLibFile);
727 dprintf(("KERNEL32: LoadLibraryW(%s) --> LoadLibraryExA(lpszLibFile, 0, 0)",
728 pszAsciiLibFile));
729 hDll = LoadLibraryExA(pszAsciiLibFile, NULL, 0);
730 dprintf(("KERNEL32: LoadLibraryW(%s) returns 0x%x",
731 pszAsciiLibFile, hDll));
732 FreeAsciiString(pszAsciiLibFile);
733
734 return hDll;
735}
736
737//******************************************************************************
738//Custom build function to disable loading of LX dlls
739static BOOL fDisableLXDllLoading = FALSE;
740//******************************************************************************
741void WIN32API ODIN_DisableLXDllLoading()
742{
743 fDisableLXDllLoading = TRUE;
744}
745
746/**
747 * LoadLibraryExA can be used to map a DLL module into the calling process's
748 * addressspace. It returns a handle that can be used with GetProcAddress to
749 * get addresses of exported entry points (functions and variables).
750 *
751 * LoadLibraryExA can also be used to map executable (.exe) modules into the
752 * address to access resources in the module. However, LoadLibrary can't be
753 * used to run an executable (.exe) module.
754 *
755 * @returns Handle to the library which was loaded.
756 * @param lpszLibFile Pointer to Unicode string giving the name of
757 * the executable image (either a Dll or an Exe) which is to
758 * be loaded.
759 *
760 * If no extention is specified the default .DLL extention is
761 * appended to the name. End the filename with an '.' if the
762 * file does not have an extention (and don't want the .DLL
763 * appended).
764 *
765 * If no path is specified, this API will use the Odin32
766 * standard search strategy to find the file. This strategy
767 * is described in the method Win32ImageBase::findDLL.
768 * This may be alterned by the LOAD_WITH_ALTERED_SEARCH_PATH
769 * flag, see below.
770 *
771 * This API likes to have backslashes (\), but will probably
772 * accept forward slashes too. Win32 SDK docs says that it
773 * should not contain forward slashes.
774 *
775 * Win32 SDK docs adds:
776 * "The name specified is the file name of the module and
777 * is not related to the name stored in the library module
778 * itself, as specified by the LIBRARY keyword in the
779 * module-definition (.def) file."
780 *
781 * @param hFile Reserved. Must be 0.
782 *
783 * @param dwFlags Flags which specifies the taken when loading the module.
784 * The value 0 makes it identical to LoadLibraryA/W.
785 *
786 * Flags:
787 *
788 * DONT_RESOLVE_DLL_REFERENCES
789 * (WinNT/2K feature): Don't load imported modules and
790 * hence don't resolve imported symbols.
791 * DllMain isn't called either. (Which is obvious since
792 * it may use one of the importe symbols.)
793 *
794 * On the other hand, if this flag is NOT set, the system
795 * load imported modules, resolves imported symbols, calls
796 * DllMain for process and thread init and term (if wished
797 * by the module).
798 *
799 *
800 * LOAD_LIBRARY_AS_DATAFILE
801 * If this flag is set, the module is mapped into the
802 * address space but is not prepared for execution. Though
803 * it's preparted for resource API. Hence, you'll use this
804 * flag when you want to load a DLL for extracting
805 * messages or resources from it.
806 *
807 * The resulting handle can be used with any Odin32 API
808 * which operates on resources.
809 * (WinNt/2k supports all resource APIs while Win9x don't
810 * support the specialized resource APIs: LoadBitmap,
811 * LoadCursor, LoadIcon, LoadImage, LoadMenu.)
812 *
813 *
814 * LOAD_WITH_ALTERED_SEARCH_PATH
815 * If this flag is set and lpszLibFile specifies a path
816 * we'll use an alternative file search strategy to find
817 * imported modules. This stratgy is simply to use the
818 * path of the module being loaded instead of the path
819 * of the executable module as the first location
820 * to search for imported modules.
821 *
822 * If this flag is clear, the standard Odin32 standard
823 * search strategy. See Win32ImageBase::findDll for
824 * further information.
825 *
826 * not implemented yet.
827 *
828 * @status Open32 Partially Implemented.
829 * @author Sander van Leeuwen (sandervl@xs4all.nl)
830 * knut st. osmundsen (knut.stange.osmundsen@pmsc.no)
831 * @remark Forwards to LoadLibraryExA.
832 */
833HINSTANCE WIN32API LoadLibraryExA(LPCTSTR lpszLibFile, HANDLE hFile, DWORD dwFlags)
834{
835 HINSTANCE hDll;
836 Win32DllBase * pModule;
837 char szModname[CCHMAXPATH];
838 BOOL fPath; /* Flags which is set if the */
839 /* lpszLibFile contains a path. */
840 ULONG fPE; /* isPEImage return value. */
841 DWORD Characteristics; //file header's Characteristics
842 char *dot;
843
844 /** @sketch
845 * Some parameter validations is probably useful.
846 */
847 if (!VALID_PSZ(lpszLibFile))
848 {
849 dprintf(("KERNEL32: LoadLibraryExA(0x%x, 0x%x, 0x%x): invalid pointer lpszLibFile = 0x%x\n",
850 lpszLibFile, hFile, dwFlags, lpszLibFile));
851 SetLastError(ERROR_INVALID_PARAMETER); //or maybe ERROR_ACCESS_DENIED is more appropriate?
852 return NULL;
853 }
854 if (!VALID_PSZMAXSIZE(lpszLibFile, CCHMAXPATH))
855 {
856 dprintf(("KERNEL32: LoadLibraryExA(%s, 0x%x, 0x%x): lpszLibFile string too long, %d\n",
857 lpszLibFile, hFile, dwFlags, strlen(lpszLibFile)));
858 SetLastError(ERROR_INVALID_PARAMETER);
859 return NULL;
860 }
861 if ((dwFlags & ~(DONT_RESOLVE_DLL_REFERENCES | LOAD_WITH_ALTERED_SEARCH_PATH | LOAD_LIBRARY_AS_DATAFILE)) != 0)
862 {
863 dprintf(("KERNEL32: LoadLibraryExA(%s, 0x%x, 0x%x): dwFlags have invalid or unsupported flags\n",
864 lpszLibFile, hFile, dwFlags));
865 SetLastError(ERROR_INVALID_PARAMETER);
866 return NULL;
867 }
868
869 /** @sketch
870 * First we'll see if the module is allready loaded - either as the EXE or as DLL.
871 * IF Executable present AND libfile matches the modname of the executable THEN
872 * RETURN instance handle of executable.
873 * Endif
874 * IF allready loaded THEN
875 * IF it's a LX dll which isn't loaded and we're using the PeLoader THEN
876 * Set Load library.
877 * Endif
878 * Inc dynamic reference count.
879 * Inc reference count.
880 * RETURN instance handle.
881 * Endif
882 */
883 strcpy(szModname, lpszLibFile);
884 strupr(szModname);
885 dot = strchr(szModname, '.');
886 if(dot == NULL) {
887 //if there's no extension or trainling dot, we
888 //assume it's a dll (see Win32 SDK docs)
889 strcat(szModname, DLL_EXTENSION);
890 }
891 else {
892 if(dot[1] == 0) {
893 //a trailing dot means the module has no extension (SDK docs)
894 *dot = 0;
895 }
896 }
897 if (WinExe != NULL && WinExe->matchModName(szModname))
898 return WinExe->getInstanceHandle();
899
900 pModule = Win32DllBase::findModule((LPSTR)szModname);
901 if (pModule)
902 {
903 pModule->incDynamicLib();
904 pModule->AddRef();
905 dprintf(("KERNEL32: LoadLibraryExA(%s, 0x%x, 0x%x): returns 0x%x. Dll found %s",
906 szModname, hFile, dwFlags, pModule->getInstanceHandle(), pModule->getFullPath()));
907 return pModule->getInstanceHandle();
908 }
909
910
911 /** @sketch
912 * Test if lpszLibFile has a path or not.
913 * Copy the lpszLibFile to szModname, rename the dll and uppercase the name.
914 * IF it hasn't a path THEN
915 * Issue a findDll to find the dll/executable to be loaded.
916 * IF the Dll isn't found THEN
917 * Set last error and RETURN.
918 * Endif.
919 * Endif
920 */
921 fPath = strchr(szModname, '\\') || strchr(szModname, '/');
922 Win32DllBase::renameDll(szModname);
923
924 if (!fPath)
925 {
926 char szModName2[CCHMAXPATH];
927 strcpy(szModName2, szModname);
928 if (!Win32ImageBase::findDll(szModName2, szModname, sizeof(szModname)))
929 {
930 dprintf(("KERNEL32: LoadLibraryExA(%s, 0x%x, 0x%x): module wasn't found. returns NULL",
931 lpszLibFile, hFile, dwFlags));
932 SetLastError(ERROR_FILE_NOT_FOUND);
933 return NULL;
934 }
935 }
936
937 //test if dll is in PE or LX format
938 fPE = Win32ImageBase::isPEImage(szModname, &Characteristics, NULL);
939
940 /** @sketch
941 * IF (fDisableLXDllLoading && (!fPeLoader || fPE == failure)) THEN
942 * Try load the executable using LoadLibrary
943 * IF successfully loaded THEN
944 * IF LX dll and is using the PE Loader THEN
945 * Set Load library.
946 * Inc reference count.
947 * Endif
948 * Inc dynamic reference count.
949 * RETURN successfully.
950 * Endif
951 * Endif
952 */
953 //only call OS/2 if LX binary or win32k process
954 if (!fDisableLXDllLoading && (!fPeLoader || fPE != ERROR_SUCCESS))
955 {
956 hDll = OSLibDosLoadModule(szModname);
957 if (hDll)
958 {
959 /* OS/2 dll, system dll, converted dll or win32k took care of it. */
960 pModule = Win32DllBase::findModuleByOS2Handle(hDll);
961 if (pModule)
962 {
963 if (pModule->isLxDll())
964 {
965 ((Win32LxDll *)pModule)->setDllHandleOS2(hDll);
966 if (fPeLoader && pModule->AddRef() == -1)
967 { //-1 -> load failed (attachProcess)
968 delete pModule;
969 SetLastError(ERROR_INVALID_EXE_SIGNATURE);
970 dprintf(("Dll %s refused to be loaded; aborting", szModname));
971 return 0;
972 }
973
974 }
975 pModule->incDynamicLib();
976 }
977 else if (fExeStarted && !fIsOS2Image) {
978 OSLibDosFreeModule(hDll);
979 SetLastError(ERROR_INVALID_EXE_SIGNATURE);
980 dprintf(("Dll %s is not an Odin dll; unload & return failure", szModname));
981 return 0;
982 }
983 else {
984 /* bird 2001-07-10:
985 * let's fail right away instead of hitting DebugInt3s and fail other places.
986 * This is very annoying when running Opera on a debug build with netscape/2
987 * plugins present. We'll make this conditional for the time being.
988 */
989 static BOOL fFailIfUnregisteredLX = -1;
990 if (fFailIfUnregisteredLX == -1)
991 fFailIfUnregisteredLX = getenv("ODIN32.FAIL_IF_UNREGISTEREDLX") != NULL;
992 if (fExeStarted && fFailIfUnregisteredLX)
993 {
994 dprintf(("KERNEL32: LoadLibraryExA(%s, 0x%x, 0x%x): returns 0x%x. Loaded OS/2 dll %s using DosLoadModule. returns NULL.",
995 lpszLibFile, hFile, dwFlags, hDll, szModname));
996 SetLastError(ERROR_INVALID_EXE_SIGNATURE);
997 return NULL;
998 }
999 dprintf(("KERNEL32: LoadLibraryExA(%s, 0x%x, 0x%x): returns 0x%x. Loaded OS/2 dll %s using DosLoadModule.",
1000 lpszLibFile, hFile, dwFlags, hDll, szModname));
1001 return hDll; //happens when LoadLibrary is called in kernel32's initterm (nor harmful)
1002 }
1003 dprintf(("KERNEL32: LoadLibraryExA(%s, 0x%x, 0x%x): returns 0x%x. Loaded %s using DosLoadModule.",
1004 lpszLibFile, hFile, dwFlags, hDll, szModname));
1005 return pModule->getInstanceHandle();
1006 }
1007 dprintf(("KERNEL32: LoadLibraryExA(%s, 0x%x, 0x%x): DosLoadModule (%s) failed. LastError=%d",
1008 lpszLibFile, hFile, dwFlags, szModname, GetLastError()));
1009 }
1010 else
1011 hDll = NULL;
1012
1013
1014 /** @sketch
1015 * If PE image THEN
1016 * IF LOAD_LIBRARY_AS_DATAFILE or Executable THEN
1017 *
1018 *
1019 * Try load the file using the Win32PeLdrDll class.
1020 * <sketch continued further down>
1021 * Else
1022 * Set last error.
1023 * (hDll is NULL)
1024 * Endif
1025 * return hDll.
1026 */
1027 if(fPE == ERROR_SUCCESS)
1028 {
1029 Win32PeLdrDll *peldrDll;
1030
1031 //SvL: If executable -> load as data file (only resources)
1032 if(!(Characteristics & IMAGE_FILE_DLL))
1033 {
1034 dwFlags |= (LOAD_LIBRARY_AS_DATAFILE | DONT_RESOLVE_DLL_REFERENCES);
1035 }
1036
1037 peldrDll = new Win32PeLdrDll(szModname);
1038 if (peldrDll == NULL)
1039 {
1040 dprintf(("KERNEL32: LoadLibraryExA(%s, 0x%x, 0x%x): Failed to created instance of Win32PeLdrDll. returns NULL.",
1041 lpszLibFile, hFile, dwFlags));
1042 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
1043 return NULL;
1044 }
1045
1046 /** @sketch
1047 * Process dwFlags
1048 */
1049 if (dwFlags & LOAD_LIBRARY_AS_DATAFILE)
1050 {
1051 dprintf(("KERNEL32: LoadLibraryExA(%s, 0x%x, 0x%x): LOAD_LIBRARY_AS_DATAFILE",
1052 lpszLibFile, hFile, dwFlags));
1053 peldrDll->setLoadAsDataFile();
1054 peldrDll->disableLibraryCalls();
1055 }
1056 if (dwFlags & DONT_RESOLVE_DLL_REFERENCES)
1057 {
1058 dprintf(("KERNEL32: LoadLibraryExA(%s, 0x%x, 0x%x): DONT_RESOLVE_DLL_REFERENCES",
1059 lpszLibFile, hFile, dwFlags));
1060 peldrDll->disableLibraryCalls();
1061 peldrDll->disableImportHandling();
1062 }
1063 if (dwFlags & LOAD_WITH_ALTERED_SEARCH_PATH)
1064 {
1065 dprintf(("KERNEL32: LoadLibraryExA(%s, 0x%x, 0x%x): Warning dwFlags LOAD_WITH_ALTERED_SEARCH_PATH is not implemented.",
1066 lpszLibFile, hFile, dwFlags));
1067 //peldrDll->setLoadWithAlteredSearchPath();
1068 }
1069
1070 /** @sketch
1071 * Initiate the peldr DLL.
1072 * IF successful init THEN
1073 * Inc dynamic ref count.
1074 * Inc ref count.
1075 * Attach to process
1076 * IF successful THEN
1077 * hDLL <- instance handle.
1078 * ELSE
1079 * set last error
1080 * delete Win32PeLdrDll instance.
1081 * Endif
1082 * ELSE
1083 * set last error
1084 * delete Win32PeLdrDll instance.
1085 * Endif.
1086 */
1087 if (peldrDll->init(0))
1088 {
1089 peldrDll->AddRef();
1090 if (peldrDll->attachProcess())
1091 {
1092 hDll = peldrDll->getInstanceHandle();
1093 //Must be called *after* attachprocess, since attachprocess may also
1094 //trigger LoadLibrary calls
1095 //Those dlls must not be put in front of this dll in the dynamic
1096 //dll list; or else the unload order is wrong:
1097 //i.e. RPAP3260 loads PNRS3260 in DLL_PROCESS_ATTACH
1098 // this means that in ExitProcess, PNRS3260 needs to be removed
1099 // first since RPAP3260 depends on it
1100 peldrDll->incDynamicLib();
1101 }
1102 else
1103 {
1104 dprintf(("KERNEL32: LoadLibraryExA(%s, 0x%x, 0x%x): attachProcess call to Win32PeLdrDll instance failed. returns NULL.",
1105 lpszLibFile, hFile, dwFlags));
1106 SetLastError(ERROR_DLL_INIT_FAILED);
1107 delete peldrDll;
1108 return NULL;
1109 }
1110 }
1111 else
1112 {
1113 dprintf(("KERNEL32: LoadLibraryExA(%s, 0x%x, 0x%x): Failed to init Win32PeLdrDll instance. error=%d returns NULL.",
1114 lpszLibFile, hFile, dwFlags, peldrDll->getError()));
1115 SetLastError(ERROR_INVALID_EXE_SIGNATURE);
1116 delete peldrDll;
1117 return NULL;
1118 }
1119 }
1120 else
1121 {
1122 dprintf(("KERNEL32: LoadLibraryExA(%s, 0x%x, 0x%x) library wasn't found (%s) or isn't loadable; err %x",
1123 lpszLibFile, hFile, dwFlags, szModname, fPE));
1124 SetLastError(fPE);
1125 return NULL;
1126 }
1127
1128 return hDll;
1129}
1130
1131
1132/**
1133 * LoadLibraryExW can be used to map a DLL module into the calling process's
1134 * addressspace. It returns a handle that can be used with GetProcAddress to
1135 * get addresses of exported entry points (functions and variables).
1136 *
1137 * LoadLibraryExW can also be used to map executable (.exe) modules into the
1138 * address to access resources in the module. However, LoadLibrary can't be
1139 * used to run an executable (.exe) module.
1140 *
1141 * @returns Handle to the library which was loaded.
1142 * @param lpszLibFile Pointer to Unicode string giving the name of
1143 * the executable image (either a Dll or an Exe) which is to
1144 * be loaded.
1145 *
1146 * If no extention is specified the default .DLL extention is
1147 * appended to the name. End the filename with an '.' if the
1148 * file does not have an extention (and don't want the .DLL
1149 * appended).
1150 *
1151 * If no path is specified, this API will use the Odin32
1152 * standard search strategy to find the file. This strategy
1153 * is described in the method Win32ImageBase::findDLL.
1154 * This may be alterned by the LOAD_WITH_ALTERED_SEARCH_PATH
1155 * flag, see below.
1156 *
1157 * This API likes to have backslashes (\), but will probably
1158 * accept forward slashes too. Win32 SDK docs says that it
1159 * should not contain forward slashes.
1160 *
1161 * Win32 SDK docs adds:
1162 * "The name specified is the file name of the module and
1163 * is not related to the name stored in the library module
1164 * itself, as specified by the LIBRARY keyword in the
1165 * module-definition (.def) file."
1166 *
1167 * @param hFile Reserved. Must be 0.
1168 *
1169 * @param dwFlags Flags which specifies the taken when loading the module.
1170 * The value 0 makes it identical to LoadLibraryA/W.
1171 *
1172 * Flags:
1173 *
1174 * DONT_RESOLVE_DLL_REFERENCES
1175 * (WinNT/2K feature): Don't load imported modules and
1176 * hence don't resolve imported symbols.
1177 * DllMain isn't called either. (Which is obvious since
1178 * it may use one of the importe symbols.)
1179 *
1180 * On the other hand, if this flag is NOT set, the system
1181 * load imported modules, resolves imported symbols, calls
1182 * DllMain for process and thread init and term (if wished
1183 * by the module).
1184 *
1185 * LOAD_LIBRARY_AS_DATAFILE
1186 * If this flag is set, the module is mapped into the
1187 * address space but is not prepared for execution. Though
1188 * it's preparted for resource API. Hence, you'll use this
1189 * flag when you want to load a DLL for extracting
1190 * messages or resources from it.
1191 *
1192 * The resulting handle can be used with any Odin32 API
1193 * which operates on resources.
1194 * (WinNt/2k supports all resource APIs while Win9x don't
1195 * support the specialized resource APIs: LoadBitmap,
1196 * LoadCursor, LoadIcon, LoadImage, LoadMenu.)
1197 *
1198 * LOAD_WITH_ALTERED_SEARCH_PATH
1199 * If this flag is set and lpszLibFile specifies a path
1200 * we'll use an alternative file search strategy to find
1201 * imported modules. This stratgy is simply to use the
1202 * path of the module being loaded instead of the path
1203 * of the executable module as the first location
1204 * to search for imported modules.
1205 *
1206 * If this flag is clear, the standard Odin32 standard
1207 * search strategy. See Win32ImageBase::findDll for
1208 * further information.
1209 *
1210 * @sketch Convert Unicode name to ascii.
1211 * Call LoadLibraryExA.
1212 * Free ascii string.
1213 * return handle from LoadLibraryExA.
1214 * @status Open32 Partially Implemented.
1215 * @author Sander van Leeuwen (sandervl@xs4all.nl)
1216 * knut st. osmundsen (knut.stange.osmundsen@pmsc.no)
1217 * @remark Forwards to LoadLibraryExA.
1218 */
1219HINSTANCE WIN32API LoadLibraryExW(LPCWSTR lpszLibFile, HANDLE hFile, DWORD dwFlags)
1220{
1221 char * pszAsciiLibFile;
1222 HINSTANCE hDll;
1223
1224 pszAsciiLibFile = UnicodeToAsciiString(lpszLibFile);
1225 dprintf(("KERNEL32: LoadLibraryExW(%s, 0x%x, 0x%x) --> LoadLibraryExA",
1226 pszAsciiLibFile, hFile, dwFlags));
1227 hDll = LoadLibraryExA(pszAsciiLibFile, hFile, dwFlags);
1228 dprintf(("KERNEL32: LoadLibraryExW(%s, 0x%x, 0x%x) returns 0x%x",
1229 pszAsciiLibFile, hFile, dwFlags, hDll));
1230 FreeAsciiString(pszAsciiLibFile);
1231
1232 return hDll;
1233}
1234//******************************************************************************
1235//******************************************************************************
1236HINSTANCE16 WIN32API LoadLibrary16(LPCTSTR lpszLibFile)
1237{
1238 dprintf(("ERROR: LoadLibrary16 %s, not implemented", lpszLibFile));
1239 return 0;
1240}
1241//******************************************************************************
1242//******************************************************************************
1243VOID WIN32API FreeLibrary16(HINSTANCE16 hinstance)
1244{
1245 dprintf(("ERROR: FreeLibrary16 %x, not implemented", hinstance));
1246}
1247//******************************************************************************
1248//******************************************************************************
1249FARPROC WIN32API GetProcAddress16(HMODULE hModule, LPCSTR lpszProc)
1250{
1251 dprintf(("ERROR: GetProcAddress16 %x %x, not implemented", hModule, lpszProc));
1252 return 0;
1253}
1254
1255
1256/**
1257 * Internal function which gets the commandline (string) used to start the current process.
1258 * @returns OS/2 / Windows return code
1259 * On successful return (NO_ERROR) the global variables
1260 * pszCmdLineA and pszCmdLineW are set.
1261 *
1262 * @param pszPeExe Pass in the name of the PE exe of this process. We'll
1263 * us this as exename and skip the first argument (ie. argv[1]).
1264 * If NULL we'll use the commandline from OS/2 as it is.
1265 * @status Completely implemented and tested.
1266 * @author knut st. osmundsen (knut.stange.osmundsen@mynd.no)
1267 */
1268ULONG InitCommandLine(const char *pszPeExe)
1269{
1270 PCHAR pib_pchcmd; /* PIB pointer to commandline. */
1271 CHAR szFilename[CCHMAXPATH]; /* Filename buffer used to get the exe filename in. */
1272 ULONG cch; /* Commandline string length. (including terminator) */
1273 PSZ psz; /* Temporary string pointer. */
1274 PSZ psz2; /* Temporary string pointer. */
1275 APIRET rc; /* OS/2 return code. */
1276 BOOL fQuotes; /* Flag used to remember if the exe filename should be in quotes. */
1277
1278 /** @sketch
1279 * Get commandline from the PIB.
1280 */
1281 pib_pchcmd = (PCHAR)OSLibGetPIB(PIB_PCHCMD);
1282
1283 /** @sketch
1284 * Two methods of making the commandline:
1285 * (1) The first argument is skipped and the second is used as exe filname.
1286 * This applies to PE.EXE launched processes only.
1287 * (2) No skipping. First argument is the exe filename.
1288 * This applies to all but PE.EXE launched processes.
1289 *
1290 * Note: We could do some code size optimization here. Much of the code for
1291 * the two methods are nearly identical.
1292 *
1293 */
1294 if(pszPeExe)
1295 {
1296 /** @sketch
1297 * Allocate memory for the commandline.
1298 * Build commandline:
1299 * Copy exe filename.
1300 * Add arguments.
1301 */
1302 cch = strlen(pszPeExe)+1;
1303
1304 // PH 2002-04-11
1305 // Note: intentional memory leak, pszCmdLineW will not be freed
1306 // or allocated after process startup
1307 pszCmdLineA = psz = (PSZ)malloc(cch);
1308 if (psz == NULL)
1309 {
1310 dprintf(("KERNEL32: InitCommandLine(%p): malloc(%d) failed\n", pszPeExe, cch));
1311 return ERROR_NOT_ENOUGH_MEMORY;
1312 }
1313 strcpy((char *)pszCmdLineA, pszPeExe);
1314
1315 rc = NO_ERROR;
1316 }
1317 else
1318 {
1319 /** @sketch Method (2):
1320 * First we'll have to determin the size of the commandline.
1321 *
1322 * As we don't assume that OS/2 allways puts a fully qualified EXE name
1323 * as the first string, we'll check if it's empty - and get the modulename
1324 * in that case - and allways get the fully qualified filename.
1325 */
1326 if (pib_pchcmd == NULL || pib_pchcmd[0] == '\0')
1327 {
1328 rc = OSLibDosQueryModuleName(OSLibGetPIB(PIB_HMTE), sizeof(szFilename), szFilename);
1329 if (rc != NO_ERROR)
1330 {
1331 dprintf(("KERNEL32: InitCommandLine(%p): OSLibQueryModuleName(0x%x,...) failed with rc=%d\n",
1332 pszPeExe, OSLibGetPIB(PIB_HMTE), rc));
1333 return rc;
1334 }
1335 }
1336 else
1337 {
1338 rc = OSLibDosQueryPathInfo(pib_pchcmd, FIL_QUERYFULLNAME, szFilename, sizeof(szFilename));
1339 if (rc != NO_ERROR)
1340 {
1341 dprintf(("KERNEL32: InitCommandLine(%p): (info) OSLibDosQueryPathInfo failed with rc=%d\n", pszPeExe, rc));
1342 strcpy(szFilename, pib_pchcmd);
1343 rc = NO_ERROR;
1344 }
1345 }
1346
1347 /** @sketch
1348 * We're still measuring the size of the commandline:
1349 * Check if we have to quote the exe filename.
1350 * Determin the length of the executable name including quotes and '\0'-terminator.
1351 * Count the length of the arguments. (We here count's all argument strings.)
1352 */
1353 fQuotes = strchr(szFilename, ' ') != NULL;
1354 cch = strlen(szFilename) + fQuotes*2 + 1;
1355 if (pib_pchcmd != NULL)
1356 {
1357 psz2 = pib_pchcmd + strlen(pib_pchcmd) + 1;
1358 while (*psz2 != '\0')
1359 {
1360 register int cchTmp = strlen(psz2) + 1; /* + 1 is for terminator (psz2) and space (cch). */
1361 psz2 += cchTmp;
1362 cch += cchTmp;
1363 }
1364 }
1365
1366 /** @sketch
1367 * Allocate memory for the commandline.
1368 * Build commandline:
1369 * Copy exe filename.
1370 * Add arguments.
1371 */
1372 pszCmdLineA = psz = (PSZ)malloc(cch);
1373 if (psz == NULL)
1374 {
1375 dprintf(("KERNEL32: InitCommandLine(%p): malloc(%d) failed\n", pszPeExe, cch));
1376 return ERROR_NOT_ENOUGH_MEMORY;
1377 }
1378
1379 if (fQuotes)
1380 *psz++ = '"';
1381 strcpy(psz, szFilename);
1382 psz += strlen(psz);
1383 if (fQuotes)
1384 {
1385 *psz++ = '"';
1386 *psz = '\0';
1387 }
1388
1389 if (pib_pchcmd != NULL)
1390 {
1391 psz2 = pib_pchcmd + strlen(pib_pchcmd) + 1;
1392 while (*psz2 != '\0')
1393 {
1394 register int cchTmp = strlen(psz2) + 1; /* + 1 is for terminator (psz). */
1395 *psz++ = ' '; /* add space */
1396 memcpy(psz, psz2, cchTmp);
1397 psz2 += cchTmp;
1398 psz += cchTmp - 1;
1399 }
1400 }
1401 }
1402
1403 /** @sketch
1404 * If successfully build ASCII commandline then convert it to UniCode.
1405 */
1406 if (rc == NO_ERROR)
1407 {
1408 // PH 2002-04-11
1409 // Note: intentional memory leak, pszCmdLineW will not be freed
1410 // or allocated after process startup
1411 cch = strlen(pszCmdLineA) + 1;
1412
1413 pszCmdLineW = (WCHAR*)malloc(cch * 2);
1414 if (pszCmdLineW != NULL) {
1415 //Translate from OS/2 to Windows codepage & ascii to unicode
1416 MultiByteToWideChar(CP_OEMCP, 0, pszCmdLineA, -1, (LPWSTR)pszCmdLineW, cch-1);
1417 ((LPWSTR)pszCmdLineW)[cch-1] = 0;
1418
1419 //ascii command line is still in OS/2 codepage, so convert it
1420 WideCharToMultiByte(CP_ACP, 0, pszCmdLineW, -1, (LPSTR)pszCmdLineA, cch-1, 0, NULL);
1421 ((LPSTR)pszCmdLineA)[cch-1] = 0;
1422 }
1423 else
1424 {
1425 DebugInt3();
1426 dprintf(("KERNEL32: InitCommandLine(%p): malloc(%d) failed (2)\n", pszPeExe, cch));
1427 rc = ERROR_NOT_ENOUGH_MEMORY;
1428 }
1429 }
1430
1431 return rc;
1432}
1433
1434/**
1435 * Gets the command line of the current process.
1436 * @returns On success:
1437 * Command line of the current process. One single string.
1438 * The first part of the command line string is the executable filename
1439 * of the current process. It might be in quotes if it contains spaces.
1440 * The rest of the string is arguments.
1441 *
1442 * On error:
1443 * NULL. Last error set. (does Win32 set last error this?)
1444 * @sketch IF not inited THEN
1445 * Init commandline assuming !PE.EXE
1446 * IF init failes THEN set last error.
1447 * ENDIF
1448 * return ASCII/ANSI commandline.
1449 * @status Completely implemented and tested.
1450 * @author knut st. osmundsen (knut.stange.osmundsen@mynd.no)
1451 * @remark The Ring-3 PeLdr is resposible for calling InitCommandLine before anyone
1452 * is able to call this function.
1453 */
1454LPCSTR WIN32API GetCommandLineA(VOID)
1455{
1456 /*
1457 * Check if the commandline is initiated.
1458 * If not we'll have to do it.
1459 * ASSUMES that if not inited this isn't a PE.EXE lauched process.
1460 */
1461 if (pszCmdLineA == NULL)
1462 {
1463 APIRET rc;
1464 rc = InitCommandLine(NULL);
1465 if (rc != NULL)
1466 SetLastError(rc);
1467 }
1468
1469 dprintf(("KERNEL32: GetCommandLineA: %s\n", pszCmdLineA));
1470 return pszCmdLineA;
1471}
1472
1473
1474/**
1475 * Gets the command line of the current process.
1476 * @returns On success:
1477 * Command line of the current process. One single string.
1478 * The first part of the command line string is the executable filename
1479 * of the current process. It might be in quotes if it contains spaces.
1480 * The rest of the string is arguments.
1481 *
1482 * On error:
1483 * NULL. Last error set. (does Win32 set last error this?)
1484 * @sketch IF not inited THEN
1485 * Init commandline assuming !PE.EXE
1486 * IF init failes THEN set last error.
1487 * ENDIF
1488 * return Unicode commandline.
1489 * @status Completely implemented and tested.
1490 * @author knut st. osmundsen (knut.stange.osmundsen@mynd.no)
1491 * @remark The Ring-3 PeLdr is resposible for calling InitCommandLine before anyone
1492 * is able to call this function.
1493 */
1494LPCWSTR WIN32API GetCommandLineW(void)
1495{
1496 /*
1497 * Check if the commandline is initiated.
1498 * If not we'll have to do it.
1499 * ASSUMES that if not inited this isn't a PE.EXE lauched process.
1500 */
1501 if (pszCmdLineW == NULL)
1502 {
1503 APIRET rc;
1504 rc = InitCommandLine(NULL);
1505 if (rc != NULL)
1506 SetLastError(rc);
1507 }
1508
1509 dprintf(("KERNEL32: GetCommandLineW: %s\n", pszCmdLineA));
1510 return pszCmdLineW;
1511}
1512
1513
1514/**
1515 * GetModuleFileName gets the full path and file name for the specified module.
1516 * @returns Bytes written to the buffer (lpszPath). This count includes the
1517 * terminating '\0'.
1518 * On error 0 is returned. Last error is set.
1519 *
1520 * 2002-04-25 PH
1521 * Q - Do we set ERROR_BUFFER_OVERFLOW when cch > cchPath?
1522 * Q - Does NT really set the last error?
1523 * A > Win2k does not set LastError here, remains OK
1524 *
1525 * While GetModuleFileName does add a trailing termination zero
1526 * if there is enough room, the returned number of characters
1527 * *MUST NOT* include the zero character!
1528 * (Notes R6 Installer on Win2kSP6, verified Testcase)
1529 *
1530 * @param hModule Handle to the module you like to get the file name to.
1531 * @param lpszPath Output buffer for full path and file name.
1532 * @param cchPath Size of the lpszPath buffer.
1533 * @sketch Validate lpszPath.
1534 * Find the module object using handle.
1535 * If found Then
1536 * Get full path from module object.
1537 * If found path Then
1538 * Copy path to buffer and set the number of bytes written.
1539 * Else
1540 * IPE!
1541 * Else
1542 * Call Open32 GetModuleFileName. (kernel32 initterm needs/needed this)
1543 * Log result.
1544 * Return number of bytes written to the buffer.
1545 *
1546 * @status Completely implemented, Open32.
1547 * @author knut st. osmundsen (knut.stange.osmundsen@mynd.no)
1548 * Sander van Leeuwen (sandervl@xs4all.nl)
1549 * Patrick Haller (patrick.haller@innotek.de)
1550 * @remark - Do we still have to call Open32?
1551 */
1552DWORD WIN32API GetModuleFileNameA(HMODULE hModule, LPTSTR lpszPath, DWORD cchPath)
1553{
1554 Win32ImageBase * pMod; /* Pointer to the module object. */
1555 DWORD cch = 0; /* Length of the */
1556
1557 // PH 2002-04-24 Note:
1558 // WIN2k just crashes in NTDLL if lpszPath is invalid!
1559 if (!VALID_PSZ(lpszPath))
1560 {
1561 dprintf(("KERNEL32: GetModuleFileNameA(0x%x, 0x%x, 0x%x): invalid pointer lpszLibFile = 0x%x\n",
1562 hModule, lpszPath, cchPath, lpszPath));
1563 SetLastError(ERROR_INVALID_PARAMETER); //or maybe ERROR_ACCESS_DENIED is more appropriate?
1564 return 0;
1565 }
1566
1567 pMod = Win32ImageBase::findModule(hModule);
1568 if (pMod != NULL)
1569 {
1570 const char *pszFn = pMod->getFullPath();
1571 if (pszFn)
1572 {
1573 cch = strlen(pszFn);
1574 if (cch >= cchPath)
1575 cch = cchPath;
1576 else
1577 // if there is sufficient room for the zero termination,
1578 // write it additionally, uncounted
1579 lpszPath[cch] = '\0';
1580
1581 memcpy(lpszPath, pszFn, cch);
1582 }
1583 else
1584 {
1585 dprintf(("KERNEL32: GetModuleFileNameA(%x,...): IPE - getFullPath returned NULL or empty string\n"));
1586 DebugInt3();
1587 SetLastError(ERROR_INVALID_HANDLE);
1588 }
1589 }
1590 else
1591 {
1592 SetLastError(ERROR_INVALID_HANDLE);
1593 //only needed for call inside kernel32's initterm (profile init)
1594 //(console init only it seems...)
1595 cch = OSLibDosGetModuleFileName(hModule, lpszPath, cchPath);
1596 }
1597
1598 if (cch > 0)
1599 dprintf(("KERNEL32: GetModuleFileNameA(%x %x): %s %d\n", hModule, lpszPath, lpszPath, cch));
1600 else
1601 dprintf(("KERNEL32: WARNING: GetModuleFileNameA(%x,...) - not found!", hModule));
1602
1603 return cch;
1604}
1605
1606
1607/**
1608 * GetModuleFileName gets the full path and file name for the specified module.
1609 * @returns Bytes written to the buffer (lpszPath). This count includes the
1610 * terminating '\0'.
1611 * On error 0 is returned. Last error is set.
1612 * @param hModule Handle to the module you like to get the file name to.
1613 * @param lpszPath Output buffer for full path and file name.
1614 * @param cchPath Size of the lpszPath buffer.
1615 * @sketch Find the module object using handle.
1616 * If found Then
1617 * get full path from module object.
1618 * If found path Then
1619 * Determin path length.
1620 * Translate the path to into the buffer.
1621 * Else
1622 * IPE.
1623 * else
1624 * SetLastError to invalid handle.
1625 * Log result.
1626 * return number of bytes written to the buffer.
1627 *
1628 * @status Completely implemented.
1629 * @author knut st. osmundsen (knut.stange.osmundsen@mynd.no)
1630 * @remark - We do _NOT_ call Open32.
1631 * - Do we set ERROR_BUFFER_OVERFLOW when cch > cchPath?
1632 * - Does NT really set the last error?
1633 */
1634DWORD WIN32API GetModuleFileNameW(HMODULE hModule, LPWSTR lpszPath, DWORD cchPath)
1635{
1636 Win32ImageBase * pMod;
1637 DWORD cch = 0;
1638
1639 if (!VALID_PSZ(lpszPath))
1640 {
1641 dprintf(("KERNEL32: GetModuleFileNameW(0x%x, 0x%x, 0x%x): invalid pointer lpszLibFile = 0x%x\n",
1642 hModule, lpszPath, cchPath, lpszPath));
1643 SetLastError(ERROR_INVALID_PARAMETER); //or maybe ERROR_ACCESS_DENIED is more appropriate?
1644 return 0;
1645 }
1646
1647 pMod = Win32ImageBase::findModule(hModule);
1648 if (pMod != NULL)
1649 {
1650 const char *pszFn = pMod->getFullPath();
1651 if (pszFn || *pszFn != '\0')
1652 {
1653 cch = strlen(pszFn) + 1;
1654 if (cch > cchPath)
1655 cch = cchPath;
1656 AsciiToUnicodeN(pszFn, lpszPath, cch);
1657 }
1658 else
1659 {
1660 dprintf(("KERNEL32: GetModuleFileNameW(%x,...): IPE - getFullPath returned NULL or empty string\n"));
1661 DebugInt3();
1662 SetLastError(ERROR_INVALID_HANDLE);
1663 }
1664 }
1665 else
1666 SetLastError(ERROR_INVALID_HANDLE);
1667
1668 if (cch > 0)
1669 dprintf(("KERNEL32: GetModuleFileNameW(%x,...): %s %d\n", hModule, lpszPath, cch));
1670 else
1671 dprintf(("KERNEL32: WARNING: GetModuleFileNameW(%x,...) - not found!", hModule));
1672
1673 return cch;
1674}
1675
1676
1677//******************************************************************************
1678//NOTE: GetModuleHandleA does NOT support files with multiple dots (i.e.
1679// very.weird.exe)
1680//
1681// hinst = LoadLibrary("WINSPOOL.DRV"); -> succeeds
1682// hinst2 = GetModuleHandle("WINSPOOL.DRV"); -> succeeds
1683// hinst3 = GetModuleHandle("WINSPOOL."); -> fails
1684// hinst4 = GetModuleHandle("WINSPOOL"); -> fails
1685// hinst = LoadLibrary("KERNEL32.DLL"); -> succeeds
1686// hinst2 = GetModuleHandle("KERNEL32.DLL"); -> succeeds
1687// hinst3 = GetModuleHandle("KERNEL32."); -> fails
1688// hinst4 = GetModuleHandle("KERNEL32"); -> succeeds
1689// Same behaviour as observed in NT4, SP6
1690//******************************************************************************
1691HANDLE WIN32API GetModuleHandleA(LPCTSTR lpszModule)
1692{
1693 HANDLE hMod = 0;
1694 Win32DllBase *windll;
1695 char szModule[CCHMAXPATH];
1696 char *dot;
1697
1698 if(lpszModule == NULL)
1699 {
1700 if(WinExe)
1701 hMod = WinExe->getInstanceHandle();
1702 else
1703 {
1704 // // Just fail this API
1705 // hMod = 0;
1706 // SetLastError(ERROR_INVALID_HANDLE);
1707 // Wrong: in an ODIN32-LX environment, just
1708 // assume a fake handle
1709 hMod = -1;
1710 }
1711 }
1712 else
1713 {
1714 strcpy(szModule, OSLibStripPath((char *)lpszModule));
1715 strupr(szModule);
1716 dot = strchr(szModule, '.');
1717 if(dot == NULL) {
1718 //if no extension -> add .DLL (see SDK docs)
1719 strcat(szModule, DLL_EXTENSION);
1720 }
1721 else {
1722 if(dot[1] == 0) {
1723 //a trailing dot means the module has no extension (SDK docs)
1724 *dot = 0;
1725 }
1726 }
1727 if(WinExe && WinExe->matchModName(szModule)) {
1728 hMod = WinExe->getInstanceHandle();
1729 }
1730 else {
1731 windll = Win32DllBase::findModule(szModule);
1732 if(windll) {
1733 hMod = windll->getInstanceHandle();
1734 }
1735 }
1736 }
1737 dprintf(("KERNEL32: GetModuleHandle %s returned %X\n", lpszModule, hMod));
1738 return(hMod);
1739}
1740//******************************************************************************
1741//******************************************************************************
1742HMODULE WIN32API GetModuleHandleW(LPCWSTR lpwszModuleName)
1743{
1744 HMODULE rc;
1745 char *astring = NULL;
1746
1747 if (NULL != lpwszModuleName)
1748 astring = UnicodeToAsciiString((LPWSTR)lpwszModuleName);
1749
1750 rc = GetModuleHandleA(astring);
1751 dprintf(("KERNEL32: OS2GetModuleHandleW %s returned %X\n", astring, rc));
1752
1753 if (NULL != astring)
1754 FreeAsciiString(astring);
1755
1756 return(rc);
1757}
1758//******************************************************************************
1759//******************************************************************************
1760const char *szPECmdLoader = "PEC.EXE";
1761const char *szPEGUILoader = "PE.EXE";
1762const char *szNELoader = "w16odin.exe";
1763//******************************************************************************
1764//Override loader names (PEC, PE, W16ODIN)
1765//NOTE: String must be resident
1766//******************************************************************************
1767BOOL WIN32API ODIN_SetLoaders(LPCSTR pszPECmdLoader, LPCSTR pszPEGUILoader,
1768 LPCSTR pszNELoader)
1769{
1770 if(pszPECmdLoader) szPECmdLoader = pszPECmdLoader;
1771 if(pszPEGUILoader) szPEGUILoader = pszPEGUILoader;
1772 if(pszNELoader) szNELoader = pszNELoader;
1773
1774 return TRUE;
1775}
1776//******************************************************************************
1777//******************************************************************************
1778BOOL WINAPI CreateProcessA( LPCSTR lpApplicationName, LPSTR lpCommandLine,
1779 LPSECURITY_ATTRIBUTES lpProcessAttributes,
1780 LPSECURITY_ATTRIBUTES lpThreadAttributes,
1781 BOOL bInheritHandles, DWORD dwCreationFlags,
1782 LPVOID lpEnvironment, LPCSTR lpCurrentDirectory,
1783 LPSTARTUPINFOA lpStartupInfo,
1784 LPPROCESS_INFORMATION lpProcessInfo )
1785{
1786 TEB *pThreadDB = (TEB*)GetThreadTEB();
1787 char *cmdline = NULL;
1788 BOOL rc;
1789
1790 dprintf(("KERNEL32: CreateProcessA %s cline:%s inherit:%d cFlags:%x Env:%x CurDir:%s StartupFlags:%x\n",
1791 lpApplicationName, lpCommandLine, bInheritHandles, dwCreationFlags,
1792 lpEnvironment, lpCurrentDirectory, lpStartupInfo));
1793
1794#ifdef DEBUG
1795 if(lpStartupInfo) {
1796 dprintf(("lpStartupInfo->lpReserved %x", lpStartupInfo->lpReserved));
1797 dprintf(("lpStartupInfo->lpDesktop %x", lpStartupInfo->lpDesktop));
1798 dprintf(("lpStartupInfo->lpTitle %s", lpStartupInfo->lpTitle));
1799 dprintf(("lpStartupInfo->dwX %x", lpStartupInfo->dwX));
1800 dprintf(("lpStartupInfo->dwY %x", lpStartupInfo->dwY));
1801 dprintf(("lpStartupInfo->dwXSize %x", lpStartupInfo->dwXSize));
1802 dprintf(("lpStartupInfo->dwYSize %x", lpStartupInfo->dwYSize));
1803 dprintf(("lpStartupInfo->dwXCountChars %x", lpStartupInfo->dwXCountChars));
1804 dprintf(("lpStartupInfo->dwYCountChars %x", lpStartupInfo->dwYCountChars));
1805 dprintf(("lpStartupInfo->dwFillAttribute %x", lpStartupInfo->dwFillAttribute));
1806 dprintf(("lpStartupInfo->dwFlags %x", lpStartupInfo->dwFlags));
1807 dprintf(("lpStartupInfo->wShowWindow %x", lpStartupInfo->wShowWindow));
1808 dprintf(("lpStartupInfo->hStdInput %x", lpStartupInfo->hStdInput));
1809 dprintf(("lpStartupInfo->hStdOutput %x", lpStartupInfo->hStdOutput));
1810 dprintf(("lpStartupInfo->hStdError %x", lpStartupInfo->hStdError));
1811 }
1812#endif
1813
1814 // open32 does not support DEBUG_ONLY_THIS_PROCESS
1815 if(dwCreationFlags & DEBUG_ONLY_THIS_PROCESS)
1816 dwCreationFlags |= DEBUG_PROCESS;
1817
1818 if(O32_CreateProcess(lpApplicationName, lpCommandLine, lpProcessAttributes,
1819 lpThreadAttributes, bInheritHandles, dwCreationFlags,
1820 lpEnvironment, lpCurrentDirectory, lpStartupInfo,
1821 lpProcessInfo) == TRUE)
1822 {
1823 if (dwCreationFlags & DEBUG_PROCESS && pThreadDB != NULL)
1824 {
1825 if(pThreadDB->o.odin.pidDebuggee != 0)
1826 {
1827 // TODO: handle this
1828 dprintf(("KERNEL32: CreateProcess ERROR: This thread is already a debugger\n"));
1829 }
1830 else
1831 {
1832 pThreadDB->o.odin.pidDebuggee = lpProcessInfo->dwProcessId;
1833 OSLibStartDebugger((ULONG*)&pThreadDB->o.odin.pidDebuggee);
1834 }
1835 }
1836 else pThreadDB->o.odin.pidDebuggee = 0;
1837
1838 return(TRUE);
1839 }
1840
1841 // PH 2001-05-07
1842 // verify why O32_CreateProcess actually failed.
1843 // If GetLastError() == 191 (ERROR_INVALID_EXE_SIGNATURE)
1844 // we can continue to call "PE.EXE".
1845 // Note: Open32 does not translate ERROR_INVALID_EXE_SIGNATURE,
1846 // it is also valid in Win32.
1847 DWORD dwError = GetLastError();
1848 if (ERROR_INVALID_EXE_SIGNATURE != dwError && ERROR_FILE_NOT_FOUND != dwError && ERROR_ACCESS_DENIED != dwError)
1849 {
1850 dprintf(("CreateProcess: O32_CreateProcess failed with rc=%d, not PE-executable !",
1851 dwError));
1852
1853 // the current value of GetLastError() is still valid.
1854
1855 return FALSE;
1856 }
1857
1858 // else ...
1859
1860 //probably a win32 exe, so run it in the pe loader
1861 if(lpApplicationName) {
1862 if(lpCommandLine) {
1863 //skip exe name in lpCommandLine
1864 //TODO: doesn't work for directories with spaces!
1865 while(*lpCommandLine != 0 && *lpCommandLine != ' ')
1866 lpCommandLine++;
1867
1868 if(*lpCommandLine != 0) {
1869 lpCommandLine++;
1870 }
1871 cmdline = (char *)malloc(strlen(lpApplicationName)+strlen(lpCommandLine) + 16);
1872 sprintf(cmdline, "%s %s", lpApplicationName, lpCommandLine);
1873 }
1874 else {
1875 cmdline = (char *)malloc(strlen(lpApplicationName) + 16);
1876 sprintf(cmdline, "%s", lpApplicationName);
1877 }
1878 }
1879 else {
1880 cmdline = (char *)malloc(strlen(lpCommandLine) + 16);
1881 sprintf(cmdline, "%s", lpCommandLine);
1882 }
1883
1884 char szAppName[MAX_PATH];
1885 char buffer[MAX_PATH];
1886 DWORD fileAttr;
1887 char *exename = buffer;
1888 strncpy(buffer, cmdline, sizeof(szAppName));
1889 buffer[MAX_PATH-1] = 0;
1890 if(*exename == '"') {
1891 exename++;
1892 while(*exename != 0 && *exename != '"')
1893 exename++;
1894
1895 if(*exename != 0) {
1896 *exename = 0;
1897 }
1898 exename++;
1899 if (SearchPathA( NULL, &buffer[1], ".exe", sizeof(szAppName), szAppName, NULL ) ||
1900 SearchPathA( NULL, &buffer[1], NULL, sizeof(szAppName), szAppName, NULL ))
1901 {
1902 //
1903 }
1904 }
1905 else {
1906 BOOL fTerminate = FALSE;
1907 DWORD fileAttr;
1908
1909 while(*exename != 0) {
1910 while(*exename != 0 && *exename != ' ')
1911 exename++;
1912
1913 if(*exename != 0) {
1914 *exename = 0;
1915 fTerminate = TRUE;
1916 }
1917 dprintf(("Trying '%s'", buffer ));
1918 if (SearchPathA( NULL, buffer, ".exe", sizeof(szAppName), szAppName, NULL ) ||
1919 SearchPathA( NULL, buffer, NULL, sizeof(szAppName), szAppName, NULL ))
1920 {
1921 if(fTerminate) exename++;
1922 break;
1923 }
1924
1925 if(fTerminate) {
1926 *exename = ' ';
1927 exename++;
1928 fTerminate = FALSE;
1929 }
1930 }
1931 }
1932 lpCommandLine = exename; //start of command line parameters
1933
1934 fileAttr = GetFileAttributesA(szAppName);
1935 if(fileAttr == -1 || (fileAttr & FILE_ATTRIBUTE_DIRECTORY)) {
1936 dprintf(("CreateProcess: can't find executable!"));
1937 SetLastError(ERROR_FILE_NOT_FOUND);
1938 return FALSE;
1939 }
1940
1941 dprintf(("KERNEL32: CreateProcess %s %s", szAppName, lpCommandLine));
1942
1943 DWORD Characteristics, SubSystem, fNEExe;
1944 if(Win32ImageBase::isPEImage(szAppName, &Characteristics, &SubSystem, &fNEExe) == 0)
1945 {
1946 char *lpszPE;
1947 char *lpszExecutable;
1948 int iNewCommandLineLength;
1949
1950 // calculate base length for the new command line
1951 iNewCommandLineLength = strlen(szAppName) + strlen(lpCommandLine);
1952
1953 if(SubSystem == IMAGE_SUBSYSTEM_WINDOWS_CUI)
1954 lpszExecutable = (LPSTR)szPECmdLoader;
1955 else
1956 lpszExecutable = (LPSTR)szPEGUILoader;
1957
1958 lpszPE = lpszExecutable;
1959
1960 // 2002-04-24 PH
1961 // set the ODIN32.DEBUG_CHILD environment variable to start new PE processes
1962 // under a new instance of the (IPMD) debugger.
1963#ifdef DEBUG
1964 CHAR debug_szPE[ 512 ];
1965 PSZ debug_pszOS2Debugger = getenv("ODIN32.DEBUG_CHILD");
1966 if (NULL != debug_pszOS2Debugger)
1967 {
1968 // build new start command
1969 strcpy(debug_szPE, debug_pszOS2Debugger);
1970 strcat(debug_szPE, " ");
1971 strcat(debug_szPE, lpszExecutable);
1972
1973 // we require more space in the new command line
1974 iNewCommandLineLength += strlen( debug_szPE );
1975
1976 // only launch the specified executable (ICSDEBUG.EXE)
1977 lpszPE = debug_szPE;
1978 lpszExecutable = debug_pszOS2Debugger;
1979 }
1980#endif
1981
1982 //SvL: Allright. Before we call O32_CreateProcess, we must take care of
1983 // lpCurrentDirectory ourselves. (Open32 ignores it!)
1984 if(lpCurrentDirectory) {
1985 char *newcmdline;
1986
1987 newcmdline = (char *)malloc(strlen(lpCurrentDirectory) + iNewCommandLineLength + 32);
1988 sprintf(newcmdline, "%s /OPT:[CURDIR=%s] %s %s", lpszPE, lpCurrentDirectory, szAppName, lpCommandLine);
1989 free(cmdline);
1990 cmdline = newcmdline;
1991 }
1992 else {
1993 char *newcmdline;
1994
1995 newcmdline = (char *)malloc(iNewCommandLineLength + 16);
1996 sprintf(newcmdline, "%s %s %s", lpszPE, szAppName, lpCommandLine);
1997 free(cmdline);
1998 cmdline = newcmdline;
1999 }
2000
2001 dprintf(("KERNEL32: CreateProcess starting [%s],[%s]",
2002 lpszExecutable,
2003 cmdline));
2004
2005 rc = O32_CreateProcess(lpszExecutable, (LPCSTR)cmdline,lpProcessAttributes,
2006 lpThreadAttributes, bInheritHandles, dwCreationFlags,
2007 lpEnvironment, lpCurrentDirectory, lpStartupInfo,
2008 lpProcessInfo);
2009 }
2010 else
2011 if(fNEExe) {//16 bits windows app
2012 char *newcmdline;
2013
2014 newcmdline = (char *)malloc(strlen(szAppName) + strlen(cmdline) + 512);
2015
2016 sprintf(newcmdline, "%s /PELDR=[%s] %s", szNELoader, szPEGUILoader, szAppName, lpCommandLine);
2017 free(cmdline);
2018 cmdline = newcmdline;
2019 //Force Open32 to use DosStartSession (DosExecPgm won't do)
2020 dwCreationFlags |= CREATE_NEW_PROCESS_GROUP;
2021
2022 dprintf(("KERNEL32: CreateProcess starting [%s],[%s]",
2023 szNELoader,
2024 cmdline));
2025 rc = O32_CreateProcess(szNELoader, (LPCSTR)cmdline, lpProcessAttributes,
2026 lpThreadAttributes, bInheritHandles, dwCreationFlags,
2027 lpEnvironment, lpCurrentDirectory, lpStartupInfo,
2028 lpProcessInfo);
2029 }
2030 else {//os/2 app??
2031 rc = O32_CreateProcess(szAppName, (LPCSTR)lpCommandLine, lpProcessAttributes,
2032 lpThreadAttributes, bInheritHandles, dwCreationFlags,
2033 lpEnvironment, lpCurrentDirectory, lpStartupInfo,
2034 lpProcessInfo);
2035 }
2036 if(rc == TRUE)
2037 {
2038 if (dwCreationFlags & DEBUG_PROCESS && pThreadDB != NULL)
2039 {
2040 if(pThreadDB->o.odin.pidDebuggee != 0)
2041 {
2042 // TODO: handle this
2043 dprintf(("KERNEL32: CreateProcess ERROR: This thread is already a debugger\n"));
2044 }
2045 else
2046 {
2047 pThreadDB->o.odin.pidDebuggee = lpProcessInfo->dwProcessId;
2048 OSLibStartDebugger((ULONG*)&pThreadDB->o.odin.pidDebuggee);
2049 }
2050 }
2051 else
2052 pThreadDB->o.odin.pidDebuggee = 0;
2053 }
2054 if(cmdline)
2055 free(cmdline);
2056
2057 if(lpProcessInfo)
2058 {
2059 lpProcessInfo->dwThreadId = MAKE_THREADID(lpProcessInfo->dwProcessId, lpProcessInfo->dwThreadId);
2060 dprintf(("KERNEL32: CreateProcess returned %d hPro:%x hThr:%x pid:%x tid:%x\n",
2061 rc, lpProcessInfo->hProcess, lpProcessInfo->hThread,
2062 lpProcessInfo->dwProcessId,lpProcessInfo->dwThreadId));
2063 }
2064 else
2065 dprintf(("KERNEL32: CreateProcess returned %d\n", rc));
2066 return(rc);
2067}
2068//******************************************************************************
2069//******************************************************************************
2070BOOL WIN32API CreateProcessW(LPCWSTR lpApplicationName, LPWSTR lpCommandLine,
2071 PSECURITY_ATTRIBUTES lpProcessAttributes,
2072 PSECURITY_ATTRIBUTES lpThreadAttributes,
2073 BOOL bInheritHandles, DWORD dwCreationFlags,
2074 LPVOID lpEnvironment,
2075 LPCWSTR lpCurrentDirectory, LPSTARTUPINFOW lpStartupInfo,
2076 LPPROCESS_INFORMATION lpProcessInfo)
2077{
2078 BOOL rc;
2079 char *astring1 = 0, *astring2 = 0, *astring3 = 0;
2080
2081 dprintf(("KERNEL32: CreateProcessW"));
2082 if(lpApplicationName)
2083 astring1 = UnicodeToAsciiString((LPWSTR)lpApplicationName);
2084 if(lpCommandLine)
2085 astring2 = UnicodeToAsciiString(lpCommandLine);
2086 if(lpCurrentDirectory)
2087 astring3 = UnicodeToAsciiString((LPWSTR)lpCurrentDirectory);
2088 rc = CreateProcessA(astring1, astring2, lpProcessAttributes, lpThreadAttributes,
2089 bInheritHandles, dwCreationFlags, lpEnvironment,
2090 astring3, (LPSTARTUPINFOA)lpStartupInfo,
2091 lpProcessInfo);
2092 if(astring3) FreeAsciiString(astring3);
2093 if(astring2) FreeAsciiString(astring2);
2094 if(astring1) FreeAsciiString(astring1);
2095 return(rc);
2096}
2097//******************************************************************************
2098//******************************************************************************
2099HINSTANCE WIN32API WinExec(LPCSTR lpCmdLine, UINT nCmdShow)
2100{
2101 STARTUPINFOA startinfo = {0};
2102 PROCESS_INFORMATION procinfo;
2103 DWORD rc;
2104 HINSTANCE hInstance;
2105
2106 dprintf(("KERNEL32: WinExec %s\n", lpCmdLine));
2107 startinfo.dwFlags = nCmdShow;
2108 if(CreateProcessA(NULL, (LPSTR)lpCmdLine, NULL, NULL, FALSE, 0, NULL, NULL,
2109 &startinfo, &procinfo) == FALSE)
2110 {
2111 hInstance = (HINSTANCE)GetLastError();
2112 if(hInstance >= 32) {
2113 hInstance = 11;
2114 }
2115 dprintf(("KERNEL32: WinExec failed with rc %d", hInstance));
2116 return hInstance;
2117 }
2118 //block until the launched app waits for input (or a timeout of 15 seconds)
2119 //TODO: Shouldn't call Open32, but the api in user32..
2120 if(fVersionWarp3) {
2121 Sleep(1000); //WaitForInputIdle not available in Warp 3
2122 }
2123 else {
2124 dprintf(("Calling WaitForInputIdle %x %d", procinfo.hProcess, 15000));
2125 rc = WaitForInputIdle(procinfo.hProcess, 15000);
2126#ifdef DEBUG
2127 if(rc != 0) {
2128 dprintf(("WinExec: WaitForInputIdle %x returned %x", procinfo.hProcess, rc));
2129 }
2130 else dprintf(("WinExec: WaitForInputIdle successfull"));
2131#endif
2132 }
2133 CloseHandle(procinfo.hThread);
2134 CloseHandle(procinfo.hProcess);
2135 return 33;
2136}
2137//******************************************************************************
2138//DWORD idAttach; /* thread to attach */
2139//DWORD idAttachTo; /* thread to attach to */
2140//BOOL fAttach; /* attach or detach */
2141//******************************************************************************
2142BOOL WIN32API AttachThreadInput(DWORD idAttach, DWORD idAttachTo, BOOL fAttach)
2143{
2144 dprintf(("USER32: AttachThreadInput, not implemented\n"));
2145 return(TRUE);
2146}
2147//******************************************************************************
2148//******************************************************************************
2149DWORD WIN32API WaitForInputIdle(HANDLE hProcess, DWORD dwTimeOut)
2150{
2151 dprintf(("USER32: WaitForInputIdle %x %d\n", hProcess, dwTimeOut));
2152
2153 if(fVersionWarp3) {
2154 Sleep(1000);
2155 return 0;
2156 }
2157 else return O32_WaitForInputIdle(hProcess, dwTimeOut);
2158}
2159/**********************************************************************
2160 * LoadModule (KERNEL32.499)
2161 *
2162 * Wine: 20000909
2163 *
2164 * Copyright 1995 Alexandre Julliard
2165 */
2166HINSTANCE WINAPI LoadModule( LPCSTR name, LPVOID paramBlock )
2167{
2168 LOADPARAMS *params = (LOADPARAMS *)paramBlock;
2169 PROCESS_INFORMATION info;
2170 STARTUPINFOA startup;
2171 HINSTANCE hInstance;
2172 LPSTR cmdline, p;
2173 char filename[MAX_PATH];
2174 BYTE len;
2175
2176 dprintf(("LoadModule %s %x", name, paramBlock));
2177
2178 if (!name) return ERROR_FILE_NOT_FOUND;
2179
2180 if (!SearchPathA( NULL, name, ".exe", sizeof(filename), filename, NULL ) &&
2181 !SearchPathA( NULL, name, NULL, sizeof(filename), filename, NULL ))
2182 return GetLastError();
2183
2184 len = (BYTE)params->lpCmdLine[0];
2185 if (!(cmdline = (LPSTR)HeapAlloc( GetProcessHeap(), 0, strlen(filename) + len + 2 )))
2186 return ERROR_NOT_ENOUGH_MEMORY;
2187
2188 strcpy( cmdline, filename );
2189 p = cmdline + strlen(cmdline);
2190 *p++ = ' ';
2191 memcpy( p, params->lpCmdLine + 1, len );
2192 p[len] = 0;
2193
2194 memset( &startup, 0, sizeof(startup) );
2195 startup.cb = sizeof(startup);
2196 if (params->lpCmdShow)
2197 {
2198 startup.dwFlags = STARTF_USESHOWWINDOW;
2199 startup.wShowWindow = params->lpCmdShow[1];
2200 }
2201
2202 if (CreateProcessA( filename, cmdline, NULL, NULL, FALSE, 0,
2203 params->lpEnvAddress, NULL, &startup, &info ))
2204 {
2205 /* Give 15 seconds to the app to come up */
2206 if ( WaitForInputIdle ( info.hProcess, 15000 ) == 0xFFFFFFFF )
2207 dprintf(("ERROR: WaitForInputIdle failed: Error %ld\n", GetLastError() ));
2208 hInstance = 33;
2209 /* Close off the handles */
2210 CloseHandle( info.hThread );
2211 CloseHandle( info.hProcess );
2212 }
2213 else if ((hInstance = GetLastError()) >= 32)
2214 {
2215 dprintf(("ERROR: Strange error set by CreateProcess: %d\n", hInstance ));
2216 hInstance = 11;
2217 }
2218
2219 HeapFree( GetProcessHeap(), 0, cmdline );
2220 return hInstance;
2221}
2222//******************************************************************************
2223//******************************************************************************
2224FARPROC WIN32API GetProcAddress(HMODULE hModule, LPCSTR lpszProc)
2225{
2226 Win32ImageBase *winmod;
2227 FARPROC proc;
2228 ULONG ulAPIOrdinal;
2229
2230 if(hModule == 0 || hModule == -1 || (WinExe && hModule == WinExe->getInstanceHandle())) {
2231 winmod = WinExe;
2232 }
2233 else winmod = (Win32ImageBase *)Win32DllBase::findModule((HINSTANCE)hModule);
2234
2235 if(winmod) {
2236 ulAPIOrdinal = (ULONG)lpszProc;
2237 if (ulAPIOrdinal <= 0x0000FFFF) {
2238 proc = (FARPROC)winmod->getApi((int)ulAPIOrdinal);
2239 }
2240 else proc = (FARPROC)winmod->getApi((char *)lpszProc);
2241 if(proc == 0) {
2242#ifdef DEBUG
2243 if(ulAPIOrdinal <= 0x0000FFFF) {
2244 dprintf(("GetProcAddress %x %x not found!", hModule, ulAPIOrdinal));
2245 }
2246 else dprintf(("GetProcAddress %x %s not found!", hModule, lpszProc));
2247#endif
2248 SetLastError(ERROR_PROC_NOT_FOUND);
2249 }
2250 if(HIWORD(lpszProc))
2251 dprintf(("KERNEL32: GetProcAddress %s from %X returned %X\n", lpszProc, hModule, proc));
2252 else dprintf(("KERNEL32: GetProcAddress %x from %X returned %X\n", lpszProc, hModule, proc));
2253
2254 SetLastError(ERROR_SUCCESS);
2255 return proc;
2256 }
2257 proc = (FARPROC)OSLibDosGetProcAddress(hModule, lpszProc);
2258 if(HIWORD(lpszProc))
2259 dprintf(("KERNEL32: GetProcAddress %s from %X returned %X\n", lpszProc, hModule, proc));
2260 else dprintf(("KERNEL32: GetProcAddress %x from %X returned %X\n", lpszProc, hModule, proc));
2261 SetLastError(ERROR_SUCCESS);
2262 return(proc);
2263}
2264//******************************************************************************
2265//Retrieve the version
2266//******************************************************************************
2267BOOL SYSTEM GetVersionStruct(char *lpszModName, char *verstruct, ULONG bufLength)
2268{
2269 Win32ImageBase *winimage;
2270 HINSTANCE hDll;
2271 BOOL rc = FALSE;
2272
2273 dprintf(("GetVersionStruct of module %s %x %d", lpszModName, verstruct, bufLength));
2274 if(verstruct == NULL) {
2275 SetLastError(ERROR_INVALID_PARAMETER);
2276 return FALSE;
2277 }
2278 if (WinExe != NULL && WinExe->matchModName(lpszModName)) {
2279 return WinExe->getVersionStruct(verstruct, bufLength);
2280 }
2281 hDll = LoadLibraryExA(lpszModName, 0, LOAD_LIBRARY_AS_DATAFILE);
2282 if(hDll == 0) {
2283 dprintf(("ERROR: GetVersionStruct: Unable to load module!!"));
2284 return 0;
2285 }
2286 winimage = (Win32ImageBase *)Win32DllBase::findModule(hDll);
2287 if(winimage != NULL) {
2288 rc = winimage->getVersionStruct(verstruct, bufLength);
2289 }
2290 else {
2291 dprintf(("GetVersionStruct; just loaded dll %s, but can't find it now!", lpszModName));
2292//// DebugInt3();
2293 }
2294 FreeLibrary(hDll);
2295 return rc;
2296}
2297//******************************************************************************
2298//******************************************************************************
2299ULONG SYSTEM GetVersionSize(char *lpszModName)
2300{
2301 Win32ImageBase *winimage;
2302 HINSTANCE hDll;
2303 ULONG size = 0;
2304
2305 dprintf(("GetVersionSize of %s", lpszModName));
2306 if (WinExe != NULL && WinExe->matchModName(lpszModName)) {
2307 return WinExe->getVersionSize();
2308 }
2309
2310 hDll = LoadLibraryExA(lpszModName, 0, LOAD_LIBRARY_AS_DATAFILE);
2311 if(hDll == 0) {
2312 dprintf(("ERROR: GetVersionStruct: Unable to load module!!"));
2313 return 0;
2314 }
2315 winimage = (Win32ImageBase *)Win32DllBase::findModule(hDll);
2316 if(winimage != NULL) {
2317 size = winimage->getVersionSize();
2318 }
2319 else {
2320 dprintf(("GetVersionSize; just loaded dll %s, but can't find it now!", lpszModName));
2321//// DebugInt3();
2322 }
2323 FreeLibrary(hDll);
2324 return size;
2325}
2326//******************************************************************************
2327//******************************************************************************
2328BOOL WIN32API DisableThreadLibraryCalls(HMODULE hModule)
2329{
2330 Win32DllBase *winmod;
2331 FARPROC proc;
2332 ULONG ulAPIOrdinal;
2333
2334 winmod = Win32DllBase::findModule((HINSTANCE)hModule);
2335 if(winmod)
2336 {
2337 // don't call ATTACH/DETACH thread functions in DLL
2338 winmod->disableThreadLibraryCalls();
2339 return TRUE;
2340 }
2341 else
2342 {
2343 // raise error condition
2344 SetLastError(ERROR_INVALID_HANDLE);
2345 return FALSE;
2346 }
2347}
2348//******************************************************************************
2349//******************************************************************************
Note: See TracBrowser for help on using the repository browser.