ec528d2ead2fd703b54858406d1cb4e7e243e276
[reactos.git] / dll / appcompat / shims / shimlib / shimlib.c
1 /*
2 * PROJECT: ReactOS Shim helper library
3 * LICENSE: GPL-2.0+ (https://spdx.org/licenses/GPL-2.0+)
4 * PURPOSE: Shim helper functions
5 * COPYRIGHT: Copyright 2016,2017 Mark Jansen (mark.jansen@reactos.org)
6 */
7
8 #define WIN32_NO_STATUS
9 #include <windef.h>
10 #include <winbase.h>
11 #include <shimlib.h>
12 #include <strsafe.h>
13 #include <ndk/rtlfuncs.h>
14
15 typedef struct UsedShim
16 {
17 SLIST_ENTRY Entry;
18 PSHIMREG pShim;
19 #if (WINVER > _WIN32_WINNT_WS03)
20 BOOL bInitCalled;
21 #endif
22 } UsedShim, *pUsedShim;
23
24
25 ULONG g_ShimEngDebugLevel = 0xffffffff;
26 static HANDLE g_ShimLib_Heap;
27 static PSLIST_HEADER g_UsedShims;
28
29 void ShimLib_Init(HINSTANCE hInstance)
30 {
31 g_ShimLib_Heap = HeapCreate(0, 0x10000, 0);
32
33 g_UsedShims = (PSLIST_HEADER)ShimLib_ShimMalloc(sizeof(SLIST_HEADER));
34 RtlInitializeSListHead(g_UsedShims);
35 }
36
37 void ShimLib_Deinit()
38 {
39 // Is this a good idea?
40 HeapDestroy(g_ShimLib_Heap);
41 }
42
43 PVOID ShimLib_ShimMalloc(SIZE_T dwSize)
44 {
45 return HeapAlloc(g_ShimLib_Heap, 0, dwSize);
46 }
47
48 void ShimLib_ShimFree(PVOID pData)
49 {
50 HeapFree(g_ShimLib_Heap, 0, pData);
51 }
52
53 PCSTR ShimLib_StringDuplicateA(PCSTR szString)
54 {
55 SIZE_T Length = lstrlenA(szString);
56 PSTR NewString = ShimLib_ShimMalloc(Length+1);
57 return lstrcpyA(NewString, szString);
58 }
59
60 BOOL ShimLib_StrAEqualsW(PCSTR szString, PCWSTR wszString)
61 {
62 while (*szString == *wszString)
63 {
64 if (!*szString)
65 return TRUE;
66
67 szString++; wszString++;
68 }
69 return FALSE;
70 }
71
72 #if defined(_MSC_VER)
73
74 #if defined(_M_IA64) || defined(_M_AMD64)
75 #define _ATTRIBUTES read
76 #else
77 #define _ATTRIBUTES read
78 #endif
79
80
81 #pragma section(".shm",long,read)
82 #pragma section(".shm$AAA",long,read)
83 #pragma section(".shm$ZZZ",long,read)
84 #endif
85
86 #ifdef _MSC_VER
87 #pragma comment(linker, "/merge:.shm=.rdata")
88 #endif
89
90
91 _SHMALLOC(".shm") SHIMREG _shim_start = { 0 };
92 _SHMALLOC(".shm$ZZZ") SHIMREG _shim_end = { 0 };
93
94
95 /* Generic GetHookAPIs function.
96 The macro's from <setup_shim.inl> and <implement_shim.inl> will register a list of all apis that should be hooked
97 for a specific shim
98 This helper function will return the correct shim, and call the init function */
99 PHOOKAPI WINAPI ShimLib_GetHookAPIs(IN LPCSTR szCommandLine, IN LPCWSTR wszShimName, OUT PDWORD pdwHookCount)
100 {
101 PSHIMREG ps = &_shim_start;
102 ps++;
103 for (; ps != &_shim_end; ps++)
104 {
105 if (ps->GetHookAPIs != NULL && ps->ShimName != NULL)
106 {
107 if (ShimLib_StrAEqualsW(ps->ShimName, wszShimName))
108 {
109 pUsedShim shim = (pUsedShim)ShimLib_ShimMalloc(sizeof(UsedShim));
110 shim->pShim = ps;
111 #if (WINVER > _WIN32_WINNT_WS03)
112 shim->bInitCalled = FALSE;
113 #endif
114 RtlInterlockedPushEntrySList(g_UsedShims, &(shim->Entry));
115
116 return ps->GetHookAPIs(SHIM_NOTIFY_ATTACH, szCommandLine, pdwHookCount);
117 }
118 }
119 }
120 return NULL;
121 }
122
123
124 BOOL WINAPI ShimLib_NotifyShims(DWORD fdwReason, PVOID ptr)
125 {
126 PSLIST_ENTRY pEntry = RtlFirstEntrySList(g_UsedShims);
127
128 if (fdwReason < SHIM_REASON_INIT)
129 fdwReason += (SHIM_REASON_INIT - SHIM_NOTIFY_ATTACH);
130
131 while (pEntry)
132 {
133 pUsedShim pUsed = CONTAINING_RECORD(pEntry, UsedShim, Entry);
134 _PVNotify Notify = pUsed->pShim->Notify;
135 #if (WINVER > _WIN32_WINNT_WS03)
136 if (pUsed->bInitCalled && fdwReason == SHIM_REASON_INIT)
137 Notify = NULL;
138 #endif
139 if (Notify)
140 Notify(fdwReason, ptr);
141 #if (WINVER > _WIN32_WINNT_WS03)
142 if (fdwReason == SHIM_REASON_INIT)
143 pUsed->bInitCalled = TRUE;
144 #endif
145
146 pEntry = pEntry->Next;
147 }
148
149 return TRUE;
150 }
151
152
153 VOID SeiInitDebugSupport(VOID)
154 {
155 static const UNICODE_STRING DebugKey = RTL_CONSTANT_STRING(L"SHIM_DEBUG_LEVEL");
156 UNICODE_STRING DebugValue;
157 NTSTATUS Status;
158 ULONG NewLevel = 0;
159 WCHAR Buffer[40];
160
161 RtlInitEmptyUnicodeString(&DebugValue, Buffer, sizeof(Buffer));
162
163 Status = RtlQueryEnvironmentVariable_U(NULL, &DebugKey, &DebugValue);
164
165 if (NT_SUCCESS(Status))
166 {
167 if (!NT_SUCCESS(RtlUnicodeStringToInteger(&DebugValue, 10, &NewLevel)))
168 NewLevel = 0;
169 }
170 g_ShimEngDebugLevel = NewLevel;
171 }
172
173
174 /**
175 * Outputs diagnostic info.
176 *
177 * @param [in] Level The level to log this message with, choose any of [SHIM_ERR,
178 * SHIM_WARN, SHIM_INFO].
179 * @param [in] FunctionName The function this log should be attributed to.
180 * @param [in] Format The format string.
181 * @param ... Variable arguments providing additional information.
182 *
183 * @return Success: TRUE Failure: FALSE.
184 */
185 BOOL WINAPIV SeiDbgPrint(SEI_LOG_LEVEL Level, PCSTR Function, PCSTR Format, ...)
186 {
187 char Buffer[512];
188 char* Current = Buffer;
189 const char* LevelStr;
190 size_t Length = sizeof(Buffer);
191 va_list ArgList;
192 HRESULT hr;
193
194 if (g_ShimEngDebugLevel == 0xffffffff)
195 SeiInitDebugSupport();
196
197 if (Level > g_ShimEngDebugLevel)
198 return FALSE;
199
200 switch (Level)
201 {
202 case SEI_MSG:
203 LevelStr = "MSG ";
204 break;
205 case SEI_FAIL:
206 LevelStr = "FAIL";
207 break;
208 case SEI_WARN:
209 LevelStr = "WARN";
210 break;
211 case SEI_INFO:
212 LevelStr = "INFO";
213 break;
214 default:
215 LevelStr = "USER";
216 break;
217 }
218
219 if (Function)
220 hr = StringCchPrintfExA(Current, Length, &Current, &Length, STRSAFE_NULL_ON_FAILURE, "[%s] [%s] ", LevelStr, Function);
221 else
222 hr = StringCchPrintfExA(Current, Length, &Current, &Length, STRSAFE_NULL_ON_FAILURE, "[%s] ", LevelStr);
223
224 if (!SUCCEEDED(hr))
225 return FALSE;
226
227 va_start(ArgList, Format);
228 hr = StringCchVPrintfExA(Current, Length, &Current, &Length, STRSAFE_NULL_ON_FAILURE, Format, ArgList);
229 va_end(ArgList);
230 if (!SUCCEEDED(hr))
231 return FALSE;
232
233 DbgPrint("%s", Buffer);
234 return TRUE;
235 }
236