Este artigo foi traduzido por máquina.

Windows com C++

O disco virtual API no Windows 7

Kenny Kerr

Este artigo se baseia em uma versão de pré-lançamento do Windows 7. Todas as informações aqui contidas estão sujeitas a alterações.

Conteúdo

O formato do VHD
Criando discos virtuais
Abrir discos virtuais
Anexar discos virtuais
Consultando os discos virtuais
O que há Avançar

À medida que escreve isso, a versão beta do Windows 7 foi disponível por alguns dias, e deve digo é bem a como. Como sempre, levaram uma olhada nos bastidores para ver o que é novo no SDK do Windows. Windows 7 é muito parecido um menor versão quanto o SDK está interessado e é uma coisa boa. Os fundamentos da criação de C++ nativos aplicativos para Windows 7 não mudaram muito em comparação com a forma que eles são alterados para o Windows Vista. No entanto, ter que disse que Windows 7 possui alguns recursos totalmente novos que certeza juros alguém tentando para aproveitar a plataforma.

Um desses recursos é a API de disco virtual. Embora projetado com outros formatos em mente, a API de disco virtual em beta Windows 7 muito é direcionada para o formato do Microsoft Virtual Hard Disk (VHD) popularizado por produtos de virtualização da Microsoft, como Hyper-V e Virtual PC.

Isso realmente leva-me porque eu tenha envolvido com a virtualização para alguns anos. Quando comecei trabalhar com tecnologia de virtualização quase dez anos, VMware era o líder não criptografado. Em seguida, no 2003 a Microsoft adquiriu tecnologia da máquina virtual da Connectix, e tudo alterada. De um repente havia dois jogadores grandes. Ficou claro que o formato do vhd do que a Microsoft adquiriu do Connectix era bastante adequado para a direção que Microsoft queria sejam com a virtualização, isto é para ativá-lo em uma plataforma. Enquanto os formatos de disco virtual VMware foram proprietários e muito diferente, geralmente alterando totalmente de uma versão para o próximo, o formato do VHD era desde o início simples e flexível o suficiente para o teste de tempo de espera. Nos anos intermediárias, o formato do VHD provou próprio várias vezes, tendo sido adotados por outros produtos e tecnologias da Microsoft e por outras empresas de software, grandes e de pequena empresa.

Por esse motivo estou feliz ver que o Windows 7 suporta o formato do VHD nativamente. Isso significa que os usuários e administradores podem facilmente criar e anexar discos virtuais como se fossem dispositivos de armazenamento física adicional, sem instalar os drivers de terceiros ou ferramentas. Por exemplo, você pode usar o snap-in do MMC de Gerenciamento de disco (console de gerenciamento do Microsoft) ou a ferramenta de linha de comando DISKPART para criar e anexar discos virtuais. Você pode e partição, formatar e usá-los como qualquer outro disco rígido em seu computador.

Além disso, você também obtém controle muito granular sobre a criação e o gerenciamento de discos virtuais. No núcleo desses recursos é VirtDisk.dll, que fornece um baixo nível C API, conhecido como a API de disco virtual, para criar e manipular discos virtuais. Essa API fornece acesso a um número de modo kernel drivers necessários para representar, na verdade, o disco e seus volumes no subsistema de armazenamento para que os drivers do sistema de arquivos podem ser camadas na parte superior sem precisar ter conhecimento da fonte real o armazenamento.

Ferramentas de gerenciamento de disco normalmente interagem com os discos virtuais via o virtual disco Service (VDS) que próprio depende a API de disco virtual para lidar com VHD armazenamento. É claro Hyper-V tem seu próprio baseada de WMI API para criar e manipular máquinas virtuais e depende muito da API de disco virtual.

Antes de eu mergulhar no e mostram como você pode usar a API de disco virtual, descreverei rapidamente os fundamentos do formato VHD. Você verá que dado dessa nova API, você pode agora jogar fora do código necessário anteriormente para o gerenciamento de discos virtuais.

O formato do VHD

O formato do VHD oferece três tipos de imagem diferente: fixo, discos dinâmicos e diferenciados. Todos os tipos de disco três incluem um rodapé VHD 512 bytes residentes no final do arquivo do disco. Mais sobre isso em um minuto.

Discos fixos são o tipo mais simples e fornecem o melhor desempenho, porque o arquivo do disco é totalmente alocado para acomodar o tamanho solicitado quando os discos são criados. Um disco de fixo de 500 MB será exatamente 500 x 1024 x 1024 + 512 bytes de tamanho. Como o rodapé está no final do disco, armazenamento em do disco pode alinhar com o início do arquivo para garantir o acesso aleatório mais simples e mais rápida possível.

Discos dinâmicos são chamados de discos esparsos pela API de disco virtual e esparsa é uma boa maneira de pensar em-los. Os arquivos do disco são criados inicialmente com espaço apenas suficiente para armazenar o rodapé do VHD bem como alguns metadados adicionais que é necessário para gerenciar a natureza dinâmica da alocação de armazenamento. Quando dados são gravados no disco, blocos adicionais de armazenamento são alocados no final do arquivo do disco. Discos dinâmicos são vantajosas porque eles consomem muito menos espaço se não forem usados em capacidade total, que é o caso na maioria das vezes. A desvantagem é que o engano adicional e o gerenciamento de metadados necessários para ler, gravar e aumentar um disco dinâmico em demanda obtém sua chamada no desempenho. Por esse motivo, os discos dinâmicos são favorecidos no teste de cenários, enquanto os discos fixos são preferenciais em cenários de produção porque desempenho é uma prioridade alta.

Discos diferenciados são muito semelhantes em discos dinâmicos internamente, mas outras características são muito diferentes. Como ocorre com discos dinâmicos, diferenciação de uso de discos dinamicamente alocados blocos, mas esses blocos contêm somente modificações relacionadas a um disco do pai. Este tipo de disco, portanto, é dependente de um disco de pai para a função. O pai de um disco diferenciado pode ser um fixo ou um disco dinâmico. O pai na verdade, pode ser outro disco diferenciado, permitindo que uma cadeia ou árvore de discos a serem criados. Discos que representam nós folha podem ser livremente gravados, mas não-folha nós devem ser considerados somente leitura porque os discos diferenciados descendentes dependem delas para preencher o branco, por assim dizer e se eles estivessem para alterá-la seria muito provavelmente resultar em discos virtuais corrompidos.

Como mencionei anteriormente, os diferentes tipos de discos compartilham um rodapé comum. Este rodapé contém informações como o tamanho lógico do disco, geometria de disco, o tipo de disco, o disco identificador global exclusivo e assim por diante. Para discos dinâmicos e diferenciados o rodapé também inclui um valor de deslocamento indicando em que o cabeçalho dinâmico reside relativo para o início de um disco. Essa estrutura secundária chiefly contém informações que podem ser necessárias para localizar e identificar um disco de pai, se necessário, bem como outro deslocamento indicando onde reside a tabela de alocação do disco bloco (BAT). Esta tabela indica os blocos foram alocados, bem como seus deslocamentos absolutos no arquivo de disco.

Com essa introdução fora do caminho, vamos nos aprofundar na e vejamos a API de disco virtual.

Tenha em mente que a API foi projetada para permitir que a Microsoft adicionar suporte para formatos adicionais no futuro. Na verdade, o suporte para o formato de imagem de disco óptico ISO tem sido considerado, mas não tenha sido adicionado como da versão beta do Windows 7. Espero que o provedor de suporte de disco virtual necessário será incluído na compilação de lançamento. Isso permitiria que os usuários anexem e imagens de montagem ISO como discos rígidos de somente leitura.

Criando discos virtuais

Discos virtuais são representados por alças opacas, muito parecido com outro sistema objetos como arquivos, chaves do Registro e assim por diante. A função de CloseHandle familiarizada ainda é usada para fechar um identificador de disco virtual. Do Active Template Library (ATL) CHandle classe é uma boa opção para gerenciar esse recurso. Você pode derivar uma classe de "VirtualDisk" de CHandle para quebrar até a parte do código clichê necessário para manipular os discos virtuais.

Sempre que você abre ou criar um disco virtual, você precisará especificar o tipo de armazenamento do disco. Isso é feito com a estrutura VIRTUAL_STORAGE_TYPE. Tipos de armazenamento são definidos para os formatos de ISO e VHD. A estrutura também Especifica o fornecedor que oferece a implementação para o tipo de armazenamento específica. Eis como você pode identificar o tipo de armazenamento do vhd do:

VIRTUAL_STORAGE_TYPE storageType =
{
    VIRTUAL_STORAGE_TYPE_DEVICE_VHD,
    VIRTUAL_STORAGE_TYPE_VENDOR_MICROSOFT
};

A função CreateVirtualDisk cria todos os três tipos de discos virtuais. Junto com o tipo de armazenamento, você deve preencher uma estrutura CREATE_VIRTUAL_DISK_PARAMETERS. Como essa estrutura é preenchida depende do tipo de disco virtual que você deseja criar. Muitas das estruturas de API de disco virtual usam um esquema de versão para acomodar atualizações futuras para a API. As estruturas começam com um membro chamado versão seguido por uma união de estruturas. Por exemplo, eis como você pode preencher os campos comuns para criar discos virtuais:

CREATE_VIRTUAL_DISK_PARAMETERS parameters =
{
    CREATE_VIRTUAL_DISK_VERSION_1
};
parameters.Version1.MaximumSize = size;
parameters.Version1.BlockSizeInBytes = CREATE_VIRTUAL_DISK_PARAMETERS_DEFAULT_BLOCK_SIZE;
parameters.Version1.SectorSizeInBytes = CREATE_VIRTUAL_DISK_PARAMETERS_DEFAULT_SECTOR_SIZE;

O tamanho do disco virtual é especificado em bytes e deve ser um múltiplo de 512. O bloco de tamanho de setor e o tamanho é configurável, mas se você precisar garantir o maior nível de compatibilidade entre implementações os valores padrão são uma boa opção. A estrutura Version1 também oferece um membro IdExclusiva, mas se você deixar esse zerado, a função CreateVirtualDisk irá gerar um GUID para você. O membro SourcePath também pode ser especificado para todos os tipos de disco, exceto os discos diferenciados. Isso instrui CreateVirtualDisk para copiar o conteúdo do disco de origem para o disco virtual recém-criado. Os dois não precisará ser do mesmo tipo. Na verdade, o disco de origem não ainda precisa ser um disco virtual e pode ser um disco físico que você deseja fazer uma cópia do.

a Figura 1 fornece os inícios de uma classe de wrapper VirtualDisk que inclui uma função de membro CreateFixed para criar fixo discos virtuais. Observe que para discos fixos você deve especificar o sinalizador CREATE_VIRTUAL_DISK_FLAG_FULL_PHYSICAL_ALLOCATION. Criar um disco dinâmico é exatamente o mesmo que criar um disco fixo, exceto que você deve omitir esse sinalizador e pode especificar o sinalizador CREATE_VIRTUAL_DISK_FLAG_NONE em vez disso. Criar um disco diferenciado também é muito o mesmo. A diferença existe que você não deve definiu o tamanho, que é inferida do disco pai, e você deve especificar o pai com o membro ParentPath da estrutura Version1. O SourcePath não pode ser definida para diferenciação discos por motivos óbvios.

A Figura 1 Criando discos fixos

class VirtualDisk : public CHandle
{
public:

    DWORD CreateFixed(PCWSTR path,
                      ULONGLONG size,
                      VIRTUAL_DISK_ACCESS_MASK accessMask,
                      __in_opt PCWSTR source,
                      __in_opt PSECURITY_DESCRIPTOR securityDescriptor,
                      __in_opt OVERLAPPED* overlapped)
    {
        ASSERT(0 == m_h);
        ASSERT(0 != path);
        ASSERT(0 == size % 512);

        VIRTUAL_STORAGE_TYPE storageType =
        {
            VIRTUAL_STORAGE_TYPE_DEVICE_VHD,
            VIRTUAL_STORAGE_TYPE_VENDOR_MICROSOFT
        };

        CREATE_VIRTUAL_DISK_PARAMETERS parameters =
        {
            CREATE_VIRTUAL_DISK_VERSION_1
        };

        parameters.Version1.MaximumSize = size;
        parameters.Version1.BlockSizeInBytes = CREATE_VIRTUAL_DISK_        PARAMETERS_DEFAULT_BLOCK_SIZE;
        parameters.Version1.SectorSizeInBytes = CREATE_VIRTUAL_DISK_        PARAMETERS_DEFAULT_SECTOR_SIZE;
        parameters.Version1.SourcePath = source;

        return ::CreateVirtualDisk(&storageType,
                                   path,
                                   accessMask,
                                   securityDescriptor,
                                   CREATE_VIRTUAL_DISK_FLAG_FULL_                                   PHYSICAL_ALLOCATION,
                                   0, // no provider-specific flags
                                   &parameters,
                                   overlapped,
                                   &m_h);
    } 

A função CreateVirtualDisk fornece alguns parâmetros adicionais que vale a pena mencionar. A enumeração VIRTUAL_DISK_ACCESS_MASK fornece um conjunto de sinalizadores para controlar o acesso a API concederá os chamadores ao identificador resultante. Embora você possa especificar VIRTUAL_DISK_ACCESS_ALL, é geralmente indesejável como ele impede que você executar determinadas operações como consultar um disco virtual que está conectado no momento como um dispositivo de armazenamento. Outro recurso útil é a capacidade para especificar uma estrutura OVERLAPPED. Isso é suportado por um número das funções de API de disco virtual, e como você poderia esperar tem o efeito de executar a operação de forma assíncrona. Simplesmente fornece um manual de redefinir o evento e irá ser sinalizado após a conclusão.

Abrir discos virtuais

A função OpenVirtualDisk pode ser usada para abrir um disco virtual. Como com a criação do disco virtual, você deve fornecer uma estrutura VIRTUAL_STORAGE_TYPE para identificar o tipo de armazenamento. Uma estrutura OPEN_VIRTUAL_DISK_PARAMETERS opcionalmente podem ser fornecida mas normalmente é somente necessário ao manipular diferenciadas relações de disco.

a Figura 2 fornece uma função de membro Open para adicionar à classe de wrapper VirtualDisk iniciada em Figura 1 . Abrir discos virtuais é normalmente mais simples do que criá-las, mas alguns dos sinalizadores e opções devem ser usado de uma maneira muito específica para permitir que determinadas operações de manutenção, como mesclar e anexar discos virtuais.

A Figura 2 inicial virtual discos

DWORD Open(PCWSTR path,
           VIRTUAL_DISK_ACCESS_MASK accessMask,
           OPEN_VIRTUAL_DISK_FLAG flags, // OPEN_VIRTUAL_DISK_FLAG_NONE
           ULONG readWriteDepth) // OPEN_VIRTUAL_DISK_RW_DEPTH_DEFAULT
{
    ASSERT(0 == m_h);
    ASSERT(0 != path);

    VIRTUAL_STORAGE_TYPE storageType =
    {
        VIRTUAL_STORAGE_TYPE_DEVICE_VHD,
        VIRTUAL_STORAGE_TYPE_VENDOR_MICROSOFT
    };

    OPEN_VIRTUAL_DISK_PARAMETERS parameters =
    {
        OPEN_VIRTUAL_DISK_VERSION_1
    };

    parameters.Version1.RWDepth = readWriteDepth;

    return ::OpenVirtualDisk(&storageType,
                             path,
                             accessMask,
                             flags,
                             &parameters,
                             &m_h);
}

Anexar discos virtuais

A versão beta do Windows 7 utiliza o termo superfície ou surfacing, como anexar o disco como um dispositivo de armazenamento no sistema operacional. Isso posteriormente foi alterado para a palavra mais óbvia anexar. A função AttachVirtualDisk (chamado SurfaceVirtualDisk na versão beta) anexa o disco virtual. O disco virtual é identificado por um identificador obtido de uma chamada para CreateVirtualDisk ou OpenVirtualDisk anteriormente. Você deve verificar se o identificador tem o acesso apropriado definido para ele. Para Anexar e desanexar um disco virtual, você também deve ter o privilégio SE_MANAGE_VOLUME_NAME presente no seu token. Esse privilégio é removido do token do administrador quando o controle de conta de usuário está em uso, portanto, você talvez precisará elevar seu aplicativo para acessar o token Irrestrito, que inclui esse privilégio.

a Figura 3 fornece uma função de membro anexar para adicionar à classe de wrapper VirtualDisk. O parâmetro ATTACH_VIRTUAL_DISK_FLAG (chamado SURFACE_VIRTUAL_DISK_FLAG na versão beta) é como você controlar o método no qual o disco virtual está associado.

A Figura 3 anexando discos

DWORD Attach(ATTACH_VIRTUAL_DISK_FLAG flags,
              __in_opt PSECURITY_DESCRIPTOR securityDescriptor,
              __in_opt OVERLAPPED* overlapped)
{
    ASSERT(0 != m_h);


    return ::AttachVirtualDisk(m_h,
                                securityDescriptor,
                                flags,
                                0, // no provider-specific flags
                                0, // no parameters
                                overlapped);
}

ATTACH_VIRTUAL_DISK_FLAG_READ_ONLY (chamado SURFACE_VIRTUAL_DISK_FLAG_READ_ONLY na versão beta) pode ser especificado para garantir que o disco anexado está protegido contra gravação. Isso não pode ser substituído por uma tentativa de tornar o disco gravável com VDS.

ATTACH_VIRTUAL_DISK_FLAG_NO_DRIVE_LETTER (chamado SURFACE_VIRTUAL_DISK_FLAG_NO_DRIVE_LETTER na versão beta) impedirá Windows automaticamente atribuir letras de unidade a volumes presentes no disco virtual. Você está livre, em seguida, para montar os volumes através de programação ou não funcionar, dependendo das suas necessidades. A função GetVirtualDiskPhysicalPath pode ser usada para identificar o caminho físico em que o disco virtual foi anexado.

ATTACH_VIRTUAL_DISK_FLAG_PERMANENT_LIFETIME (chamado SURFACE_VIRTUAL_DISK_FLAG_PERMANENT_LIFETIME na versão beta) garante que o disco virtual permaneça anexado mesmo depois que o identificador de disco virtual for fechado. Falha ao especificar este sinalizador resulta no disco virtual sendo desanexado automaticamente quando o identificador é fechado. Para desanexar o disco virtual nesse caso, você precisará chamar a função DetachVirtualDisk (chamada UnsurfaceVirtualDisk na versão beta).

Consultando os discos virtuais

A função GetVirtualDiskInformation permite consultar um disco virtual para classes diferentes de informações. As informações são comunicadas usando a estrutura GET_VIRTUAL_DISK_INFO, que usa o mesmo padrão de versão usado por muitas das outras estruturas de API de disco virtual. Por exemplo, para obter informações de tamanho do disco você definir versão a estrutura para GET_VIRTUAL_DISK_INFO_SIZE. O membro de união tamanho correspondente, em seguida, será preenchido. a Figura 4 ilustra isso.

A Figura 4 Obtendo informações de tamanho

DWORD GetSize(__out ULONGLONG& virtualSize,
              __out ULONGLONG& physicalSize,
              __out ULONG& blockSize,
              __out ULONG& sectorSize) const
{
    ASSERT(0 != m_h);

    GET_VIRTUAL_DISK_INFO info =
    {
        GET_VIRTUAL_DISK_INFO_SIZE
    };

    ULONG size = sizeof(GET_VIRTUAL_DISK_INFO);

    const DWORD result = ::GetVirtualDiskInformation(m_h,
                                                     &size,
                                                     &info,
                                                     0); // fixed size

    if (ERROR_SUCCESS == result)
    {
        virtualSize = info.Size.VirtualSize;
        physicalSize = info.Size.PhysicalSize;
        blockSize = info.Size.BlockSize;
        sectorSize = info.Size.SectorSize;
    }

    return result;
} 

O GetVirtualDiskInformation função opera em uma alça de disco virtual, então, novamente você precisará garantir que tenham o acesso apropriado. Nesse caso, a permissão de VIRTUAL_DISK_ACCESS_GET_INFO é necessária. Desde algumas das informações que podem ser obtidas usando GetVirtualDiskInformation é variável de comprimento, ele fornece dois parâmetros adicionais para especificar quanto de armazenamento é inicialmente sendo fornecido e quanto for finalmente preenchido. Para a maioria das informações que você irá consultar o tamanho é conhecido antecipadamente e o último parâmetro pode ser omitido, como no caso da Figura 4 .

A exceção notável é ao consultar um disco diferenciado para o local, ou caminho de um disco virtual pai. Nesse caso você precisará inicie determinando a quantidade de memória que é necessária com uma chamada inicial para GetVirtualDiskInformation. Essa chamada irá falhar com ERROR_INSUFFICIENT_BUFFER mas fornecer-lhe o tamanho do buffer que precise ser alocado. Você então pode chamar a função uma segunda vez para realmente obter as informações. O sinalizador de versão GET_VIRTUAL_DISK_INFO_PARENT_LOCATION é usado para obter o local do pai. No entanto, ele é um pouco mais complicado ainda. Como manter uma referência a um pai é tão fundamental para a operação de um disco diferenciada, o formato do VHD fornece um grau de redundância pode ser explorada no link para o pai seria quebrado. É suficiente dizer que a consultar o local pai envolve analisando uma seqüência de seqüências de caracteres terminada por caractere nulo, finalizada por uma seqüência de caracteres vazia. Isso é semelhante ao tipo de valor do Registro REG_MULTI_SZ. a Figura 5 mostra como isso é feito. CAtlArray coleção o exemplo usa ATL classe bem CString classe do ATL.

A Figura 5 iniciando pai local

DWORD GetParentLocation(__out bool& resolved,
                        __out CAtlArray<CString>& paths) const
{
    ASSERT(0 != m_h);

    GET_VIRTUAL_DISK_INFO info =
    {
        GET_VIRTUAL_DISK_INFO_PARENT_LOCATION
    };

    ULONG size = sizeof(GET_VIRTUAL_DISK_INFO);

    DWORD result = ::GetVirtualDiskInformation(m_h,
                                               &size,
                                               &info,
                                               0); // not used

    if (ERROR_INSUFFICIENT_BUFFER != result)
    {
        return result;
    }

    CAtlArray<BYTE> buffer;

    if (!buffer.SetCount(size))
    {
        return ERROR_NOT_ENOUGH_MEMORY;
    }

    GET_VIRTUAL_DISK_INFO* pInfo = reinterpret_cast<GET_VIRTUAL_DISK_    INFO*>(buffer.GetData());
    pInfo->Version = GET_VIRTUAL_DISK_INFO_PARENT_LOCATION;

    result = ::GetVirtualDiskInformation(m_h,
                                         &size,
                                         pInfo,
                                         0); // not used

    if (ERROR_SUCCESS == result)
    {
        resolved = 0 != pInfo->ParentLocation.ParentResolved;
        PCWSTR path = pInfo->ParentLocation.ParentLocationBuffer;

        while (0 != *path)
        {
            paths.Add(path);

            path += paths[paths.GetCount() - 1].GetLength() + 1;
        }
    }

    return result;
}

O que há Avançar

A API de disco virtual fornece funções de mais alguns principalmente o objetivo de manter ou reparando discos virtuais. A função MergeVirtualDisk permite que você mesclar quaisquer alterações em um disco diferenciada novamente em um disco de pai. Ele suporta mesclagem com qualquer pai da cadeia. Funções também são fornecidas para compactar e expandir discos virtuais, bem como atualizar metadados relacional no caso de discos de diferenciação.

Observe que o SDK do Windows para a versão beta do Windows 7 omitido o arquivo de VirtDisk.lib necessário para vincular as funções de API de disco virtual. Isso será corrigido para o lançamento. Os desenvolvedores usando a versão beta podem usar as funções LoadLibrary e GetProcAddress para carregar e as funções de endereço ou gerar o arquivo lib você mesmo.

Envie suas dúvidas e comentários para mmwincpp@Microsoft.com

Kenny Kerr é um profissional de fabricação de software especializado no desenvolvimento de software para o Windows. Ele adora escrever e ensinar aos desenvolvedores sobre design de programação e software. Você pode acessar Kenny em weblogs.asp. NET/kennykerr.