
/*++

Copyright (c) 1995-2000  Microsoft Corporation

Module Name:

    pnpapi.c

Abstract:

    This module contains the user-mode plug-and-play API stubs.

Author:

    Paula Tomlinson (paulat) 9-18-1995

Environment:

    User-mode only.

Revision History:

    18-Sept-1995     paulat

        Creation and initial implementation.

--*/

#ifndef UNICODE
#define UNICODE
#endif

#ifndef _UNICODE
#define _UNICODE
#endif

#define MAX_PROFILEID_LEN     5
#define MAX_UUID_LEN          39


//
// includes
//
#include <nt.h>
#include <ntrtl.h>
#include <nturtl.h>
#include <windows.h>
#include <rpc.h>
#include <rpcdce.h>
#include <stdlib.h>
#include <stdio.h>
#include <wtypes.h>


//
// private prototypes
//
PSTR
UnicodeToMultiByte(
    IN PCWSTR UnicodeString,
    IN UINT   Codepage
    );

PWSTR
MultiByteToUnicode(
    IN PCSTR String,
    IN UINT  Codepage
    );

RPC_STATUS
GuidToString(
   UUID   *Uuid,
   LPTSTR StringGuid
   );


//
// global data
//
WCHAR pszRegIDConfigDB[] = L"System\\CurrentControlSet\\Control\\IDConfigDB";

WCHAR pszRegKnownDockingStates[] =  L"Hardware Profiles";
WCHAR pszRegCurrentConfig[] =       L"CurrentConfig";
WCHAR pszRegDefaultFriendlyName[] = L"Noname Hardware Profile";
WCHAR pszRegHwProfileGuid[] =       L"HwProfileGuid";
WCHAR pszRegFriendlyName[] =        L"FriendlyName";
WCHAR pszRegDockState[] =           L"DockState";
WCHAR pszRegDockingState[] =        L"DockingState";
WCHAR pszCurrentDockInfo[] =        L"CurrentDockInfo";


BOOL
GetCurrentHwProfileW (
    OUT LPHW_PROFILE_INFOW  lpHwProfileInfo
    )

/*++

Routine Description:


Arguments:

    lpHwProfileInfo  Points to a HW_PROFILE_INFO structure that will receive
                     the information for the current hardware profile.

Return Value:

    If the function succeeds, the return value is TRUE.  If the function
    fails, the return value is FALSE.  To get extended error information,
    call GetLastError.

--*/

{
   BOOL     Status = TRUE;
   WCHAR    RegStr[MAX_PATH];
   HKEY     hKey = NULL, hCfgKey = NULL;
   HKEY     hCurrentDockInfoKey = NULL;
   ULONG    ulCurrentConfig = 1, ulSize = 0;
   UUID     NewGuid;


   try {
      //
      // validate parameter
      //
      if (lpHwProfileInfo == NULL) {
         SetLastError(ERROR_INVALID_PARAMETER);
         Status = FALSE;
         goto Clean0;
      }
      

      //
      // open the IDConfigDB key
      //
      if (RegOpenKeyEx(
               HKEY_LOCAL_MACHINE, pszRegIDConfigDB, 0,
               KEY_QUERY_VALUE, &hKey) != ERROR_SUCCESS) {
	
         SetLastError(ERROR_REGISTRY_CORRUPT);
         Status = FALSE;
         goto Clean0;
      }
      
      //
      // retrieve the current config id
      //
      ulSize = sizeof(ULONG);
      if (RegQueryValueEx(hKey, pszRegCurrentConfig, NULL, NULL,
                          (LPBYTE)&ulCurrentConfig, &ulSize) != ERROR_SUCCESS) {          
          SetLastError(ERROR_REGISTRY_CORRUPT);
          Status = FALSE;
          goto Clean0;
      }

      //
      // open the profile key for the current configuration
      //
      _snwprintf(RegStr, MAX_PATH, L"%s\\%04u",
                 pszRegKnownDockingStates,
                 ulCurrentConfig);
      
      if (RegOpenKeyEx(hKey, RegStr, 0, KEY_QUERY_VALUE,
                       &hCfgKey) != ERROR_SUCCESS) {
          SetLastError(ERROR_REGISTRY_CORRUPT);
          Status = FALSE;
          goto Clean0;
      }
      
      //
      // retrieve the dock state for the current profile
      //      
      if (RegOpenKeyEx(hKey, pszCurrentDockInfo, 0,  KEY_QUERY_VALUE,
                       &hCurrentDockInfoKey) != ERROR_SUCCESS) {
          //
          // No CurrentDockInfo Key, something's wrong.
          //
          SetLastError(ERROR_REGISTRY_CORRUPT);
          Status = FALSE;
          goto Clean0;
      }      
      
      //
      // Look in CurrentDockInfo for
      // a hardware determined DockingState value.
      //
      ulSize = sizeof(ULONG);                  
      if ((RegQueryValueEx(hCurrentDockInfoKey,
                           pszRegDockingState, 
                           NULL, 
                           NULL,
                           (LPBYTE)&lpHwProfileInfo->dwDockInfo,
                           &ulSize) != ERROR_SUCCESS)
          || (!lpHwProfileInfo->dwDockInfo) 
          || ((lpHwProfileInfo->dwDockInfo & DOCKINFO_UNDOCKED) && 
              (lpHwProfileInfo->dwDockInfo & DOCKINFO_DOCKED))) {          

          //
          // If there's no such value, or the value was set to 0 (unspported),
          // or if the value is "unknown", resort to user supplied docking info.
          // Look under the IDConfigDB profile for a user set DockState value.
          //                    
          if ((RegQueryValueEx(hCfgKey, pszRegDockState, NULL, NULL,
                               (LPBYTE)&lpHwProfileInfo->dwDockInfo,
                               &ulSize) != ERROR_SUCCESS)
              || (!lpHwProfileInfo->dwDockInfo)) {
              
              //
              // If there's no such value, or the value was set to 0,
              // there is no user specified docking state to resort to;
              // return the "user-supplied unknown" docking state.
              //
              lpHwProfileInfo->dwDockInfo =
                  DOCKINFO_USER_SUPPLIED | DOCKINFO_DOCKED | DOCKINFO_UNDOCKED;
          }
      }
      
      //
      // retrieve the profile guid.  if we can't get one, set it to NULL
      //
      ulSize = HW_PROFILE_GUIDLEN * sizeof(WCHAR);
      if (RegQueryValueEx(hCfgKey, pszRegHwProfileGuid, NULL, NULL,
                          (LPBYTE)&lpHwProfileInfo->szHwProfileGuid,
                          &ulSize) != ERROR_SUCCESS) {
          lpHwProfileInfo->szHwProfileGuid[0] = '\0';
      }
      
      //
      // retrieve the friendly name.  if we can't get one, set it to NULL
      //
      ulSize = MAX_PROFILE_LEN * sizeof(WCHAR);
      if (RegQueryValueEx(hCfgKey, pszRegFriendlyName, NULL, NULL,
                          (LPBYTE)&lpHwProfileInfo->szHwProfileName,
                          &ulSize) != ERROR_SUCCESS) {
          lpHwProfileInfo->szHwProfileName[0] = '\0';
      }
      
      
   Clean0:
      ;
      
   } except(EXCEPTION_EXECUTE_HANDLER) {
       
       SetLastError(ERROR_INVALID_PARAMETER);
       Status = FALSE;
   }
   
   if (hKey != NULL) RegCloseKey(hKey);
   if (hCfgKey != NULL) RegCloseKey(hCfgKey);
   if (hCurrentDockInfoKey != NULL) RegCloseKey(hCurrentDockInfoKey);
   
   return Status;
   
} // GetCurrentHwProfileW





BOOL
GetCurrentHwProfileA (
    OUT LPHW_PROFILE_INFOA  lpHwProfileInfo
    )

/*++

Routine Description:


Arguments:

    lpHwProfileInfo  Points to a HW_PROFILE_INFO structure that will receive
                     the information for the current hardware profile.

Return Value:

    If the function succeeds, the return value is TRUE.  If the function
    fails, the return value is FALSE.  To get extended error information,
    call GetLastError.

--*/

{
   BOOL              Status = TRUE;
   HW_PROFILE_INFOW  HwProfileInfoW;
   LPSTR             pAnsiString;


   try {
      //
      // validate parameter
      //
      if (lpHwProfileInfo == NULL) {
         SetLastError(ERROR_INVALID_PARAMETER);
         Status = FALSE;
         goto Clean0;
      }

      //
      // call the Unicode version
      //
      if (!GetCurrentHwProfileW(&HwProfileInfoW)) {
         Status = FALSE;
         goto Clean0;
      }

      //
      // on successful return, convert unicode form of struct
      // to ANSI and copy struct members to callers struct
      //
      lpHwProfileInfo->dwDockInfo = HwProfileInfoW.dwDockInfo;

      pAnsiString = UnicodeToMultiByte(
               HwProfileInfoW.szHwProfileGuid, CP_ACP);
      if (!pAnsiString) {
          Status = FALSE;
          goto Clean0;
      }

      lstrcpyA(lpHwProfileInfo->szHwProfileGuid, pAnsiString);
      LocalFree(pAnsiString);

      pAnsiString = UnicodeToMultiByte(
               HwProfileInfoW.szHwProfileName, CP_ACP);
      if (!pAnsiString) {
          Status = FALSE;
          goto Clean0;
      }

      lstrcpyA(lpHwProfileInfo->szHwProfileName, pAnsiString);
      LocalFree(pAnsiString);


      Clean0:
         ;

   } except(EXCEPTION_EXECUTE_HANDLER) {
      SetLastError(ERROR_INTERNAL_ERROR);
      Status = FALSE;
   }

   return Status;

} // GetCurrentHwProfileA





PSTR
UnicodeToMultiByte(
    IN PCWSTR UnicodeString,
    IN UINT   Codepage
    )

/*++

Routine Description:

    Convert a string from unicode to ansi.

Arguments:

    UnicodeString - supplies string to be converted.

    Codepage - supplies codepage to be used for the conversion.

Return Value:

    NULL if out of memory or invalid codepage.
    Caller can free buffer with MyFree().

--*/

{
    UINT WideCharCount;
    PSTR String;
    UINT StringBufferSize;
    UINT BytesInString;
    PSTR p;

    WideCharCount = lstrlenW(UnicodeString) + 1;

    //
    // Allocate maximally sized buffer.
    // If every unicode character is a double-byte
    // character, then the buffer needs to be the same size
    // as the unicode string. Otherwise it might be smaller,
    // as some unicode characters will translate to
    // single-byte characters.
    //
    StringBufferSize = WideCharCount * sizeof(WCHAR);
    String = (PSTR)LocalAlloc(LPTR, StringBufferSize);
    if(String == NULL) {
        return(NULL);
    }

    //
    // Perform the conversion.
    //
    BytesInString = WideCharToMultiByte(
                        Codepage,
                        0,                      // default composite char behavior
                        UnicodeString,
                        WideCharCount,
                        String,
                        StringBufferSize,
                        NULL,
                        NULL
                        );

    if(BytesInString == 0) {
        LocalFree(String);
        return(NULL);
    }

    //
    // Resize the string's buffer to its correct size.
    // If the realloc fails for some reason the original
    // buffer is not freed.
    //
    if(p = LocalReAlloc(String,BytesInString, LMEM_ZEROINIT | LMEM_MOVEABLE)) {
        String = p;
    }

    return(String);

} // UnicodeToMultiByte




PWSTR
MultiByteToUnicode(
    IN PCSTR String,
    IN UINT  Codepage
    )

/*++

Routine Description:

    Convert a string to unicode.

Arguments:

    String - supplies string to be converted.

    Codepage - supplies codepage to be used for the conversion.

Return Value:

    NULL if string could not be converted (out of memory or invalid cp)
    Caller can free buffer with MyFree().

--*/

{
    UINT BytesIn8BitString;
    UINT CharsInUnicodeString;
    PWSTR UnicodeString;
    PWSTR p;

    BytesIn8BitString = lstrlenA(String) + 1;

    //
    // Allocate maximally sized buffer.
    // If every character is a single-byte character,
    // then the buffer needs to be twice the size
    // as the 8bit string. Otherwise it might be smaller,
    // as some characters are 2 bytes in their unicode and
    // 8bit representations.
    //
    UnicodeString = (PWSTR)LocalAlloc(LPTR, BytesIn8BitString * sizeof(WCHAR));
    if(UnicodeString == NULL) {
        SetLastError(ERROR_NOT_ENOUGH_MEMORY);
        return(NULL);
    }

    //
    // Perform the conversion.
    //
    CharsInUnicodeString = MultiByteToWideChar(
                                Codepage,
                                MB_PRECOMPOSED,
                                String,
                                BytesIn8BitString,
                                UnicodeString,
                                BytesIn8BitString
                                );

    if(CharsInUnicodeString == 0) {
        LocalFree(UnicodeString);
        return(NULL);
    }

    //
    // Resize the unicode string's buffer to its correct size.
    // If the realloc fails for some reason the original
    // buffer is not freed.
    //
    if(p = (PWSTR)LocalReAlloc(UnicodeString,CharsInUnicodeString*sizeof(WCHAR),
            LMEM_ZEROINIT | LMEM_MOVEABLE)) {

        UnicodeString = p;
    }

    return(UnicodeString);

} // MultiByteToUnicode




RPC_STATUS
GuidToString(
   UUID   *Uuid,
   LPTSTR StringGuid
   )
{
   RPC_STATUS  Status;
   LPTSTR      pTempStringGuid;
   UNICODE_STRING UnicodeStringGuid;

   Status = UuidToString(Uuid, &pTempStringGuid);

   if (Status == RPC_S_OK) {
      //
      // the form we want is all uppercase and with curly brackets around,
      // like what OLE does
      //
      lstrcpy(StringGuid, TEXT("{"));
      lstrcat(StringGuid, pTempStringGuid);
      lstrcat(StringGuid, TEXT("}"));
      RtlInitUnicodeString(&UnicodeStringGuid, StringGuid);
      RtlUpcaseUnicodeString(&UnicodeStringGuid, &UnicodeStringGuid, FALSE);

      RpcStringFree(&pTempStringGuid);
   }

   return Status;

} // GuidToString
