rundll32: Convert rundll32 to Unicode.
[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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, 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 /*
47  * Control_RunDLL has these parameters
48  */
49 typedef void (WINAPI *EntryPointW)(HWND hWnd, HINSTANCE hInst, LPWSTR lpszCmdLine, int nCmdShow);
50 typedef void (WINAPI *EntryPointA)(HWND hWnd, HINSTANCE hInst, LPSTR lpszCmdLine, int nCmdShow);
51
52 /*
53  * Control_RunDLL needs to have a window. So lets make us a very
54  * simple window class.
55  */
56 static const WCHAR szTitle[] = {'r','u','n','d','l','l','3','2',0};
57 static const WCHAR szWindowClass[] = {'c','l','a','s','s','_','r','u','n','d','l','l','3','2',0};
58 static const WCHAR kernel32[] = {'k','e','r','n','e','l','3','2','.','d','l','l',0};
59 static const WCHAR shell32[] = {'s','h','e','l','l','3','2','.','d','l','l',0};
60
61 static HINSTANCE16 (WINAPI *pLoadLibrary16)(LPCSTR libname);
62 static FARPROC16 (WINAPI *pGetProcAddress16)(HMODULE16 hModule, LPCSTR name);
63 static void (WINAPI *pRunDLL_CallEntry16)( FARPROC proc, HWND hwnd, HINSTANCE inst,
64                                            LPCSTR cmdline, INT cmdshow );
65
66 static ATOM register_class(void)
67 {
68     WNDCLASSEXW wcex;
69
70     wcex.cbSize = sizeof(WNDCLASSEXW);
71
72     wcex.style          = CS_HREDRAW | CS_VREDRAW;
73     wcex.lpfnWndProc    = DefWindowProcW;
74     wcex.cbClsExtra     = 0;
75     wcex.cbWndExtra     = 0;
76     wcex.hInstance      = NULL;
77     wcex.hIcon          = NULL;
78     wcex.hCursor        = LoadCursorW(NULL, (LPCWSTR)IDC_ARROW);
79     wcex.hbrBackground  = (HBRUSH)(COLOR_WINDOW+1);
80     wcex.lpszMenuName   = NULL;
81     wcex.lpszClassName  = szWindowClass;
82     wcex.hIconSm        = NULL;
83
84     return RegisterClassExW(&wcex);
85 }
86
87 static HINSTANCE16 load_dll16( LPCWSTR dll )
88 {
89     HINSTANCE16 ret = 0;
90     DWORD len = WideCharToMultiByte( CP_ACP, 0, dll, -1, NULL, 0, NULL, NULL );
91     char *dllA = HeapAlloc( GetProcessHeap(), 0, len );
92
93     if (dllA)
94     {
95         WideCharToMultiByte( CP_ACP, 0, dll, -1, dllA, len, NULL, NULL );
96         pLoadLibrary16 = (void *)GetProcAddress( GetModuleHandleW(kernel32), (LPCSTR)35 );
97         if (pLoadLibrary16) ret = pLoadLibrary16( dllA );
98         HeapFree( GetProcessHeap(), 0, dllA );
99     }
100     return ret;
101 }
102
103 static FARPROC16 get_entry_point16( HINSTANCE16 inst, LPCWSTR entry )
104 {
105     FARPROC16 ret = 0;
106     DWORD len = WideCharToMultiByte( CP_ACP, 0, entry, -1, NULL, 0, NULL, NULL );
107     char *entryA = HeapAlloc( GetProcessHeap(), 0, len );
108
109     if (entryA)
110     {
111         WideCharToMultiByte( CP_ACP, 0, entry, -1, entryA, len, NULL, NULL );
112         pGetProcAddress16 = (void *)GetProcAddress( GetModuleHandleW(kernel32), (LPCSTR)37 );
113         if (pGetProcAddress16) ret = pGetProcAddress16( inst, entryA );
114         HeapFree( GetProcessHeap(), 0, entryA );
115     }
116     return ret;
117 }
118
119 static void *get_entry_point32( HMODULE module, LPCWSTR entry, BOOL *unicode )
120 {
121     void *ret;
122     DWORD len = WideCharToMultiByte( CP_ACP, 0, entry, -1, NULL, 0, NULL, NULL );
123     char *entryA = HeapAlloc( GetProcessHeap(), 0, len + 1 );
124
125     if (!entryA)
126         return NULL;
127
128     WideCharToMultiByte( CP_ACP, 0, entry, -1, entryA, len, NULL, NULL );
129
130     /* first try the W version */
131     *unicode = TRUE;
132     strcat( entryA, "W" );
133     if (!(ret = GetProcAddress( module, entryA )))
134     {
135         /* now the A version */
136         *unicode = FALSE;
137         entryA[strlen(entryA)-1] = 'A';
138         if (!(ret = GetProcAddress( module, entryA )))
139         {
140             /* now the version without suffix */
141             entryA[strlen(entryA)-1] = 0;
142             ret = GetProcAddress( module, entryA );
143         }
144     }
145     HeapFree( GetProcessHeap(), 0, entryA );
146     return ret;
147 }
148
149 static LPWSTR get_next_arg(LPWSTR *cmdline)
150 {
151     LPWSTR s;
152     LPWSTR arg,d;
153     int in_quotes,bcount,len=0;
154
155     /* count the chars */
156     bcount=0;
157     in_quotes=0;
158     s=*cmdline;
159     while (1) {
160         if (*s==0 || ((*s=='\t' || *s==' ') && !in_quotes)) {
161             /* end of this command line argument */
162             break;
163         } else if (*s=='\\') {
164             /* '\', count them */
165             bcount++;
166         } else if ((*s=='"') && ((bcount & 1)==0)) {
167             /* unescaped '"' */
168             in_quotes=!in_quotes;
169             bcount=0;
170         } else {
171             /* a regular character */
172             bcount=0;
173         }
174         s++;
175         len++;
176     }
177     arg=HeapAlloc(GetProcessHeap(), 0, (len+1)*sizeof(WCHAR));
178     if (!arg)
179         return NULL;
180
181     bcount=0;
182     in_quotes=0;
183     d=arg;
184     s=*cmdline;
185     while (*s) {
186         if ((*s=='\t' || *s==' ') && !in_quotes) {
187             /* end of this command line argument */
188             break;
189         } else if (*s=='\\') {
190             /* '\\' */
191             *d++=*s++;
192             bcount++;
193         } else if (*s=='"') {
194             /* '"' */
195             if ((bcount & 1)==0) {
196                 /* Preceded by an even number of '\', this is half that
197                  * number of '\', plus a quote which we erase.
198                  */
199                 d-=bcount/2;
200                 in_quotes=!in_quotes;
201                 s++;
202             } else {
203                 /* Preceded by an odd number of '\', this is half that
204                  * number of '\' followed by a '"'
205                  */
206                 d=d-bcount/2-1;
207                 *d++='"';
208                 s++;
209             }
210             bcount=0;
211         } else {
212             /* a regular character */
213             *d++=*s++;
214             bcount=0;
215         }
216     }
217     *d=0;
218     *cmdline=s;
219
220     /* skip the remaining spaces */
221     while (**cmdline=='\t' || **cmdline==' ') {
222         (*cmdline)++;
223     }
224
225     return arg;
226 }
227
228 int WINAPI wWinMain(HINSTANCE instance, HINSTANCE hOldInstance, LPWSTR szCmdLine, int nCmdShow)
229 {
230     HWND hWnd;
231     LPWSTR szDllName,szEntryPoint;
232     void *entry_point;
233     BOOL unicode = FALSE, win16;
234     STARTUPINFOW info;
235     HMODULE hDll;
236
237     hWnd=NULL;
238     hDll=NULL;
239     szDllName=NULL;
240
241     /* Initialize the rundll32 class */
242     register_class();
243     hWnd = CreateWindowW(szWindowClass, szTitle,
244           WS_OVERLAPPEDWINDOW|WS_VISIBLE,
245           CW_USEDEFAULT, 0, CW_USEDEFAULT, 0, NULL, NULL, NULL, NULL);
246
247     /* Get the dll name and API EntryPoint */
248     WINE_TRACE("CmdLine=%s\n",wine_dbgstr_w(szCmdLine));
249     szDllName = get_next_arg(&szCmdLine);
250     if (!szDllName || *szDllName==0)
251         goto CLEANUP;
252     WINE_TRACE("DllName=%s\n",wine_dbgstr_w(szDllName));
253     if ((szEntryPoint = strchrW(szDllName, ',' )))
254         *szEntryPoint++=0;
255     else
256         szEntryPoint = get_next_arg(&szCmdLine);
257     WINE_TRACE("EntryPoint=%s\n",wine_dbgstr_w(szEntryPoint));
258
259     /* Load the library */
260     hDll=LoadLibraryW(szDllName);
261     if (hDll)
262     {
263         win16 = FALSE;
264         entry_point = get_entry_point32( hDll, szEntryPoint, &unicode );
265     }
266     else
267     {
268         HINSTANCE16 dll = load_dll16( szDllName );
269         if (dll <= 32)
270         {
271             /* Windows has a MessageBox here... */
272             WINE_ERR("Unable to load %s\n",wine_dbgstr_w(szDllName));
273             goto CLEANUP;
274         }
275         win16 = TRUE;
276         unicode = FALSE;
277         entry_point = get_entry_point16( dll, szEntryPoint );
278     }
279
280     if (!entry_point)
281     {
282         /* Windows has a MessageBox here... */
283         WINE_ERR( "Unable to find the entry point %s in %s\n",
284                   wine_dbgstr_w(szEntryPoint), wine_dbgstr_w(szDllName) );
285         goto CLEANUP;
286     }
287
288     GetStartupInfoW( &info );
289     if (!(info.dwFlags & STARTF_USESHOWWINDOW)) info.wShowWindow = SW_SHOWDEFAULT;
290
291     if (unicode)
292     {
293         EntryPointW pEntryPointW = entry_point;
294
295         WINE_TRACE( "Calling %s (%p,%p,%s,%d)\n", wine_dbgstr_w(szEntryPoint),
296                     hWnd, instance, wine_dbgstr_w(szCmdLine), info.wShowWindow );
297
298         pEntryPointW( hWnd, instance, szCmdLine, info.wShowWindow );
299     }
300     else
301     {
302         DWORD len = WideCharToMultiByte( CP_ACP, 0, szCmdLine, -1, NULL, 0, NULL, NULL );
303         char *cmdline = HeapAlloc( GetProcessHeap(), 0, len );
304
305         if (!cmdline)
306             goto CLEANUP;
307
308         WideCharToMultiByte( CP_ACP, 0, szCmdLine, -1, cmdline, len, NULL, NULL );
309
310         WINE_TRACE( "Calling %s (%p,%p,%s,%d)\n", wine_dbgstr_w(szEntryPoint),
311                     hWnd, instance, wine_dbgstr_a(cmdline), info.wShowWindow );
312
313         if (win16)
314         {
315             HMODULE shell = LoadLibraryW( shell32 );
316             if (shell) pRunDLL_CallEntry16 = (void *)GetProcAddress( shell, (LPCSTR)122 );
317             if (pRunDLL_CallEntry16)
318                 pRunDLL_CallEntry16( entry_point, hWnd, instance, cmdline, info.wShowWindow );
319         }
320         else
321         {
322             EntryPointA pEntryPointA = entry_point;
323             pEntryPointA( hWnd, instance, cmdline, info.wShowWindow );
324         }
325         HeapFree( GetProcessHeap(), 0, cmdline );
326     }
327
328 CLEANUP:
329     if (hWnd)
330         DestroyWindow(hWnd);
331     if (hDll)
332         FreeLibrary(hDll);
333     HeapFree(GetProcessHeap(),0,szDllName);
334     return 0; /* rundll32 always returns 0! */
335 }