Added support for loading 16-bit dlls.
[wine] / programs / rundll32 / rundll32.c
1 /*
2  * PURPOSE: Load a DLL and run an entry point with the specified parameters
3  *
4  * Copyright 2002 Alberto Massari
5  * Copyright 2001-2003 Aric Stewart for CodeWeavers
6  * Copyright 2003 Mike McCormack for CodeWeavers
7  *
8  * This library is free software; you can redistribute it and/or
9  * modify it under the terms of the GNU Lesser General Public
10  * License as published by the Free Software Foundation; either
11  * version 2.1 of the License, or (at your option) any later version.
12  *
13  * This library is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
16  * Lesser General Public License for more details.
17  *
18  * You should have received a copy of the GNU Lesser General Public
19  * License along with this library; if not, write to the Free Software
20  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
21  *
22  */
23
24 /*
25  *
26  *  rundll32 dllname,entrypoint [arguments]
27  *
28  *  Documentation for this utility found on KB Q164787
29  *
30  */
31
32 #include <stdio.h>
33 #include <string.h>
34 #include <stdlib.h>
35
36 /* Exclude rarely-used stuff from Windows headers */
37 #define WIN32_LEAN_AND_MEAN
38 #include "windows.h"
39 #include "wine/winbase16.h"
40 #include "wine/unicode.h"
41 #include "wine/debug.h"
42
43 WINE_DEFAULT_DEBUG_CHANNEL(rundll32);
44
45
46 extern void WINAPI RunDLL_CallEntry16( FARPROC proc, HWND hwnd, HINSTANCE inst,
47                                        LPCSTR cmdline, INT cmdshow );
48
49 /*
50  * Control_RunDLL has these parameters
51  */
52 typedef void (WINAPI *EntryPointW)(HWND hWnd, HINSTANCE hInst, LPWSTR lpszCmdLine, int nCmdShow);
53 typedef void (WINAPI *EntryPointA)(HWND hWnd, HINSTANCE hInst, LPSTR lpszCmdLine, int nCmdShow);
54
55 /*
56  * Control_RunDLL needs to have a window. So lets make us a very
57  * simple window class.
58  */
59 static TCHAR  *szTitle = "rundll32";
60 static TCHAR  *szWindowClass = "class_rundll32";
61
62 static ATOM MyRegisterClass(HINSTANCE hInstance)
63 {
64     WNDCLASSEX wcex;
65
66     wcex.cbSize = sizeof(WNDCLASSEX);
67
68     wcex.style          = CS_HREDRAW | CS_VREDRAW;
69     wcex.lpfnWndProc    = (WNDPROC)DefWindowProc;
70     wcex.cbClsExtra     = 0;
71     wcex.cbWndExtra     = 0;
72     wcex.hInstance      = hInstance;
73     wcex.hIcon          = NULL;
74     wcex.hCursor        = LoadCursor(NULL, IDC_ARROW);
75     wcex.hbrBackground  = (HBRUSH)(COLOR_WINDOW+1);
76     wcex.lpszMenuName   = NULL;
77     wcex.lpszClassName  = szWindowClass;
78     wcex.hIconSm        = NULL;
79
80     return RegisterClassEx(&wcex);
81 }
82
83 static HINSTANCE16 load_dll16( LPCWSTR dll )
84 {
85     HINSTANCE16 ret;
86     DWORD len = WideCharToMultiByte( CP_ACP, 0, dll, -1, NULL, 0, NULL, NULL );
87     char *dllA = HeapAlloc( GetProcessHeap(), 0, len );
88     WideCharToMultiByte( CP_ACP, 0, dll, -1, dllA, len, NULL, NULL );
89     ret = LoadLibrary16( dllA );
90     HeapFree( GetProcessHeap(), 0, dllA );
91     return ret;
92 }
93
94 static FARPROC16 get_entry_point16( HINSTANCE16 inst, LPCWSTR entry )
95 {
96     FARPROC16 ret;
97     DWORD len = WideCharToMultiByte( CP_ACP, 0, entry, -1, NULL, 0, NULL, NULL );
98     char *entryA = HeapAlloc( GetProcessHeap(), 0, len );
99     WideCharToMultiByte( CP_ACP, 0, entry, -1, entryA, len, NULL, NULL );
100     ret = GetProcAddress16( inst, entryA );
101     HeapFree( GetProcessHeap(), 0, entryA );
102     return ret;
103 }
104
105 static void *get_entry_point32( HMODULE module, LPCWSTR entry, BOOL *unicode )
106 {
107     void *ret;
108     DWORD len = WideCharToMultiByte( CP_ACP, 0, entry, -1, NULL, 0, NULL, NULL );
109     char *entryA = HeapAlloc( GetProcessHeap(), 0, len + 1 );
110     WideCharToMultiByte( CP_ACP, 0, entry, -1, entryA, len, NULL, NULL );
111
112     /* first try the W version */
113     *unicode = TRUE;
114     strcat( entryA, "W" );
115     if (!(ret = GetProcAddress( module, entryA )))
116     {
117         /* now the A version */
118         *unicode = FALSE;
119         entryA[strlen(entryA)-1] = 'A';
120         if (!(ret = GetProcAddress( module, entryA )))
121         {
122             /* now the version without suffix */
123             entryA[strlen(entryA)-1] = 0;
124             ret = GetProcAddress( module, entryA );
125         }
126     }
127     HeapFree( GetProcessHeap(), 0, entryA );
128     return ret;
129 }
130
131 static LPWSTR GetNextArg(LPWSTR *cmdline)
132 {
133     LPWSTR s;
134     LPWSTR arg,d;
135     int in_quotes,bcount,len=0;
136
137     /* count the chars */
138     bcount=0;
139     in_quotes=0;
140     s=*cmdline;
141     while (1) {
142         if (*s==0 || ((*s=='\t' || *s==' ') && !in_quotes)) {
143             /* end of this command line argument */
144             break;
145         } else if (*s=='\\') {
146             /* '\', count them */
147             bcount++;
148         } else if ((*s=='"') && ((bcount & 1)==0)) {
149             /* unescaped '"' */
150             in_quotes=!in_quotes;
151             bcount=0;
152         } else {
153             /* a regular character */
154             bcount=0;
155         }
156         s++;
157         len++;
158     }
159     arg=HeapAlloc(GetProcessHeap(), 0, (len+1)*sizeof(WCHAR));
160     if (!arg)
161         return NULL;
162
163     bcount=0;
164     in_quotes=0;
165     d=arg;
166     s=*cmdline;
167     while (*s) {
168         if ((*s=='\t' || *s==' ') && !in_quotes) {
169             /* end of this command line argument */
170             break;
171         } else if (*s=='\\') {
172             /* '\\' */
173             *d++=*s++;
174             bcount++;
175         } else if (*s=='"') {
176             /* '"' */
177             if ((bcount & 1)==0) {
178                 /* Preceeded by an even number of '\', this is half that
179                  * number of '\', plus a quote which we erase.
180                  */
181                 d-=bcount/2;
182                 in_quotes=!in_quotes;
183                 s++;
184             } else {
185                 /* Preceeded by an odd number of '\', this is half that
186                  * number of '\' followed by a '"'
187                  */
188                 d=d-bcount/2-1;
189                 *d++='"';
190                 s++;
191             }
192             bcount=0;
193         } else {
194             /* a regular character */
195             *d++=*s++;
196             bcount=0;
197         }
198     }
199     *d=0;
200     *cmdline=s;
201
202     /* skip the remaining spaces */
203     while (**cmdline=='\t' || **cmdline==' ') {
204         (*cmdline)++;
205     }
206
207     return arg;
208 }
209
210 int main(int argc, char* argv[])
211 {
212     HWND hWnd;
213     LPWSTR szCmdLine;
214     LPWSTR szDllName,szEntryPoint;
215     void *entry_point;
216     BOOL unicode, win16;
217     STARTUPINFOW info;
218     HMODULE hDll, instance;
219
220     hWnd=NULL;
221     hDll=NULL;
222     szDllName=NULL;
223
224     /* Initialize the rundll32 class */
225     MyRegisterClass( NULL );
226     hWnd = CreateWindow(szWindowClass, szTitle,
227           WS_OVERLAPPEDWINDOW|WS_VISIBLE,
228           CW_USEDEFAULT, 0, CW_USEDEFAULT, 0, NULL, NULL, NULL, NULL);
229
230     /* Skip the rundll32.exe path */
231     szCmdLine=GetCommandLineW();
232     WINE_TRACE("CmdLine=%s\n",wine_dbgstr_w(szCmdLine));
233     szDllName=GetNextArg(&szCmdLine);
234     if (!szDllName || *szDllName==0)
235         goto CLEANUP;
236     HeapFree(GetProcessHeap(),0,szDllName);
237
238     /* Get the dll name and API EntryPoint */
239     szDllName=GetNextArg(&szCmdLine);
240     if (!szDllName || *szDllName==0)
241         goto CLEANUP;
242     WINE_TRACE("DllName=%s\n",wine_dbgstr_w(szDllName));
243     if (!(szEntryPoint = strchrW( szDllName, ',' ))) goto CLEANUP;
244     *szEntryPoint++=0;
245     WINE_TRACE("EntryPoint=%s\n",wine_dbgstr_w(szEntryPoint));
246
247     /* Load the library */
248     hDll=LoadLibraryW(szDllName);
249     if (hDll)
250     {
251         win16 = FALSE;
252         entry_point = get_entry_point32( hDll, szEntryPoint, &unicode );
253     }
254     else
255     {
256         HINSTANCE16 dll = load_dll16( szDllName );
257         if (dll <= 32)
258         {
259             /* Windows has a MessageBox here... */
260             WINE_ERR("Unable to load %s\n",wine_dbgstr_w(szDllName));
261             goto CLEANUP;
262         }
263         win16 = TRUE;
264         unicode = FALSE;
265         entry_point = get_entry_point16( dll, szEntryPoint );
266     }
267
268     if (!entry_point)
269     {
270         /* Windows has a MessageBox here... */
271         WINE_ERR( "Unable to find the entry point %s in %s\n",
272                   wine_dbgstr_w(szEntryPoint), wine_dbgstr_w(szDllName) );
273         goto CLEANUP;
274     }
275
276     GetStartupInfoW( &info );
277     if (!(info.dwFlags & STARTF_USESHOWWINDOW)) info.wShowWindow = SW_SHOWDEFAULT;
278     instance = GetModuleHandleW(NULL);  /* Windows always uses that, not hDll */
279
280     if (unicode)
281     {
282         EntryPointW pEntryPointW = entry_point;
283
284         WINE_TRACE( "Calling %s (%p,%p,%s,%d)\n", wine_dbgstr_w(szEntryPoint),
285                     hWnd, instance, wine_dbgstr_w(szCmdLine), info.wShowWindow );
286
287         pEntryPointW( hWnd, instance, szCmdLine, info.wShowWindow );
288     }
289     else
290     {
291         DWORD len = WideCharToMultiByte( CP_ACP, 0, szCmdLine, -1, NULL, 0, NULL, NULL );
292         char *cmdline = HeapAlloc( GetProcessHeap(), 0, len );
293         WideCharToMultiByte( CP_ACP, 0, szCmdLine, -1, cmdline, len, NULL, NULL );
294
295         WINE_TRACE( "Calling %s (%p,%p,%s,%d)\n", wine_dbgstr_w(szEntryPoint),
296                     hWnd, instance, wine_dbgstr_a(cmdline), info.wShowWindow );
297
298         if (win16) RunDLL_CallEntry16( entry_point, hWnd, instance, cmdline, info.wShowWindow );
299         else
300         {
301             EntryPointA pEntryPointA = entry_point;
302             pEntryPointA( hWnd, instance, cmdline, info.wShowWindow );
303         }
304         HeapFree( GetProcessHeap(), 0, cmdline );
305     }
306
307 CLEANUP:
308     if (hWnd)
309         DestroyWindow(hWnd);
310     if (hDll)
311         FreeLibrary(hDll);
312     if (szDllName)
313         HeapFree(GetProcessHeap(),0,szDllName);
314     return 0; /* rundll32 always returns 0! */
315 }