/***************************************************************************** * * OsUtil.c * * Copyright (c) 1996 Microsoft Corporation. All Rights Reserved. * * Abstract: * Various OS dependent utility functions * * Contents: * * History: * * vlads 11/05/1996 created *****************************************************************************/ /* #include #include #include #include #include #include #include #include #include #include #include #include #include #include "wia.h" #include "stipriv.h" #include "stiapi.h" #include "stirc.h" #include "debug.h" */ #include "sticomm.h" #define DbgFl DbgFlUtil BOOL WINAPI OSUtil_IsPlatformUnicode() { OSVERSIONINFOA ver; BOOL bReturn = FALSE; ZeroX(ver); ver.dwOSVersionInfoSize = sizeof(OSVERSIONINFOA); // Just always call the ANSI function if(!GetVersionExA(&ver)) { DebugOutPtszV(DbgFl, TEXT("Unable to determinte platform -- setting flag to ANSI")); bReturn = FALSE; } else { switch(ver.dwPlatformId) { case VER_PLATFORM_WIN32_WINDOWS: bReturn = FALSE; break; case VER_PLATFORM_WIN32_NT: bReturn = TRUE; break; default: bReturn = FALSE; break; } } // Keep the compiler happy return bReturn; } // endproc OSUtil_IsUnicodePlatform HRESULT WINAPI OSUtil_GetAnsiString( LPSTR * ppszAnsi, LPCWSTR lpszWide ) { HRESULT hres; int cbStrLen; if (!lpszWide) { *ppszAnsi = NULL; return S_OK; } // We can use WideToChar to figure out exact resultant width cbStrLen = 2*(OSUtil_StrLenW(lpszWide)+1); hres = AllocCbPpv(cbStrLen, ppszAnsi); if (!*ppszAnsi) { DebugOutPtszV(DbgFl, TEXT("could not get ansi string -- out of memory")); return E_OUTOFMEMORY; } UToA(*ppszAnsi,cbStrLen,lpszWide); return S_OK; } // OSUtil_GetAnsiString HRESULT WINAPI OSUtil_GetWideString( LPWSTR *ppszWide, LPCSTR pszAnsi ) { HRESULT hres; int cbStrLen; if (!pszAnsi) { *ppszWide = NULL; return S_OK; } // We can use WideToChar to figure out exact resultant width cbStrLen = 2*(lstrlenA(pszAnsi)+1); hres = AllocCbPpv(cbStrLen, (PPV)ppszWide); if (!*ppszWide) { DebugOutPtszV(DbgFl,TEXT("Could not get unicode string -- out of memory")); return E_OUTOFMEMORY; } AToU(*ppszWide,cbStrLen,pszAnsi); return S_OK; } // OSUtil_GetWideString HINSTANCE OSUtil_LoadLibraryW( LPCWSTR lpszWFileName ) { HINSTANCE hinstRet = NULL; if (g_NoUnicodePlatform) { CHAR *pFileNameA = NULL; if ( SUCCEEDED(OSUtil_GetAnsiString(&pFileNameA,lpszWFileName))) { hinstRet = LoadLibraryA(pFileNameA); FreePpv(&pFileNameA); } else { DebugOutPtszV(DbgFl,TEXT("Failed in LoadLibraryW")); } } else { hinstRet = LoadLibraryW(lpszWFileName); } return hinstRet; } // OSUtil_LoadLibrary /* // There seems to be no getproc addressW FARPROC WINAPI OSUtil_GetProcAddress(HMODULE hModule,LPCTSTR lpProcName) { #ifndef UNICODE return GetProcAddress(hModule,lpProcNameW); #else FARPROC pProcAddr = NULL; CHAR *pProcNameA = NULL; if ( SUCCEEDED(OSUtil_GetAnsiString(&pProcNameA,lpProcName))) { pProcAddr = GetProcAddress(hModule,pProcNameA); FreePpv(&pProcNameA); } else { DebugOutPtszV(DbgFl,TEXT("Failed in GetProcAddress ")); } return pProcAddr; #endif } // OSUtil_GetProcAddress */ LONG WINAPI OSUtil_RegOpenKeyExW( HKEY hKey, LPCWSTR lpszKeyStrW, DWORD dwReserved, REGSAM samDesired, PHKEY phkResult) { LONG lRet = 0L; if (g_NoUnicodePlatform) { CHAR *lpszKeyStrA = NULL; if ( SUCCEEDED(OSUtil_GetAnsiString(&lpszKeyStrA,lpszKeyStrW))) { lRet = RegOpenKeyExA(hKey,lpszKeyStrA,dwReserved,samDesired,phkResult); FreePpv(&lpszKeyStrA); } else { DebugOutPtszV(DbgFl,TEXT("Failed in RegOpenKeyExW")); lRet = ERROR_INVALID_PARAMETER; } } else { lRet = RegOpenKeyExW(hKey,lpszKeyStrW,dwReserved,samDesired,phkResult); } return (lRet); } LONG WINAPI OSUtil_RegDeleteValueW( HKEY hKey, LPWSTR lpszValueNameW ) { long lRet = NOERROR; if (g_NoUnicodePlatform) { CHAR *lpszValueNameA = NULL; if ( SUCCEEDED(OSUtil_GetAnsiString(&lpszValueNameA,lpszValueNameW))) { lRet = RegDeleteValueA(hKey, lpszValueNameA); FreePpv(&lpszValueNameA); } else { DebugOutPtszV(DbgFl,TEXT("Failed in RegDeleteValueKeyW")); lRet = ERROR_INVALID_PARAMETER; } } else { lRet = RegDeleteValueW(hKey, lpszValueNameW); } return lRet; } LONG WINAPI OSUtil_RegCreateKeyExW( HKEY hKey, LPWSTR lpszSubKeyW, DWORD dwReserved, LPWSTR lpszClassW, DWORD dwOptions, REGSAM samDesired, LPSECURITY_ATTRIBUTES lpSecurityAttributes, PHKEY phkResult, LPDWORD lpdwDisposition ) { LPSTR lpszSubKeyA = NULL; LPSTR lpszClassA = NULL; long lRet = NOERROR; if (g_NoUnicodePlatform) { if ( SUCCEEDED(OSUtil_GetAnsiString(&lpszClassA,lpszClassW)) && SUCCEEDED(OSUtil_GetAnsiString(&lpszSubKeyA,lpszSubKeyW))) { lRet = RegCreateKeyExA(hKey, lpszSubKeyA, dwReserved, lpszClassA, dwOptions, samDesired, lpSecurityAttributes, phkResult, lpdwDisposition); FreePpv(&lpszSubKeyA); FreePpv(&lpszClassA); } else { DebugOutPtszV(DbgFl,TEXT("Failed in RegCreateKeyExW")); lRet = ERROR_INVALID_PARAMETER; } } else { lRet = RegCreateKeyExW(hKey, lpszSubKeyW, dwReserved, lpszClassW, dwOptions, samDesired, lpSecurityAttributes, phkResult, lpdwDisposition); } return lRet; } LONG WINAPI OSUtil_RegQueryValueExW( HKEY hKey, LPCWSTR lpszValueNameW, DWORD *pdwType, BYTE* lpData, DWORD *pcbData, BOOL fUnicodeCaller // =FALSE ) { long lRet = NOERROR; DWORD cbDataGiven = *pcbData ; if (g_NoUnicodePlatform || !fUnicodeCaller) { CHAR *lpszValueNameA = NULL; if ( SUCCEEDED(OSUtil_GetAnsiString(&lpszValueNameA,lpszValueNameW))) { lRet = RegQueryValueExA( hKey, lpszValueNameA, NULL, pdwType, lpData, pcbData ); FreePpv(&lpszValueNameA); #if 1 // // The string came back as ANSI but we need UNICODE. Conversion in place // is not allowed, so do it with second buffer // if ( fUnicodeCaller && (NOERROR == lRet) && (*pdwType == REG_SZ)) { LPWSTR pwszWideData = NULL; DWORD dwNewSize = *pcbData * sizeof(WCHAR); HRESULT hres; if (cbDataGiven >= dwNewSize ) { hres = OSUtil_GetWideString(&pwszWideData,lpData); if (SUCCEEDED(hres) && pwszWideData) { memcpy(lpData,pwszWideData,dwNewSize); FreePpv(&pwszWideData); } else { lRet = GetLastError(); } } else { lRet = ERROR_MORE_DATA; } *pcbData *= sizeof(WCHAR); } #endif } else { DebugOutPtszV(DbgFl,TEXT("Failed in RegDeleteValueKeyW")); lRet = ERROR_INVALID_PARAMETER; } } else { lRet = RegQueryValueExW( hKey, lpszValueNameW, NULL, pdwType, lpData, pcbData ); } return lRet; } LONG WINAPI OSUtil_RegSetValueExW( HKEY hKey, LPCWSTR lpszValueNameW, DWORD dwType, BYTE* lpData, DWORD cbData, BOOL fUnicodeCaller // =FALSE ) { long lRet = NOERROR; if (g_NoUnicodePlatform || !fUnicodeCaller) { CHAR *lpszValueNameA = NULL; if ( SUCCEEDED(OSUtil_GetAnsiString(&lpszValueNameA,lpszValueNameW))) { lRet = RegSetValueExA(hKey, lpszValueNameA, 0, dwType, lpData, cbData); FreePpv(&lpszValueNameA); } else { DebugOutPtszV(DbgFl,TEXT("Failed in RegDeleteValueKeyW")); lRet = ERROR_INVALID_PARAMETER; } } else { lRet = RegSetValueExW( hKey, (LPVOID)lpszValueNameW, 0, dwType, lpData, cbData); } return lRet; } DWORD ReadRegistryDwordW( HKEY hkey, LPCWSTR pszValueNameW, DWORD dwDefaultValue ) { DWORD err = NOERROR; DWORD dwBuffer = 0; DWORD cbBuffer = sizeof(dwBuffer); DWORD dwType; if( hkey != NULL ) { if (g_NoUnicodePlatform) { CHAR *pszValueNameA = NULL; dwType = REG_DWORD; if ( SUCCEEDED(OSUtil_GetAnsiString(&pszValueNameA,pszValueNameW))) { err = RegQueryValueExA( hkey, pszValueNameA, NULL, &dwType, (LPBYTE)&dwBuffer, &cbBuffer ); FreePpv(&pszValueNameA); } else { DebugOutPtszV(DbgFl,TEXT("Failed in ReadRegistryDwordW ")); err = ERROR_INVALID_PARAMETER; } } else { err = RegQueryValueExW( hkey, pszValueNameW, NULL, &dwType, (LPBYTE)&dwBuffer, &cbBuffer ); } if( ( err == NO_ERROR ) && ( ( dwType == REG_DWORD ) || ( dwType == REG_BINARY ) ) ) { dwDefaultValue = dwBuffer; } } return dwDefaultValue; } // ReadRegistryDwordW() DWORD WriteRegistryDwordW( IN HKEY hkey, IN LPCWSTR pszValueNameW, IN DWORD dwValue) { DWORD err = NOERROR; if ( hkey == NULL || pszValueNameW == NULL) { err = ( ERROR_INVALID_PARAMETER); } else { if (g_NoUnicodePlatform) { CHAR *pszValueNameA = NULL; if ( SUCCEEDED(OSUtil_GetAnsiString(&pszValueNameA,pszValueNameW))) { err = RegSetValueExA( hkey,pszValueNameA,0,REG_DWORD,(LPBYTE ) &dwValue,sizeof( dwValue)); FreePpv(&pszValueNameA); } else { DebugOutPtszV(DbgFl,TEXT("Failed in ReadRegistryDwordW ")); err = ERROR_INVALID_PARAMETER; } } else { err = RegSetValueExW( hkey,(LPVOID)pszValueNameW,0,REG_DWORD,(LPBYTE ) &dwValue,sizeof( dwValue)); } } return ( err); } // WriteRegistryDwordW() DWORD WriteRegistryStringA( IN HKEY hkey, IN LPCSTR pszValueName, IN LPCSTR pszValue, IN DWORD cbValue, IN DWORD fdwType) { DWORD err = NOERROR; if ( hkey == NULL || pszValueName == NULL || cbValue == 0 ) { err = ERROR_INVALID_PARAMETER; } else { err = RegSetValueExA( hkey, pszValueName, 0, fdwType, (LPBYTE ) pszValue, cbValue); // + 1 for null character } return ( err); } // WriteRegistryStringA() DWORD WriteRegistryStringW( IN HKEY hkey, IN LPCWSTR pszValueNameW, IN LPCWSTR pszValueW, IN DWORD cbValue, IN DWORD fdwType) { DWORD err = NOERROR; DWORD le; if ( hkey == NULL || pszValueNameW == NULL || cbValue == 0 ) { err = ERROR_INVALID_PARAMETER; } else { if (g_NoUnicodePlatform) { err = ERROR_INVALID_PARAMETER; if ( (fdwType == REG_SZ) || (fdwType == REG_EXPAND_SZ)) { LPSTR lpszAnsi = NULL; LPSTR lpszAnsiName = NULL; if ( SUCCEEDED(OSUtil_GetAnsiString(&lpszAnsi,pszValueW)) && SUCCEEDED(OSUtil_GetAnsiString(&lpszAnsiName,pszValueNameW)) ) { err = WriteRegistryStringA(hkey, lpszAnsiName, lpszAnsi, (lstrlenA(lpszAnsi)+1)*sizeof(CHAR), fdwType); } FreePpv(&lpszAnsi); FreePpv(&lpszAnsiName); } else { ValidateF(FALSE,("Wrong registry value type in WriteRegistryStringW")); err = ERROR_INVALID_PARAMETER; } } else { err = RegSetValueExW( hkey, pszValueNameW, 0, fdwType, (LPBYTE ) pszValueW, cbValue); le = GetLastError(); } } return ( err); } // WriteRegistryStringW() HRESULT ReadRegistryStringA( HKEY hkey, LPCWSTR pszValueNameW, LPCWSTR pszDefaultValueW, BOOL fExpand, LPWSTR * ppwszResult ) { WCHAR * pszBuffer1 = NULL; WCHAR * pszBuffer2 = NULL; DWORD cbBuffer; DWORD dwType; DWORD err = NOERROR; CHAR *pszValueNameA = NULL; if (!ppwszResult) { return STIERR_INVALID_PARAM; } *ppwszResult = NULL; if ( !SUCCEEDED(OSUtil_GetAnsiString(&pszValueNameA,pszValueNameW))) { return STIERR_INVALID_PARAM; } // // Determine the buffer size. // pszBuffer1 = NULL; pszBuffer2 = NULL; cbBuffer = 0; if( hkey == NULL ) { // // Pretend the key wasn't found. // err = ERROR_FILE_NOT_FOUND; } else { err = RegQueryValueExA( hkey, pszValueNameA, NULL, &dwType, NULL, &cbBuffer ); if( ( err == NO_ERROR ) || ( err == ERROR_MORE_DATA ) ) { if( ( dwType != REG_SZ ) && ( dwType != REG_MULTI_SZ ) && ( dwType != REG_EXPAND_SZ ) ) { // // Type mismatch, registry data NOT a string. // Use default. // err = ERROR_FILE_NOT_FOUND; } else { // // Item found, allocate a buffer. // if (!SUCCEEDED(AllocCbPpv(2*cbBuffer+sizeof(WCHAR),&pszBuffer1)) ){ err = GetLastError(); } else { // // Now read the value into the buffer. // //if (g_NoUnicodePlatform) { DWORD dwType; err = RegQueryValueExA( hkey, pszValueNameA, NULL, &dwType, (LPBYTE)pszBuffer1, &cbBuffer ); // // The string came back as ANSI but we need UNICODE. Conversion in place // is not allowed, so do it with second buffer // if (SUCCEEDED(AllocCbPpv(2*cbBuffer+sizeof(WCHAR),&pszBuffer2))){ AToU(pszBuffer2,cbBuffer,(LPSTR)pszBuffer1); memcpy(pszBuffer1,pszBuffer2,2*cbBuffer+sizeof(WCHAR)); FreePpv(&pszBuffer2); // // Truncate returned string to MAX_REG_CHAR characters // (only for REG_SZ) // if ((dwType == REG_SZ) && (cbBuffer >= MAX_REG_CHAR)) { pszBuffer1[MAX_REG_CHAR] = L'\0'; } } //} } } } } FreePpv(&pszValueNameA); //if( err == ERROR_FILE_NOT_FOUND ) { if( err != NOERROR ) { // // Item not found, use empty string (L"") as value // err = NO_ERROR; if( pszDefaultValueW != NULL ) { if (!SUCCEEDED(AllocCbPpv((OSUtil_StrLenW(L"") + 1) * sizeof(WCHAR),&pszBuffer1))) { err = GetLastError(); } else { OSUtil_lstrcpyW( pszBuffer1, L"" ); } } } if( err != NO_ERROR ) { // // Tragic error reading registry, abort now. // goto ErrorCleanup; } // // pszBuffer1 holds the registry value. Now expand // the environment strings if necessary. // if( !fExpand ) { *ppwszResult = pszBuffer1; return S_OK; } #if 0 // Does not work on Win95 ? // // Returns number of characters // cbBuffer = ExpandEnvironmentStrings( pszBuffer1, NULL, 0 ); if (!SUCCEEDED(OSUtil_GetAnsiString(&pszBuffer2, (cbBuffer+1)*sizeof(TCHAR) ))){ goto ErrorCleanup; } if( ExpandEnvironmentStrings( pszBuffer1, pszBuffer2, cbBuffer ) > cbBuffer ) { goto ErrorCleanup; } // // pszBuffer2 now contains the registry value with // environment strings expanded. // FreePpv(&pszBuffer1); return pszBuffer2; #endif ErrorCleanup: // // Something tragic happend; free any allocated buffers // and return NULL to the caller, indicating failure. // FreePpv(&pszValueNameA); FreePpv(&pszBuffer1); FreePpv(&pszBuffer2); return HRESULT_FROM_WIN32(err); } HRESULT ReadRegistryStringW( HKEY hkey, LPCWSTR pszValueNameW, LPCWSTR pszDefaultValueW, BOOL fExpand, LPWSTR * ppwszResult ) { WCHAR * pszBuffer1 = NULL; DWORD cbBuffer; DWORD dwType; DWORD err = NOERROR; if (!ppwszResult) { return STIERR_INVALID_PARAM; } *ppwszResult = NULL; // // Determine the buffer size. // pszBuffer1 = NULL; cbBuffer = 0; if( hkey == NULL ) { // // Pretend the key wasn't found. // err = ERROR_FILE_NOT_FOUND; } else { err = RegQueryValueExW( hkey, pszValueNameW, NULL, &dwType, NULL, &cbBuffer ); if( ( err == NO_ERROR ) || ( err == ERROR_MORE_DATA ) ) { if( ( dwType != REG_SZ ) && ( dwType != REG_MULTI_SZ ) && ( dwType != REG_EXPAND_SZ ) ) { // // Type mismatch, registry data NOT a string. // Use default. // err = ERROR_FILE_NOT_FOUND; } else { // // Item found, allocate a buffer. // if (!SUCCEEDED(AllocCbPpv(cbBuffer+sizeof(WCHAR),&pszBuffer1)) ){ err = GetLastError(); } else { // // Now read the value into the buffer. // DWORD dwType; err = RegQueryValueExW( hkey, pszValueNameW, NULL, &dwType, (LPBYTE)pszBuffer1, &cbBuffer ); } } } } if( err != NOERROR ) { // // Item not found, use empty string (L"") as value // err = NO_ERROR; if( pszDefaultValueW != NULL ) { if (!SUCCEEDED(AllocCbPpv((OSUtil_StrLenW(L"") + 1) * sizeof(WCHAR),&pszBuffer1))) { err = GetLastError(); } else { OSUtil_lstrcpyW( pszBuffer1, L"" ); } } } if( err != NO_ERROR ) { // // Tragic error reading registry, abort now. // goto ErrorCleanup; } // // pszBuffer1 holds the registry value. Now expand // the environment strings if necessary. // if( !fExpand ) { *ppwszResult = pszBuffer1; return S_OK; } ErrorCleanup: // // Something tragic happend; free any allocated buffers // and return NULL to the caller, indicating failure. // FreePpv(&pszBuffer1); return HRESULT_FROM_WIN32(err); } HANDLE WINAPI OSUtil_CreateFileW( LPCWSTR lpszFileNameW, DWORD dwDesiredAccess, DWORD dwShareMode, LPSECURITY_ATTRIBUTES lpSecurityAttributes, DWORD dwCreationDisposition, DWORD dwFlagsAndAttributes, HANDLE hTemplateFile ) { HANDLE hRet = INVALID_HANDLE_VALUE; if (g_NoUnicodePlatform) { CHAR *pFileNameA = NULL; if ( SUCCEEDED(OSUtil_GetAnsiString(&pFileNameA,lpszFileNameW))) { hRet = CreateFileA(pFileNameA, dwDesiredAccess,dwShareMode,lpSecurityAttributes,dwCreationDisposition, dwFlagsAndAttributes,hTemplateFile); FreePpv(&pFileNameA); } else { DebugOutPtszV(DbgFl,TEXT("Failed in LoadLibraryW")); hRet = INVALID_HANDLE_VALUE; } } else { hRet = CreateFileW(lpszFileNameW, dwDesiredAccess,dwShareMode,lpSecurityAttributes,dwCreationDisposition, dwFlagsAndAttributes,hTemplateFile); } return hRet; } // OSUtil_LoadLibrary HRESULT WINAPI ExtractCommandLineArgumentW( LPCSTR lpszSwitchName, LPWSTR pwszSwitchValue ) { HRESULT hres; LPSTR lpszCommandLine; LPSTR lpszSwitch; LPSTR pszT; DWORD cch; lpszCommandLine = GetCommandLineA(); if (!lpszCommandLine || !*lpszCommandLine) { return STIERR_GENERIC; } if (!pwszSwitchValue) { return STIERR_GENERIC; } // Search for switch with given name lpszSwitch = strstr(lpszCommandLine,lpszSwitchName); if (!lpszSwitch ) { return STIERR_GENERIC; } lpszSwitch += lstrlenA(lpszSwitchName); if (*lpszSwitch != ':') { return STIERR_GENERIC; } lpszSwitch=CharNextA(lpszSwitch); // Skip till space pszT = lpszSwitch; while (*pszT && *pszT > ' ') { pszT = CharNextA(pszT); } cch = MultiByteToWideChar(CP_ACP, 0, lpszSwitch, (INT)(pszT-lpszSwitch), pwszSwitchValue,STI_MAX_INTERNAL_NAME_LENGTH ); pwszSwitchValue[cch] = L'\0'; hres = (cch) ? STI_OK : HRESULT_FROM_WIN32(GetLastError()); return hres; } HRESULT WINAPI ExtractCommandLineArgumentA( LPCSTR lpszSwitchName, LPSTR pszSwitchValue ) { HRESULT hres; LPSTR lpszCommandLine; LPSTR lpszSwitch; LPSTR pszT; DWORD cch; lpszCommandLine = GetCommandLineA(); if (!lpszCommandLine || !*lpszCommandLine) { return STIERR_GENERIC; } if (!pszSwitchValue) { return STIERR_GENERIC; } // Search for switch with given name lpszSwitch = strstr(lpszCommandLine,lpszSwitchName); if (!lpszSwitch ) { return STIERR_GENERIC; } lpszSwitch += lstrlenA(lpszSwitchName); if (*lpszSwitch != ':') { return STIERR_GENERIC; } lpszSwitch=CharNextA(lpszSwitch); // Skip till space pszT = lpszSwitch; while (*pszT && *pszT > ' ') { pszT = CharNextA(pszT); } cch = min((INT)(pszT-lpszSwitch),STI_MAX_INTERNAL_NAME_LENGTH); memcpy(pszSwitchValue,lpszSwitch,cch); pszSwitchValue[cch] = L'\0'; hres = (cch) ? STI_OK : STIERR_INVALID_PARAM; return hres; } /***************************************************************************** * * @doc INTERNAL * * @func HRESULT | DupEventHandle | * * Duplicate an event handle intra-process-ly. If the incoming * handle is NULL, then so is the output handle (and the call * succeeds). * * @parm HANDLE | h | * * Source handle. * * @parm LPHANDLE | phOut | * * Receives output handle. * *****************************************************************************/ HRESULT EXTERNAL DupEventHandle(HANDLE h, LPHANDLE phOut) { HRESULT hres; EnterProc(DupEventHandle, (_ "p", h)); if (h) { HANDLE hProcessMe = GetCurrentProcess(); if (DuplicateHandle(hProcessMe, h, hProcessMe, phOut, EVENT_MODIFY_STATE, 0, 0)) { hres = S_OK; } else { hres = hresLe(GetLastError()); } } else { *phOut = h; hres = S_OK; } ExitOleProc(); return hres; } void WINAPI StiLogTrace( DWORD dwMessageType, DWORD idMessage, ... ) { va_list list; va_start (list, idMessage); if(g_hStiFileLog) { TCHAR *pchBuff = NULL; DWORD cch; HMODULE hm; DWORD dwError; pchBuff = NULL; hm = GetModuleHandleA("STI.dll") ; cch = FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_MAX_WIDTH_MASK | FORMAT_MESSAGE_FROM_HMODULE, hm, idMessage, 0, (LPTSTR) &pchBuff, 1024, (va_list *)&list ); dwError = GetLastError(); if (cch && pchBuff) { ReportStiLogMessage(g_hStiFileLog, dwMessageType, pchBuff); } if (pchBuff) { LocalFree(pchBuff); } } va_end(list); }