/*++

Copyright (c) 1997 Microsoft Corporation

Module Name:

    ipc.c

Abstract:

  The routines in this source file implement an interprocess communication
  mechanism to allow migration DLLs to be isolated into a separate process
  ("sandboxing").  This is done so that no DLL can affect the results of
  any other DLL or Setup.

  The IPC mechanism used here is memory mapped files.  Writes to the
  memory mapped file are synchronized by two events, one for the receiver
  and one by the host.

Author:

    Jim Schmidt (jimschm) 22-Mar-1997

Revision History:

    jimschm     19-Mar-2001 Removed DVD check (now in migration dll)
    jimschm     02-Jun-1999 Added IPC-based DVD check
    jimschm     21-Sep-1998 Converted from mailslots to memory mapped files.
                            (There are bugs in both Win9x and NT mailslots
                            that broke the original design.)
    jimschm     19-Jan-1998  Added beginings of WinVerifyTrust calls

    jimschm     15-Jul-1997  Added many workarounds for Win95 bugs.

--*/


#include "pch.h"
#include "migutilp.h"

#include <softpub.h>

#ifdef UNICODE
#error Build must be ANSI
#endif

#define DBG_IPC "Ipc"




typedef struct {
    HANDLE Mapping;
    HANDLE DoCommand;
    HANDLE GetResults;
} IPCDATA, *PIPCDATA;

static PCTSTR g_Mode;
static HANDLE g_ProcessHandle;
static BOOL g_Host;
static IPCDATA g_IpcData;

VOID
pCloseIpcData (
    VOID
    );

BOOL
pOpenIpcData (
    VOID
    );

BOOL
pCreateIpcData (
    IN      PSECURITY_ATTRIBUTES psa
    );

typedef struct {
    DWORD   Command;
    DWORD   Result;
    DWORD   TechnicalLogId;
    DWORD   GuiLogId;
    DWORD   DataSize;
    BYTE    Data[];
} MAPDATA, *PMAPDATA;



BOOL
OpenIpcA (
    IN      BOOL Win95Side,
    IN      PCSTR ExePath,                  OPTIONAL
    IN      PCSTR RemoteArg,                OPTIONAL
    IN      PCSTR WorkingDir                OPTIONAL
    )

/*++

Routine Description:

  OpenIpc has two modes of operation, depending on who the caller is.  If the
  caller is w95upg.dll or w95upgnt.dll, then the IPC mode is called "host mode."
  If the caller is migisol.exe, then the IPC mode is called "remote mode."

  In host mode, OpenIpc creates all of the objects necessary to implement
  the IPC.  This includes two events, DoCommand and GetResults, and a
  file mapping.  After creating the objects, the remote process is launched.

  In remote mode, OpenIpc opens the existing objects that have already
  been created.

Arguments:

  Win95Side - Used in host mode only.  Specifies that w95upg.dll is running
              when TRUE, or that w95upgnt.dll is running when FALSE.

  ExePath   - Specifies the command line for migisol.exe.  Specifies NULL
              to indicate remote mode.

  RemoteArg - Used in host mode only.  Specifies the migration DLL
              path.  Ignored in remote mode.

  WorkingDir - Used in host mode only.  Specifies the working directory path
               for the migration DLL.  Ignored in remote mode.

Return value:

  TRUE if the IPC channel was opened.  If host mode, TRUE indicates that
  migisol.exe is up and running.  If remote mode, TRUE indicates that
  migisol is ready for commands.

--*/

{
    CHAR CmdLine[MAX_CMDLINE];
    STARTUPINFOA si;
    PROCESS_INFORMATION pi;
    BOOL ProcessResult;
    HANDLE SyncEvent = NULL;
    HANDLE ObjectArray[2];
    DWORD rc;
    PSECURITY_DESCRIPTOR psd = NULL;
    SECURITY_ATTRIBUTES sa, *psa;
    BOOL Result = FALSE;

#ifdef DEBUG
    g_Mode = ExePath ? TEXT("host") : TEXT("remote");
#endif

    __try {

        g_ProcessHandle = NULL;

        g_Host = (ExePath != NULL);

        if (ISNT()) {
            //
            // Create nul DACL for NT
            //

            ZeroMemory (&sa, sizeof (sa));

            psd = (PSECURITY_DESCRIPTOR) MemAlloc (g_hHeap, 0, SECURITY_DESCRIPTOR_MIN_LENGTH);

            if (!InitializeSecurityDescriptor (psd, SECURITY_DESCRIPTOR_REVISION)) {
                __leave;
            }

            if (!SetSecurityDescriptorDacl (psd, TRUE, (PACL) NULL, FALSE)) {
                 __leave;
            }

            sa.nLength = sizeof (sa);
            sa.lpSecurityDescriptor = psd;

            psa = &sa;

        } else {
            psa = NULL;
        }

        if (g_Host) {
            //
            // Create the IPC objects
            //

            if (!pCreateIpcData (psa)) {
                DEBUGMSG ((DBG_ERROR, "Cannot create IPC channel"));
                __leave;
            }

            MYASSERT (RemoteArg);

            SyncEvent = CreateEvent (NULL, FALSE, FALSE, TEXT("win9xupg"));
            MYASSERT (SyncEvent);

            //
            // Create the child process
            //

            wsprintfA (
                CmdLine,
                "\"%s\" %s \"%s\"",
                ExePath,
                Win95Side ? "-r" : "-m",
                RemoteArg
                );

            ZeroMemory (&si, sizeof (si));
            si.cb = sizeof (si);
            si.dwFlags = STARTF_FORCEOFFFEEDBACK;

            ProcessResult = CreateProcessA (
                                NULL,
                                CmdLine,
                                NULL,
                                NULL,
                                FALSE,
                                CREATE_DEFAULT_ERROR_MODE,
                                NULL,
                                WorkingDir,
                                &si,
                                &pi
                                );

            if (ProcessResult) {
                CloseHandle (pi.hThread);
            } else {
                LOG ((LOG_ERROR, "Cannot start %s", CmdLine));
                __leave;
            }

            //
            // Wait for process to fail or wait for it to set the win95upg event
            //

            ObjectArray[0] = SyncEvent;
            ObjectArray[1] = pi.hProcess;
            rc = WaitForMultipleObjects (2, ObjectArray, FALSE, 60000);
            g_ProcessHandle = pi.hProcess;

            if (rc != WAIT_OBJECT_0) {
                DEBUGMSG ((
                    DBG_WARNING,
                    "Process %x did not signal 'ready'. Wait timed out. (%s)",
                    g_ProcessHandle,
                    g_Mode
                    ));

                LOG ((LOG_ERROR, "Upgrade pack failed during process creation."));

                __leave;
            }

            DEBUGMSG ((DBG_IPC, "Process %s is running (%s)", CmdLine, g_Mode));

        } else {        // !g_Host
            //
            // Open the IPC objects
            //

            if (!pOpenIpcData()) {
                DEBUGMSG ((DBG_ERROR, "Cannot open IPC channel"));
                __leave;
            }

            //
            // Set event notifying setup that we've created our mailslot
            //

            SyncEvent = OpenEvent (EVENT_ALL_ACCESS, FALSE, TEXT("win9xupg"));
            if (!SyncEvent) {
                __leave;
            }
            SetEvent (SyncEvent);
        }

        Result = TRUE;
    }

    __finally {
        //
        // Cleanup code
        //

        PushError();

        if (!Result) {
            CloseIpc();
        }

        if (SyncEvent) {
            CloseHandle (SyncEvent);
        }

        if (psd) {
            MemFree (g_hHeap, 0, psd);
        }

        PopError();
    }

    return Result;

}


BOOL
OpenIpcW (
    IN      BOOL Win95Side,
    IN      PCWSTR ExePath,                 OPTIONAL
    IN      PCWSTR RemoteArg,               OPTIONAL
    IN      PCWSTR WorkingDir               OPTIONAL
    )
{
    PCSTR AnsiExePath, AnsiRemoteArg, AnsiWorkingDir;
    BOOL b;

    if (ExePath) {
        AnsiExePath = ConvertWtoA (ExePath);
    } else {
        AnsiExePath = NULL;
    }

    if (RemoteArg) {
        AnsiRemoteArg = ConvertWtoA (RemoteArg);
    } else {
        AnsiRemoteArg = NULL;
    }

    if (WorkingDir) {
        AnsiWorkingDir = ConvertWtoA (WorkingDir);
    } else {
        AnsiWorkingDir = NULL;
    }

    b = OpenIpcA (Win95Side, AnsiExePath, AnsiRemoteArg, AnsiWorkingDir);

    FreeConvertedStr (AnsiExePath);
    FreeConvertedStr (AnsiRemoteArg);
    FreeConvertedStr (AnsiWorkingDir);

    return b;
}


VOID
CloseIpc (
    VOID
    )

/*++

  Routine Description:

    Tells migisol.exe process to terminate, and then cleans up all resources
    opened by OpenIpc.

  Arguments:

    none

  Return Value:

    none

--*/

{
    if (g_Host) {
        //
        // Tell migisol.exe to terminate
        // if the communications channel is up
        //
        if (g_IpcData.Mapping && !SendIpcCommand (IPC_TERMINATE, NULL, 0)) {
            KillIpcProcess();
        }

        if (g_ProcessHandle) {
            WaitForSingleObject (g_ProcessHandle, 10000);
        }
    }

    pCloseIpcData();

    if (g_ProcessHandle) {
        CloseHandle (g_ProcessHandle);
        g_ProcessHandle = NULL;
    }
}


VOID
pCloseIpcData (
    VOID
    )
{
    if (g_IpcData.DoCommand) {
        CloseHandle (g_IpcData.DoCommand);
        g_IpcData.DoCommand = NULL;
    }

    if (g_IpcData.GetResults) {
        CloseHandle (g_IpcData.GetResults);
        g_IpcData.GetResults = NULL;
    }

    if (g_IpcData.Mapping) {
        CloseHandle (g_IpcData.Mapping);
        g_IpcData.Mapping = NULL;
    }
}


BOOL
pCreateIpcData (
    IN      PSECURITY_ATTRIBUTES psa
    )

/*++

Routine Description:

  pCreateIpcData creates the objects necessary to transfer data between
  migisol.exe and w95upg*.dll.  This function is called in host mode (i.e.,
  from w95upg.dll or w95upgnt.dll).

Arguments:

  psa - Specifies NT nul DACL, or NULL on Win9x

Return Value:

  TRUE if the objects were created properly, or FALSE if not.

--*/

{
    ZeroMemory (&g_IpcData, sizeof (g_IpcData));

    g_IpcData.DoCommand  = CreateEvent (psa, FALSE, FALSE, TEXT("Setup.DoCommand"));
    g_IpcData.GetResults = CreateEvent (psa, FALSE, FALSE, TEXT("Setup.GetResults"));

    g_IpcData.Mapping = CreateFileMapping (
                            INVALID_HANDLE_VALUE,
                            psa,
                            PAGE_READWRITE,
                            0,
                            0x10000,
                            TEXT("Setup.IpcData")
                            );

    if (!g_IpcData.DoCommand ||
        !g_IpcData.GetResults ||
        !g_IpcData.Mapping
        ) {
        pCloseIpcData();
        return FALSE;
    }

    return TRUE;
}


BOOL
pOpenIpcData (
    VOID
    )

/*++

Routine Description:

  pOpenIpcData opens objects necessary to transfer data between migisol.exe
  and w95upg*.dll.  This funciton is called in remote mode (i.e., by
  migisol.exe).  This function must be called after the host has created the
  objects with pCreateIpcData.

Arguments:

  None.

Return Value:

  TRUE of the objects were opened successfully, FALSE otherwise.

--*/

{
    ZeroMemory (&g_IpcData, sizeof (g_IpcData));

    g_IpcData.DoCommand  = OpenEvent (EVENT_ALL_ACCESS, FALSE, TEXT("Setup.DoCommand"));
    g_IpcData.GetResults = OpenEvent (EVENT_ALL_ACCESS, FALSE, TEXT("Setup.GetResults"));

    g_IpcData.Mapping = OpenFileMapping (
                            FILE_MAP_READ|FILE_MAP_WRITE,
                            FALSE,
                            TEXT("Setup.IpcData")
                            );

    if (!g_IpcData.DoCommand ||
        !g_IpcData.GetResults ||
        !g_IpcData.Mapping
        ) {
        pCloseIpcData();
        return FALSE;
    }

    return TRUE;
}


BOOL
IsIpcProcessAlive (
    VOID
    )

/*++

Routine Description:

  IsIpcProcessAlive checks for the presense of migisol.exe.  This function is
  intended only for host mode.

Arguments:

  None.

Return Value:

  TRUE if migisol.exe is still running, FALSE otherwise.

--*/

{
    if (!g_ProcessHandle) {
        return FALSE;
    }

    if (WaitForSingleObject (g_ProcessHandle, 0) == WAIT_OBJECT_0) {
        return FALSE;
    }

    return TRUE;
}


VOID
KillIpcProcess (
    VOID
    )

/*++

Routine Description:

  KillIpcProcess forcefully terminates an open migisol.exe process.  This is
  used in GUI mode when the DLL refuses to die.

Arguments:

  None.

Return Value:

  None.

--*/

{
    PushError();

    if (IsIpcProcessAlive()) {
        TerminateProcess (g_ProcessHandle, 0);
    }

    PopError();
}


DWORD
CheckForWaitingData (
    IN      HANDLE Slot,
    IN      DWORD MinimumSize,
    IN      DWORD Timeout
    )

/*++

Routine Description:

  CheckForWaitingData waits for data to be received by a mailslot.

  If the data does not arrive within the specified timeout, then zero is
  returned, and ERROR_SEM_TIMEOUT is set as the last error.

  If the data arrives within the specified timeout, then the number of
  waiting bytes are returned to the caller.

  This routine works around a Win95 bug with GetMailslotInfo.  Please
  change with caution.

Arguments:

  Slot - Specifies handle to inbound mailslot

  MinimumSize - Specifies the number of bytes that must be available before
                the routine considers the data to be available.  NOTE: If
                a message smaller than MinimumSize is waiting, this
                routine will be blocked until the timeout expires.
                This parameter must be greater than zero.

  Timeout - Specifies the number of milliseconds to wait for the message.

Return value:

  The number of bytes waiting in the mailslot, or 0 if the timeout was
  reached.

--*/

{
    DWORD WaitingSize;
    DWORD UnreliableTimeout;
    DWORD End;

    MYASSERT (MinimumSize > 0);

    End = GetTickCount() + Timeout;

    //
    // The wrap case -- this is really rare (once every 27 days),
    // so just let the tick count go back to zero
    //

    if (End < GetTickCount()) {
        while (End < GetTickCount()) {
            Sleep (100);
        }
        End = GetTickCount() + Timeout;
    }

    do {
        if (!GetMailslotInfo (Slot, NULL, &WaitingSize, NULL, &UnreliableTimeout)) {
            DEBUGMSG ((DBG_ERROR, "CheckForWaitingData: GetMailslotInfo failed (%s)", g_Mode));
            return 0;
        }

        //
        // WARNING: Win95 doesn't always return 0xffffffff when there is no data
        // available.  On some machines, Win9x has returned 0xc0ffffff.
        //

        WaitingSize = LOWORD(WaitingSize);

        if (WaitingSize < 0xffff && WaitingSize >= MinimumSize) {
            return WaitingSize;
        }
    } while (GetTickCount() < End);

    SetLastError (ERROR_SEM_TIMEOUT);
    return 0;
}



BOOL
pWriteIpcData (
    IN      HANDLE Mapping,
    IN      PBYTE Data,             OPTIONAL
    IN      DWORD DataSize,
    IN      DWORD Command,
    IN      DWORD ResultCode,
    IN      DWORD TechnicalLogId,
    IN      DWORD GuiLogId
    )

/*++

Routine Description:

  pWriteIpcData puts data in the memory mapped block that migisol.exe and
  w95upg*.dll share.  The OS takes care of the synchronization for us.

Arguments:

  Mapping        - Specifies the open mapping object

  Data           - Specifies binary data to write

  DataSize       - Specifies the number of bytes in Data, or 0 if Data is NULL

  Command        - Specifies a command DWORD, or 0 if not required

  ResultCode     - Specifies the result code of the last command, or 0 if not
                   applicable

  TechnicalLogId - Specifies the message constant ID (MSG_*) to be added to
                   setupact.log, or 0 if not applicable

  GuiLogId       - Specifies the message constant (MSG_*) of the message to
                   be presented via a popup, or 0 if not applicable

Return Value:

  TRUE if the data was written, FALSE if a sharing violation or other error
  occurs

--*/

{
    PMAPDATA MapData;

    MYASSERT (Mapping);
    MapData = (PMAPDATA) MapViewOfFile (Mapping, FILE_MAP_WRITE, 0, 0, 0);
    if (!MapData) {
        return FALSE;
    }

    if (!Data) {
        DataSize = 0;
    }

    MapData->Command        = Command;
    MapData->Result         = ResultCode;
    MapData->TechnicalLogId = TechnicalLogId;
    MapData->GuiLogId       = GuiLogId;
    MapData->DataSize       = DataSize;

    if (DataSize) {
        CopyMemory (MapData->Data, Data, DataSize);
    }

    UnmapViewOfFile (MapData);

    return TRUE;
}


BOOL
pReadIpcData (
    IN      HANDLE Mapping,
    OUT     PBYTE *Data,            OPTIONAL
    OUT     PDWORD DataSize,        OPTIONAL
    OUT     PDWORD Command,         OPTIONAL
    OUT     PDWORD ResultCode,      OPTIONAL
    OUT     PDWORD TechnicalLogId,  OPTIONAL
    OUT     PDWORD GuiLogId         OPTIONAL
    )

/*++

Routine Description:

  pReadIpcData retrieves data put in the shared memory block.  The OS takes
  care of synchronization for us.

Arguments:

  Mapping        - Specifies the memory mapping object

  Data           - Receives the inbound binary data, if any is available, or
                   NULL if no data is available.  The caller must free this
                   data with MemFree.

  DataSize       - Receives the number of bytes in Data

  Command        - Receives the inbound command, or 0 if no command was
                   specified

  ResultCode     - Receives the command result code, or 0 if not applicable

  TechnicalLogId - Receives the message constant (MSG_*) of the message to be
                   logged to setupact.log, or 0 if no message is to be logged

  GuiLogId       - Receives the message constant (MSG_*) of the message to be
                   presented in a popup, or 0 if no message is to be presented

Return Value:

  TRUE if data was read, or FALSE if a sharing violation or other error occurs

--*/

{
    PMAPDATA MapData;

    MapData = (PMAPDATA) MapViewOfFile (Mapping, FILE_MAP_READ, 0, 0, 0);
    if (!MapData) {
        return FALSE;
    }

    if (Data) {
        if (MapData->DataSize) {
            *Data = MemAlloc (g_hHeap, 0, MapData->DataSize);
            MYASSERT (*Data);
            CopyMemory (*Data, MapData->Data, MapData->DataSize);
        } else {
            *Data = NULL;
        }
    }

    if (DataSize) {
        *DataSize = MapData->DataSize;
    }

    if (Command) {
        *Command = MapData->Command;
    }

    if (ResultCode) {
        *ResultCode = MapData->Result;
    }

    if (TechnicalLogId) {
        *TechnicalLogId = MapData->TechnicalLogId;
    }

    if (GuiLogId) {
        *GuiLogId = MapData->GuiLogId;
    }

    UnmapViewOfFile (MapData);

    return TRUE;
}


BOOL
SendIpcCommand (
    IN      DWORD Command,
    IN      PBYTE Data,             OPTIONAL
    IN      DWORD DataSize
    )

/*++

Routine Description:

  SendIpcCommand puts a command and optional binary data in the shared memory
  block.  It then sets the DoCommand event, triggering the other process to
  read the shared memory.  It is required that a command result is sent
  before the next SendIpcCommand.  See SendIpcCommandResult.

Arguments:

  Command  - Specifies the command to be executed by migisol.exe

  Data     - Specifies the data associated with the command

  DataSize - Specifies the number of bytes in Data, or 0 if Data is NULL

Return Value:

  TRUE if the command was placed in the shared memory block, FALSE otherwise

--*/

{
    if (!pWriteIpcData (
            g_IpcData.Mapping,
            Data,
            DataSize,
            Command,
            0,
            0,
            0
            )) {
        DEBUGMSG ((DBG_ERROR, "SendIpcCommand: Can't send the command to the remote process"));
        return FALSE;
    }

    SetEvent (g_IpcData.DoCommand);

    return TRUE;
}


BOOL
GetIpcCommandResults (
    IN      DWORD Timeout,
    OUT     PBYTE *ReturnData,      OPTIONAL
    OUT     PDWORD ReturnDataSize,  OPTIONAL
    OUT     PDWORD ResultCode,      OPTIONAL
    OUT     PDWORD TechnicalLogId,  OPTIONAL
    OUT     PDWORD GuiLogId         OPTIONAL
    )

/*++

Routine Description:

  GetIpcCommandResults reads the shared memory block and returns the
  available data.

Arguments:

  Timeout        - Specifies the amount of time to wait for a command result
                   (in ms), or INFINITE to wait forever.

  ReturnData     - Receives the binary data associated with the command
                   result, or NULL if no data is associated with the result.
                   The caller must free this data with MemFree.

  ReturnDataSize - Receives the number of bytes in ReturnData, or 0 if
                   ReturnData is NULL.

  ResultCode     - Receives the command result code

  TechnicalLogId - Receives the message constant (MSG_*) to be logged in
                   setupact.log, or 0 if no message is specified

  GuiLogId       - Receives the message constant (MSG_*) of the message to be
                   presented in a popup, or 0 if no message is to be presented

Return Value:

  TRUE if command results were obtained, or FALSE if the wait timed out or
  the IPC connection crashed

--*/

{
    DWORD rc;
    BOOL b;

    rc = WaitForSingleObject (g_IpcData.GetResults, Timeout);

    if (rc != WAIT_OBJECT_0) {
        SetLastError (ERROR_NO_DATA);
        return FALSE;
    }

    b = pReadIpcData (
            g_IpcData.Mapping,
            ReturnData,
            ReturnDataSize,
            NULL,
            ResultCode,
            TechnicalLogId,
            GuiLogId
            );

    return b;
}


BOOL
GetIpcCommand (
    IN      DWORD Timeout,
    IN      PDWORD Command,         OPTIONAL
    IN      PBYTE *Data,            OPTIONAL
    IN      PDWORD DataSize         OPTIONAL
    )

/*++

Routine Description:

  GetIpcCommand obtains the command that needs to be processed.  This routine
  is called by migisol.exe (the remote process).

Arguments:

  Timeout  - Specifies the amount of time (in ms) to wait for a command, or
             INFINITE to wait forever

  Command  - Receives the command that needs to be executed

  Data     - Receives the data associated with the command.  The caller must
             free this block with MemFree.

  DataSize - Receives the number of bytes in Data, or 0 if Data is NULL.

Return Value:

  TRUE if a command was received, FALSE otherwise.

--*/

{
    DWORD rc;
    BOOL b;

    rc = WaitForSingleObject (g_IpcData.DoCommand, Timeout);

    if (rc != WAIT_OBJECT_0) {
        SetLastError (ERROR_NO_DATA);
        return FALSE;
    }

    b = pReadIpcData (
            g_IpcData.Mapping,
            Data,
            DataSize,
            Command,
            NULL,
            NULL,
            NULL
            );

    return b;
}


BOOL
SendIpcCommandResults (
    IN      DWORD ResultCode,
    IN      DWORD TechnicalLogId,
    IN      DWORD GuiLogId,
    IN      PBYTE Data,             OPTIONAL
    IN      DWORD DataSize
    )

/*++

Routine Description:

  SendIpcCommandResults puts the command results in the shared memory block.
  This routine is called by migisol.exe (the remote process).

Arguments:

  ResultCode     - Specifies the result code of the command.

  TechnicalLogId - Specifies the message constant (MSG_*) of the message to
                   be logged in setupact.log, or 0 if no message is to be
                   logged

  GuiLogId       - Specifies the message constant (MSG_*) of the message to
                   be presented in a popup to the user, or 0 if no message
                   needs to be presented

  Data           - Specifies the binary data to pass as command results, or
                   NULL of no binary data is required

  DataSize       - Specifies the number of bytes in Data, or 0 if Data is NULL

Return Value:

  TRUE if the command results were placed in shared memory, FALSE otherwise.

--*/

{
    BOOL b;

    b = pWriteIpcData (
            g_IpcData.Mapping,
            Data,
            DataSize,
            0,
            ResultCode,
            TechnicalLogId,
            GuiLogId
            );

    if (!b) {
        DEBUGMSG ((DBG_ERROR, "Can't write command results to IPC buffer"));
        return FALSE;
    }

    SetEvent (g_IpcData.GetResults);

    return TRUE;
}


BOOL
IsDllSignedA (
    IN      WINVERIFYTRUST WinVerifyTrustApi,
    IN      PCSTR DllSpec
    )
{
    PCWSTR UnicodeStr;
    BOOL b;

    UnicodeStr = CreateUnicode (DllSpec);
    if (!UnicodeStr) {
        return FALSE;
    }

    b = IsDllSignedW (WinVerifyTrustApi, UnicodeStr);

    DestroyUnicode (UnicodeStr);

    return b;
}


BOOL
IsDllSignedW (
    IN      WINVERIFYTRUST WinVerifyTrustApi,
    IN      PCWSTR DllSpec
    )
{
    GUID VerifyGuid = WINTRUST_ACTION_GENERIC_VERIFY_V2;
    WINTRUST_DATA WinTrustData;
    WINTRUST_FILE_INFO WinTrustFileInfo;
    LONG rc;

    if (!WinVerifyTrustApi) {
        return TRUE;
    }

    ZeroMemory (&WinTrustData, sizeof (WinTrustData));
    ZeroMemory (&WinTrustFileInfo, sizeof (WinTrustFileInfo));

    WinTrustData.cbStruct       = sizeof(WINTRUST_DATA);
    WinTrustData.dwUIChoice     = WTD_UI_NONE;
    WinTrustData.dwUnionChoice  = WTD_CHOICE_FILE;
    WinTrustData.pFile          = &WinTrustFileInfo;

    WinTrustFileInfo.cbStruct      = sizeof(WINTRUST_FILE_INFO);
    WinTrustFileInfo.hFile         = INVALID_HANDLE_VALUE;
    WinTrustFileInfo.pcwszFilePath = DllSpec;

    rc = WinVerifyTrustApi (
            INVALID_HANDLE_VALUE,
            &VerifyGuid,
            &WinTrustData
            );

    return rc == ERROR_SUCCESS;
}


