/* * assoc.c - Type association routines. */ /* Headers **********/ #include "project.h" #pragma hdrstop #include #define _INTSHCUT_ /* for intshcut.h */ #include #include /* ALL_???_FLAGS */ #include "assoc.h" #include "extricon.h" #include "openas.h" #pragma warning(disable:4001) /* "single line comment" warning */ #include "filetype.h" #include "resource.h" #pragma warning(default:4001) /* "single line comment" warning */ #include "shlstock.h" #include "shlvalid.h" /* Global Constants *******************/ #pragma data_seg(DATA_SEG_READ_ONLY) PUBLIC_DATA const HKEY g_hkeyURLProtocols = HKEY_CLASSES_ROOT; PUBLIC_DATA const HKEY g_hkeyMIMESettings = HKEY_CLASSES_ROOT; PUBLIC_DATA CCHAR g_cszURLProtocol[] = "URL Protocol"; PUBLIC_DATA CCHAR g_cszContentType[] = "Content Type"; PUBLIC_DATA CCHAR g_cszExtension[] = "Extension"; #pragma data_seg() /* Module Constants *******************/ #pragma data_seg(DATA_SEG_READ_ONLY) PRIVATE_DATA CCHAR s_cszShellOpenCmdSubKeyFmt[] = "%s\\shell\\open\\command"; PRIVATE_DATA CCHAR s_cszAppOpenCmdFmt[] = "%s %%1"; PRIVATE_DATA CCHAR s_cszDefaultIconSubKeyFmt[] = "%s\\DefaultIcon"; PRIVATE_DATA CCHAR s_cszDefaultIcon[] = "url.dll,0"; #pragma data_seg() /***************************** Private Functions *****************************/ /* ** RegisterAppAsURLProtocolHandler() ** ** Under HKEY_CLASSES_ROOT\url-protocol\shell\open\command, add default value = ** "c:\foo\bar.exe %1". ** ** Arguments: ** ** Returns: ** ** Side Effects: none */ PRIVATE_CODE BOOL RegisterAppAsURLProtocolHandler(PCSTR pcszProtocol, PCSTR pcszApp) { BOOL bResult = FALSE; DWORD dwcbShellOpenCmdSubKeyLen; PSTR pszShellOpenCmdSubKey; ASSERT(IS_VALID_STRING_PTR(pcszProtocol, CSTR)); ASSERT(IS_VALID_STRING_PTR(pcszApp, CSTR)); /* (+ 1) for null terminator. */ dwcbShellOpenCmdSubKeyLen = sizeof(s_cszShellOpenCmdSubKeyFmt) + 1 + lstrlen(pcszProtocol); if (AllocateMemory(dwcbShellOpenCmdSubKeyLen, &pszShellOpenCmdSubKey)) { DWORD dwcbAppOpenCmdLen; PSTR pszAppOpenCmd; /* FEATURE: We should quote pcszApp here only if it contains spaces. */ /* (+ 1) for null terminator. */ dwcbAppOpenCmdLen = sizeof(s_cszAppOpenCmdFmt) + 1 + lstrlen(pcszApp); if (AllocateMemory(dwcbAppOpenCmdLen, &pszAppOpenCmd)) { EVAL((DWORD)wsprintf(pszShellOpenCmdSubKey, s_cszShellOpenCmdSubKeyFmt, pcszProtocol) < dwcbShellOpenCmdSubKeyLen); EVAL((DWORD)wsprintf(pszAppOpenCmd, s_cszAppOpenCmdFmt, pcszApp) < dwcbAppOpenCmdLen); /* (+ 1) for null terminator. */ bResult = (SetRegKeyValue(g_hkeyURLProtocols, pszShellOpenCmdSubKey, NULL, REG_SZ, (PCBYTE)pszAppOpenCmd, lstrlen(pszAppOpenCmd) + 1) == ERROR_SUCCESS); FreeMemory(pszShellOpenCmdSubKey); pszShellOpenCmdSubKey = NULL; } FreeMemory(pszAppOpenCmd); pszAppOpenCmd = NULL; } return(bResult); } /* ** RegisterURLProtocolDescription() ** ** Under g_hkeyURLSettings\url-protocol, add default value = ** URL:Url-protocol Protocol. ** ** Arguments: ** ** Returns: ** ** Side Effects: none */ PRIVATE_CODE BOOL RegisterURLProtocolDescription(PCSTR pcszProtocol) { BOOL bResult = FALSE; PSTR pszProtocolCopy; ASSERT(IS_VALID_STRING_PTR(pcszProtocol, CSTR)); if (StringCopy(pcszProtocol, &pszProtocolCopy)) { char szDescriptionFmt[MAX_PATH_LEN]; /* * Convert first character of protocol to upper case for description * string. */ *pszProtocolCopy = (CHAR)PtrToUlong(CharUpper((LPSTR)(DWORD_PTR)*pszProtocolCopy)); if (MLLoadStringA(IDS_URL_DESC_FORMAT, szDescriptionFmt, sizeof(szDescriptionFmt))) { char szDescription[MAX_PATH_LEN]; if ((UINT)lstrlen(szDescriptionFmt) + (UINT)lstrlen(pszProtocolCopy) < sizeof(szDescription)) { EVAL(wsprintf(szDescription, szDescriptionFmt, pszProtocolCopy) < sizeof(szDescription)); /* (+ 1) for null terminator. */ bResult = (SetRegKeyValue(g_hkeyURLProtocols, pcszProtocol, NULL, REG_SZ, (PCBYTE)szDescription, lstrlen(szDescription) + 1) == ERROR_SUCCESS); } } FreeMemory(pszProtocolCopy); pszProtocolCopy = NULL; } return(bResult); } /* ** RegisterURLProtocolFlags() ** ** Under g_hkeyURLSettings\url-protocol, add EditFlags = FTA_Show. ** ** Arguments: ** ** Returns: ** ** Side Effects: none */ PRIVATE_CODE BOOL RegisterURLProtocolFlags(PCSTR pcszProtocol) { DWORD dwEditFlags = FTA_Show; ASSERT(IS_VALID_STRING_PTR(pcszProtocol, CSTR)); /* FEATURE: What about preserving any existing EditFlags here? */ /* (+ 1) for null terminator. */ return(SetRegKeyValue(g_hkeyURLProtocols, pcszProtocol, g_cszEditFlags, REG_BINARY, (PCBYTE)&dwEditFlags, sizeof(dwEditFlags)) == ERROR_SUCCESS); } /* ** RegisterURLProtocol() ** ** Under g_hkeyURLSettings\url-protocol, add URL Protocol = "". ** ** Arguments: ** ** Returns: ** ** Side Effects: none */ PRIVATE_CODE BOOL RegisterURLProtocol(PCSTR pcszProtocol) { ASSERT(IS_VALID_STRING_PTR(pcszProtocol, CSTR)); /* (+ 1) for null terminator. */ return(SetRegKeyValue(g_hkeyURLProtocols, pcszProtocol, g_cszURLProtocol, REG_SZ, (PCBYTE)EMPTY_STRING, lstrlen(EMPTY_STRING) + 1) == ERROR_SUCCESS); } /* ** RegisterURLProtocolDefaultIcon() ** ** Under g_hkeyURLSettings\url-protocol\DefaultIcon, add default value = ** app.exe,0. ** ** Arguments: ** ** Returns: ** ** Side Effects: none */ PRIVATE_CODE BOOL RegisterURLProtocolDefaultIcon(PCSTR pcszProtocol) { BOOL bResult = FALSE; DWORD dwcbDefaultIconSubKeyLen; PSTR pszDefaultIconSubKey; ASSERT(IS_VALID_STRING_PTR(pcszProtocol, CSTR)); /* (+ 1) for null terminator. */ dwcbDefaultIconSubKeyLen = sizeof(s_cszDefaultIconSubKeyFmt) + 1 + lstrlen(pcszProtocol); if (AllocateMemory(dwcbDefaultIconSubKeyLen, &pszDefaultIconSubKey)) { EVAL((DWORD)wsprintf(pszDefaultIconSubKey, s_cszDefaultIconSubKeyFmt, pcszProtocol) < dwcbDefaultIconSubKeyLen); bResult = (SetRegKeyValue(g_hkeyURLProtocols, pszDefaultIconSubKey, NULL, REG_SZ, (PCBYTE)s_cszDefaultIcon, sizeof(s_cszDefaultIcon)) == ERROR_SUCCESS); FreeMemory(pszDefaultIconSubKey); pszDefaultIconSubKey = NULL; } return(bResult); } PRIVATE_CODE BOOL AllowedToRegisterMIMEType(PCSTR pcszMIMEContentType) { BOOL bResult; #pragma data_seg(DATA_SEG_READ_ONLY) bResult = (lstrcmpi(pcszMIMEContentType, "application/octet-stream") != 0 && lstrcmpi(pcszMIMEContentType, "application/octet-string") != 0); #pragma data_seg() if (bResult) TRACE_OUT(("AllowedToRegisterMIMEType(): MIME type %s may be registered.", pcszMIMEContentType)); else WARNING_OUT(("AllowedToRegisterMIMEType(): MIME type %s may not be registered.", pcszMIMEContentType)); return(bResult); } /****************************** Public Functions *****************************/ /* ** RegisterMIMETypeForExtension() ** ** Under HKEY_CLASSES_ROOT\.ext, add Content Type = mime/type. ** ** Arguments: ** ** Returns: ** ** Side Effects: none */ PUBLIC_CODE BOOL RegisterMIMETypeForExtension(PCSTR pcszExtension, PCSTR pcszMIMEContentType) { ASSERT(IS_VALID_STRING_PTR(pcszExtension, CSTR)); ASSERT(IS_VALID_STRING_PTR(pcszMIMEContentType, CSTR)); ASSERT(IsValidExtension(pcszExtension)); /* (+ 1) for null terminator. */ return(SetRegKeyValue(HKEY_CLASSES_ROOT, pcszExtension, g_cszContentType, REG_SZ, (PCBYTE)pcszMIMEContentType, lstrlen(pcszMIMEContentType) + 1) == ERROR_SUCCESS); } /* ** UnregisterMIMETypeForExtension() ** ** Deletes Content Type under HKEY_CLASSES_ROOT\.ext. ** ** Arguments: ** ** Returns: ** ** Side Effects: none */ PUBLIC_CODE BOOL UnregisterMIMETypeForExtension(PCSTR pcszExtension) { ASSERT(IS_VALID_STRING_PTR(pcszExtension, CSTR)); ASSERT(IsValidExtension(pcszExtension)); return(NO_ERROR == SHDeleteValue(HKEY_CLASSES_ROOT, pcszExtension, g_cszContentType)); } /* ** RegisterExtensionForMIMEType() ** ** Under g_hkeyMIMESettings\MIME\Database\Content Type\mime/type, add ** Content Type = mime/type and Extension = .ext. ** ** Arguments: ** ** Returns: ** ** Side Effects: none */ PUBLIC_CODE BOOL RegisterExtensionForMIMEType(PCSTR pcszExtension, PCSTR pcszMIMEContentType) { BOOL bResult; char szMIMEContentTypeSubKey[MAX_PATH_LEN]; ASSERT(IS_VALID_STRING_PTR(pcszExtension, CSTR)); ASSERT(IS_VALID_STRING_PTR(pcszMIMEContentType, CSTR)); ASSERT(IsValidExtension(pcszExtension)); bResult = GetMIMETypeSubKey(pcszMIMEContentType, szMIMEContentTypeSubKey, sizeof(szMIMEContentTypeSubKey)); if (bResult) /* (+ 1) for null terminator. */ bResult = (SetRegKeyValue(g_hkeyMIMESettings, szMIMEContentTypeSubKey, g_cszExtension, REG_SZ, (PCBYTE)pcszExtension, lstrlen(pcszExtension) + 1) == ERROR_SUCCESS); if (bResult) TRACE_OUT(("RegisterExtensionForMIMEType(): Registered extension %s as default extension for MIME type %s.", pcszExtension, pcszMIMEContentType)); else WARNING_OUT(("RegisterExtensionForMIMEType(): Failed to register extension %s as default extension for MIME type %s.", pcszExtension, pcszMIMEContentType)); return(bResult); } /* ** UnregisterExtensionForMIMEType() ** ** Deletes Extension under ** g_hkeyMIMESettings\MIME\Database\Content Type\mime/type. If no other values ** or sub keys are left, deletes ** g_hkeyMIMESettings\MIME\Database\Content Type\mime/type. ** ** Arguments: ** ** Returns: ** ** Side Effects: May also delete MIME key. */ PUBLIC_CODE BOOL UnregisterExtensionForMIMEType(PCSTR pcszMIMEContentType) { BOOL bResult; char szMIMEContentTypeSubKey[MAX_PATH_LEN]; ASSERT(IS_VALID_STRING_PTR(pcszMIMEContentType, CSTR)); bResult = (GetMIMETypeSubKey(pcszMIMEContentType, szMIMEContentTypeSubKey, sizeof(szMIMEContentTypeSubKey)) && SHDeleteValue(g_hkeyMIMESettings, szMIMEContentTypeSubKey, g_cszExtension) == ERROR_SUCCESS && SHDeleteOrphanKey(g_hkeyMIMESettings, szMIMEContentTypeSubKey) == ERROR_SUCCESS); if (bResult) TRACE_OUT(("UnregisterExtensionForMIMEType(): Unregistered default extension for MIME type %s.", pcszMIMEContentType)); else WARNING_OUT(("UnregisterExtensionForMIMEType(): Failed to unregister default extension for MIME type %s.", pcszMIMEContentType)); return(bResult); } PUBLIC_CODE BOOL RegisterMIMEAssociation(PCSTR pcszFile, PCSTR pcszMIMEContentType) { BOOL bResult; PCSTR pcszExtension; ASSERT(IS_VALID_STRING_PTR(pcszFile, CSTR)); ASSERT(IS_VALID_STRING_PTR(pcszMIMEContentType, CSTR)); pcszExtension = ExtractExtension(pcszFile); /* * Don't allow association of flag unknown MIME types * application/octet-stream and application/octet-string. */ if (EVAL(*pcszExtension) && AllowedToRegisterMIMEType(pcszMIMEContentType)) bResult = (RegisterMIMETypeForExtension(pcszExtension, pcszMIMEContentType) && RegisterExtensionForMIMEType(pcszExtension, pcszMIMEContentType)); else bResult = FALSE; return(bResult); } PUBLIC_CODE BOOL RegisterURLAssociation(PCSTR pcszProtocol, PCSTR pcszApp) { ASSERT(IS_VALID_STRING_PTR(pcszProtocol, CSTR)); ASSERT(IS_VALID_STRING_PTR(pcszApp, CSTR)); return(RegisterAppAsURLProtocolHandler(pcszProtocol, pcszApp) && RegisterURLProtocolDescription(pcszProtocol) && RegisterURLProtocol(pcszProtocol) && RegisterURLProtocolFlags(pcszProtocol) && RegisterURLProtocolDefaultIcon(pcszProtocol)); } PUBLIC_CODE HRESULT MyMIMEAssociationDialog(HWND hwndParent, DWORD dwInFlags, PCSTR pcszFile, PCSTR pcszMIMEContentType, PSTR pszAppBuf, UINT ucAppBufLen) { HRESULT hr; OPENASINFO oainfo; ASSERT(IS_VALID_HANDLE(hwndParent, WND)); ASSERT(FLAGS_ARE_VALID(dwInFlags, ALL_MIMEASSOCDLG_FLAGS)); ASSERT(IS_VALID_STRING_PTR(pcszFile, CSTR)); ASSERT(IS_VALID_STRING_PTR(pcszMIMEContentType, CSTR)); ASSERT(IS_VALID_WRITE_BUFFER_PTR(pszAppBuf, STR, ucAppBufLen)); /* Use default file name if not supplied by caller. */ if (ucAppBufLen > 0) *pszAppBuf = '\0'; oainfo.pcszFile = pcszFile; oainfo.pcszClass = pcszMIMEContentType; oainfo.dwInFlags = 0; if (IS_FLAG_SET(dwInFlags, MIMEASSOCDLG_FL_REGISTER_ASSOC)) SET_FLAG(oainfo.dwInFlags, (OPENASINFO_FL_ALLOW_REGISTRATION | OPENASINFO_FL_REGISTER_EXT)); hr = MyOpenAsDialog(hwndParent, &oainfo); if (hr == S_OK && IS_FLAG_SET(dwInFlags, MIMEASSOCDLG_FL_REGISTER_ASSOC)) hr = RegisterMIMEAssociation(pcszFile, pcszMIMEContentType) ? S_OK : E_OUTOFMEMORY; if (SUCCEEDED(hr)) lstrcpyn(pszAppBuf, oainfo.szApp, ucAppBufLen); ASSERT(! ucAppBufLen || (IS_VALID_STRING_PTR(pszAppBuf, STR) && EVAL((UINT)lstrlen(pszAppBuf) < ucAppBufLen))); ASSERT(SUCCEEDED(hr) || (! ucAppBufLen || EVAL(! *pszAppBuf))); return(hr); } PUBLIC_CODE HRESULT MyURLAssociationDialog(HWND hwndParent, DWORD dwInFlags, PCSTR pcszFile, PCSTR pcszURL, PSTR pszAppBuf, UINT ucAppBufLen) { HRESULT hr; PSTR pszProtocol; ASSERT(IS_VALID_HANDLE(hwndParent, WND)); ASSERT(FLAGS_ARE_VALID(dwInFlags, ALL_URLASSOCDLG_FLAGS)); ASSERT(IS_FLAG_SET(dwInFlags, URLASSOCDLG_FL_USE_DEFAULT_NAME) || IS_VALID_STRING_PTR(pcszFile, CSTR)); ASSERT(IS_VALID_STRING_PTR(pcszURL, CSTR)); ASSERT(IS_VALID_WRITE_BUFFER_PTR(pszAppBuf, STR, ucAppBufLen)); /* Use URL protocol as class name. */ if (ucAppBufLen > 0) *pszAppBuf = '\0'; hr = CopyURLProtocol(pcszURL, &pszProtocol); if (hr == S_OK) { char szInternetShortcut[MAX_PATH_LEN]; OPENASINFO oainfo; /* Use default file name if not supplied by caller. */ if (IS_FLAG_SET(dwInFlags, URLASSOCDLG_FL_USE_DEFAULT_NAME) && EVAL(MLLoadStringA(IDS_INTERNET_SHORTCUT, szInternetShortcut, sizeof(szInternetShortcut)))) pcszFile = szInternetShortcut; oainfo.pcszFile = pcszFile; oainfo.pcszClass = pszProtocol; oainfo.dwInFlags = 0; if (IS_FLAG_SET(dwInFlags, URLASSOCDLG_FL_REGISTER_ASSOC)) SET_FLAG(oainfo.dwInFlags, OPENASINFO_FL_ALLOW_REGISTRATION); hr = MyOpenAsDialog(hwndParent, &oainfo); if (hr == S_OK && IS_FLAG_SET(dwInFlags, URLASSOCDLG_FL_REGISTER_ASSOC)) hr = RegisterURLAssociation(pszProtocol, oainfo.szApp) ? S_OK : E_OUTOFMEMORY; if (SUCCEEDED(hr)) lstrcpyn(pszAppBuf, oainfo.szApp, ucAppBufLen); FreeMemory(pszProtocol); pszProtocol = NULL; } ASSERT(! ucAppBufLen || (IS_VALID_STRING_PTR(pszAppBuf, STR) && EVAL((UINT)lstrlen(pszAppBuf) < ucAppBufLen))); ASSERT(SUCCEEDED(hr) || (! ucAppBufLen || EVAL(! *pszAppBuf))); return(hr); } #ifdef DEBUG PUBLIC_CODE BOOL IsValidPCOPENASINFO(PCOPENASINFO pcoainfo) { return(IS_VALID_READ_PTR(pcoainfo, COPENASINFO) && IS_VALID_STRING_PTR(pcoainfo->pcszFile, CSTR) && (! pcoainfo->pcszClass || IS_VALID_STRING_PTR(pcoainfo->pcszClass, CSTR)) && FLAGS_ARE_VALID(pcoainfo->dwInFlags, ALL_OPENASINFO_FLAGS) && (! *pcoainfo->szApp || IS_VALID_STRING_PTR(pcoainfo->szApp, STR))); } #endif /* DEBUG */ /***************************** Exported Functions ****************************/ INTSHCUTAPI HRESULT WINAPI MIMEAssociationDialogA(HWND hwndParent, DWORD dwInFlags, PCSTR pcszFile, PCSTR pcszMIMEContentType, PSTR pszAppBuf, UINT ucAppBufLen) { HRESULT hr; DebugEntry(MIMEAssociationDialogA); #ifdef EXPV /* Verify parameters. */ if (IS_VALID_HANDLE(hwndParent, WND) && IS_VALID_STRING_PTR(pcszFile, CSTR) && IS_VALID_STRING_PTR(pcszMIMEContentType, CSTR) && IS_VALID_WRITE_BUFFER_PTR(pszAppBuf, STR, ucAppBufLen)) { if (FLAGS_ARE_VALID(dwInFlags, ALL_MIMEASSOCDLG_FLAGS)) #endif { hr = MyMIMEAssociationDialog(hwndParent, dwInFlags, pcszFile, pcszMIMEContentType, pszAppBuf, ucAppBufLen); } #ifdef EXPV else hr = E_FLAGS; } else hr = E_POINTER; #endif DebugExitHRESULT(MIMEAssociationDialogA, hr); return(hr); } #pragma warning(disable:4100) /* "unreferenced formal parameter" warning */ INTSHCUTAPI HRESULT WINAPI MIMEAssociationDialogW(HWND hwndParent, DWORD dwInFlags, PCWSTR pcszFile, PCWSTR pcszMIMEContentType, PWSTR pszAppBuf, UINT ucAppBufLen) { HRESULT hr; DebugEntry(MIMEAssociationDialogW); SetLastError(ERROR_NOT_SUPPORTED); hr = E_NOTIMPL; DebugExitHRESULT(MIMEAssociationDialogW, hr); return(hr); } #pragma warning(default:4100) /* "unreferenced formal parameter" warning */ INTSHCUTAPI HRESULT WINAPI URLAssociationDialogA(HWND hwndParent, DWORD dwInFlags, PCSTR pcszFile, PCSTR pcszURL, PSTR pszAppBuf, UINT ucAppBufLen) { HRESULT hr; DebugEntry(URLAssociationDialogA); #ifdef EXPV /* Verify parameters. */ if (IS_VALID_HANDLE(hwndParent, WND) && (IS_FLAG_SET(dwInFlags, URLASSOCDLG_FL_USE_DEFAULT_NAME) || IS_VALID_STRING_PTR(pcszFile, CSTR)) && IS_VALID_STRING_PTR(pcszURL, CSTR) && IS_VALID_WRITE_BUFFER_PTR(pszAppBuf, STR, ucAppBufLen)) { if (FLAGS_ARE_VALID(dwInFlags, ALL_URLASSOCDLG_FLAGS)) #endif { hr = MyURLAssociationDialog(hwndParent, dwInFlags, pcszFile, pcszURL, pszAppBuf, ucAppBufLen); } #ifdef EXPV else hr = E_FLAGS; } else hr = E_POINTER; #endif DebugExitHRESULT(URLAssociationDialogA, hr); return(hr); } #pragma warning(disable:4100) /* "unreferenced formal parameter" warning */ INTSHCUTAPI HRESULT WINAPI URLAssociationDialogW(HWND hwndParent, DWORD dwInFlags, PCWSTR pcszFile, PCWSTR pcszURL, PWSTR pszAppBuf, UINT ucAppBufLen) { HRESULT hr; DebugEntry(URLAssociationDialogW); SetLastError(ERROR_NOT_SUPPORTED); hr = E_NOTIMPL; DebugExitHRESULT(URLAssociationDialogW, hr); return(hr); } #pragma warning(default:4100) /* "unreferenced formal parameter" warning */