oracle / odpi

ODPI-C: Oracle Database Programming Interface for Drivers and Applications
https://oracle.github.io/odpi/
Other
268 stars 78 forks source link

Custom error message when Visual Studio distributable package is not installed on Windows #48

Open kubo opened 6 years ago

kubo commented 6 years ago

How about custom error message when Visual Studio distributable package is not installed? IMO, https://github.com/oracle/node-oracledb/pull/404#issuecomment-345918388 should be checked in ODPI-C.

When GetLastError() returns ERROR_MOD_NOT_FOUND(The specified module could not be found), do the following.

  1. Search OCI.DLL in the following directories in this order:
    1. Executable directory (Get the full path of the executable by GetModuleFileName(NULL, buf, sizeof(buf)) and truncate the file name.)
    2. Current directory
    3. each directory in the environment variable PATH.
  2. If OCI.DLL is not found, use the message from FormatMessage().
  3. If OCI.DLL is found, get the bit of the DLL (see the function below) and skip if it isn't same with the executable.
  4. If step 3 passes, get import DLL files (see the function below) and get the DLL whose name is 'MSVCR*.DLL'.
  5. Call LoadLibrary with the 'MSVCR*.DLL' at step 4.
  6. If step 5 fails, map the 'MSVCR*.DLL' name to VS distributable package name and create a custom error message.

The following is a sample function which prints bit and dependent DLL files. It is easy to customize this to fit this issue's need.

// link dbghelp.lib also
#include <dbghelp.h>

static void print_import_dlls(const char *name)
{
    HANDLE hFile = CreateFile(name, GENERIC_READ, FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE,
                              NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
    HANDLE hFileMapping = NULL;
    void *base = NULL;
    if (hFile == INVALID_HANDLE_VALUE) {
        printf("failed to open: %s\n", name);
        return;
    }
    hFileMapping = CreateFileMapping(hFile, NULL, PAGE_READONLY, 0, 0, NULL);
    if (hFileMapping == NULL) {
        printf("failed to create file mapping: %s\n", name);
        goto cleanup;
    }
    base = MapViewOfFile(hFileMapping, FILE_MAP_READ, 0, 0, 0);
    if (base == NULL) {
        printf("failed to map file: %s\n", name);
        goto cleanup;
    }
    IMAGE_NT_HEADERS *nt_hdr = ImageNtHeader(base);
    if (nt_hdr == NULL) {
        printf("not executable nor DLL: %s\n", name);
        goto cleanup;
    }

    printf("%s (%s bit):\n", name, nt_hdr->FileHeader.Machine == IMAGE_FILE_MACHINE_I386 ? "32" : "64");
    ULONG uSize = 0;
    IMAGE_IMPORT_DESCRIPTOR *desc = (IMAGE_IMPORT_DESCRIPTOR *)ImageDirectoryEntryToDataEx(base, FALSE, IMAGE_DIRECTORY_ENTRY_IMPORT, &uSize, NULL);

    while (desc->Characteristics != 0) {
        char *name = (char*)ImageRvaToVa(nt_hdr, base, desc->Name, NULL);
        printf("  %s\n", name);
        desc++;
    }
cleanup:
    if (base != NULL) {
        UnmapViewOfFile(base);
    }
    if (hFileMapping != NULL) {
        CloseHandle(hFileMapping);
    }
    CloseHandle(hFile);
}
anthony-tuininga commented 6 years ago

Thanks, @kubo. Adding this code to ODPI-C does make sense. I've marked this as an enhancement request.