Convertendo uma pasta em aplicação no IIS via código (C#)

Somente pelo IIS é possível converter uma pasta em aplicação? Mais ou menos. Na versão 7 do IIS já dispõe de módulos que simplificam o processo de criação de site, diretório virtual, pools de aplicação e as aplicações em si. Nesse artigo mostrarei bem rápido como converter uma pasta em uma aplicação.


Crie sua aplicação e adicione, como referência, o binário Microsoft.Web.Administration.dll que fica na pasta C:\Windows\System32\inetsrv . Agora adicione o seguinte código:

using Microsoft.Web.Administration;



private void CriaEntradaIIS(string diretorio)
    {
        try
        {
            // Cria a pasta do site
            if (!Directory.Exists(diretorio))
                Directory.CreateDirectory(diretorio);


            // Servidor de Aplicação
            ServerManager sm = new ServerManager();


            // Captura o site raiz onde irá adicionar a aplicação
            Site st = sm.Sites["Default Web Site"];


            // Cria a aplicação no diretório criado apontando o caminho, ex: aplicacao
            Application app = st.Applications.Add("/aplicacao", diretorio);
            app.ApplicationPoolName = "ASP.NET v4.0";
            sm.CommitChanges();


            // Libera
            sm.Dispose();
        }
        catch (Exception erro)
        {
            return;
        }
    }


Se tentar executar dará erro... Antes dê permissão total ao grupo Serviço de Rede (Network Service) à pasta C:\Windows\System32\inetsrv\config . Agora sim irá criar. Para mais exemplos veja nesse post aqui.

Converter PDF em Imagem em C# com GhostScript

Esse é um artigo bem old, mas que muita gente me enviou e-mails pedindo como fazer isso. Então criei um artigo bem resumido baseado no de Lord TaGoH. Primeiramente, você precisa ter a DLL do GhostScript em seu projeto: ou você instala ou pega de alguém que já tenha instalado. O arquivo que me refiro é o gsdll32.dll. Obs: a conversão só funciona para as versões mais recentes então pegue alguma a partir da versão 9 como garantia.

Lembra do meu último post de scaneamento? Adicionei um botão de anexar um PDF e converter em imagem, então irei aproveitar a mesma aplicação adicionando essa funcionalidade. Copie a DLL para seu projeto e, em Propriedades, deixe configurado o atributo Copiar sempre para quando for compilá-la caso seja um Windows Form. Se for um Web Form, adicione em algum local onde a classe conversora possa encontrá-la.


Na página do CodeProject dispõe de uma classe (binária) já pronta para uso uso da DLL chamada de PdfToImage. Então você podem baixar de  ou aqui. Baixado a DLL, você pode adicionar como referência ao seu projeto/página. Agora é só código...

using PdfToImage;

// Objeto de conversão
PDFConvert converter = new PDFConvert();

// Arquivo PDF selecionado de um OpenFileDialog
string arquivo = FileUploadSelect.FileName;

// Local de saída do arquivo convertido
string output = _directoryForImages + "LJ_" + _loja + "_Scan_" + DateTime.Now.ToString("yyyy_MM_dd_HH_mm_ss") + ".jpg";

// Configurações de conversão
converter.OutputToMultipleFile = false;
converter.TextAlphaBit = 4;
converter.FirstPageToConvert = 1;
converter.LastPageToConvert = 1;
converter.FitPage = false;
converter.JPEGQuality = 10;
converter.OutputFormat = "png16m";

// Faz a conversão e retorna true se estiver tudo OK
bool resultado = converter.Convert(arquivo, output);

Pronto! Mais fácil do que isso impossível. Fique atento às configurações de conversão, senão não funciona.

Scanear um documento com C#

Um dos melhores componentes de scaneamento que já utilizei é o da VintaSoft. Ele não é free, mas pode ser utilizado normalmente. O único empecilho é que, para cada scan que fizer, ele irá solicitar que clique em Evaluate. Quem comprar, vale a pena pois é muito bom e seu suporte sempre está a disposição. Chega de elogios e vamos ao que interessa.

O componente permite tanto utilizar em Windows Form quando Web Form e no site há todos os exemplos necessários. Baixe o componente e instale. Para nosso exemplo, trabalharemos com um Windows Form. Fiz uma pequena aplicação de scanear nota fiscal.


Vá na pasta de instalação do componente, copie o arquivo Vintasoft.Twain.dll para sua aplicação e adicione como referência. No Code-Behind do formulário a ser trabalhado, adicione o seguinte código para inicializar o componente:

using Vintasoft.Twain;


// Carrega o gerenciador de dispositivo
DeviceManager _deviceManager = new DeviceManager();
Device _device;


// Avalia se é um dispositivo útil
if (!_deviceManager.IsTwainAvailable)
{
           MessageBox.Show("Não é um dispositivo TWAIN!", "Erro", MessageBoxButtons.OK, MessageBoxIcon.Error);
           return;
}


try
{
           // Abre o gerenciador de dispositivo
           _deviceManager.Open();
           // Seta o dispositivo corrente
           _device = _deviceManager.Devices.Current;
           lblDipositivo.Text = _deviceManager.Devices.Current.Info.ProductName;
}
catch (TwainDeviceManagerException ex)
{
           MessageBox.Show(ex.Message, "Gerenciador de Dispositivos");
           return;
}


Agora, ao clicar no botão Scannear, adicionamos o seguinte método:

private void btnScanear_Click(object sender, EventArgs e)
        {
            try
            {
                // Seta as configurações
                _device.TransferMode = TransferMode.File;
                try
                {
                    _device.ShowUI = false;
                    _device.ShowIndicators = false;
                    _device.DisableAfterAcquire = true;
                }
                catch { }


                // Abre o dispositivo
                _device.Open();


                // Propriedades do scaneamento
                try
                {
                    _device.UnitOfMeasure = UnitOfMeasure.Inches;
                    _device.Resolution = new Resolution(float.Parse(resolucaoX.SelectedItem.ToString()), float.Parse(resolucaoY.SelectedItem.ToString()));
                }
                catch { }


                // Informação sobre o arquivo
                _arquivo = "LJ_" + _loja + "_Scan_" + DateTime.Now.ToString("yyyy_MM_dd_HH_mm_ss") + ".jpg";
                _device.FileName = _directoryForImages + _arquivo;
                //_device.FileFormat = TwainImageFileFormat.Jpeg;


                // Captura a imagem
                AcquireModalState acquireModalState = AcquireModalState.None;
                do
                {
                    acquireModalState = _device.AcquireModal();
                    switch (acquireModalState)
                    {
                        case AcquireModalState.ImageAcquired:


                            _device.FileName = _directoryForImages + _arquivo;
                            //_device.FileFormat = TwainImageFileFormat.Jpeg;
                            break;


                        case AcquireModalState.ScanCompleted:


                            CloseDevice();


                            break;


                        case AcquireModalState.ScanCanceled:


                            CloseDevice();


                            break;


                        case AcquireModalState.ScanFailed:


                            CloseDevice();


                            break;


                        case AcquireModalState.UserInterfaceClosed:


                            CloseDevice();


                            break;
                    }
                }
                while (acquireModalState != AcquireModalState.None);
            }
            catch (TwainException ex)
            {
                MessageBox.Show(ex.Message + " - " + ex.StackTrace, "Erro", MessageBoxButtons.OK, MessageBoxIcon.Error);
                return;
            }
        }


E outros métodos são necessários adicionar ao form, como:



        /// Fecha o dispositivo e a gerência

        private void CloseDeviceAndDeviceManager()
        {
            // Verifica de há dispositivo
            if (_device != null)
            {
                if (_device.State != DeviceState.Closed)
                    _device.Close();
            }


            // Fecha a gerência
            if (_deviceManager.State != DeviceManagerState.Closed)
                _deviceManager.Close();
        }




        /// Fecha o dispositivo

        private void CloseDevice()
        {
            // Desabilita o serviço
            if (_device.State == DeviceState.Enabled)
            {
                if (!_device.ShowUI)
                    _device.Disable();
            }


            // Fecha
            if (_device.State == DeviceState.Opened)
                _device.Close();
        }




        /// Ao fechar o formulário, libera o uso

        private void Scan_FormClosing(object sender, FormClosingEventArgs e)
        {
            if (_device != null)
            {
                // Verifica se está em uso
                if (_device.State == DeviceState.Transferring ||
                    _device.State == DeviceState.TransferReady)
                {
                    // Envia a solicitação de cancelamento
                    _device.CancelTransfer();
                    // Aguarda finalizar
                    e.Cancel = true;
                    MessageBox.Show("O dispositivo está em uso! Aguarde alguns instantes enquanto é solicitado a liberação do scanner.", "Informação");
                    return;
                }
            }


            // Fecha o dispositivo e a gerência
            CloseDeviceAndDeviceManager();
        }

Pronto! O scaneamento é realizado e é gerado um arquivo JPG. Reparem que está comentado o atributo _device.FileFormat porque nem todo scanner dá a possibilidade de configuração. Alguns aceitam e outros não. Se utilizar alguma configuração que o scanner não suporta, deve aparecer a mensagem: "Failed to get/get to capability". Então você precisa deixar apenas as configurações que o scanner suporte.
Bons scans!

Recortando uma Imagem em Tempo de Execução (Image Crop) em C#

Lembram do post de Redimensionar Imagem? Esse post será bem similar e explicarei em poucas palavras o que ele faz. O que é Crop? Segundo a WikipediaCrop (Recorte) refere-se à remoção da parte exterior de uma imagem para melhorar o seu enquadramento. Um exemplo mais entendível: temos uma galeria de fotos nas quais todas as imagens possuem largura e altura diferentes que não são proporcionais; o redimensionamento de imagem para gerar um thumb não irá funcionar pois o produto final ou ficará distorcido ou não ficarão com tamanhos iguais. É aí que o recorte entra...

Veja o exemplo abaixo (retirado da Wikipedia):
No caso em questão era desejado obter um thumb quadrado da imagem. O enquadramento desejado não é proporcional à image original, logo é feito o recorte.

Dessa vez não irei colocar o código pois é um pouco grandinho, mas, como disse, é bem similar ao código do post de Redimensionar Imagem bem como seu uso (em forma de handler.ashx). Esse código é bem velhinho e não é meu. Ele está aí na net a muito tempo e já o vi em vários blogs e artigos. A última vez que o vi estava no subversion de Assembla.

Logo, baixem aqui e usem-o como desejar.

Usando o ScripManager em sequência

Esse artigo será apenas uma dica para os desatentos. Quando você usa o ScriptManager para exibir um alerta, por exemplo, e em outro trecho do código fazer a mesma coisa, verá que apenas o primeiro alerta é demonstrado e o segundo não. O porquê disso é se estiver reaproveitando o mesmo script e não diferenciar o bloco de execução dele, sempre será exibido apenas o primeiro. Logo, o certo, é ter algo do tipo:


// Exibe o primeiro alerta
ScriptManager.RegisterStartupScript(Page, typeof(Page), "script1", "alert('Alerta 1');", true);


...



// Exibe o segundo alerta
ScriptManager.RegisterStartupScript(Page, typeof(Page), "script2", "alert('Alerta 2');", true);

Reparou no bloco script1 e script2? É isso.

Adicionando uma página de cadastro de usuários (Hotspot) em ASP.NET no Mikrotik

Cada projeto que pego, é uma aventura nova a ser encarada! Dessa vez me adentrei no universo de Linux, rede, SSH, infra e tudo o que é direito. Quem não conhece, a Mikrotik é uma empresa de grande referência no que tange em equipamentos para redes de computadores. Seu principal destaque é o RouterOS que é sistema operacional que torna o roteador um poderoso gerente de rede. Dentro do Mikrotik (irei me referenciar sempre dessa forma generalizando como um produto), há o HotSpot. Com ele pode-se gerenciar quem irá autenticar na rede para usar a internet (ou apenas a intranet), banda usada, taxa de transferência e outras funcionalidades por usuário ou de forma geral. Um exemplo de uso desse sistema é de quando você vai a uma rede corporativa onde precisa autenticar-se para usar a internet... é isso!


Qual é o objetivo desse artigo então? A princípio é apenas obter as páginas originais de hotspot do Mikrotik, colocar um redirecionador para uma página externa, criar uma página externa de cadastro de usuário (para que possa acessar a rede) e fazer funcionar. Não ensinarei como configurar ou criar o hotspot. Pela net há vários tutoriais muito bons sobre o assunto e também não é de minha área. Recomendo fortemente o fórum da Under-Linux que lá o pessoal manja muito no assunto.

Nesse artigo usei o Mikrotik MKBR100 que é bem fácil de usar: basta plugar o cabo de rede da internet, do servidor e da energia. Pronto!

Para acessar seu SO, deve-se utilizar o Winbox como se fosse um daqueles players de máquina virtual ou OpenSSH. Vamos lá colocar a mão na massa!

Conecte o cabo de rede de seu PC (ou servidor) na porta (interface) ether2 do Mikrotik. Agora conecte outro cabo na ether1 para os outros computadores da rede (ou roteador simples). Porquê na ether1? Estou considerando que o hotspot está configurado para monitorar essa interface enquanto a ether2 será utilizado para que o servidor de comunique com o aparelho. Seria algo do tipo:



Vá em IP > Address no Mikrotik para que sejam configuradas as interfaces e sejam endereçados conforme os exemplos abaixo:

  • ether1: 192.168.88.1
  • ether2: 192.168.88.2

Somente através de IP que será possível enviar os arquivos do hotspot. Se ficar conectando, via MAC, pelo Winbox, você terá a mensagem de erro: "router has been disconnected" (logo, conecte-se usando a ether2 - 192.168.88.2). E outra coisa é: se não seguir o esquema acima, outros erros você terá, como:

could not connect to ... no response
could not connect to ... connection refused

could not connect to ... network is unreachable
could not fetch index from ... not found


Tudo isso por causa de configuração apenas! É bom deixar o IP do servidor fixo para que possa dar permissão de porta, firewall, etc. No caso deixei como 192.168.88.30 (que no caso era minha própria máquina). Uma dica: às vezes dê um reboot no aparelho!

O próximo passo é ativar a porta da API que o Mikrotik dispõe para integração com linguagens de programação. É através dela que iremos conectar e fazer a comunicação. Só que precisamos ativá-la. Vá em IP > Services e ative a API na porta 8728.


Também libere, no Firewall, as portas para conexão tanto do sistema quanto do Winbox (pelo IP), senão quando estiver acessando pela página de cadastro, irá surgir o erro: "nenhuma conexão pôde ser feita porque a máquina de destino as recusou ativamente ..:8728.".


Para permitir a navegação e envio de informações, adicione as permissões no Walled Garden.


Vejam que até o momento tudo é configuração e nada de programação. Continuemos...

Agora vamos em Files e selecione todos os arquivos da pasta hotspot, arraste para sua área de trabalho que será copiado. Para enviar faça o processo inverso... Se estiver acessando pelo IP vai dar certo, senão cairá a conexão (lembra que disse acima?).

Abra o HTML da página login.html e acione um link para a página de cadastro que ficará hospedada no servidor. Adicionei o link <a href="http://192.168.88.30/mikrotik/cadastro.aspx">cadastre-se</a> . Customize as demais páginas como desejar e as envie de volta. Praticamente terminamos de trabalhar no Mikrotik.

Agora vem o mais simples: criar a página ASP.NET. Você pode utilizar qualquer linguagem de programação que possa usar TCP na comunicação e que a API suporte: PHP, Delphi, C, C++, C#, Flash, Ruby on Rails, Java, Python, VB.NET, etc.

Faça o download da classe MK, em C# (eles já dispõe de classes prontas para outras linguagens também), nesse link aqui e adicione no seu projeto. Agora crie um Web Form adicionando os campos que queira trabalhar. No meu exemplo, só quis login (username) e senha. Adicione um Button e um método para executar o cadastro do usuário conforme exemplo abaixo:


             // Autenticação
            MK mikrotik = new MK("192.168.88.2");
            if (!mikrotik.Login("admin", ""))
            {
                ScriptManager.RegisterStartupScript(Page, typeof(Page), "alert", "alert('Houve um problema de comunicação com o Hotspot! Por favor, tente mais tarde.');", true);
                mikrotik.Close();
                return;
            }

            // Requisição
            mikrotik.Send("/ip/hotspot/user/add");
            mikrotik.Send("=name=" + nome.Text);
            mikrotik.Send("=password=" + senha.Text, true);

            //Retorno
            string retorno = string.Empty;
            foreach (string h in mikrotik.Read())
            {
                if (retorno != string.Empty)
                    retorno += ", ";
                retorno += h;
            }

            // Fecha objeto
            mikrotik.Close();

            // Validação
            if (retorno.ToLower().Contains("!done=ret="))
                ScriptManager.RegisterStartupScript(Page, typeof(Page), "alert", "alert('Cadastro realizado com sucesso!'); location.href='http://192.168.88.1/login.html';", true);
            else if (retorno.ToLower().Contains("message=failure: already have user with this name for this server"))
                ScriptManager.RegisterStartupScript(Page, typeof(Page), "alert", "alert('Já existe um usuário com esse nome!');", true);
            else
                ScriptManager.RegisterStartupScript(Page, typeof(Page), "alert", "alert('Falha no cadastro: " + retorno + "!');", true);


Você poderia não só fazer o cadastro de usuário no hotspot como também executar qualquer comando (SSH) no Mikrotik. Lembrando que eu atropelei muita coisa aí no que diz respeito a configuração do dispositivo na qual disse logo no início que não era minha finalidade. Vocês observaram que a parte de programação é bem simples do que configuração... e com certeza é! Agora nunca irá sair de minha cabeça a palavra Mikrotik Mikrotik Mikrotik Mikrotik Mikrotik ... Por favor, qualquer erro ou informação adicional me avise!

[]'s

UPDATE: disponibilizei uma implementação no post http://thiagomarcal.blogspot.com.br/2016/09/implementacao-do-aspnet-c-com-o.html

Recuperando a senha da conta identidade do Pool de Aplicativos do Plesk no IIS

Mais um título longo... um tanto confuso, mas em resposta a uma dúvida de um amigo:
"Exclui um Application Pool no IIS e nela estava configurada uma conta interna do Plesk: IWAM_plesk. Não sei a senha utilizada. Tem como recuperar a senha para recriar um novo application?"
Sim! O Plesk sempre nos pregando aquela peça... Os dois pools que o Plesk utiliza estão vinculados à conta IWAM_plesk (conta interna) para que possam manipular corretamente os arquivos e seus aplicativos (bem como as permissões necessárias) no sistema operacional:

  1. plesk(default)(2.0)(pool)
  2. plesk(default)(4.0)(pool)

 Então façamos as seguintes etapas:

  • Abra o Prompt de Comando em Modo Administrador;
  • Navegue para a pasta C:\Windows\system32\inetsrv;
  • Execute o comando appcmd.exe list apppool “plesk(default)(2.0)(pool)” /text:*
Aparecerá a descrição completa desse pool. Procure a entrada referente ao usuário e senha do Plesk conforme a figura abaixo:


Agora você tem a senha do usuário! No site de Dhiraj tem mais detalhes acerca desse tipo de recuperação.

Calculando saldo final no Crystal Reports

Antes de começar o artigo, queria dizer que nesse mês irei responder muitas das questões que recebo por e-mail (calma que um dia sempre respondo) em forma de artigo, então muitos deles serão curtos e rápidos. Uma das respostas será nesse artigo.
Bem, como calculo o saldo, linha a linha, de entradas e saídas no Crystal Reports? De forma bem simples: uso de variáveis globais. Um bom artigo que sempre recomendo é o de Vince Varallo: não tem erro!
Supondo que você tenha o seguinte relatório e com os respectivos campos:


Esses dados já devem ser puxados do DataSet utilizado (na dúvida, veja o post de criar relatórios). Agora adicionamos um Formula Field com o nome SALDO.


Clique em Use Editor e escreva a seguinte fórmula:



Global NumberVar SALDO_FINAL;

if (RECORDNUMBER = 1) then
    SALDO_FINAL := @ENTRADA - @SAIDA
else
    SALDO_FINAL := SALDO_FINAL + @ENTRADA - @SAIDA


Ou seja, na primeira linha obtenho o saldo inicial diminuindo a ENTRADA da SAIDA. Nos demais já adiciono o saldo anterior. Tranquilo? Agora basta adicionar no relatório esse campo criado.

Testando um Web-Site na própria máquina como se fosse externamente

Tentei elaborar um título de forma que ficasse entendível o que eu queria dizer. Não sei se ajuda, mas significa que:
"Testaremos o acesso a um web-site que está publicado no IIS mas que no navegador não iremos usar localhost para buscá-lo e sim o próprio endereço."
Ficou bom agora? Então vamos lá. No IIS crie um web-site. O processo é bem simples:


Pronto! Já temos nosso site, mas se formos testar: http://thiagomarcal.com.br/ não encontra!


Por que isso? Quando queremos procurar um site, o sistema operacional analisa o hostname para saber onde encontrar esse endereço: se for interno e mapeado usa-se a rede interna, senão procura a rede externa dentre outros. Como não existe o mapeamento e tampouco o domínio externo, não acha nada. Então vamos alterar o arquivo de host.

Siga para o caminho: C:\Windows\System32\drivers\etc e abra o arquivo hosts, em modo texto, para adicionarmos algumas entradas.

# copyright (c) 1993-2009 microsoft corp.
#
# this is a sample hosts file used by microsoft tcp/ip for windows.
#
# this file contains the mappings of ip addresses to host names. each
# entry should be kept on an individual line. the ip address should
# be placed in the first column followed by the corresponding host name.
# the ip address and the host name should be separated by at least one
# space.
#
# additionally, comments (such as these) may be inserted on individual
# lines or following the machine name denoted by a '#' symbol.
#
# for example:
#
#      102.54.94.97     rhino.acme.com          # source server
#       38.25.63.10     x.acme.com              # x client host

# localhost name resolution is handled within dns itself.
#	127.0.0.1       localhost
#	::1             localhost

127.0.0.1	thiagomarcal.com.br

Coloque, na última linha, o IP, tabulação e nome do domínio nessa ordem para o site desejado. Salve e teste novamente o acesso. E pronto!


Mais fácil do que isso: impossível!

Consumindo Web-Service PHP através do ASP.NET (C#)

Já passou por aqui um post sobre de como consumir um web-service, mas era em ASP.NET. Agora como consumir um web-service em PHP? Da mesma forma. Não muda! O fato de escrever esse post é apenas e pelo simples fato de ter um certo inconveniente na elaboração do web-service em PHP.
Se procurar na net, recomendo o artigo de Maurício Reckziegel da iMasters que é muito bom e explica certinho como fazer. Se você seguir à risca o que está escrito irá conseguir elaborar um web-service em PHP normalmente e irá conseguir consumir, menos em ASP.NET. Não é que a matéria esteja errada, é porque o ASP.NET é chato com a validação do WSDL que é gerado pelo PHP. O documento (WSDL) tem que ser gerado na mão e, quando não é gerado de forma automática, estamos susceptíveis ao erro. Com o web-service em PHP é assim...
Então, digamos que você tenha elaborado o web-service em PHP e já fez referência na sua aplicação. Contudo sua aplicação pode dar erro ou retornar nada. Primeiramente, todo WSDL que iremos usar deve ser validado antes de ser devidamente consumido. O Visual Studio dispõe de uma ferramenta que valida e gera a classe consumidora (que não precisar ser usada pois está intrínseco na ferramenta). Vá em Iniciar > Todos os Programas > Microsoft Visual StudioVisual Studio ToolsVisual Studio 2008 Command Prompt. Irá abrir um prompt de comando. Escreva por exemplo: wsdl http://thiagomarcal.blogspot.com/webservice.php?wsdl . Se der tudo certo, irá surgir um resultado semelhante a esse:


Se estiver tudo OK, então pode usar tranquilamente, caso contrário irá surgir o seguinte problema:

Microsoft (R) Web Services Description Language Utility
[Microsoft (R) .NET Framework, Version 2.0.50727.3038]
Copyright (C) Microsoft Corporation. All rights reserved.

Warning: This web reference does not conform to WS-I Basic Profile v1.1.

Ou seja, o WSDL gerado pelo PHP não está conforme o padrão. Dando uma alterada aqui e ali, podemos usar o seguinte código para gerarmos o web-service em PHP dentro dos conformes:

<?php
// Requerimento aos componentes
require_once('nusoap/nusoap.php');
include("class.phpmailer.php");
include("class.smtp.php");


// Criação da instância
$server = new soap_server();


// Registro do método
$server->register('EnviaMail');


// WSDL
$server->configureWSDL('server.EnviaMail','urn:server.EnviaMail');
$server->wsdl->schemaTargetNamespace = 'urn:server.EnviaMail';
// registra o método a ser oferecido
$server->register('EnviaMail', //nome do método
array('email' => 'xsd:string'), //parâmetros de entrada
array('return' => 'xsd:string'), //parâmetros de saída
'urn:server.EnviaMail', //namespace
'urn:server.EnviaMail#EnviaMail', //soapaction
'rpc', //style
'literal', //use
'Retorna se o e-mail foi enviado' //documentação do serviço
);


// Definição do método a ser utilizado
function EnviaMail($email) {
return "E-email enviado com sucesso!";
}


// Requisição para uso do serviço
$HTTP_RAW_POST_DATA = isset($HTTP_RAW_POST_DATA) ? $HTTP_RAW_POST_DATA : '';
$server->service($HTTP_RAW_POST_DATA);
?>

Perceberam a diferença? Praticamente nenhuma, mas há: o estilo do documento. Não irei entrar em detalhes, mas dois tipos de documentos mais usados: encoded e literal (veja mais nesse artigo). Use literal! Com isso seu web-service será consumido certinho...
Se estiver com o erro: O cliente encontrou o tipo conteúdo de resposta de ' text/html; charset = utf-8 ', mas era esperado 'text/xml' ou The client find the answer 'text/html', but was expected 'text/xml'. verifique se seu web-service está imprimindo conteúdo HTML (echo) e comprometendo o retorno dos dados. Tranquilo, né?