#define STRICT
#define LEAN_AND_MEAN
#include <windows.h>
#include <setupapi.h>
#include <assert.h>
#include <stdlib.h>
#include <stdio.h>
#include <mbstring.h>
#include "miginf.h"
#define DIRECTORYKEY "SOFTWARE\\Microsoft\\DevStudio\\5.0\\Products\\Microsoft Visual C++"
#define DIRECTORYVALUE "ProductDir"
#define DIR_95SPECIFIC "\\bin\\win95"
#define FILE_PVIEW "pview.exe"
#define MESSAGE \
"PVIEW.EXE has been found in one or more directories outside of your Visual C++ 5.0" \
" installation directory. These copies will not be updated with the NT version." \
#define MESSAGE_SECTION "Microsoft\\Visual C++ 5.0\\Process Viewer"
#define SIZENEEDED 100000L
#define CP_USASCII 1252
//
// 9x side globals.
//
const CHAR g_ProductId[] = {"Microsoft Visual C++ 5.0"};
UINT g_DllVersion = 1;
INT g_CodePageArray[] = {CP_USASCII,-1};
CHAR g_ExeNamesBuffer[] = {"pview95.exe\0""\0"};
//
// Nt side globals.
//
//
// Uncomment next line to get popups.
//
#define MYDEBUG
#ifdef MYDEBUG
# define INFO(x) (MessageBoxA(NULL,(x),"PVIEW Sample Migration Dll",MB_OK | MB_ICONINFORMATION))
#else
# define INFO(x)
#endif
static
BOOL
PathIsInPath(
IN PCSTR SubPath,
IN PCSTR ParentPath
)
{
DWORD parentLength;
BOOL rInPath;
//
// This function assumes both parameters are non-NULL.
//
assert(SubPath);
assert(ParentPath);
parentLength = _mbslen(ParentPath);
//
// A path is considered "in" another path if the path is in the ParentPath
// or a subdirectory of it.
//
rInPath = !_mbsnicmp(SubPath,ParentPath,parentLength);
if (rInPath) {
rInPath = SubPath[parentLength] == 0 || SubPath[parentLength] == '\\';
}
return rInPath;
}
static
PSTR
GetPviewDirectoryNt (
VOID
)
{
HKEY softwareKey;
LONG rc;
LONG valueType;
LONG sizeNeeded;
PSTR rString = NULL;
//
// First, open the key.
//
rc = RegOpenKeyEx(
HKEY_LOCAL_MACHINE,
DIRECTORYKEY,
0,
KEY_READ,
&softwareKey
);
if (rc == ERROR_SUCCESS) {
//
// Determine how large of a buffer to allocate.
//
rc = RegQueryValueEx(
softwareKey,
DIRECTORYVALUE,
0,
&valueType,
NULL,
&sizeNeeded
);
//
// Allocate enough space for the registry path, with the additional
// subpath to Visual C++ 5.0's win 95 specific binaries.
//
rString = LocalAlloc(0,sizeNeeded + lstrlen(DIR_95SPECIFIC) + 1);
}
if (rc == ERROR_SUCCESS && rString != NULL) {
//
// Read in the buffer.
//
rc = RegQueryValueEx(
softwareKey,
DIRECTORYVALUE,
0,
&valueType,
(PBYTE) rString,
&sizeNeeded
);
if (rc == ERROR_SUCCESS) {
if (valueType != REG_SZ) {
rc = ERROR_INVALID_DATATYPE;
}
}
}
//
// If we didn't complete successfully, set the last error, and free the
// return string if it was allocated.
//
if (rc != ERROR_SUCCESS) {
SetLastError(rc);
if (rString) {
LocalFree(rString);
rString = NULL;
}
}
return rString;
}
static
PSTR
GetPviewDirectory9x (
VOID
)
{
HKEY softwareKey;
LONG rc;
LONG valueType;
LONG sizeNeeded;
PSTR rString = NULL;
//
// First, open the key.
//
rc = RegOpenKeyEx(
HKEY_LOCAL_MACHINE,
DIRECTORYKEY,
0,
KEY_READ,
&softwareKey
);
if (rc == ERROR_SUCCESS) {
//
// Determine how large of a buffer to allocate.
//
rc = RegQueryValueEx(
softwareKey,
DIRECTORYVALUE,
0,
&valueType,
NULL,
&sizeNeeded
);
//
// Allocate enough space for the registry path, with the additional
// subpath to Visual C++ 5.0's win 95 specific binaries.
//
rString = LocalAlloc(0,sizeNeeded + lstrlen(DIR_95SPECIFIC) + 1);
}
if (rc == ERROR_SUCCESS && rString != NULL) {
//
// Read in the buffer.
//
rc = RegQueryValueEx(
softwareKey,
DIRECTORYVALUE,
0,
&valueType,
(PBYTE) rString,
&sizeNeeded
);
if (rc == ERROR_SUCCESS) {
if (valueType == REG_SZ) {
//
// We have successfully read in the value of the installation
// directory into rString. Now, all we need to do is tack on
// the win 95 specific portion that we care about.
//
lstrcat(rString,DIR_95SPECIFIC);
}
else {
rc = ERROR_INVALID_DATATYPE;
}
}
}
//
// If we didn't complete successfully, set the last error, and free the
// return string if it was allocated.
//
if (rc != ERROR_SUCCESS) {
SetLastError(rc);
if (rString) {
LocalFree(rString);
rString = NULL;
}
}
return rString;
}
static
LONG
CheckForInstalledComponents (
VOID
)
{
BOOL rc;
HKEY softwareKey;
//
// Attempt to open the key.
//
rc = RegOpenKeyEx(
HKEY_LOCAL_MACHINE,
DIRECTORYKEY,
0,
KEY_READ,
&softwareKey
);
//
// If the key exists, then assume that Microsoft Visual C++ 5.0 is installed.
//
RegCloseKey(softwareKey);
return rc;
}
BOOL
WINAPI
DllMain (
IN HANDLE Instance,
IN ULONG Reason,
IN LPVOID Reserved
)
{
switch (Reason) {
case DLL_PROCESS_ATTACH:
break;
case DLL_PROCESS_DETACH:
//
// Ensure that the MigInf structure is cleaned up before unloading this DLL.
//
MigInf_CleanUp();
break;
}
return TRUE;
}
LONG
CALLBACK
QueryVersion (
OUT LPCSTR * ProductId,
OUT LPUINT DllVersion,
OUT LPINT * CodePageArray,OPTIONAL
OUT LPCSTR * ExeNamesBuf,OPTIONAL
LPVOID Reserved
)
{
LONG rc;
INFO("Entering QueryVersion.");
assert(ProductId);
assert(DllVersion);
assert(CodePageArray);
assert(ExeNamesBuf);
//
// Setup is calling us to query our version information and to identify if
// we need processing. We always need to provide the product ID and the
// DLL version.
//
*ProductId = g_ProductId;
*DllVersion = g_DllVersion;
//
// Check to see if there is anything to do.
//
if (CheckForInstalledComponents() == ERROR_SUCCESS) {
//
// There are installed components. Return the information Setup is
// asking for.
//
*CodePageArray = g_CodePageArray; // Use the CP_ACP code page for conversion to unicode,
*ExeNamesBuf = g_ExeNamesBuffer;
//
// Since there is work to do, return EXIT_SUCCESS. This informs Setup
// that this dll does require processing during migration.
//
rc = ERROR_SUCCESS;
}
else {
rc = ERROR_NOT_INSTALLED;
}
return rc;
}
LONG
CALLBACK
Initialize9x (
IN LPCSTR WorkingDirectory,
IN LPCSTR SourceDirectories,
LPVOID Reserved
)
{
LONG rc;
//
// Setup guarantees that the source directories parameter is valid.
//
assert(SourceDirectories);
INFO("Entering Initialize9x.");
//
// Initialize the MigInf structure.
//
if (!MigInf_Initialize()) {
rc = GetLastError();
}
else {
rc = ERROR_SUCCESS;
}
return rc;
}
LONG
CALLBACK
MigrateUser9x (
IN HWND ParentWnd,
IN LPCSTR UnattendFile,
IN HKEY UserRegKey,
IN LPCSTR UserName,
LPVOID Reserved
)
{
//
// Setup guarantees that UnattendFile,UserRegKey will be non-NULL
//
assert(UnattendFile);
assert(UserRegKey);
INFO("Entering MigrateUser9x.");
//
// Nothing to do per user, so, return ERROR_NO_MORE_FILES
//
return ERROR_NOT_INSTALLED;
}
LONG
CALLBACK
MigrateSystem9x (
IN HWND ParentWnd,
IN LPCSTR UnattendFile,
LPVOID Reserved
)
{
LONG rc = EXIT_SUCCESS;
PSTR visualCppDirectory = NULL;
MIGINFSECTIONENUM sectionEnum;
BOOL firstMessage = TRUE;
PCSTR messageSection = NULL;
//
// Setup guarantees that UnattendFile will be non-NULL.
//
assert(UnattendFile);
INFO("Entering MigrateSystem9x");
//
// Since we are in this function, Initialize9x MUST have returned ERROR_SUCCESS.
// Microsoft Visual C++ is installed on this machine.
//
//
// Initialize the miginf module to handle interfacing with Migrate.Inf and retrieve
// the installation directory for Visual C++.
//
visualCppDirectory = GetPviewDirectory9x();
if (!visualCppDirectory) {
rc = GetLastError();
}
else {
//
// The migration INF was successfully initialized. See if there is anything for
// us to do. There is work to be done if (1) the [Migration Paths] section of
// Migrate.inf contains some paths (Indicating that setup found some of the files
// we asked it to look for in ExeNamesBuf) and (2) The Visual CPP Install directory
// is not in the Excluded Paths Section.
//
if (MigInf_FirstInSection(SECTION_MIGRATIONPATHS,§ionEnum)
&& !MigInf_PathIsExcluded(visualCppDirectory)) {
//
// All checks are good. We have work to do.
// we need to sift through the files that
// Setup returned in the Migration paths section. If the files
// returned are in the installation directory, we will write them
// to both the [Handled Files] sections and the [Moved Files]
// sections. If not, we will write the file to the [Handled Files]
// section and then write a message to the [Incompatible Messages]
// section. This will allow us to override the message that
// Setup is providing for these files with a more meaningful one.
//
do {
if (PathIsInPath(sectionEnum.Key,visualCppDirectory)) {
//
// This file is in our installation path. We'll be handling it.
//
if (!MigInf_AddObject(
MIG_FILE,
SECTION_HANDLED,
sectionEnum.Key,
NULL
)) {
rc = ERROR_CANTWRITE;
break;
}
//
// We also need to note the amount of space that we will use.
//
if (!MigInf_UseSpace(sectionEnum.Key,SIZENEEDED)) {
rc = ERROR_CANTWRITE;
break;
}
}
else {
//
// This file is not in our installation path.
//
if (firstMessage) {
//
// We'll only add one message to the incompatible messages
// section, no matter how many PVIEW's we find outside of
// the installation directory. However, we'll add all of
// those files to the section that controls that message.
// That way, the message will always appear unless _every_
// file in that section has been handled by something
// (i.e. another migration DLL.)
//
firstMessage = FALSE;
if (!MigInf_AddObject(
MIG_MESSAGE,
SECTION_INCOMPATIBLE,
MESSAGE_SECTION,
MESSAGE
)) {
rc = ERROR_CANTWRITE;
break;
}
}
if (!MigInf_AddObject(
MIG_FILE,
MESSAGE_SECTION,
sectionEnum.Key,
NULL
)) {
rc = ERROR_CANTWRITE;
break;
}
}
} while (MigInf_NextInSection(§ionEnum));
}
else {
//
// There is nothing for us to do.
//
rc = ERROR_NOT_INSTALLED;
}
MigInf_WriteInfToDisk();
}
//
// Free the memory allocated in GetVisualCppDirectory.
//
if (visualCppDirectory) {
LocalFree(visualCppDirectory);
}
return rc;
}
LONG
CALLBACK
InitializeNT (
IN LPCWSTR WorkingDirectory,
IN LPCWSTR SourceDirectory,
LPVOID Reserved
)
{
//
// Setup ensures that WorkingDirectory and SourceDirectory will be non-NULL.
//
assert(WorkingDirectory != NULL && SourceDirectory != NULL);
//
// We do not need to do anything in this call. Simply return ERROR_SUCCES
//
return ERROR_SUCCESS;
}
LONG
CALLBACK
MigrateUserNT (
IN HINF UnattendInfHandle,
IN HKEY UserRegHandle,
IN LPCWSTR UserName,
LPVOID Reserved
)
{
//
// Setup guarantees that UnattendInfHandle and UserRegHandle are non-NULL and valid.
// UserName can be NULL, however, for the default user.
//
assert(UnattendInfHandle);
//
// Nothing to do per user. Simply return ERROR_SUCCESS.
// (Note the difference in return codes between MigrateUser9x and MigrateUserNT.)
//
return ERROR_SUCCESS;
}
LONG
CALLBACK
MigrateSystemNT (
IN HINF UnattendInfHandle,
LPVOID Reserved
)
{
LONG rc;
PSTR pviewDir;
CHAR fromPath[MAX_PATH];
CHAR toPath[MAX_PATH];
//
// Setup guarantees that UnattendInfHandle is non-NULL and is valid.
//
assert(UnattendInfHandle && UnattendInfHandle != INVALID_HANDLE_VALUE);
//
// If we have gotten to this point, we know that we are installed. All we need to do is copy
// the NT version of PVIEW into the installation directory of Visual C++ 5.0 Note that we do
// not replace the 9x version as a normal Visual C++ install on NT would have the 9x of PVIEW
// as well.
//
pviewDir = GetPviewDirectoryNt();
if (pviewDir) {
sprintf(fromPath,".\\%s",FILE_PVIEW);
sprintf(toPath,"%s\\%s",pviewDir,FILE_PVIEW);
if (!CopyFileA(fromPath,toPath,FALSE)) {
rc = GetLastError();
}
else {
rc = ERROR_SUCCESS;
}
}
else {
rc = GetLastError();
}
return rc = ERROR_SUCCESS;
}