Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

visual studio 2012 win32 project targeting windows 2000

Tags:

c++

windows

I need to modify a very old project written some years ago in win32 that MUST run on windows 2000 server.

Having recently upgraded my computer i moved to visual studio 2012 and hence my problems.

Ihave read a lot of posts here, and i kinda got confused.

first using platform toolsets i need to install vs2008 & vs2010 right? this is unacceptable.

second, some posts say that i need to overwrite the function DecodePointer/EncodePointer .

third, just be using the defines

#ifndef WINVER
#define WINVER 0x0501
#endif

#ifndef _WIN32_WINNT   // Specifies that the minimum required platform is Windows XP.
#define _WIN32_WINNT 0x0501
#endif

option 1 is not acceptable. which of the other 2 options work with vs2012???

thanks to all.

like image 788
Ray Avatar asked Oct 22 '13 11:10

Ray


2 Answers

The runtime for the VS2012 compiler supports targetting XP, but does not support earlier versions. In fact, on release, XP targetting was not supported and that was added in a later update. If you must support Win2k, you must use the toolset from an earlier version of VS that does support Win2k.

like image 199
David Heffernan Avatar answered Oct 18 '22 06:10

David Heffernan


Here's an expansion on Cody Gray's code for VS2017. It could use a few extra eyeballs to ensure it would work properly. As of now apps will start and run on NT4 built with VS2017. It will also work on Win98 if using MS Layer for Unicode (unicows).

.386
.MODEL flat, stdcall

    USE_W_STRINGS EQU 1                 ; Flag on which GetModuleHandle version to use
    USE_WIN9X     EQU 1                 ; Flag on if to include Win9x specific requirements

   EXTRN STDCALL ImplementGetModuleHandleExW@12 : PROC
   EXTRN STDCALL ImplementSetFilePointerEx@20 : PROC

   ;; Declare functions that we will call statically
   IF USE_W_STRINGS
    EXTRN STDCALL _imp__GetModuleHandleW@4  : DWORD
   ELSE
    EXTRN STDCALL _imp__GetModuleHandleA@4  : DWORD
   ENDIF

   EXTRN STDCALL _imp__GetProcAddress@8    : DWORD


.DATA
   ;; Override the import symbols from kernel32.dll
   __imp__InitializeSListHead@4    DWORD DownlevelInitializeSListHead
   __imp__GetModuleHandleExW@12    DWORD DownlevelGetModuleHandleExW
   __imp__EncodePointer@4          DWORD DownlevelEncodeDecodePointer
   __imp__DecodePointer@4          DWORD DownlevelEncodeDecodePointer
   ;__imp__HeapSetInformation@16 dd DownlevelHeapSetInformation
   __imp__SetFilePointerEx@20      DWORD ImplementSetFilePointerEx@20

   EXTERNDEF STDCALL __imp__InitializeSListHead@4 : DWORD
   EXTERNDEF STDCALL __imp__GetModuleHandleExW@12 : DWORD
   EXTERNDEF STDCALL __imp__EncodePointer@4 : DWORD
   EXTERNDEF STDCALL __imp__DecodePointer@4 : DWORD
   ;EXTERNDEF STDCALL __imp__HeapSetInformation@16 : DWORD
   EXTERNDEF STDCALL __imp__SetFilePointerEx@20 : DWORD


   ; For Win9x support - need to change return value to TRUE for the crt startup
   IF USE_WIN9X
    __imp__InitializeCriticalSectionAndSpinCount@8 DWORD DownlevelInitializeCriticalSectionAndSpinCount
    EXTERNDEF STDCALL __imp__InitializeCriticalSectionAndSpinCount@8 : DWORD
   ENDIF

CONST SEGMENT
   IF USE_W_STRINGS
     kszKernel32            DB 'k', 00H, 'e', 00H, 'r', 00H, 'n', 00H, 'e', 00H, 'l', 00H, '3', 00H, '2', 00H, 00H, 00H
     kszAdvApi32            DB 'a', 00H, 'd', 00H, 'v', 00H, 'a', 00H, 'p', 00H, 'i', 00H, '3', 00H, '2', 00H, 00H, 00H
   ELSE
     kszKernel32            DB "kernel32", 00H
     kszAdvApi32            DB "advapi32", 00H
   ENDIF

   kszInitializeSListHead DB "InitializeSListHead", 00H
   kszGetModuleHandleExW  DB "GetModuleHandleExW", 00H

   kszInitializeCriticalSectionAndSpinCount DB "InitializeCriticalSectionAndSpinCount", 00H
   kszGetVersion DB "GetVersion", 00H

  ; Windows XP and Server 2003 and later have RtlGenRandom, which is exported as SystemFunction036.
  ; If needed, we could fall back to CryptGenRandom(), but that will be much slower
  ; because it has to drag in the entire crypto API.
  ; (See also: https://blogs.msdn.microsoft.com/michael_howard/2005/01/14/cryptographically-secure-random-number-on-windows-without-using-cryptoapi/)
;   kszSystemFunction036   DB "SystemFunction036", 00H
CONST ENDS

.CODE

   ; C++ translation:
   ;    extern "C" VOID WINAPI DownlevelInitializeSListHead(PSLIST_HEADER pHead)
   ;    {
   ;       const HMODULE hmodKernel32 = ::GetModuleHandleW(L"kernel32");
   ;       typedef decltype(InitializeSListHead)* pfnInitializeSListHead;
   ;       const pfnInitializeSListHead pfn = reinterpret_cast<pfnInitializeSListHead>(::GetProcAddress(hmodKernel32, "InitializeSListHead"));
   ;       if (pfn)
   ;       {
   ;          // call WinAPI function
   ;          pfn(pHead);
   ;       }
   ;       else
   ;       {
   ;          // fallback implementation for downlevel
   ;          pHead->Alignment = 0;
   ;       }
   ;    }
   DownlevelInitializeSListHead PROC
      ;; Get a handle to the DLL containing the function of interest.
      push  OFFSET kszKernel32
   IF USE_W_STRINGS
      call  DWORD PTR _imp__GetModuleHandleW@4  ; Returns the handle to the library in EAX.
   ELSE
      call  DWORD PTR _imp__GetModuleHandleA@4  ; Returns the handle to the library in EAX.
   ENDIF

       ;; Attempt to obtain a pointer to the function of interest.
      push  OFFSET kszInitializeSListHead       ; Push 2nd parameter (string containing function's name).
      push  eax                                 ; Push 1st parameter (handle to the library).
      call  DWORD PTR _imp__GetProcAddress@8    ; Returns the pointer to the function in EAX.

      ;; Test for success, and call the function if we succeeded.
      test  eax, eax                            ; See if we successfully retrieved a pointer to the function.
      je    SHORT FuncNotSupported              ; Jump on failure (ptr == 0), or fall through in the most-likely case.
      jmp   eax                                 ; We succeeded (ptr != 0), so tail-call the function.

      ;; The dynamic call failed, presumably because the function isn't available.
      ;; So do what _RtlInitializeSListHead@4 (which is what we jump to on uplevel platforms) does,
      ;; which is to set pHead->Alignment to 0. It is a QWORD-sized value, so 32-bit code must
      ;; clear both of the DWORD halves.
    FuncNotSupported:
      mov   edx, DWORD PTR [esp+4]      ; get pHead->Alignment
      xor   eax, eax
      mov   DWORD PTR [edx],   eax      ; pHead->Alignment = 0
      mov   DWORD PTR [edx+4], eax
      ret   4
   DownlevelInitializeSListHead ENDP


   ; C++ translation:
   ;     extern "C" BOOL WINAPI DownlevelGetModuleHandleExW(DWORD dwFlags, LPCTSTR lpModuleName, HMODULE* phModule)
   ;     {
   ;        const HMODULE hmodKernel32 = ::GetModuleHandleW(L"kernel32");
   ;        typedef decltype(GetModuleHandleExW)* pfnGetModuleHandleExW;
   ;        const pfnGetModuleHandleExW pfn = reinterpret_cast<pfnGetModuleHandleExW>(::GetProcAddress(hmodKernel32, "GetModuleHandleExW"));
   ;        if (pfn)
   ;        {
   ;           // call WinAPI function
   ;           return pfn(dwFlags, lpModuleName, phModule);
   ;        }
   ;        else
   ;        {
   ;           // fallback for downlevel: return failure
   ;           return FALSE;
   ;        }
   ;     }
   DownlevelGetModuleHandleExW PROC
      ;; Get a handle to the DLL containing the function of interest.
      push  OFFSET kszKernel32
   IF USE_W_STRINGS
      call  DWORD PTR _imp__GetModuleHandleW@4  ; Returns the handle to the library in EAX.
   ELSE
      call  DWORD PTR _imp__GetModuleHandleA@4  ; Returns the handle to the library in EAX.
   ENDIF

       ;; Attempt to obtain a pointer to the function of interest.
      push  OFFSET kszGetModuleHandleExW        ; Push 2nd parameter (string containing function's name).
      push  eax                                 ; Push 1st parameter (handle to the library).
      call  DWORD PTR _imp__GetProcAddress@8    ; Returns the pointer to the function in EAX.

      ;; Test for success, and call the function if we succeeded.
      test  eax, eax                            ; See if we successfully retrieved a pointer to the function.
      je    SHORT FuncNotSupported              ; Jump on failure (ptr == 0), or fall through in the most-likely case.
      jmp   eax                                 ; We succeeded (ptr != 0), so tail-call the function.

      ;; The dynamic call failed, presumably because the function isn't available.
      ;; The basic VS 2015 CRT (used in a simple Win32 app) only calls this function
      ;; in try_cor_exit_process(), as called from common_exit (both in exit.cpp),
      ;; where it uses it to attempt to get a handle to the module mscoree.dll.
      ;; Since we don't care about managed apps, that attempt should rightfully fail.
      ;; If this turns out to be used in other contexts, we'll need to revisit this
      ;; and implement a proper fallback.
    FuncNotSupported:
      jmp   ImplementGetModuleHandleExW@12

   DownlevelGetModuleHandleExW ENDP

   DownlevelEncodeDecodePointer proc
      mov eax, [esp+4]
      ret 4
   DownlevelEncodeDecodePointer endp

;   DownlevelHeapSetInformation proc
;      mov eax, 1
;      ret 10h
;   DownlevelHeapSetInformation endp

;   DownlevelSystemFunction036 PROC
;      int 3   ; break --- stub unimplemented
;      ret 8
;   DownlevelSystemFunction036 ENDP


; Win9x section
IF USE_WIN9X

   ; here we need to return 1 for the crt startup code which checks the return value which on 9x is "none" (throws exception instead)
   ; This section will change the imported function on the first call for NT based OS so that every call doesn't require additional processing.

   DownlevelInitializeCriticalSectionAndSpinCount proc

      push  OFFSET kszKernel32
   IF USE_W_STRINGS
      call  DWORD PTR _imp__GetModuleHandleW@4  ; Returns the handle to the library in EAX.
   ELSE
      call  DWORD PTR _imp__GetModuleHandleA@4  ; Returns the handle to the library in EAX.
   ENDIF

       ;; Save copy of handle
      push  eax

       ;; Attempt to obtain a pointer to the function of interest.
      push  OFFSET kszInitializeCriticalSectionAndSpinCount ; Push 2nd parameter (string containing function's name).
      push  eax                                 ; Push 1st parameter (handle to the library).
      call  DWORD PTR _imp__GetProcAddress@8    ; Returns the pointer to the function in EAX.

      ;; Test for success, and call the function if we succeeded.
      test  eax, eax                            ; See if we successfully retrieved a pointer to the function.
      jz    SHORT FuncNotSupported              ; Jump on failure (ptr == 0), or fall through in the most-likely case.

      ;; save address and get back kernel32 handle
      xchg  dword ptr [esp],eax

       ;; Attempt to obtain a pointer to the function of interest.
      push  OFFSET kszGetVersion                ; Push 2nd parameter (string containing function's name).
      push  eax                                 ; Push 1st parameter (handle to the library).
      call  DWORD PTR _imp__GetProcAddress@8    ; Returns the pointer to the function in EAX.

      ;; See if function exists
      test  eax,eax
      jz    SHORT FuncNotSupported

      ;; call GetVersion
      call  eax

      ;; check if win9x
      test  eax,80000000h
      pop   eax                                 ; get back address of InitializeCriticalSectionAndSpinCount function
      jz    WinNT

      ;; for Win9x we need to call and then change return code
      push  [esp+8]                             ; Copy the 1st parameter (+4 for IP return address, +4 for fist param)
      push  [esp+8]                             ; Copy the 2nd parameter (recall esp moves on push)
      call  eax
      mov   eax,1
      ret   8

WinNT:
      ;; Change the call address for next calls and call directly
      mov   __imp__InitializeCriticalSectionAndSpinCount@8,eax
      jmp   eax


      ;; We should never end up here but if we do, fail the call
    FuncNotSupported:
      pop  eax
      xor  eax,eax
      ret  8

   DownlevelInitializeCriticalSectionAndSpinCount endp

ENDIF

END

There is a part 2 as well:

// Win9x:
//
// The CRT now uses several "W" versions of functions which is more practial to require
// the use of the Microsoft Layer for Unicode (MSLU) for Windows 9x to implement it.  The 
// unicows.dll (for 9x) should be placed in the program folder with the .exe if using it.
// unicows.dll is only loaded on 9x platforms. The way Win9x works without the MSLU is 
// that several "W" version of functions are located in kernel32.dll but they are just a
// stub that returns failure code. To implement the unicode layer (unicows) the unicode.lib
// must be linked prior to the other libs that should then linked in after unicode.lib.  
// The libraries are:
//
//    kernel32.lib advapi32.lib user32.lib gdi32.lib shell32.lib comdlg32.lib 
//    version.lib mpr.lib rasapi32.lib winmm.lib winspool.lib vfw32.lib 
//    secur32.lib oleacc.lib oledlg.lib sensapi.lib
//


#include <windows.h>

// pull items from ntdef.h
#define NT_SUCCESS(Status) (((NTSTATUS)(Status)) >= 0)
typedef _Return_type_success_(return >= 0) LONG NTSTATUS;

//
// implementation of replacement function for GetModuleHandleExW
//
extern "C" BOOL WINAPI ImplementGetModuleHandleExW(DWORD dwFlags, LPCWSTR lpModuleName, HMODULE* phModule)
{
  // check flag combinations
  if (phModule==NULL ||
      dwFlags & ~(GET_MODULE_HANDLE_EX_FLAG_PIN | GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT | GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS) ||
      ((dwFlags & GET_MODULE_HANDLE_EX_FLAG_PIN) && (dwFlags & GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT)) ||
      (lpModuleName==NULL && (dwFlags & GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS))) {
    SetLastError(ERROR_INVALID_PARAMETER);
    return FALSE;
  }


  // check if to get by address
  if (dwFlags & GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS) {
    *phModule = NULL;

    typedef PVOID (NTAPI *LPFN_RtlPcToFileHeader)(PVOID PcValue, PVOID * BaseOfImage);

    const HMODULE hmodkernel32 = GetModuleHandleW(L"kernel32");
    const LPFN_RtlPcToFileHeader pfnRtlPcToFileHeader = reinterpret_cast<LPFN_RtlPcToFileHeader>(GetProcAddress(hmodkernel32, "RtlPcToFileHeader"));

    if (pfnRtlPcToFileHeader) {
      // use RTL function (nt4+)
      pfnRtlPcToFileHeader((PVOID)lpModuleName, (PVOID*)phModule);
    }
    else {
      // query memory directly (win9x)
      MEMORY_BASIC_INFORMATION mbi; 
      if (VirtualQuery((PVOID)lpModuleName, &mbi, sizeof(mbi)) >= offsetof(MEMORY_BASIC_INFORMATION, AllocationProtect)) {
        *phModule = reinterpret_cast<HMODULE>(mbi.AllocationBase);
      }
    }
  }
  else {
    // standard getmodulehandle - to see if loaded
    *phModule = GetModuleHandleW(lpModuleName);
  }


  // check if module found
  if (*phModule == NULL) {
    SetLastError(ERROR_DLL_NOT_FOUND);
    return FALSE;
  }

  // check if reference needs updating
  if ((dwFlags & GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT) == 0) {

    typedef NTSTATUS(NTAPI *LPFN_LdrAddRefDll)(ULONG Flags, PVOID BaseAddress);
    #define LDR_ADDREF_DLL_PIN   0x00000001

    const HMODULE hmodntdll = GetModuleHandleW(L"ntdll");
    const LPFN_LdrAddRefDll pfnLdrAddRefDll = reinterpret_cast<LPFN_LdrAddRefDll>(GetProcAddress(hmodntdll, "LdrAddRefDll"));

    if (pfnLdrAddRefDll) {
      // update dll reference
      if (!NT_SUCCESS(pfnLdrAddRefDll((dwFlags & GET_MODULE_HANDLE_EX_FLAG_PIN) ? LDR_ADDREF_DLL_PIN : 0, *phModule))) {
        SetLastError(ERROR_GEN_FAILURE);
        return FALSE;
      }
    }
    else if (dwFlags & GET_MODULE_HANDLE_EX_FLAG_PIN) {
      SetLastError(ERROR_NOT_SUPPORTED);
      return FALSE;
    }
    else {
      WCHAR *filename;
      if ((filename=reinterpret_cast<WCHAR*>(VirtualAlloc(NULL, MAX_PATH*sizeof(WCHAR), MEM_COMMIT, PAGE_READWRITE)))!=NULL) {
        DWORD ret = GetModuleFileNameW(*phModule, filename, MAX_PATH);
        if (ret < MAX_PATH) {
          *phModule = LoadLibraryW(filename);
        }
        else *phModule = NULL;
        VirtualFree(filename, 0, MEM_RELEASE);
        // ensure load library success
        if (*phModule == NULL) {
          return FALSE;
        }
      }
      else {
        SetLastError(ERROR_NOT_ENOUGH_MEMORY);
        return FALSE;
      }
    }
  }
  return TRUE;
}

//
// implementation of replacement function for SetFilePointerEx
//
extern "C" BOOL WINAPI ImplementSetFilePointerEx(HANDLE hFile, LARGE_INTEGER liDistanceToMove, PLARGE_INTEGER lpNewFilePointer, DWORD dwMoveMethod)
{
  DWORD ret=SetFilePointer(hFile, liDistanceToMove.LowPart, &liDistanceToMove.HighPart, dwMoveMethod);
  if (ret == INVALID_SET_FILE_POINTER) {
    if (GetLastError() != NO_ERROR) {
      return FALSE;
    }
  }
  // check if provide file location
  if (lpNewFilePointer) {
    lpNewFilePointer->LowPart = ret;
    lpNewFilePointer->HighPart = liDistanceToMove.HighPart;
  }
  // success
  return TRUE;
}
like image 37
user3161924 Avatar answered Oct 18 '22 05:10

user3161924