Lost in the matrix

C/C++/C#/Java, Multithreading

Bonsoir,

Vous avez probablement déjà voulu faire une requête qui recherche
des éléments dans une table avec une clause de type LIKE.
Cependant, comment peut-on faire pour rendre le LIKE non sensible
aux accents. Voici donc la méthode pour effectuer cette recherche
avec Microsoft SQL Server.

En un mot la solution est : COLLATE.

La clause COLLATE permet (entre-autre) la conversion du classement
d'une expression.
Cette clause doit être combinée avec une chaîne
spécifiant le nom d'un classement SQL Server.

Dans notre cas j'ai choisi : SQL_Latin1_General_Cp437_CI_AI.

C'est à dire:

Latin1_General: identifiant l'alphabet ou la langue dont les
règles de tri sont appliquées.

Cp437: page de code 437.
CI: ne distingue pas la casse.
AI:
ne distingue pas les accents.


-----

Cela donne cette requête:

SELECT * FROM MY_TABLE WHERE MY_TABLE.STR LIKE '%Libellé%'
COLLATE SQL_Latin1_General_Cp437_CI_AI;

-----

Par consequent, cette commande renverra les entrées dans
la table si le champ STR est égale aux valeurs ci-dessous:

libelle
LiBeLlE
libéllE
Libellé








Pour notre démonstration, nous allons créer un plugin qui permet
d'ouvrir un onglet contenant un UserControl de votre choix.
Cette démonstration sera
réalisée en C# sous visual studio 2008.

Grace à cet exemple vous pourrez étendre de façon exponentielle
les possibilités de Visual Studio.

1) Ouvrez Visual Studio et créez un nouveau projet.

2) Sélectionnez “Complément Visual Studio

3) Cliquez sur “Suivant”

4) Sélectionnez “C#” et cliquez sur “Suivant”

5) Sélectionnez “Microsoft Visual Studio 200X” et
cliquez sur “Suivant”

6) Entrez les informations sur votre plugin et
cliquez sur “Suivant”.

7) Selectionnez les élements comme ci-dessous et
cliquez sur “Suivant”

8) Cliquez sur “Suivant”

9) Cliquez sur “Suivant”

10) Ouvrez le fichier “Connect.cs”

Ce fichier contient une classe “Connect” qui est le point
d’entrer de notre plugin. Attention, c’est du C#.Net 1.0.
Par conséquent le code généré est plutôt moche.

Cette classe contient 7 méthodes:

Methode Action
OnConnection Appelée lors du chargement du plugin.
OnDisconnection Appelée lors du déchargement du plugin.
OnAddInsUpdate Appelée lorsque des plugins on été chargés ou déchargés
OnStartupComplete Appelée lorsque le Visual est en cours de lancement
OnBeginShutdown Appelée lorsque le Visual est en cours de fermeture
QueryStatus Appelée lorsque le paramétrage d’une commande a changé
Exec Appelée lors de l’exécution d’une commande dans le plugin


Pour l’instant, nous n’allons pas modifier ce fichier.

Remarque : un plugin est en réalité qu’une bibliothèque
de classes compilée (MyFirstAddIn.dll dans notre cas)

11) Allez dans les propriétés du projet.

Notre plugin est destiné à être utilisé avec Visual Studio. C’est-à-dire
que pour déboguer notre plugin, nous allons lancer une instance de
Visual Studio qui sera l’hôte de notre application.
Ainsi, nous allons indiquer à Visual Studio qu’il doit lancer une instance
de
Visual “devenv.exe” en mode Debug.

12) Dans l'onglet “Déboguer” Sélectionnez “Démarrer le programme externe”
dans “Action de démarrage”.

Assurez-vous que le chemin vers le programme “devenv.exe” est
correct. Normalement les paramètres par défaut sont corrects.

13) Lancez l’application en mode “Debug”.

Comme nous pouvons le voir Visual va lancer une instance Visual.
Nous pouvons aussi remarquer qu’un nouveau bouton “"MyFirstAddIn”
est apparu dans “Outils”. Si nous cliquons dessus, il se passe
bien entendu rien du tout, car nous n’avons rien implémenté dans
le fichier “Connect.cs”.

14) Ajoutez un nouveau UserControl.

Nous avons vu que le plugin est parfaitement chargé par Visual Studio.
Il faut maintenant créer le Contrôle utilisateur(UserControl)
pour notre démonstration.

15) Entrez un nom puis cliquez sur “Ajouter”.

Pour notre exemple, j’ai appelé le UserControl “MyUserControl”

16) Ajoutez les éléments que vous desirez dans ce UserControl.

Dans notre exemple j’ai ajouté une PictureBox.

17) Implémentez le fichier “Connect.cs”

Nous sommes maintenant en mesure d’implémenter notre plugin.
Pour des raisons pratiques, je vais seulement indiquer les lignes à rajouter dans ce fichier afin
de ne pas nuire à la visibilité de ce tutoriel (le code complet est disponible en bas du tutoriel).

A) Ajoutez un “using” dans l’entête du fichier pour indiquer que nous allons utiliser les “Windows Forms”:

using System.Windows.Forms;

B) Ajoutez deux attributs privés dans la classe:

// Instance de notre UserControl
private UserControl _UserControl;
// Instance sur la fenêtre qui contient notre UserControl
private Window _toolWindow;

C) Ajoutez à la fin de la méthode “OnConnection” le code ci-dessous:

else if (connectMode != ext_ConnectMode.ext_cm_UISetup)
{
try
{
object obj = null;// Instance du contrôle renvoyé par CreateToolWindow2
Windows2 windows2 = this._applicationObject.Windows as Windows2;
this._toolWindow = windows2.CreateToolWindow2(this._addInInstance,
Assembly.GetExecutingAssembly().Location,
"MyFirstAddIn.MyUserControl", // Nom du contrôle
"MyFirstAddIn", // Nom du plugin
//
Identificateur unique pour la nouvelle fenêtre.
"{84A3675C-CDA0-4c8b-858F-8F00BEACF199}",
ref obj);
this._UserControl = obj as UserControl;
this._toolWindow.Linkable = false;
this._toolWindow.IsFloating = false;
}
catch (Exception) // A implémenter
{
}
}

D) Ajoutez dans la méthode “Exec” le code ci-dessous
juste avant le “handled = true;”:

this._toolWindow.Visible = true;

E) Compilez, exécutez, puis cliquez dans le bouton “MyFirstAddIn”
dans “Outils”.

Remarque: ne lancez jamais le plugin dans l’instance du Visual que vous
utilisez pour développer le plugin, sinon vous devrez redémarrer Visual.

Notre plugin est maintenant fonctionnel. Il ne reste qu’à implémenter
notre UserControl. Vous pouvez ainsi réaliser une multitude d’outil
pour améliorer la productivité dans votre organisation comme:

- Un client VIM intégré dans visual.
- Un client de messagerie instantané.
- Des outils de diagnostiques.
- Des “Profiler” de code.
- Un gestionnaire de documentation colaboratif.

Afin de gagner du temps, vous pouvez assigner un raccourcis clavier à
votre commande. Pour cela il suffit d’aller dans “Outils” puis “Options”.

Sélectionnez “Environnement –> Clavier”. Faites une recherche afin de retrouver
la commande. Dans notre cas la commande s’appelle “MyFirstAddIn.
Connect.MyFirstAddIn”. Il ne vous reste plus qu’à assigner un raccourcis.

18) Déployment du plugin.

Pour Déployer le plugin, il suffit de fournir la bibliothèque de classe
qui se trouve dans le répertoire de sortie du projet (Dans notre cas
“MyFirstAddIn.dll” ) Ainsi qu’un fichier XML avec l’extension “.AddIn”.

Ce fichier XML est visible dans l’explorateur de la solution.
Dans notre cas il s’appelle “MyFirstAddIn.AddIn”. Il suffit de le copier
dans le répertoire “Addins” de l’utilisateur voir ci-dessous:

Remarque: Un fichier est déjà existant dans votre répertoire.
Il s’agit du fichier utiliser par Visual pour déboguer votre application.

Attention, la seule réstriction c’est que la valeur contenue dans la
balise “Assembly” soit correct. La valeur doit pointer sur le chemin du
plugin que vous deployer. Ainsi le choix de l’emplacement du plugin
est à votre discretion.

Merci d’avoir suivi ce tutoriel.

Dans le prochain article, nous exploiterons un peu plus les possibilités
de Visual Studio (Lancement de compilation, automatisation de tâches,
parsing de code).

Code source du tutoriel : ICI



/// <summary>
/// String2Stream
/// </summary>
/// <param name="str">la chaine</param>
/// <returns>le flux</returns>
public static Stream String2Stream(string str)
{
MemoryStream memStream = new MemoryStream();
byte[] data = Encoding.Unicode.GetBytes(str);
memStream.Write(data, 0, data.Length);
return (memStream as Stream);
}


/// <summary>
/// CSVsplitter
/// </summary>
/// <param name="str">la chaine</param>
/// <param name="delimiter">le séparateur</param>
/// <param name="hasQuote">utilise les quotes</param>
/// <returns>tableau de chaines</returns>
public static string[] CSVsplitter(string str, char delimiter, bool hasQuote)
{
if (!hasQuote) // Si il n'y a pas de "quote"
return (str.Split(delimiter));
// On remplace le séparateur pour éviter les conflits
// avec l'expression régulière
str = str.Replace(delimiter, '¤');
string[] tab = (new Regex('¤' + "(?=(?:[^\"]*\"\"[^\"]*\"\")*(?![^\"]+\"\"))",
RegexOptions.IgnorePatternWhitespace)).Split(str);
// On retire les "quotes"
for (int i = 0; i < tab.Length; ++i)
tab[i] = (new StringBuilder(tab[i], 1,
tab[i].Length - 2, tab[i].Length - 2)).ToString();
return (tab);
}


1 - Ajoutez les dépendances dans votre "setup"





2 - Ajoutez le module Crystal Reports.




3 - Sélectionnez le bon module, ici : "
CrystalReports11_5_NET_2005.msm"



4 - Modifiez les propriétés du module Crystal.




5 - Sélectionnez "MergeModuleProperties"




6 - Entrez votre code de licence pour le déploiement.


Il est agréable qu'une application mobile fonctionne de la même
façon sur windows mobile et windows CE. Sous windows mobile,
lorsqu'une application est lancée et que l'on tente de la relancer,
on récupère simplement le "focus" sur l'application existante.
Contrairement à Windows CE qui autorise les instances multiples.
Afin d'homogénéiser le comportement sur ces deux plateformes,
un code très simple permet de résoudre ce problème.

(le code compatible Windows mobile >= 3.0)

using System;
using System.Windows.Forms;
using System.Runtime.InteropServices;
using System.Reflection;
using MobileTools;

namespace MobileTools
{
static class SingleInstanceApplication
{
[DllImport("coredll.dll")]
private static extern IntPtr FindWindow(string lpClassName, string lpWindowName);

[DllImport("coredll.dll")]
private static extern bool SetForegroundWindow(IntPtr hWnd);

[DllImport("coredll.dll", SetLastError = true)]
private static extern IntPtr CreateMutex(IntPtr Attr, bool Own, string Name);

[DllImport("coredll.dll", SetLastError = true)]
private static extern bool ReleaseMutex(IntPtr hMutex);

public static void Run(Form frm)
{
SingleInstanceApplication.Run(frm, frm.Text);
}

public static void Run(Form frm, string caption)
{
// On créer un mutex avec un nom qui nous sert de référence.
IntPtr MtxHandle = CreateMutex(IntPtr.Zero,
true, Assembly.GetExecutingAssembly().GetName().Name);
// Si le mutex existe déjà on récupère le focus sur
// l'application existante par son nom (caption)
if (Marshal.GetLastWin32Error() == SingleInstanceApplication.ALREADY_EXISTS)
SetForegroundWindow(FindWindow(null, caption));
else
Application.Run(frm); // Sinon on lance la forme
// On libère le mutex
ReleaseMutex(MtxHandle);
}

private const int ALREADY_EXISTS = 183;
}
}

//Reference: Microsoft.WindowsCE.Forms

using Microsoft.WindowsCE.Forms;

///


/// Renvoie: WinCEGeneric, Smartphone ou PocketPC
///
public static WinCEPlatform GetPlatformTarget()
{
   return (SystemSettings.Platform);
}

#
# Usage : simple_cut <chaine> <separateur> <colonne>
#

simple_cut()
{
sep=`echo -e "\006"`
echo -n `echo -n $1 | tr "\n" $sep | tr $2 "\n" | head -n $3 | tail -n 1 | tr $sep "\n"`
}

using System.Reflection;
using System.IO;

namespace Tools
{
public static class PDATool
{
#region Property

public static string ApplicationDirectory
{
get
{
if (PDATool._Path == null) // On calcul la première fois le chemin.
PDATool._Path = Path.GetDirectoryName(Assembly.GetExecutingAssembly().
GetName().CodeBase);
return (PDATool._Path);
}
}

#endregion

#region Attribut

// On conserve une le chemin pour éviter de le recalculer à chaque fois.
private static string _Path = null;

#endregion
}
}

#include <windows.h>

void getMemoryUsage(unsigned long *total,
unsigned long *avail,
float *usage,
unsigned long *virt_total,
unsigned long *virt_avail)
{
MEMORYSTATUSEX statex;

statex.dwLength = sizeof(statex);
GlobalMemoryStatusEx(&statex);
if (total != NULL)
// mémoire physique totale
*total = (unsigned long)(statex.ullTotalPhys / 1024);
if (avail != NULL)
// mémoire physique disponible
*avail = (unsigned long)(statex.ullAvailPhys / 1024);
if (usage != NULL)
// pourcentage d'utilisation de la mémoire physique
*usage = (float)(statex.dwMemoryLoad);
if (virt_total != NULL) // mémoire virtuelle totale
*virt_total = (unsigned long)(statex.ullTotalVirtual / 1024);
if (virt_avail != NULL)
// mémoire virtuelle disponible
*virt_avail = (unsigned long)(statex.ullAvailVirtual / 1024);
}

#include <windows.h>

unsigned int getCPUcount(void)
{
SYSTEM_INFO sysinfo;

GetSystemInfo(&sysinfo);
return ((unsigned int)sysinfo.dwNumberOfProcessors);
}

Bonjour,

Voici le tout premier webcast, de la saga sur le multithreading.

video

Pour plus d'information sur le CSP, voir ici
Page Wikipédia de Sir Charles Antony Richard Hoare : ici

using System;
using System.CodeDom.Compiler;
using System.Diagnostics;

public class MicroCompiler
{
public MicroCompiler()
{
this.Language = "CSharp";
this.WarningLevel = 1;
this.OutputPath = "./~TMP.exe";
}

public bool Compile()
{
this.Result = string.Empty;
this.KillCompiledProgram();
using (CodeDomProvider codeProvider = CodeDomProvider.CreateProvider(this.Language))
{
// Paramètres de la compilation
CompilerParameters parameters = new CompilerParameters();
parameters.GenerateExecutable = true;
parameters.GenerateInMemory = false;
parameters.WarningLevel = this.WarningLevel;
parameters.OutputAssembly = this.OutputPath;
// on compile
CompilerResults results = codeProvider.CompileAssemblyFromSource(parameters,
new string[] { this.Source });
// est ce qu'il y a des erreurs ?
if (results.Errors.Count > 0)
{
foreach (CompilerError CompErr in results.Errors)
this.Result += "Line: " + CompErr.Line + " : " + CompErr.ErrorNumber +
" => " + CompErr.ErrorText + Environment.NewLine;
return (false);
}
}
// on créer le processus pour éxecuter le code
this.CompiledProgram = new Process();
this.CompiledProgram.StartInfo.FileName = this.OutputPath;
return (true);
}

public bool KillCompiledProgram()
{
if (this.CompiledProgram == null || this.CompiledProgram.HasExited)
return (false);
this.CompiledProgram.Kill();
return (true);
}

public bool StartCompiledProgram()
{
if (this.CompiledProgram == null)
return (false);
return (this.CompiledProgram.Start());
}

#region Properties

// Niveau d'avertissement.
public int WarningLevel { get; set; }

// Emplacement du programme généré
public string OutputPath { get; set; }

// Résultat de la compilation
public string Result { get; private set; }

// Le code source
public string Source { get; set; }

// Langage de la source (ex: CSharp)
public string Language { get; set; }

// Instance du programme en exécution.
public Process CompiledProgram { get; private set; }

#endregion
}

namespace Test
{
class Program
{
static void Main(string[] args)
{
MicroCompiler mc = new MicroCompiler();
mc.Source = "mettre le code C# ici!";
if (mc.Compile())
mc.StartCompiledProgram();
}
}
}

#include <windows.h>

int __stdcall FileExistsUnicode(wchar_t *fname)
{
return ((long)GetFileAttributesW(fname) > 0);
}

int __stdcall FileExistsAscii(char *fname)
{
return ((long)GetFileAttributesA(fname) > 0);
}

#include <windows.h>
#include <stdlib.h>
#include <string.h>
#include <stdio.h>

int copy_to_clipboard(char const *str)
{
HGLOBAL clipbuffer;
char *buffer;

// On ouvre le presse-papier
if (!OpenClipboard(NULL))
return (0);
// on vide le presse-papier
EmptyClipboard();
// on alloue la mémoire nécéssaire
clipbuffer = GlobalAlloc(GMEM_DDESHARE, strlen(str) + 1);
// on lock le buffer pour avoir un accès exclusif sur le presse-papier
if ((buffer = (char *)GlobalLock(clipbuffer)) == NULL)
{
GlobalUnlock(clipbuffer);
CloseClipboard();
return (0);
}
// On copie notre texte dans le presse-papier
strcpy(buffer, str);
// On déverouille le presse-papier
GlobalUnlock(clipbuffer);
// On met à jour le presse-papier
SetClipboardData(CF_TEXT, clipbuffer);
// On ferme le presse-papier
CloseClipboard();
return (1);
}

char *get_clipboard()
{
char *buffer;
HANDLE hData;

// On ouvre le presse-papier
if (!OpenClipboard(NULL))
return (NULL);
// On recupère le "handle" du presse-papier en mode texte
if ((hData = GetClipboardData(CF_TEXT)) == NULL)
return (NULL);
// On récupère via le "handle" le presse-papier
if ((buffer = (char *)GlobalLock(hData)) == NULL)
return (NULL);
// On déverouille le presse-papier
GlobalUnlock(hData);
// On ferme le presse-papier
CloseClipboard();
return (buffer);
}

int main(int argc, char **argv)
{
copy_to_clipboard("ma chaine");
printf("%s\n", get_clipboard());
getchar();
return (EXIT_SUCCESS);
}

Aujourd'hui nous allons voir comment embarquer une image dans une page HTML.
Pour cela nous avons besoin d'une image quelconque au format gif(ou autre chose),
d'un encodeur en base 64 et d'une minute.

1)allez sur le site : http://www.motobit.com/util/base64-decoder-encoder.asp.

2)Sélectionnez une image et encodez-la en base 64.

3)Créez une page HTML comme ci-dessous:

<html>
<head>
<title>Image embarquée</title>
</head>
<body>
<div style="text-align: center;">
<img SRC="data:image/gif;base64,"ALT="Mon image"/>
</div>
</body>
</html>

4)Insérez l'image encodée en base 64 après "base64,"

5)C'est fini!

/!\ Attention suivant les navigateurs l'image peut ne pas s'afficher!
Je déconseille par conséquent son utilisation, en production.

Le code ci-dessous affiche une petite étoile:
____________________________________________________________________________

<html>
<head>
<title>Image embarquée</title>
</head>
<body>
<div style="text-align: center;">
<img SRC="data:image/gif;base64,
R0lGODlhEAAQAOZIAP/HYf+kP//IY/HTvfGsTsvDvvKsTerKtP/Xr6RqSvzfmfro2P7fm/3TqfS1
e6dGDt6xlfKmSKNtTv7gnfWUNdZyINVxIP7Xg/y6aNV0I9t4JP3u4PbEheybTP759OuvfbRPDffK
odBsHfGxbfzhov7ajf7hkvebOuzLs9FxIv7UefvhydNzI/uaN/W1ZemPOffl1v7pof+xZKaXjvb2
9v/HYv7dhuvr6/7povPz89HR0e+SNcCGY/7Rb+Li4vn5+bl/XL5tPL2tpP7fpv7yvv+rRv+9WP+0
T////wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACH5
BAEAAEgALAAAAAAQABAAAAengEiCg4SFhkgIMgiHhQ0YQxgNjIIeFApEChQehhsrIQ4uMTgxLg4h
KxuCCy8dIxwkJjYmJBwjHS8LSB8EFwwTJSo9KiUTDBcEH0gwFQYAAAICNdDOBhUwggMiEUbc3UYR
IgOEKCxH5udHLCiFBylF7/BFKQeFEBkBASc7J/gZEIQ5eGhoYQFEEBAWWmgA4uOHoBtCEjyQUABJ
AQkPEszQQWOSx4+DAgEAOw=="ALT="Mon image"/>
</div>
</body>
</html>

   Il arrive quelquefois que nous devions utiliser des composants
dont certaines parties sont devenues obsolètes avec le temps.
Malheureusement, il est parfois impossible (contrainte de temps
et/ou d'argent) de refaire le code en intégralité. Ainsi, la
plupart du temps nous en-capsulons les anciens composants dans
de nouveaux.

Seulement comment interdire l'accès aux propriétés obsolètes
de la classe de base et les remplacer par de nouvelles ?

En réalité c'est assez simple. Cette problématique a été résolue
Dans la version 2 du framework .Net. Il suffit d'utiliser l'attribut
"Obsolete" et de "override" certaines propriétés.

___________________________________________________________________

using System;

namespace Test
{
class A
{
// Rem: la class A peut toujours utiliser cette propriété.
public bool OldProperty { get; set; }
}

class B : A
{
// On indique au compilateur que la propriété est obsolète
[Obsolete("utilisez NewProperty à la place.", true)]
public new bool OldProperty { get; set; } // on "override" la propriété

public bool NewProperty { get; set; }
}

class Program
{
static void Main(string[] args)
{
B test = new B();
test.NewProperty = true; // OK
test.OldProperty = true; // Provoque une erreur à la compilation
}
}
}

   Jusqu'à la version 3.0, GCC utilisait la comparaison des noms
des symboles pour savoir si deux types étaient équivalent. Mais
depuis la nouvelle ABI (application binary interface) il ne compare
plus les noms, mais les adresses. Cela a pour but d'améliorer les performances.

Ce choix technique ne pose aucun problème avec un "linkage" statique,
car le les adresses sont déterminées et connues du compilateur.
Contrairement aux bibliothèques partagées (.dll, .so) qui
requièrent une résolution des types à l'exécution (runtime).

En effet si nous prenons en exemple une classe C qui
implémente deux interfaces I1 et I2 définies dans l'exécutable
et dans une bibliothèque partagée. Si nous essayions d'effectuer
un dynamic_cast sur une instance de C renvoyé par la bibliothèque
partagée en I1 ou I2, dynamic_cast renvoie toujours "null".
Le problème est aussi visible via throw et typeid.

Ce problème est dû au changement dans la résolution des types
énoncés ci-dessus. Pour corriger ce problème, il faut utiliser
l'option "-rdynamic" de GCC qui a pour but de créer une "global
symbol table" dans la bibliothèque partagée. Sous Linux/BSD le
compilateur réalise cette opération en passant l'option
"-export-dynamic" au "linker".

#include <unistd.h>

void my_print_hexa(int fd, unsigned int d)
{
if (d & ~0xF) /* si d > 16 */
my_print_hexa(fd, d >> 4); /* rec de d / 16 */
write(fd, (d & 0xF)["0123456789ABCDEF"], 1); /* tab + (d & 16) */
}

Dans cet article, nous allons étudier le framework JNI.
Ce framework Java permet d'exécuter du code non-managé
C/C++ dans du code Java.

Pour cette démonstration nous allons utiliser GCC 3.4.2(Win32)
et le JDK 6. Le code en C sera disponible dans une DLL.
Dev-CPP ou CodeBlocks conviennent parfaitement pour ce tutoriel.
____________________________________________________________________

1) Pour commencer nous allons créer une classe Java "Hello" avec
une methode native "Test" dans un fichier "Hello.java".

public class Hello
{
// Methode native qui correspond au prototype de la fonction
// qui sera appelé dans la DLL
public native void Test();

// Nous chargerons une DLL qui s'appelera "Hello.dll"
static
{
System.loadLibrary("Hello");
}
}

____________________________________________________________________

2) On compile la classe.

$>C:\Sun\SDK\jdk\bin\javac.exe Hello.java

____________________________________________________________________

3) On génère le fichier entête "Hello.h" pour la DLL.

$>C:\Sun\SDK\jdk\bin\javah.exe -jni Hello

#include <jni.h>
#ifndef _Included_Hello
#define _Included_Hello
#ifdef __cplusplus
extern "C" {
#endif
/*
* Class: Hello
* Method: Test
* Signature: ()V
*/
JNIEXPORT void JNICALL Java_Hello_Test(JNIEnv *, jobject);
#ifdef __cplusplus
}
#endif
#endif

____________________________________________________________________

4) On implémente dans un fichier "Hello.c" notre fonction suivant
Le prototype définit dans "Hello.h"

#include <stdio.h>
#include <stdlib.h>
#include <windows.h>
#include "Hello.h"

JNIEXPORT void JNICALL
Java_Hello_Test(JNIEnv *env, jobject obj)
{
printf("42\n");
}

____________________________________________________________________

5) On compile notre fichier source.

$>gcc -c -I"C:\Sun\SDK\jdk\include" \
-I"C:\Sun\SDK\jdk\include\win32" -o Hello.o Hello.c
____________________________________________________________________

6) On génère le fichier de définition de la DLL.

$>dlltool Hello.o -z Hello.def

Cela donne:

EXPORTS
Java_Hello_Test@8 @ 1

____________________________________________________________________

7) On Compile notre DLL.

$>gcc.exe -shared -o Hello.dll -I"C:\Sun\SDK\jdk\include" \
-I"C:\Sun\SDK\jdk\include\win32" Hello.c Hello.def


____________________________________________________________________

8) Nous allons maintenant créer un petit programme en Java pour
tester notre "wrapper" C vers Java dans un fichier "Main.java".


public class Main
{
public static void main(String[] args)
{
// Création d'une instance de notre classe "Hello"
Hello h = new Hello();
// On appel la fonction dans notre DLL (Hello.dll)
h.Test();
}

public Main()
{
super();
}
}

____________________________________________________________________

9) On Compile notre programme de test.

$>C:\Sun\SDK\jdk\bin\javac.exe Main.java

____________________________________________________________________

10) Il nous reste plus qu'à lancer le programme qui doit afficher
"42" sur la sortie standard suivi d'un retour à la ligne.

$>C:\Sun\SDK\jdk\bin\java.exe Main
$>42
$>

____________________________________________________________________

--- Le bug du UnsatisfiedLinkError ---

Il est possible qu'au moment où vous avez exécuter le programme
de l'étape 10, une erreur est apparue:


$>C:\Sun\SDK\jdk\bin\java.exe Main
$>Exception in thread "main" java.lang.UnsatisfiedLinkError: Init
$> at Hello.Test(Native Method)
$> at Main.main(Main.java:8)

Ce problème vient du fait que Java ne trouve pas le symbole de la
fonction "Test" dans notre DLL. Pour résoudre ce problème,
il nous faut revenir à l'étape 6. Dans le fichier "Hello.def"
où l'on peut lire les lignes ci-dessous:


EXPORTS
Java_Hello_Test@8 @ 1


Le problème vient des caractères que j'ai souligné en rouge.
Les noms des fonctions générées par les compilateurs ont toujours ces symboles.
La solution consiste à exporter les fonctions de la DLL sous un autre nom.
Pour cela il existe l'option -k de dlltool qui supprime les décorations
autour des fonctions. Par conséquent nous allons remplacer l'étape 6
par l'étape ci-dessous:

$>dlltool -k Hello.o -z Hello.def

Cela donne:

EXPORTS
Java_Hello_Test @ 1

Nous pouvons maintenant recompiler le projet qui fonctionne parfaitement.
---
Sous Microsoft Visual C, il faut utiliser le programme "dumpbin" afin
de récupérer les symbole de la DLL:

$>dumpbin Hello.dll /EXPORTS

Puis rajouter des directives pré-processeur #pragma pour chaque symbole
utilisé dans le fichier source ("Hello.c").

Exemple:

#pragma comment(linker, "/EXPORT:Java_Hello_Test=_Java_Hello_Test@8")

Voici une implémentation des varargs que j'ai fait dans
le train ce soir.
Elle n'est certes pas portable, mais elle
montre le principe de fonctionnement.
Idéal pour un cours de C.
_______________________________________________________________

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

# define Xva_list long *
# define Xva_start(L, X) (L) = (((Xva_list)(&(X))) + 1)
# define Xva_arg(L, T) (*(T *)(L++))

void test(int s, ...)
{
/* on déclare un pointeur pour stocker l'adresse dans la pile */
Xva_list list;

/* on récupère l'adresse de "s" que l'on décale de x octet */
/* x est égale a la taille en octet d'un pointeur. */
Xva_start(list, s);

/* on récupère nos arguments sur la pile */
printf("a = %d\n", Xva_arg(list, int));
printf("b = %c\n", Xva_arg(list, char));
printf("c = %s\n", Xva_arg(list, char *));
}

int main()
{
int a = 42;
char b = 'a';
char *c = strdup("chaine");

test(10, a, b, c);
free(c);
return (EXIT_SUCCESS);
}

Voici une méthode pour envoyer un évènement "KeyPress" à un
contrôle C#. Elle ne nécessite pas d'avoir le "focus" sur
l'élément visé.
---------------------------------------------------------------
using System;
using System.Windows.Forms;
using System.Reflection;

namespace Test
{
public sealed class KeyEventSender
{
private KeyEventSender()
{
}

// WM_CHAR est définit dans l'API de windows
// notification d'un événement clavier
internal static readonly int WM_CHAR = 258;

public static void Send(Control control, char c)
{
Message m = new Message();
m.HWnd = (IntPtr)control.Handle; // "Handle" du contrôle.
m.Msg = KeyEventSender.WM_CHAR; // type de l'évènement windows
m.LParam = IntPtr.Zero; // pas d'option
m.WParam = (IntPtr)c; // valeur de caractère
// on récupère la définition de la méthode par réflexion
// car la méthode n'est pas publique
MethodInfo dynMethod = control.GetType().GetMethod("ProcessKeyEventArgs",
BindingFlags.NonPublic BindingFlags.Instance);
// on invoque la méthode.
dynMethod.Invoke(control, new object[] { m });
}

public static void Send(Control control, KeyPressEventArgs e)
{
KeyEventSender.Send(control, e.KeyChar);
}
}
}