401 lines
11 KiB
C
401 lines
11 KiB
C
|
#include "shellprv.h"
|
||
|
#pragma hdrstop
|
||
|
#include <regstr.h>
|
||
|
|
||
|
TCHAR const c_szRunDll[] = TEXT("rundll32.exe");
|
||
|
|
||
|
//
|
||
|
// Emulate multi-threads with multi-processes.
|
||
|
//
|
||
|
STDAPI_(BOOL) SHRunDLLProcess(HWND hwnd, LPCTSTR pszCmdLine, int nCmdShow, UINT idStr, BOOL fRunAsNewUser)
|
||
|
{
|
||
|
BOOL bRet;
|
||
|
HKEY hkey;
|
||
|
SHELLEXECUTEINFO ExecInfo = {0};
|
||
|
TCHAR szPath[MAX_PATH];
|
||
|
|
||
|
// I hate network install. The windows directory is not the windows
|
||
|
// directory
|
||
|
szPath[0] = TEXT('\0');
|
||
|
if (RegOpenKey(HKEY_LOCAL_MACHINE, REGSTR_PATH_SETUP TEXT("\\Setup"), &hkey) == ERROR_SUCCESS)
|
||
|
{
|
||
|
DWORD dwType;
|
||
|
DWORD cbData = SIZEOF(szPath);;
|
||
|
if (SHQueryValueEx(hkey, TEXT("SharedDir"), NULL, &dwType, (LPBYTE)szPath, &cbData) != ERROR_SUCCESS)
|
||
|
szPath[0] = TEXT('\0');
|
||
|
RegCloseKey(hkey);
|
||
|
}
|
||
|
PathCombine(szPath, szPath, c_szRunDll);
|
||
|
|
||
|
DebugMsg(DM_TRACE, TEXT("sh TR - RunDLLProcess (%s)"), pszCmdLine);
|
||
|
FillExecInfo(ExecInfo, hwnd, NULL, szPath, pszCmdLine, szNULL, nCmdShow);
|
||
|
|
||
|
// if we want to launch this cpl as a new user, set the verb to be "runas"
|
||
|
if (fRunAsNewUser)
|
||
|
{
|
||
|
ExecInfo.lpVerb = TEXT("runas");
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
// normal execute so no ui, we do our own error messages
|
||
|
ExecInfo.fMask = SEE_MASK_FLAG_NO_UI;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// We need to put an appropriate message box.
|
||
|
//
|
||
|
bRet = ShellExecuteEx(&ExecInfo);
|
||
|
|
||
|
if (!bRet && !fRunAsNewUser)
|
||
|
{
|
||
|
// If we failed and we werent passing fRunAsNewUser, then we put up our own error UI,
|
||
|
// else, if we were running this as a new user, then we didnt pass SEE_MASK_FLAG_NO_UI
|
||
|
// so the error is already taken care of for us by shellexec.
|
||
|
TCHAR szTitle[64];
|
||
|
DWORD dwErr = GetLastError(); // LoadString can stomp on this (on failure)
|
||
|
LoadString(HINST_THISDLL, idStr, szTitle, ARRAYSIZE(szTitle));
|
||
|
ExecInfo.fMask = 0;
|
||
|
_ShellExecuteError(&ExecInfo, szTitle, dwErr);
|
||
|
}
|
||
|
|
||
|
return bRet;
|
||
|
}
|
||
|
|
||
|
LRESULT CALLBACK WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
|
||
|
{
|
||
|
switch(uMsg)
|
||
|
{
|
||
|
case STUBM_SETICONTITLE:
|
||
|
if (lParam)
|
||
|
SetWindowText(hWnd, (LPCTSTR)lParam);
|
||
|
if (wParam)
|
||
|
SendMessage(hWnd, WM_SETICON, ICON_BIG, wParam);
|
||
|
break;
|
||
|
|
||
|
case STUBM_SETDATA:
|
||
|
SetWindowLongPtr(hWnd, 0, wParam);
|
||
|
break;
|
||
|
|
||
|
case STUBM_GETDATA:
|
||
|
return GetWindowLong(hWnd, 0);
|
||
|
|
||
|
default:
|
||
|
return DefWindowProc(hWnd, uMsg, wParam, lParam) ;
|
||
|
}
|
||
|
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
|
||
|
HWND _CreateStubWindow(POINT * ppt, HWND hwndParent)
|
||
|
{
|
||
|
WNDCLASS wc;
|
||
|
int cx, cy;
|
||
|
// If the stub window is parented, then we want it to be a tool window. This prevents activation
|
||
|
// problems when this is used in multimon for positioning.
|
||
|
|
||
|
DWORD dwExStyle = hwndParent? WS_EX_TOOLWINDOW : WS_EX_APPWINDOW;
|
||
|
if (!GetClassInfo(HINST_THISDLL, c_szStubWindowClass, &wc))
|
||
|
{
|
||
|
wc.style = 0;
|
||
|
wc.lpfnWndProc = WndProc;
|
||
|
wc.cbClsExtra = 0;
|
||
|
wc.cbWndExtra = SIZEOF(DWORD) * 2;
|
||
|
wc.hInstance = HINST_THISDLL;
|
||
|
wc.hIcon = NULL;
|
||
|
wc.hCursor = LoadCursor (NULL, IDC_ARROW);
|
||
|
wc.hbrBackground = GetStockObject (WHITE_BRUSH);
|
||
|
wc.lpszMenuName = NULL;
|
||
|
wc.lpszClassName = c_szStubWindowClass;
|
||
|
|
||
|
RegisterClass(&wc);
|
||
|
}
|
||
|
|
||
|
cx = cy = CW_USEDEFAULT;
|
||
|
if (ppt)
|
||
|
{
|
||
|
cx = (int)ppt->x;
|
||
|
cy = (int)ppt->y;
|
||
|
}
|
||
|
|
||
|
if (IS_BIDI_LOCALIZED_SYSTEM())
|
||
|
{
|
||
|
dwExStyle |= dwExStyleRTLMirrorWnd;
|
||
|
}
|
||
|
|
||
|
// WS_EX_APPWINDOW makes this show up in ALT+TAB, but not the tray.
|
||
|
|
||
|
return CreateWindowEx(dwExStyle, c_szStubWindowClass, c_szNULL, hwndParent? WS_POPUP : WS_OVERLAPPED, cx, cy, 0, 0, hwndParent, NULL, HINST_THISDLL, NULL);
|
||
|
}
|
||
|
|
||
|
|
||
|
typedef struct // dlle
|
||
|
{
|
||
|
HINSTANCE hinst;
|
||
|
RUNDLLPROC lpfn;
|
||
|
BOOL fCmdIsANSI;
|
||
|
} DLLENTRY;
|
||
|
|
||
|
|
||
|
BOOL _InitializeDLLEntry(LPTSTR lpszCmdLine, DLLENTRY * pdlle)
|
||
|
{
|
||
|
LPTSTR lpStart, lpEnd, lpFunction;
|
||
|
|
||
|
DebugMsg(DM_TRACE, TEXT("sh TR - RunDLLThread (%s)"), lpszCmdLine);
|
||
|
|
||
|
for (lpStart=lpszCmdLine; ; )
|
||
|
{
|
||
|
// Skip leading blanks
|
||
|
//
|
||
|
while (*lpStart == TEXT(' '))
|
||
|
{
|
||
|
++lpStart;
|
||
|
}
|
||
|
|
||
|
// Check if there are any switches
|
||
|
//
|
||
|
if (*lpStart != TEXT('/'))
|
||
|
{
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
// Look at all the switches; ignore unknown ones
|
||
|
//
|
||
|
for (++lpStart; ; ++lpStart)
|
||
|
{
|
||
|
switch (*lpStart)
|
||
|
{
|
||
|
case TEXT(' '):
|
||
|
case TEXT('\0'):
|
||
|
goto EndSwitches;
|
||
|
break;
|
||
|
|
||
|
// Put any switches we care about here
|
||
|
//
|
||
|
|
||
|
default:
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
EndSwitches:
|
||
|
;
|
||
|
}
|
||
|
|
||
|
// We have found the DLL,FN parameter
|
||
|
//
|
||
|
lpEnd = StrChr(lpStart, TEXT(' '));
|
||
|
if (lpEnd)
|
||
|
{
|
||
|
*lpEnd++ = TEXT('\0');
|
||
|
}
|
||
|
|
||
|
// There must be a DLL name and a function name
|
||
|
//
|
||
|
lpFunction = StrChr(lpStart, TEXT(','));
|
||
|
if (!lpFunction)
|
||
|
{
|
||
|
return(FALSE);
|
||
|
}
|
||
|
*lpFunction++ = TEXT('\0');
|
||
|
|
||
|
// Load the library and get the procedure address
|
||
|
// Note that we try to get a module handle first, so we don't need
|
||
|
// to pass full file names around
|
||
|
//
|
||
|
pdlle->hinst = GetModuleHandle(lpStart);
|
||
|
if (pdlle->hinst)
|
||
|
{
|
||
|
TCHAR szName[MAXPATHLEN];
|
||
|
|
||
|
GetModuleFileName(pdlle->hinst, szName, ARRAYSIZE(szName));
|
||
|
LoadLibrary(szName);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
pdlle->hinst = LoadLibrary(lpStart);
|
||
|
if (!ISVALIDHINSTANCE(pdlle->hinst))
|
||
|
{
|
||
|
return(FALSE);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
#ifdef UNICODE
|
||
|
{
|
||
|
/*
|
||
|
* Look for a 'W' tagged Unicode function.
|
||
|
* If it is not there, then look for the 'A' tagged ANSI function
|
||
|
* if we cant find that one either, then look for an un-tagged function
|
||
|
*/
|
||
|
LPSTR pszFunctionName;
|
||
|
UINT cchLength;
|
||
|
|
||
|
cchLength = lstrlen(lpFunction)+1;
|
||
|
pdlle->fCmdIsANSI = FALSE;
|
||
|
|
||
|
pszFunctionName = (LPSTR)LocalAlloc(LMEM_FIXED, (cchLength+1)*2); // +1 for "W", *2 for DBCS
|
||
|
|
||
|
if (pszFunctionName && (WideCharToMultiByte (CP_ACP, 0, lpFunction, cchLength,
|
||
|
pszFunctionName, cchLength*2, NULL, NULL))) {
|
||
|
|
||
|
cchLength = lstrlenA(pszFunctionName);
|
||
|
pszFunctionName[cchLength] = 'W'; // convert name to Wide version
|
||
|
pszFunctionName[cchLength+1] = '\0';
|
||
|
|
||
|
pdlle->lpfn = (RUNDLLPROC)GetProcAddress(pdlle->hinst, pszFunctionName);
|
||
|
|
||
|
if (pdlle->lpfn == NULL) {
|
||
|
// No UNICODE version, try for ANSI
|
||
|
pszFunctionName[cchLength] = 'A'; // convert name to ANSI version
|
||
|
pdlle->fCmdIsANSI = TRUE;
|
||
|
|
||
|
pdlle->lpfn = (RUNDLLPROC)GetProcAddress(pdlle->hinst, pszFunctionName);
|
||
|
|
||
|
if (pdlle->lpfn == NULL) {
|
||
|
// No ANSI version either, try for non-tagged
|
||
|
pszFunctionName[cchLength] = '\0'; // convert name to ANSI version
|
||
|
|
||
|
pdlle->lpfn = (RUNDLLPROC)GetProcAddress(pdlle->hinst, pszFunctionName);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
if (pszFunctionName) {
|
||
|
LocalFree((LPVOID)pszFunctionName);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
#else
|
||
|
{
|
||
|
/*
|
||
|
* Look for 'A' tagged ANSI version.
|
||
|
* If it is not there, then look for a non-tagged function.
|
||
|
*/
|
||
|
LPSTR pszFunction;
|
||
|
int cchFunction;
|
||
|
|
||
|
pdlle->lpfn = NULL;
|
||
|
|
||
|
cchFunction = lstrlen(lpFunction);
|
||
|
|
||
|
pszFunction = LocalAlloc(LMEM_FIXED, cchFunction + sizeof(CHAR) * 2); // string + 'A' + '\0'
|
||
|
if (pszFunction != NULL)
|
||
|
{
|
||
|
CopyMemory(pszFunction, lpFunction, cchFunction);
|
||
|
|
||
|
pszFunction[cchFunction++] = 'A';
|
||
|
pszFunction[cchFunction] = '\0';
|
||
|
|
||
|
pdlle->lpfn = (RUNDLLPROC)GetProcAddress(pdlle->hinst, pszFunction);
|
||
|
|
||
|
LocalFree(pszFunction);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
#endif
|
||
|
|
||
|
if (!pdlle->lpfn)
|
||
|
{
|
||
|
FreeLibrary(pdlle->hinst);
|
||
|
return(FALSE);
|
||
|
}
|
||
|
|
||
|
// Copy the rest of the command parameters down
|
||
|
//
|
||
|
if (lpEnd)
|
||
|
{
|
||
|
lstrcpy(lpszCmdLine, lpEnd);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
*lpszCmdLine = TEXT('\0');
|
||
|
}
|
||
|
|
||
|
return(TRUE);
|
||
|
}
|
||
|
|
||
|
typedef struct tagRunThreadParam {
|
||
|
int nCmdShow;
|
||
|
TCHAR szCmdLine[1];
|
||
|
} RUNTHREADPARAM;
|
||
|
|
||
|
DWORD WINAPI _ThreadInitDLL(LPVOID pv)
|
||
|
{
|
||
|
RUNTHREADPARAM * prtp = (RUNTHREADPARAM*)pv;
|
||
|
LPTSTR pszCmdLine = (LPTSTR)&prtp->szCmdLine;
|
||
|
DLLENTRY dlle;
|
||
|
|
||
|
if (_InitializeDLLEntry(pszCmdLine, &dlle))
|
||
|
{
|
||
|
HWND hwndStub=_CreateStubWindow(NULL, NULL);
|
||
|
if (hwndStub)
|
||
|
{
|
||
|
ULONG cchCmdLine = 0;
|
||
|
SetForegroundWindow(hwndStub);
|
||
|
#ifdef UNICODE
|
||
|
if (dlle.fCmdIsANSI)
|
||
|
{
|
||
|
//
|
||
|
// If the function is an ANSI version
|
||
|
// Change the command line parameter strings to ANSI before we call the function
|
||
|
//
|
||
|
int cchCmdLine = lstrlen(pszCmdLine);
|
||
|
|
||
|
LPVOID pszCommand = LocalAlloc(LMEM_FIXED, sizeof(char) * (cchCmdLine + 1));
|
||
|
if (pszCommand)
|
||
|
{
|
||
|
WideCharToMultiByte(CP_ACP, 0, pszCmdLine, -1, pszCommand, cchCmdLine, NULL, NULL);
|
||
|
}
|
||
|
dlle.lpfn(hwndStub, g_hinst, pszCommand, prtp->nCmdShow);
|
||
|
|
||
|
}
|
||
|
else
|
||
|
#endif
|
||
|
{
|
||
|
dlle.lpfn(hwndStub, g_hinst, pszCmdLine, prtp->nCmdShow);
|
||
|
}
|
||
|
|
||
|
DestroyWindow(hwndStub);
|
||
|
}
|
||
|
FreeLibrary(dlle.hinst);
|
||
|
}
|
||
|
|
||
|
LocalFree((HLOCAL)prtp);
|
||
|
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
BOOL WINAPI SHRunDLLThread(HWND hwnd, LPCTSTR pszCmdLine, int nCmdShow)
|
||
|
{
|
||
|
BOOL fSuccess = FALSE; // assume error
|
||
|
|
||
|
// don't need +1 on lstrlen since szCmdLine is already of size 1 (for NULL)
|
||
|
RUNTHREADPARAM * prtp = LocalAlloc(LPTR, sizeof(RUNTHREADPARAM) + (lstrlen(pszCmdLine) * sizeof(TCHAR)));
|
||
|
|
||
|
if (prtp)
|
||
|
{
|
||
|
DWORD idThread;
|
||
|
HANDLE hthread;
|
||
|
|
||
|
lstrcpy(prtp->szCmdLine, pszCmdLine);
|
||
|
|
||
|
hthread = CreateThread(NULL, 0, _ThreadInitDLL, prtp, 0, &idThread);
|
||
|
if (hthread)
|
||
|
{
|
||
|
// We don't need to communicate with this thread any more.
|
||
|
// Close the handle and let it run and terminate itself.
|
||
|
//
|
||
|
// Notes: In this case, prtp will be freed by the thread.
|
||
|
//
|
||
|
CloseHandle(hthread);
|
||
|
fSuccess = TRUE;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
// Thread creation failed, we should free the buffer.
|
||
|
LocalFree((HLOCAL)prtp);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return fSuccess;
|
||
|
}
|
||
|
|