Could not load file or assembly Microsoft.VisualStudio.Shell.15.0

January 11, 2018 Leave a comment

CENÁRIO:

Montando o pipeline de Build e Release automático de um projeto SharePoint Add-in (SharePoint App – SharePoint Online 365) descobri que não tem um template de tarefa de build no Visual Studio Online (VSTS).

Então, parti para construir a tarefa de build vazia (Empty) onde colocamos os comandos para realizar o build.

Eis que ao tentar dar build usando o msbuild.exe me deparo com um erro bem estranho.

ERRO:

“C:\Program Files (x86)\Microsoft Visual Studio\2017\Enterprise\MSBuild\Microsoft\VisualStudio\v15.0\SharePointTools\Microsoft.VisualStudio.SharePoint.targets(395,5): Error : Could not load file or assembly ‘Microsoft.VisualStudio.Shell.15.0, Version=15.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a’ or one of its dependencies. The system cannot find the file specified.”

SOLUÇÃO:

Depois de algumas (muitas) pesquisas descobri que é necessário adicionar referências no projeto para fazer o build com sucesso. Passos para resolver:

  1. Adicionar um arquivo “Microsoft.VisualStudio.SharePoint.AddAssemblySearchPaths.targets” na raiz do seu projeto (conteúdo do arquivo mais abaixo)
  2. Adicionar “<Import Project=”Microsoft.VisualStudio.SharePoint.AddAssemblySearchPaths.targets” />” no arquivo .csproj para importar o arquivo adicionado.
  3. Pronto, agora é build com sucesso! 😉

Conteúdo do arquivo “Microsoft.VisualStudio.SharePoint.AddAssemblySearchPaths.targets”:

<Project xmlns=’http://schemas.microsoft.com/developer/msbuild/2003&#8242; >

  <UsingTask TaskName=”AddAssemblySearchPath” TaskFactory=”CodeTaskFactory” AssemblyFile=”$(MSBuildToolsPath)\Microsoft.Build.Tasks.Core.dll”>

    <ParameterGroup>

      <SearchPath ParameterType=”System.String” Required=”true” />

    </ParameterGroup>

    <Task>

      <Using Namespace=”System” />

      <Using Namespace=”System.IO” />

      <Using Namespace=”System.Reflection” />

      <Code Type=”Fragment” Language=”cs”>

        <![CDATA[

AppDomain.CurrentDomain.AssemblyResolve += (sender, e) =>

{

  string assemblySearchPath = Path.Combine(SearchPath, e.Name.Split(‘,’)[0] + “.dll”);

  if (File.Exists(assemblySearchPath)) return Assembly.LoadFrom(assemblySearchPath);

  return null;

};

]]>

      </Code>

    </Task>

  </UsingTask>

  <Target Name=”addAssemblySearchPaths”>

    <AddAssemblySearchPath SearchPath=”$(MSBuildExtensionsPath)\..\Common7\IDE\PrivateAssemblies\” />

    <AddAssemblySearchPath SearchPath=”$(MSBuildExtensionsPath)\..\Common7\IDE\PublicAssemblies\” />

  </Target>

</Project>

Também adicionei o arquivo para download aqui: https://1drv.ms/u/s!Ass2zQvJNjQNguMdQGn53ziBBweFBg

Para resolver essa zica, contei com a ajuda do super http://diegomarques.azurewebsites.net/.

Abraço!

Setup is unable to proceed to the following error(s)

November 24, 2017 Leave a comment

CENÁRIO:

Hoje precisei instalar um VM SharePoint 2013 para fazer um robô de migração/exportação de dados para uma outra aplicação.

Ao instalar o SharePoint 2013 SP1 no Windows Server 2012 R2, logo de cara, me deparo com erro ao instalar os pré-requisitos solicitando .NET 4.5… que já estava instalado no servidor.

ERRO:

Setup is unable to proceed to the following error(s):

This Product requires Microsoft .Net Framework 4.5

framework

SOLUÇÃO:

Depois de algumas pesquisas vi que a MS já lançou um KB para contornar esse problema, onde você irá baixar uma nova DLL e substituir nos arquivos de instalação.

https://support.microsoft.com/en-us/help/3087184/sharepoint-2013-or-project-server-2013-setup-error-if-the–net-framewo

Links para download da nova DLL do instalador:

Abraço!

Debug adapter process has terminated unexpectedly VS Code

November 15, 2017 Leave a comment

CENÁRIO:

Na vida usando, e usando muito, o Visual Studio Code com projeto .NET Core, fui debugar meu projeto… mas deu erro 😥

Deu erro pra DEBUGAR?! WFT?!

Pra quem quiser começar a experimentar o Visual Studio Code: https://code.visualstudio.com/

Estou bem satisfeito usando no Mac OS. É muito rápido! Sublime virou só bloco de notas mesmo. Rs

ERRO:

Debug adapter process has terminated unexpectedly

SOLUÇÃO:

Fechei e abri o VS Code e reiniciei o Mac (depois de várias semanas, mas é preciso) e nada resolveu.

Pesquisando um pouco, descobri que tem um console do Visual Studio Code que não é o habitual da “IDE”, onde estava procurando por erros:

Picture1

Mas nada de mensagem de erro nesses console.

Então, descobri este outro console “F12” do Visual Studio Code:

Help > Toggle Developer Tools

Picture2

Neste console havia um erro informando que não conseguia achar o “c# extension asset”. Então, pra resolver, desinstalei e instalei novamente o C# Extension do Visual Studio Code.

Picture3

Com isso, ele reinstalou o debugger e tudo voltou a funcionar! J

Abraço!

Rodando MSSQL Server on Linux no Mac usando Docker

CENÁRIO:

Depois de instalar o .NET Core, agora fazer uns “programa” que lê uns arquivos… acessa banco de dados… Hum, acessar banco? Vamos instalar o SQL Server on Linux! 😀

Se você perdeu a parte do .NET Core, ta aqui:

https://thiagottss.com.br/2017/05/07/instalando-net-core-no-mac-usando-docker/

Me surpreendi com a facilidade e agilidade de utilizar os containers “prontos” no Docker. Muito bom mesmo.

https://docs.microsoft.com/en-us/sql/linux/sql-server-linux-setup-docker

Vamos lá!

Primeiro, vamos puxar a imagem no Docker Hub da Microsoft:

docker pull microsoft/mssql-server-linux

clip_image002

Agora, instalar/rodar o container:

docker run -e ‘ACCEPT_EULA=Y’ -e ‘SA_PASSWORD=<YourStrong!Passw0rd>’ -p 1433:1433 -d microsoft/mssql-server-linux

clip_image004

Pronto! Você já está com o MSSQL Server on Linux rodando no Docker.

Agora é conectar e rodar seus comandos no SQL Server. Utilize o Management Studio para conectar no SQL do Docker, ou se preferir, ainda no Mac, pode usar o sql-cli para conectar. Ou utilizar o Visual Studio Code também! J

Para instalar o sql-cli você precisa do Node.js:

https://github.com/creationix/nvm

Instalar o sql-cli:

npm install -g sql-cli

Com o sql-cli:

Mssql –help

clip_image006

mssql –u sa –p senha

clip_image008

Com o Visual Studio Code:

https://docs.microsoft.com/en-us/sql/linux/sql-server-linux-develop-use-vscode

clip_image010

Abraço!

Instalando .Net Core no MAC usando Docker

May 7, 2017 1 comment

CENÁRIO:

Sempre utilizei o Visual Studio para todas as atividades de código, mas desde que a Microsoft lançou .NET Core e Visual Studio Code procurei testar e rodar alguns códigos diretamente no Mac OS.

Ainda acho o Visual Studio muito mais produtivo e mais completo, mas devemos sempre nos manter atualizados com as novas tecnologias.

De início, instalar o .Net Core CLI diretamente no Mac OS, mas agora vou mostrar como instalar o .Net Core no Docker!

Ainda utilizo bem pouco o Docker, mas tenho gostado bastante de ter containers para realizar algumas atividades sem precisar instalar coisas no sistema operacional.

Vamos lá!

Primeiro instale o Docker, caso ainda não o tenha:

https://www.docker.com/docker-mac

Instalar o .NET Core via Docker é muito simples!

https://www.microsoft.com/net/core#dockercmd

docker run -it microsoft/dotnet:latest

Esse comando irá baixar e instalar o .Net Core container diretamente do Docker Hub da Microsoft.

Uma vez instalado e rodando, você já estará dentro do container e pode rodar alguns comandos do .NET Core. Tente alguns:

Dotnet –version

image

dotnet –info

image

Exit

image

Até aqui eu fui bem tranquilo, mas e agora se eu quiser voltar para o contexto do container?

Demorei um pouco lendo a documentação do Docker, mas encontrei 2 modos de voltar ao contexto do container. Mas antes, utilize o Kitematic para verificar as configurações e nomes dos seus containers.

clip_image002

clip_image004

Você pode iniciar o container pelo Kitematic e/ou utilizar o comando:

image

Para entrar no contexto do container:

Docker attach netcore

image

O comando attach permite que entre no contexto do terminal que você está, mas se abrir um novo terminal.. não será possível. Então utilize este outro comando para entrar no contexto em qualquer terminal:

Docker exec –i –t netcore /bin/bash

image

E é isso, agora você tem o .NET Core rodando no Mac utilizando Docker.

Abraço!

Fechando WINWORD.EXE ao utilizar o PIA

December 21, 2016 Leave a comment

CENÁRIO:

Cenário bem comum em automação de processos é precisar criar/editar arquivos Office de forma automática e sem iteração do usuário final.

Para isso, podemos usar o Open XML SDK 2.5 para Microsoft Office: https://www.microsoft.com/en-us/download/details.aspx?id=30425.

Mas o Open XML SDK é apenas para os arquivos atuais do Office (pós 2007) que usam o OpenXML em sua concepção (.docx, .xlsx, etc).

Em tempos primórdios, tínhamos que usar o Primary Interop Assemblies (PIA) para trabalhar com arquivos Office via código: https://www.microsoft.com/en-us/download/details.aspx?id=3508.

E o trabalho é BEM MAIS árduo do que usar o Open XML SDK.

ANÁLISE:

E um cenário deveras comum, é encontrar uma aplicação que usa o Interop para manipular arquivos Office consumindo memória e nunca mais devolvendo, criando diversos processos, sem fecha-los após uso.

clip_image002

Cada processo WINWORD.EXE, neste exemplo, consumindo 25Mb de memória. E cada utilização, mais processos, até estourar memória do servidor (ou fechar os processos manualmente).

SOLUÇÃO:

A primeira dica é evitar o uso de “2 pontos” no mesmo “comando”.

Exemplo – Ruim

WordDOC.Document doc = app.Documents.Open(@"c:\teste.doc");

 

Exemplo – Bom

WordDOC.Documents d = app.Documents;

WordDOC.Document doc = d.Open(@"c:\teste.doc");

 

Bom, essa dica vai evitar que sejam criados wrappers em objetos COM que segurem o processo em memória.

A outra dica é sinalizar que os objetos já podem ser finalizados. Após o uso, vá finalizando o uso do objetos regressivamente.

 

using WordDOC = Microsoft.Office.Interop.Word;

Application app = new Application();

WordDOC.Documents d = app.Documents;

object nullobject = Type.Missing;

object doNotSaveChanges = WordDOC.WdSaveOptions.wdDoNotSaveChanges;

try

{

WordDOC.Document doc = d.Open(@"c:\teste.doc");

doc.Activate();

doc.SaveAs(finfo.FullName.Replace(".doc", "_tmp.docx"), WordDOC.WdSaveFormat.wdFormatDocumentDefault);

((Microsoft.Office.Interop.Word._Document)doc).Close(ref doNotSaveChanges, ref nullobject, ref nullobject);

Marshal.FinalReleaseComObject(doc);

Marshal.FinalReleaseComObject(d);

((Microsoft.Office.Interop.Word._Application)app).Quit(ref nullobject, ref nullobject, ref nullobject);

Marshal.FinalReleaseComObject(app);

}

catch (Exception ex)

{

HandleException(ex);

}

 

Dessa forma os objetos serão finalizados quando o GC ocorrer.

Abraço!

Enviando e-mails programaticamente usando o Office 365 (Exchange Online)

CENÁRIO:

Cenário bem comum em automação de processos é ter algum serviço que fique monitorando algum processo e depois envie e-mails de notificação aos interessados.

ANÁLISE:

Com o EWS Managed API 2.0 isso ficou muito simples. Principalmente utilizando o Office 365 ao invés de um Exchange Server on-premises.

Para facilitar BASTANTE as coisas, utilizaremos o Microsoft.Exchange.WebServices desenvolvido pela própria Microsoft.

https://msdn.microsoft.com/en-us/library/dd877012(v=exchg.150).aspx

clip_image002

Para instalar esse NugetPackage no projeto, é requisito que ele seja .NET 4.0.

HOW TO:

Instale o pacote no projeto. E o resto é bem simples.

static void Main(string[] args)

{

ExchangeService service = new ExchangeService();

service.Credentials = new WebCredentials(meuemail@dominioOffice365.com.br, "SENHA");

service.TraceEnabled = true;

service.TraceFlags = TraceFlags.All;

service.AutodiscoverUrl(meuemail@dominioOffice365.com.br, RedirectionUrlValidationCallback);

EmailMessage email = new EmailMessage(service);

email.ToRecipients.Add(meuemail@dominioOffice365.com.br);

email.Subject = "HelloWorld";

email.Body = new MessageBody("Este é o primeiro e-mail que envio usando o EWS Managed API.");

email.Send();

Console.ReadLine();

}

 

E o método de apoio para validar o esquema do AutoDiscover:

private static bool RedirectionUrlValidationCallback(string redirectionUrl)

{

// The default for the validation callback is to reject the URL.

bool result = false;

Uri redirectionUri = new Uri(redirectionUrl);

// Validate the contents of the redirection URL. In this simple validation

// callback, the redirection URL is considered valid if it is using HTTPS

// to encrypt the authentication credentials.

if (redirectionUri.Scheme == "https")

{

result = true;

}

return result;

}

Atualizado (resultado):

Screen Shot 2016-07-13 at 8.05.47 PM 

Abraço!

Devo aplicar Cumulative Updates (CU) assim que são disponibilizados?

February 16, 2016 2 comments

CENÁRIO:

Essa é uma questão que sempre vem à tona e eu sempre perco o link com informações à respeito. Devo manter meu ambiente sempre o mais atualizado possível?

Para quem não está familiarizado, a Microsoft disponibiliza constantemente updates para seus produtos (que ainda estão no ciclo de suporte – normalmente 10 anos desde seu lançamento).

Nesses updates temos hotfixes, Cumulative Updates (pacote com vários hotfixes) e Service Packs (pacote maior ainda de vários hotfixes). A frequência de lançamento também segue essa hierarquia. Ou seja, são lançados vários hotfixes até que formem um Cumulative Update, e assim por diante.

ANÁLISE:

A Microsoft recomenda o seguinte:

https://msdn.microsoft.com/en-us/library/hh479746(v=sql.120).aspx – Installing Updates for SQL Server 2014 after it has already been installed

On an installed instance of SQL Server 2014, we recommend that you apply the latest security updates and critical updates including General Distribution Releases (GDRs), and Service Packs (SPs). Individual Cumulative updates and security updates should be adopted on a case-by-case, "as-needed" basis. Evaluate the update, if it’s needed, then apply it. If not, assess the risk of applying or not.

CONCLUSÃO:

O link mostra informação sobre o SQL Server, mas o mesmo se aplicar ao SharePoint Server e imagino que aos demais produtos Microsoft.

Cumulative Updates devem ser aplicados se necessário. Sua aplicação não deve seguir uma agenda “urgente” de implantação assim que são lançados.

Obviamente que quanto mais atualizado, melhor. Mas no mundo real a aplicação de patch’s não é tão simples em ambiente produtivo.

Como é um assunto que sempre gera bons diálogos. Qual a sua consideração sobre o assunto?

Eu procuro sempre estar no último Service Pack e aplico CU’s à medida da necessidade.

Abraço!

SQL Generic Error – Busca Nativa CRM

February 4, 2016 Leave a comment

Cenário:

Ao realizar buscas no CRM, algumas telas trazem resultados, outras dão o erro SQL Generic Error (ao baixar o Arquivo de Log).

clip_image002

ERRO:

Generic SQL Error.

Mas ao investigar nos logs de Trace do CRM e EventViewer podemos notar referências ao SQL Generic Error com o erro abaixo:

“SQL Server encountered error 0x80070422 while communicating with full-text filter daemon host (FDHost) process. Make sure that the FDHost process is running. To re-start the FDHost process, run the sp_fulltext_service ‘restart_all_fdhosts’ command or restart the SQL Server instance.”

SOLUÇÃO:

Com isso observamos que o serviço “SQL Full-text Filter Daemon Launcher” estava parado, pois por padrão, o serviço vem configurado para ser “Manual”. Ou seja, se o serviço cair por algum motivo, ele não será iniciado automaticamente.

image

Para resolver o problema bastou iniciar o serviço “SQL Full-text Filter Daemon Launcher” e aguardar alguns minutos.

clip_image004

Se não resolver apenas iniciando o serviço, devemos executar um comando no SQL e talvez mandar reconstruir o catálogo.

EXEC sp_fulltext_service 'restart_all_fdhosts'

DatabaseName > Storage > Full Text Catalog > properties > Rebuild Catalog.

Abraço!

Value cannot be null – Instanciando OrganizationServiceProxy

December 30, 2015 Leave a comment

Cenário:

Meu primeiro post sobre Microsoft CRM! Smiley de boca aberta

Nas últimas semanas venho trabalhando em análise e melhoria de performance de alguns serviços que fazem integração entre uma aplicação legada e o Microsoft CRM 2015, usando IFD.

Os serviços estão deveras lentos. Nesse processo de análise estamos montando aplicações de testes paralelas aos serviços… e nessas aplicações estamos simulando maneiras diferentes de instanciar o serviço de comunicação com o CRM.

Para isso estou utilizando o SDK do CRM como base de apoio aos testes: http://go.microsoft.com/fwlink/?LinkID=512122.

ERRO:

Value cannot be null.

SOLUÇÃO:

Na correria… acabei copiando e colando uma url que deveria ser diferente!

No SDK do CRM, há um tipo de autenticação se usar os “Helpers” que existem no SDK. Nesse exemplo ele pede para instanciar assim:

 

#region Class Level Members

// To get discovery service address and organization unique name,

// Sign in to your CRM org and click Settings, Customization, Developer Resources.

// On Developer Resource page, find the discovery service address under Service Endpoints and organization unique name under Your Organization Information.

private String _discoveryServiceAddress = "https://MyCompany/XRMServices/2011/Discovery.svc&quot;;

private String _organizationUniqueName = "OrganizationUniqueName";

// Provide your user name and password.

private String _userName = "username@mydomain.com";

private String _password = "password";

// Provide domain name for the On-Premises org.

private String _domain = "mydomain";

#endregion Class Level Members

 

E aí ao trocar a url, eu copiei e colei uma url errada… coloquei a url “Organization.svc” ao invés da url correta “Discovery.svc”.

Ao colocar a url errada e tentar instanciar o OrganizationServiceProxy, dá o erro mencionado acima nesse método abaixo:

 

/// <summary>

/// Discovers the organizations that the calling user belongs to.

/// </summary>

/// <param name="service">A Discovery service proxy instance.</param>

/// <returns>Array containing detailed information on each organization that

/// the user belongs to.</returns>

public OrganizationDetailCollection DiscoverOrganizations(

IDiscoveryService service)

{

if (service == null) throw new ArgumentNullException("service");

RetrieveOrganizationsRequest orgRequest = new RetrieveOrganizationsRequest();

RetrieveOrganizationsResponse orgResponse =

(RetrieveOrganizationsResponse)service.Execute(orgRequest);

return orgResponse.Details;

}

 

Após colocar a URL correta “Discovery.svc” , tudo funcionou perfeitamente.

Abraço!