forked from mhammond/pywin32
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathdllmain.cpp
More file actions
309 lines (267 loc) · 10.4 KB
/
dllmain.cpp
File metadata and controls
309 lines (267 loc) · 10.4 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
// PythonCOM.cpp : Implementation of DLL Exports.
#include "stdafx.h"
#include <windows.h>
#include <Python.h>
#include <pythonrun.h> /* for Py_Initialize() */
#include <import.h> /* for PyImport_GetModuleDict() */
#include "PythonCOM.h"
#include "PythonCOMServer.h"
#include "PyFactory.h"
extern void FreeGatewayModule(void);
extern int PyCom_RegisterCoreSupport(void);
extern int PyCom_UnregisterCoreSupport(void);
/*
** This value counts the number of references to objects that contain code
** within this DLL. The DLL cannot be unloaded until this reaches 0.
**
** Additional locks (such as via the LockServer method on a CPyFactory) can
** add a "pseudo-reference" to ensure the DLL is not tossed.
*/
static LONG g_cLockCount = 0;
static BOOL bDidInitPython = FALSE;
static PyThreadState *ptsGlobal = NULL;
/*
** To support servers in .EXE's, PythonCOM allows you to register a threadID.
** A WM_QUIT message will be posted this thread when the external locks on the
** objects hits zero.
*/
static DWORD dwQuitThreadId = 0;
PYCOM_EXPORT void PyCom_EnableQuitMessage(DWORD threadId) { dwQuitThreadId = threadId; }
static BOOL hasInitialized = FALSE;
void PyCom_DLLAddRef(void)
{
// Must be thread-safe, although can't have the Python lock!
CEnterLeaveFramework _celf;
LONG cnt = InterlockedIncrement(&g_cLockCount);
if (cnt == 1) { // First call
// There is a situation where this code falls down. An IClassFactory destructor
// imcrements the DLL ref count, to make up for the decrement done by PyIUnknown
// (as we don't want class factories in the count). This works fine until the last
// reference is that IClassFactory - the g_cLockCount was zero, so transitions
// temporarily to 1 - leading us to this sad situation where we try and re-init
// Python as we tear down.
if (hasInitialized) {
return;
}
hasInitialized = TRUE; // must be set even if we didn't actually Py_Init.
// the last COM object
if (!Py_IsInitialized()) {
Py_Initialize();
// Make sure our Windows framework is all setup.
PyWinGlobals_Ensure();
// COM interfaces registered
PyCom_RegisterCoreSupport();
// Make sure we have _something_ as sys.argv.
if (PySys_GetObject("argv") == NULL) {
PyObject *path = PyList_New(0);
PyObject *str = PyWinCoreString_FromString("");
PyList_Append(path, str);
PySys_SetObject("argv", path);
Py_XDECREF(path);
Py_XDECREF(str);
}
// Release Python lock, as first thing we do is re-get it.
ptsGlobal = PyEval_SaveThread();
bDidInitPython = TRUE;
// NOTE: We no longer ever finalize Python!!
}
}
}
void PyCom_DLLReleaseRef(void)
{
/*** NOTE: We no longer finalize Python EVER in the COM world
see pycom-dev mailing list archives from April 2000 for why
***/
// Must be thread-safe, although can't have the Python lock!
// only needed when we finalize.
// CEnterLeaveFramework _celf;
LONG cnt = InterlockedDecrement(&g_cLockCount);
// Not optimal, but anything better is hard - g_cLockCount
// could always transition 1->0->1 at some stage, screwing this
// up. Oh well...
if (cnt == 0) {
// Send a quit message to the registered thread (if we have one)
if (dwQuitThreadId)
PostThreadMessage(dwQuitThreadId, WM_QUIT, 0, 0);
/*** Old finalize code
if (bDidInitPython) {
PyEval_RestoreThread(ptsGlobal);
PyWinGlobals_Free();
FreeGatewayModule();
Py_Finalize();
bDidInitPython=FALSE;
}
***/
}
}
/////////////////////////////////////////////////////////////////////////////
// DLL Entry Point
static DWORD g_dwCoInitThread = 0;
static BOOL g_bCoInitThreadHasInit = FALSE;
extern "C" __declspec(dllexport) BOOL WINAPI DllMain(HINSTANCE hInstance, DWORD dwReason, LPVOID lpReserved)
{
if (dwReason == DLL_PROCESS_ATTACH) {
// LogEvent("Loaded pythoncom.dll");
/*
** NOTE: we assume that our globals are not shared among processes,
** so for all intents and purposes, we can only hit this body
** of code "once" (from the standpoint of what our variables
** tell us).
*/
/* We don't assume anything about Python's init state here!
/*
** we don't need to be notified about threads
*/
DisableThreadLibraryCalls(hInstance);
}
else if (dwReason == DLL_PROCESS_DETACH) {
// LogEvent("Terminated pythoncom.dll");
{
// CEnterLeavePython celp;
/* free the gateway module if loaded (see PythonCOMObj.cpp) */
// (void)PyCom_UnregisterCoreSupport();
}
// Call our helper to do smart Uninit of OLE.
// XXX - this seems to regularly hang - probably because it is being
// called from DllMain, and therefore threading issues get in the way!
// PyCom_CoUninitialize();
}
return TRUE; // ok
}
typedef HRESULT(WINAPI *PFNCoInitializeEx)(LPVOID pvReserved, DWORD dwCoInit);
// Some clients or COM extensions (notably MAPI) are _very_
// particular about the order of shutdown - in MAPI's case, you MUST
// do the CoUninit _before_ the MAPIUninit.
// These functions have logic so the Python programmer can call either
// the Init for Term function explicitely, and the framework will detect
// it no longer needs doing.
// XXX - Needs more thought about threading implications.
HRESULT PyCom_CoInitializeEx(LPVOID reserved, DWORD dwInit)
{
// Must be thread-safe, although doesn't need the Python lock.
CEnterLeaveFramework _celf;
if (g_bCoInitThreadHasInit && g_dwCoInitThread == GetCurrentThreadId())
return S_OK;
// Do a LoadLibrary, as the Ex version may not always exist on Win95.
// TODO: We no longer support Windows 95, how should this code be updated ?
HMODULE hMod = GetModuleHandle(_T("ole32.dll"));
if (hMod == 0)
return E_HANDLE;
FARPROC fp = GetProcAddress(hMod, "CoInitializeEx");
if (fp == NULL)
return E_NOTIMPL;
PFNCoInitializeEx mypfn;
mypfn = (PFNCoInitializeEx)fp;
HRESULT hr = (*mypfn)(reserved, dwInit);
// Unlike PyCom_CoInitialize, we return _all_ errors including
// RPC_E_CHANGED_MODE
if (FAILED(hr)) {
if (hr != RPC_E_CHANGED_MODE)
PyCom_LoggerException(NULL, L"CoInitializeEx failed (0x%08lx)", hr);
return hr;
}
// If we have never been initialized before, then consider this
// thread our "main initializer" thread.
if (g_dwCoInitThread == 0 && hr == S_OK) {
g_dwCoInitThread = GetCurrentThreadId();
g_bCoInitThreadHasInit = TRUE;
}
return hr;
}
HRESULT PyCom_CoInitialize(LPVOID reserved)
{
// Must be thread-safe, although doesn't need the Python lock.
CEnterLeaveFramework _celf;
// If our "main" thread has ever called this before, just
// ignore it. If it is another thread, then that thread
// must manage itself.
if (g_bCoInitThreadHasInit && g_dwCoInitThread == GetCurrentThreadId())
return S_OK;
HRESULT hr = CoInitialize(reserved);
if ((hr != RPC_E_CHANGED_MODE) && FAILED(hr)) {
PyCom_LoggerException(NULL, L"OLE initialization failed! (0x%08lx)", hr);
return hr;
}
// If we have never been initialized before, then consider this
// thread our "main initializer" thread.
if (g_dwCoInitThread == 0 && hr == S_OK) {
g_dwCoInitThread = GetCurrentThreadId();
g_bCoInitThreadHasInit = TRUE;
}
return hr;
}
void PyCom_CoUninitialize()
{
// Must be thread-safe, although doesn't need the Python lock.
CEnterLeaveFramework _celf;
if (g_dwCoInitThread == GetCurrentThreadId()) {
// being asked to terminate on our "main" thread
// Check our flag, but always consider it success.
if (g_bCoInitThreadHasInit) {
CoUninitialize();
g_bCoInitThreadHasInit = FALSE;
}
}
else {
// Not our thread - assume caller knows what they are doing
CoUninitialize();
}
}
/////////////////////////////////////////////////////////////////////////////
// Used to determine whether the DLL can be unloaded by OLE
STDAPI DllCanUnloadNow(void)
{
// If we don't finalize Python, we should never unload!
return S_FALSE;
// return g_cLockCount ? S_FALSE : S_OK;
}
/////////////////////////////////////////////////////////////////////////////
// Returns a class factory to create an object of the requested type
STDAPI DllGetClassObject(REFCLSID rclsid, REFIID riid, LPVOID *ppv)
{
// PyCom_StreamMessage("in DllGetClassObject\n");
if (ppv == NULL)
return E_INVALIDARG;
if (!IsEqualIID(riid, IID_IUnknown) && !IsEqualIID(riid, IID_IClassFactory))
return E_INVALIDARG;
// ### validate that we support rclsid?
/* Put the factory right into *ppv; we know it supports <riid> */
*ppv = (LPVOID *)new CPyFactory(rclsid);
if (*ppv == NULL)
return E_OUTOFMEMORY;
return S_OK;
}
//////////////////////////////////////////////////////////////////////////////
// Auto Registration Stuff
// fileName is as passed to regsvr32
// argc and argv are what Python should see as sys.argv
HRESULT DoRegisterUnregister(LPCSTR fileName, int argc, char **argv)
{
FILE *fp = fopen(fileName, "r");
if (fp == NULL)
return HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND);
HRESULT hr = S_OK;
// Let the existing COM framework manage the Python state for us!
PyCom_DLLAddRef();
{ // A scope for _celp
CEnterLeavePython _celp;
PySys_SetArgv(argc, __wargv);
if (PyRun_SimpleFile(fp, (char *)fileName) != 0) {
// Convert the Python error to a HRESULT.
hr = PyCom_SetCOMErrorFromPyException();
}
} // End scope.
fclose(fp);
PyCom_DLLReleaseRef();
return hr;
}
extern "C" __declspec(dllexport) HRESULT DllRegisterServerEx(LPCSTR fileName)
{
char *argv[] = {"regsvr32.exe"};
return DoRegisterUnregister(fileName, 1, argv);
}
extern "C" __declspec(dllexport) HRESULT DllUnregisterServerEx(LPCSTR fileName)
{
char *argv[] = {"regsvr32.exe", "--unregister"};
return DoRegisterUnregister(fileName, 2, argv);
}