rundll32 requires a window for some functions to work, so create one.
[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/debug.h>
40
41 WINE_DEFAULT_DEBUG_CHANNEL(rundll32);
42
43
44 /*
45  * Control_RunDLL has these parameters
46  */
47 typedef void (WINAPI *EntryPointW)(HWND hWnd, HINSTANCE hInst, LPWSTR lpszCmdLine, int nCmdShow);
48 typedef void (WINAPI *EntryPointA)(HWND hWnd, HINSTANCE hInst, LPSTR lpszCmdLine, int nCmdShow);
49
50 /*
51  * Control_RunDLL needs to have a window. So lets make us a very
52  * simple window class.
53  */
54 static TCHAR  *szTitle = "rundll32";
55 static TCHAR  *szWindowClass = "class_rundll32";
56
57 static ATOM MyRegisterClass(HINSTANCE hInstance)
58     {
59     WNDCLASSEX wcex;
60
61     wcex.cbSize = sizeof(WNDCLASSEX);
62
63     wcex.style          = CS_HREDRAW | CS_VREDRAW;
64     wcex.lpfnWndProc    = (WNDPROC)DefWindowProc;
65     wcex.cbClsExtra     = 0;
66     wcex.cbWndExtra     = 0;
67     wcex.hInstance      = hInstance;
68     wcex.hIcon          = NULL;
69     wcex.hCursor        = LoadCursor(NULL, IDC_ARROW);
70     wcex.hbrBackground  = (HBRUSH)(COLOR_WINDOW+1);
71     wcex.lpszMenuName   = NULL;
72     wcex.lpszClassName  = szWindowClass;
73     wcex.hIconSm        = NULL;
74
75     return RegisterClassEx(&wcex);
76     }
77
78 static LPWSTR GetNextArg(LPCWSTR *cmdline)
79     {
80     LPCWSTR s;
81     LPWSTR arg,d;
82     int in_quotes,bcount,len=0;
83
84     /* count the chars */
85     bcount=0;
86     in_quotes=0;
87     s=*cmdline;
88     while (1) {
89         if (*s==0 || ((*s=='\t' || *s==' ') && !in_quotes)) {
90             /* end of this command line argument */
91             break;
92         } else if (*s=='\\') {
93             /* '\', count them */
94             bcount++;
95         } else if ((*s=='"') && ((bcount & 1)==0)) {
96             /* unescaped '"' */
97             in_quotes=!in_quotes;
98             bcount=0;
99         } else {
100             /* a regular character */
101             bcount=0;
102         }
103         s++;
104         len++;
105     }
106     arg=HeapAlloc(GetProcessHeap(), 0, (len+1)*sizeof(WCHAR));
107     if (!arg)
108         return NULL;
109
110     bcount=0;
111     in_quotes=0;
112     d=arg;
113     s=*cmdline;
114     while (*s) {
115         if ((*s=='\t' || *s==' ') && !in_quotes) {
116             /* end of this command line argument */
117             break;
118         } else if (*s=='\\') {
119             /* '\\' */
120             *d++=*s++;
121             bcount++;
122         } else if (*s=='"') {
123             /* '"' */
124             if ((bcount & 1)==0) {
125                 /* Preceeded by an even number of '\', this is half that
126                  * number of '\', plus a quote which we erase.
127                  */
128                 d-=bcount/2;
129                 in_quotes=!in_quotes;
130                 s++;
131             } else {
132                 /* Preceeded by an odd number of '\', this is half that
133                  * number of '\' followed by a '"'
134                  */
135                 d=d-bcount/2-1;
136                 *d++='"';
137                 s++;
138             }
139             bcount=0;
140         } else {
141             /* a regular character */
142             *d++=*s++;
143             bcount=0;
144     }
145     }
146     *d=0;
147     *cmdline=s;
148
149     /* skip the remaining spaces */
150     while (**cmdline=='\t' || **cmdline==' ') {
151         (*cmdline)++;
152     }
153
154     return arg;
155 }
156
157 int main(int argc, char* argv[])
158 {
159     HWND hWnd;
160     LPCWSTR szCmdLine;
161     LPWSTR szDllName,szEntryPoint;
162     char* szProcName;
163     HMODULE hDll;
164     EntryPointW pEntryPointW;
165     EntryPointA pEntryPointA;
166     int len;
167
168     hWnd=NULL;
169     hDll=NULL;
170     szDllName=NULL;
171     szProcName=NULL;
172
173     /* Initialize the rundll32 class */
174     MyRegisterClass( NULL );
175     hWnd = CreateWindow(szWindowClass, szTitle,
176           WS_OVERLAPPEDWINDOW|WS_VISIBLE,
177           CW_USEDEFAULT, 0, CW_USEDEFAULT, 0, NULL, NULL, NULL, NULL);
178
179     /* Skip the rundll32.exe path */
180     szCmdLine=GetCommandLineW();
181     WINE_TRACE("CmdLine=%s\n",wine_dbgstr_w(szCmdLine));
182     szDllName=GetNextArg(&szCmdLine);
183     if (!szDllName || *szDllName==0)
184         goto CLEANUP;
185     HeapFree(GetProcessHeap(),0,szDllName);
186
187     /* Get the dll name and API EntryPoint */
188     szDllName=GetNextArg(&szCmdLine);
189     if (!szDllName || *szDllName==0)
190         goto CLEANUP;
191     WINE_TRACE("DllName=%s\n",wine_dbgstr_w(szDllName));
192     szEntryPoint=szDllName;
193     while (*szEntryPoint!=0 && *szEntryPoint!=0x002c /* ',' */)
194         szEntryPoint++;
195     if (*szEntryPoint==0)
196         goto CLEANUP;
197     *szEntryPoint++=0;
198     WINE_TRACE("EntryPoint=%s\n",wine_dbgstr_w(szEntryPoint));
199
200     /* Load the library */
201     hDll=LoadLibraryW(szDllName);
202     if (!hDll)
203     {
204         /* Windows has a MessageBox here... */
205         WINE_WARN("Unable to load %s\n",wine_dbgstr_w(szDllName));
206         goto CLEANUP;
207     }
208
209     /* Try the Unicode entrypoint. Note that GetProcAddress only takes ascii
210      * names.
211      */
212     len=WideCharToMultiByte(CP_ACP,0,szEntryPoint,-1,NULL,0,NULL,NULL);
213     szProcName=HeapAlloc(GetProcessHeap(),0,len);
214     if (!szProcName)
215         goto CLEANUP;
216     WideCharToMultiByte(CP_ACP,0,szEntryPoint,-1,szProcName,len,NULL,NULL);
217     szProcName[len-1]=0x0057;
218     szProcName[len]=0;
219     pEntryPointW=(void*)GetProcAddress(hDll,szProcName);
220     if (pEntryPointW)
221     {
222         WCHAR* szArguments=NULL;
223
224         /* Make a copy of the arguments so they are writable */
225         len=lstrlenW(szCmdLine);
226         if (len>0)
227         {
228             szArguments=HeapAlloc(GetProcessHeap(),0,(len+1)*sizeof(WCHAR));
229             if (!szArguments)
230                 goto CLEANUP;
231             lstrcpyW(szArguments,szCmdLine);
232         }
233         WINE_TRACE("Calling %s, arguments=%s\n",
234                    szProcName,wine_dbgstr_w(szArguments));
235         pEntryPointW(hWnd,hDll,szArguments,SW_SHOWDEFAULT);
236         if (szArguments)
237             HeapFree(GetProcessHeap(),0,szArguments);
238     }
239     else
240     {
241         /* Then try to append 'A' and finally nothing */
242         szProcName[len-1]=0x0041;
243         pEntryPointA=(void*)GetProcAddress(hDll,szProcName);
244         if (!pEntryPointA)
245         {
246             szProcName[len-1]=0;
247             pEntryPointA=(void*)GetProcAddress(hDll,szProcName);
248         }
249         if (pEntryPointA)
250         {
251             char* szArguments=NULL;
252             /* Convert the command line to ascii */
253             WINE_TRACE("Calling %s, arguments=%s\n",
254                        szProcName,wine_dbgstr_a(szArguments));
255             len=WideCharToMultiByte(CP_ACP,0,szCmdLine,-1,NULL,0,NULL,NULL);
256             if (len>1)
257             {
258                 szArguments=HeapAlloc(GetProcessHeap(),0,len);
259                 if (!szArguments)
260                     goto CLEANUP;
261                 WideCharToMultiByte(CP_ACP,0,szCmdLine,-1,szArguments,len,NULL,NULL);
262             }
263             pEntryPointA(hWnd,hDll,szArguments,SW_SHOWDEFAULT);
264             if (szArguments)
265                 HeapFree(GetProcessHeap(),0,szArguments);
266     }
267     else
268     {
269             /* Windows has a MessageBox here... */
270             WINE_WARN("Unable to find the entry point: %s\n",szProcName);
271         }
272     }
273
274 CLEANUP:
275     if (hWnd)
276         DestroyWindow(hWnd);
277     if (hDll)
278         FreeLibrary(hDll);
279     if (szDllName)
280         HeapFree(GetProcessHeap(),0,szDllName);
281     if (szProcName)
282         HeapFree(GetProcessHeap(),0,szProcName);
283     return 0; /* rundll32 always returns 0! */
284 }