lunedì 28 marzo 2016

Come utilizzare le API WinRT di Windows 10 in una applicazione desktop console C#

Sono entusiasta delle possibilità offerte dalle api WinRT di Windows 10 nelle applicazioni UWP, peccato usarle solo in app "sandboxed"
In realtà la maggior parte di queste API si possono utilizzare anche in applicazioni classiche desktop.
Vediamo ad esempio come utilizzare le librerie OCR in un programma a riga di comando che prende in ingresso un file contenente un'immagine e genera un file .txt con il risultato.

Con Visual Studio 2015 genera un nuovo progetto Visual C#->Windows->Classic Desktop->Console Application OCRConsole
  • Con il pulsante destro sul progetto fai Unload Project
  • Con il pulsante destro sul progetto fai Edit OCRConsole.csproj
  • Aggiungi <TargetPlatformVersion>10.0</TargetPlatformVersion> sotto <TargetFrameworkVersion>
  • Con il pulsante destro sul progetto fai Reload Project
  • Add Reference->Browse Windows.winmd da C:\Program Files (x86)\Windows Kits\10\UnionMetadata
  • Add Reference->Browse System.Runtime.WindowsRuntime da C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.NETCore\v4.5\System.Runtime.WindowsRuntime.dll. Nelle proprietà di System.Runtime.WindowsRuntime setta "Copy Local=False"
  • Se ottieni in compilazione un errore del tipo 'await' requires that the type 'Windows.Foundation.IAsyncOperation' have a suitable GetAwaiter method., verifica che il codice inizi con using System; Verifica anche che nel progetto il percorso System.Runtime.WindowsRuntime.dll sia assoluto

using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Windows.Globalization;
using Windows.Graphics.Imaging;
using Windows.Media.Ocr;
using Windows.Storage;

namespace OCRConsole
{
    class Program
    {
        static private OcrEngine ocrEngine;
        static private string filePath;

        static async Task OCRAsync()
        {
            // Do any async anything you need here without worry
            // Load image from install folder.
            var file = await StorageFile.GetFileFromPathAsync(filePath);
            using (var stream = await file.OpenAsync(Windows.Storage.FileAccessMode.Read))
            {
                // Create image decoder.
                var decoder = await BitmapDecoder.CreateAsync(stream);
                // Load bitmap.
                var bitmap = await decoder.GetSoftwareBitmapAsync();
                // Extract text from image.
                OcrResult result = await ocrEngine.RecognizeAsync(bitmap);
                // Display recognized text.
                File.WriteAllText(Path.ChangeExtension(filePath, "txt"), result.Text, Encoding.Default);
               
            }
        }

        static void Main(string[] args)
        {
            if (args.Length != 1)
            {
                Console.WriteLine("Usage: OCRConsole <filepath>");
                return;
            }

            filePath = args[0];
            if (!File.Exists(filePath))
            {
                Console.WriteLine("The file {0} doesn't exist", filePath);
                return;
            }
           
            // Init OCR engine with Italian language.
            ocrEngine = OcrEngine.TryCreateFromLanguage(new Language("it"));
            OCRAsync().GetAwaiter().GetResult();
           
        }
    }
}

Una volta per ottenere questo risultato ci voleva un SDK apposito, tonnellate di codice C++ e qualche giorno di sviluppo.

giovedì 29 maggio 2014

Come registrare una libreria COM scritta in .NET

Dopo aver migrato dei controlli ActiveX da C++ a C#, ho speso diverso tempo prima di riuscire a registrare correttamente la libreria dll in .NET su un PC che non fosse quello di sviluppo.
Il comando che usavo:

RegAsm.exe mypath\mydll.dll

fa sì che il sistema operativo cerchi la libreria mydll nella GAC (global assembly cache).
La mia applicazione referenzia l'instanza di mydll contenuta nella stessa cartella di lavoro, e perciò non la trova.
Per far sì che mydll venga referenziata correttamente bisogna aggiungere lo switch /codebase:

RegAsm.exe /codebase mypath\mydll.dll

Il parametro codebase registra mydll con l'intero path, rendendola accessibile da qualunque locazione.

Per distribuire correttamente la libreria con InnoSetup:

[Run]
Filename: "{dotnet40}\RegAsm.exe"; Parameters: /codebase mydll.dll; WorkingDir: {app};

mercoledì 19 giugno 2013

Windows 7 e 8: abilitare l'assistente vocale per l'italiano.


L'italiano non è tra le voci ufficialmente disponibili per l'assistente vocale di Windows, ma con questo trucco potrete sentire la voce di Lucia leggere tutto quello che volete con un discreto accento italiano.
Installate prima i Runtime di Microsoft Speech Platform (Version 11)  da qui http://www.microsoft.com/en-us/download/details.aspx?id=27225 (a 32 o 64 bit, a seconda del vostro PC)
Scaricate poi da Microsoft Speech Platform - Runtime Languages (Version 11)  la voce di Lucia: http://www.microsoft.com/en-us/download/details.aspx?id=27224
Il file da scaricare e installare è MSSpeech_TTS_it-IT_Lucia.msi

Per visualizzare Lucia tra le voci disponibili dell'assistente vocale, aggiungete queste voci di registro:
=======================================================================================================================================
Windows Registry Editor Version 5.00
[HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Speech\Voices\Tokens\TTS_MS_it-IT_Lucia_11.0]
@="Microsoft Server Speech Text to Speech Voice (it-IT, Lucia)"
"410"="Microsoft Server Speech Text to Speech Voice (it-IT, Lucia)"
"CLSID"="{a12bdfa1-c3a1-48ea-8e3f-27945e16cf7e}"
"LangDataPath"="C:\\Program Files (x86)\\Common Files\\Microsoft Shared\\Speech\\Tokens\\TTS_MS_it-IT_Lucia_11.0\\MSTTSLocitIT.dat"
"VoicePath"="C:\\Program Files (x86)\\Common Files\\Microsoft Shared\\Speech\\Tokens\\TTS_MS_it-IT_Lucia_11.0\\LuciaT"
[HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Speech\Voices\Tokens\TTS_MS_it-IT_Lucia_11.0\Attributes]
@=""
"Age"="Adult"
"AudioFormats"="6"
"Gender"="Female"
"Language"="410"
"Name"="Microsoft Server Speech Text to Speech Voice (it-IT, Lucia)"
"Vendor"="Microsoft"
"Version"="11.0"
=======================================================================================================================================

Si tratta di esportare la chiave HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Speech Server\v11.0\Voices\Tokens\TTS_MS_it-IT_Lucia_11.0, rinominare cambiare il percorso da "Speech Server\v11.0" a "Speech" e importare la chiave.
Sulla mia postazione Win8x64 ha funzionato, non c'è bisogno di dire di non farlo se non siete sicuri di quello che state facendo e dei rischi che si corrono modificando il registro di Windows.

Se ora aprite l'assistente vocale, troverete la voce di Lucia tra quelle disponibili

sabato 24 dicembre 2011

Handling events in VB6 exposed on a C# class library through COM

In order to create a .NET class library that can be called by a VB6 client make ComVisible the class and add the interop tags as in the following code

namespace MyClassLibraryNamespace
{

    // The delegate type for our custom event.
    [ComVisible(false)]
    public delegate void MyEventHandler(object sender, EventArgs e);

    [InterfaceType(ComInterfaceType.InterfaceIsIDispatch)]
    [ComVisible(true)]
    public interface IMyEvents
    {
        [DispId(1)]//Each event must have a unique DispId
        void OnMyEvent(object sender, EventArgs e);
    }


    [ClassInterface(ClassInterfaceType.None)]
    [ComSourceInterfaces(typeof(IMyEvents))]
    [ComVisible(true)]
    public class MyControl
    {
        public event MyEventHandler OnMyEvent;
  
    }
}

Compile the class library with "Register for COM interop" option in the Build tab of Visual Studio 2010: it will generate a tlb file aside the dll.

Add a reference to the tlb file in the VB6 project

Private WithEvents myCtrl As MyControl

Private Sub Form_Load()
 Set myCtrl = New MyControl
End Sub


Private Sub myCtrl_OnMyEvent(ByVal sender As Variant, ByVal e As EventArgs)
 MsgBox "MyEvent"
End Sub

venerdì 11 novembre 2011

DLL Injection Windows 7 64bit

It seems that Dll injection in Windows 7 64bit could work using the same technique used in Windows XP.
The code I was using works in Windows 7 32bit but doesn't work in Windows 7 64 bit

My code is copied from sample found out in Internet and opens the process to inject a dll:

// Get a handle for the target process.
hProcess = OpenProcess(
        PROCESS_CREATE_THREAD     |   // For CreateRemoteThread
        PROCESS_VM_OPERATION      |   // For VirtualAllocEx/VirtualFreeEx
        PROCESS_VM_WRITE,             // For WriteProcessMemory
        FALSE, dwProcessId);

The code above doesn't work in Windows 7 64 bit. For windows 7 64 bit it should be:

// Get a handle for the target process.
hProcess = OpenProcess(
        PROCESS_ALL_ACCESS, //fix wor windows 7 64 bit
        FALSE, dwProcessId);

mercoledì 2 novembre 2011

How to convert raw pcm data into wav files using NAUDIO library

In my project I have a third party library that produces raw PCM files and I need to play them.
Off course tools like Audacity are able to convert PCM files, but the easiest way I found out to read and play raw PCM files programmatically is using NAUDIO:

WaveFormat format = new WaveFormat(8000, 1);
using (RawSourceWaveStream reader = new RawSourceWaveStream(new FileStream (fileName, FileMode.Open), format))
{
 WaveFileWriter.CreateWaveFile(Path.ChangeExtension(fileName, "wav"), reader);
}

giovedì 1 settembre 2011

Error: The Visual Studio Remote Debugger service on the target computer cannot connect back to this computer

I spent a lot of time trying to debug an application running on an embedded device.
My development computer is running Visual Studio 2010 SP1 on Windows XP SP3 and it is on domain.
The target computer is running Windows Embedded 2009 and it is on the default workgroup.
I'm running the Remote Debugging Monitor as application.
According to this page http://msdn.microsoft.com/en-us/library/ms164725.aspx, using local accounts that have the same username and password on both computers the remote debugging should work.
Eventually I'be been able to debug remotely only taking the development computer out of the domain.