Introducción a C# para AMS: DLL/ActiveX (1)

Desarrollo de aplicaciones de escritorio C#, c++, Java, Net, VB... y todos los frameworks y tecnologías relacionadas co este tipo de aplicaciones.
Buenas, os dejo una pequeña introducción muy rapida al tema, c# es un lenguaje muy amigable y sencillo, con una curva de aprendizaje mucho mas corta de lo normal.

http://www.youtube.com/watch?v=oOf40sJyzq4 [Ver en HQ]


Y os dejo material del curso:
-Enlace al template proyecto para crear dlls: http://sites.google.com/site/robertgies ... gedexports
-Codigo a ingresar en un control windows forms (activex):

namespace ..............
[ProgId("Webcam.PablokoControl")]
[ClassInterface(ClassInterfaceType.AutoDual)]
public partial class ************ : UserControl
...

Para meter en la clase principal

/// <summary>
/// Register the class as a control and set it's CodeBase entry
/// </summary>
/// <param name="key">The registry key of the control</param>
[ComRegisterFunction()]
public static void RegisterClass(string key)
{
// Strip off HKEY_CLASSES_ROOT\ from the passed key as I don't need it
StringBuilder sb = new StringBuilder(key);
sb.Replace(@"HKEY_CLASSES_ROOT\", "");

// Open the CLSID\{guid} key for write access
RegistryKey k = Registry.ClassesRoot.OpenSubKey(sb.ToString(), true);

// And create the 'Control' key - this allows it to show up in
// the ActiveX control container
RegistryKey ctrl = k.CreateSubKey("Control");
ctrl.Close();

// Next create the CodeBase entry - needed if not string named and GACced.
RegistryKey inprocServer32 = k.OpenSubKey("InprocServer32", true);
inprocServer32.SetValue("CodeBase", Assembly.GetExecutingAssembly().CodeBase);
inprocServer32.Close();

// Finally close the main key
k.Close();
}

/// <summary>
/// Called to unregister the control
/// </summary>
/// <param name="key">Tke registry key</param>
[ComUnregisterFunction()]
public static void UnregisterClass(string key)
{
StringBuilder sb = new StringBuilder(key);
sb.Replace(@"HKEY_CLASSES_ROOT\", "");

// Open HKCR\CLSID\{guid} for write access
RegistryKey k = Registry.ClassesRoot.OpenSubKey(sb.ToString(), true);

// Delete the 'Control' key, but don't throw an exception if it does not exist
k.DeleteSubKey("Control", false);

// Next open up InprocServer32
RegistryKey inprocServer32 = k.OpenSubKey("InprocServer32", true);

// And delete the CodeBase key, again not throwing if missing
k.DeleteSubKey("CodeBase", false);

// Finally close the main key
k.Close();
}



Propiets/assamblyinfo.cs:
[assembly: ComVisible(true)]


Este codigo siempre hay que ponerlo como directiva, una vez hecho ya esta, todo funcionara correcto como en el ejemplo

Otra cosilla digna de mencionar, las dll generadas NO SE REGISTRAN CON REGSERV32 y desconozco si se puede hacer con system.registeractivex pero supongo que NO, en su lugar esas dll se registran con un exe llamado REGASM.EXE (si lo compilas con vs re registra solo) si vemos que la cosa se complica en el momento de redistribuir monto una dll para registrar estos controles
Register the DLL again using 'Regasm' as follows.

Open the DOS prompt via
"Start > Programs > Accessories > Command Prompt".
Enter the following command according to the programming language used:
C#:
"C:\WINDOWS\Microsoft.NET\Framework\v2.0.50727\regasm.exe" "<Path to the DLL>.dll" /codebase
Aqui os dejo un pequeño ejemplo de la aplicacion para la webcam de demostración recordad registrar la dll antes de debugear el proyecto

Descargar:
HIDE: ON
Hidebb Message Hidden Description


Codigo fuente (a insertar un picturebox llamado PbxImagen)
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.Data;
using System.Text;
using System.Windows.Forms;
using System.Drawing.Imaging; // Para trabajar con imágenes
using System.Drawing.Drawing2D; // Para trabajar con Imágenes
using System.Runtime.InteropServices;
using Microsoft.Win32; // Para usar API
using System.Reflection;
using System.IO;

namespace WindowsFormsControlLibrary1
{
[ProgId("Webcam.PablokoControl")]
[ClassInterface(ClassInterfaceType.AutoDual)]
public partial class UserControl1 : UserControl
{




public UserControl1()
{
InitializeComponent();
WebCam MiWebcam = new WebCam(10, PbxImagen);
}

private void UserControl1_Load(object sender, EventArgs e)
{
WebCam MiWebcam = new WebCam(10, PbxImagen);
PbxImagen.Width = this.Width;
PbxImagen.Height = this.Height;
}

public void Question()
{
MessageBox.Show("Who is number one?");
}

public string sakatambliki = "sakatambliki!";

public void Capturar(int cp)
{
WebCam MiWebcam = new WebCam(10, PbxImagen);
MiWebcam.Capturar(cp);
}

public void Parar()
{
WebCam MiWebcam = new WebCam(10, PbxImagen);
MiWebcam.StopCaptura();
}

public void Foto()
{
WebCam MiWebcam = new WebCam(10, PbxImagen);
MiWebcam.GuardarImagen();
}

public String GetWebcams()
{
WebCam MiWebcam = new WebCam(10, PbxImagen);
return MiWebcam.Listar2();
}

/// <summary>
/// Register the class as a control and set it's CodeBase entry
/// </summary>
/// <param name="key">The registry key of the control</param>
[ComRegisterFunction()]
public static void RegisterClass(string key)
{
// Strip off HKEY_CLASSES_ROOT\ from the passed key as I don't need it
StringBuilder sb = new StringBuilder(key);
sb.Replace(@"HKEY_CLASSES_ROOT\", "");

// Open the CLSID\{guid} key for write access
RegistryKey k = Registry.ClassesRoot.OpenSubKey(sb.ToString(), true);

// And create the 'Control' key - this allows it to show up in
// the ActiveX control container
RegistryKey ctrl = k.CreateSubKey("Control");
ctrl.Close();

// Next create the CodeBase entry - needed if not string named and GACced.
RegistryKey inprocServer32 = k.OpenSubKey("InprocServer32", true);
inprocServer32.SetValue("CodeBase", Assembly.GetExecutingAssembly().CodeBase);
inprocServer32.Close();

// Finally close the main key
k.Close();
}

/// <summary>
/// Called to unregister the control
/// </summary>
/// <param name="key">Tke registry key</param>
[ComUnregisterFunction()]
public static void UnregisterClass(string key)
{
StringBuilder sb = new StringBuilder(key);
sb.Replace(@"HKEY_CLASSES_ROOT\", "");

// Open HKCR\CLSID\{guid} for write access
RegistryKey k = Registry.ClassesRoot.OpenSubKey(sb.ToString(), true);

// Delete the 'Control' key, but don't throw an exception if it does not exist
k.DeleteSubKey("Control", false);

// Next open up InprocServer32
RegistryKey inprocServer32 = k.OpenSubKey("InprocServer32", true);

// And delete the CodeBase key, again not throwing if missing
k.DeleteSubKey("CodeBase", false);

// Finally close the main key
k.Close();
}









public class WebCam
{

// Conserva esta cabecera
// Empeorxdevil 2007

class SaveImage
{

// Conserva esta cabecera
// Emperorxdevil 2007

/// <summary>
/// Imagen que contiene la clase
/// </summary>
public Image Tmp; // referencia a la imagen del Picturebox Asociado
Form Ref; // referencia que necesitare a este formulario

Form Opciones;
GroupBox Gr;
RadioButton R1;
RadioButton R2;
RadioButton R3;
TextBox Porciento;
TextBox Larg; // Objetos necesarios para formar el diálogo
TextBox Anch;
Label L1;
Label L2;
Button Acepta;
Button Cancelar;

SaveFileDialog Sv;
DialogResult rs;


/// <summary>
/// Constructor de clase SaveImage
/// </summary>
/// <param name="imagen">Imagen que se va a guardar</param>
/// <param name="Interfaz"> Si se mostrará Interfaz</param>
public SaveImage(Image imagen, bool Interfaz)
{

Tmp = imagen; // Asigno la imagen...
Opciones = new Form();
Ref = Opciones;
Opciones.Width = 333; // Configuracion
Opciones.Height = 198;
Opciones.Text = "Parámetros de Imagen";
Opciones.ControlBox = false;
Opciones.ShowInTaskbar = false;
Opciones.FormBorderStyle = FormBorderStyle.FixedDialog;

Gr = new GroupBox();
Gr.Width = 288;
Gr.Height = 105;
Gr.Text = "Configuración";
Gr.Location = new Point(18, 15);
Porciento = new TextBox();
Porciento.Enabled = false; // Configuracion
Porciento.Text = "";
Porciento.Width = 37;
Porciento.Height = 20;
Porciento.MaxLength = 3;
Porciento.TextAlign = HorizontalAlignment.Center;
Porciento.Location = new Point(147, 47);
R1 = new RadioButton();
R1.Text = "Guardar con Dimensiones Actuales";
R1.Width = 191;
R1.Height = 17;
Gr.Controls.Add(R1);
R1.Location = new Point(18, 25);
R2 = new RadioButton();
R2.CheckedChanged += new EventHandler(R2_CheckedChanged);
R2.Text = "Reducir tamaño al";
R2.Width = 115; // Configuracion
R2.Height = 17;
R2.Location = new Point(18, 48);
Gr.Controls.Add(R2);
Gr.Controls.Add(Porciento);
L1 = new Label();
L1.Text = "%";
L1.AutoSize = true;
L1.Location = new Point(195, 50);
Gr.Controls.Add(L1);
R3 = new RadioButton();
R3.CheckedChanged += new EventHandler(R3_CheckedChanged);
R3.Text = "Cambiar tamaño a"; // Configuracion
R3.Width = 113;
R3.Height = 17;
R3.Location = new Point(18, 71);
Gr.Controls.Add(R3);
L2 = new Label();
L2.Text = "X";
L2.AutoSize = true;
L2.Location = new Point(195, 75);
Gr.Controls.Add(L2);
Larg = new TextBox();
Larg.Enabled = false;
Larg.Width = 37;
Larg.Height = 20;
Larg.MaxLength = 4;
Larg.TextAlign = HorizontalAlignment.Center;
Larg.Location = new Point(147, 71);
Gr.Controls.Add(Larg);

Anch = new TextBox();
Anch.Enabled = false;
Anch.Width = 37;
Anch.Height = 20;
Anch.MaxLength = 4;
Anch.TextAlign = HorizontalAlignment.Center; // Configuracion
Anch.Location = new Point(218, 70);
Gr.Controls.Add(Anch);

Acepta = new Button();
Acepta.Text = "Aceptar";
Acepta.Width = 59;
Acepta.Height = 26;
Acepta.Location = new Point(247, 133);
Acepta.Click += new EventHandler(Acepta_Click);

Cancelar = new Button();
Cancelar.Text = "Cancelar";
Cancelar.Width = 59;
Cancelar.Height = 26;
Cancelar.Location = new Point(18, 133);
Cancelar.Click += new EventHandler(Cancelar_Click);
Opciones.Controls.Add(Gr);
Opciones.Controls.Add(Acepta);
Opciones.Controls.Add(Cancelar);
Opciones.StartPosition = FormStartPosition.CenterScreen;
Opciones.ShowDialog();

}


/// <summary>
/// Constructor de la clase SaveImage sin Interfaz
/// </summary>
/// <param name="imagen">Imagen a Guardar</param>
public SaveImage(Image imagen)
{
Tmp = imagen; // Asigno la imagen...
}

void R3_CheckedChanged(object sender, EventArgs e)
{
if (this.R3.Checked) { this.Anch.Enabled = true; this.Larg.Enabled = true; }
else { this.Anch.Enabled = false; this.Larg.Enabled = false; }
}
void R2_CheckedChanged(object sender, EventArgs e)
{
if (this.R2.Checked) { this.Porciento.Enabled = true; }
else { this.Porciento.Enabled = false; }
}
void Acepta_Click(object sender, EventArgs e)
{
try
{
if (R1.Checked || R2.Checked || R3.Checked)
{

if (R2.Checked) { Tmp = Resize(Tmp, Convert.ToInt32(Porciento.Text)); }

if (R3.Checked) { Tmp = Resize(Tmp, Convert.ToInt32(Larg.Text), Convert.ToInt32(Anch.Text)); }

Sv = new SaveFileDialog(); // Creo un diálogo para salvar la imagen
Sv.Title = "Guardar imagen como..."; // Lo configuro
Sv.Filter = "Archivo JPG|*.jpg|Archivo PNG|*.png|Archivo GIF|*.gif|Mapa de Bits|*.bmp";
rs = Sv.ShowDialog(); // Lo muestro modal


if (rs == DialogResult.OK)
{

int x = Sv.FilterIndex; // Ontengo la extensión seleccionada

switch (x)
{
// Salvo la imagen en el formato deseado
case 1:
Tmp.Save(Sv.FileName, ImageFormat.Jpeg);
break;
case 2:
Tmp.Save(Sv.FileName, ImageFormat.Png);
break;
case 3:
Tmp.Save(Sv.FileName, ImageFormat.Gif);
break;
case 4:
Tmp.Save(Sv.FileName, ImageFormat.Bmp);
break;
}
Ref.Dispose(); // Cierro el formulario
}
}
else { MessageBox.Show("Al menos debe selecionar una opción", "xdevilWebCapture", MessageBoxButtons.OK, MessageBoxIcon.Warning); }

}

catch
{
MessageBox.Show("Introduzca valores válidos", "xdevilWebCapture", MessageBoxButtons.OK, MessageBoxIcon.Exclamation);
}

}
void Cancelar_Click(object sender, EventArgs e)
{
Ref.Dispose(); // Cierro el formulario
}



/// <summary>
/// Escala la imagen capturada al % especificado
/// </summary>
/// <param name="foto"> Imagen que se quiere escalar </param>
/// <param name="TantoPorCiento"> Porcentaje de reducción</param>
/// <returns>Imagen escalada</returns>
public Image Resize(Image foto, int TantoPorCiento)
{

float Tporciento = ((float)TantoPorCiento / 100); // Obtengo el coeficiente de dimension
int ImgOrAncho = foto.Width;
int ImgOrAlto = foto.Height; // Obtengo las dimensiones originales de la foto

int OrigX = 0;
int OrigY = 0;
int ResX = 0; // Variables referencia para saber donde empiezo a contar px
int ResY = 0;

int ResAncho = (int)(ImgOrAncho * Tporciento);
int ResAlto = (int)(ImgOrAlto * Tporciento); // Obtengo las dimensiones al % especificado

Bitmap RszIm = new Bitmap(ResAncho, ResAlto, PixelFormat.Format24bppRgb); // Creo una imagen con esas dimensiones y bpp
RszIm.SetResolution(foto.HorizontalResolution, foto.VerticalResolution); // Le doy la misma res. que la original

Graphics Gfoto = Graphics.FromImage(RszIm); // Creo una intancia de Graphics para manejar la imagen nueva
Gfoto.InterpolationMode = InterpolationMode.HighQualityBicubic; // Especifico la calidad del algoritmo de sampleo
// De la foto original, obtengo la redimensionada (mediante un rectángulo)
Gfoto.DrawImage(foto, new Rectangle(ResX, ResY, ResAncho, ResAlto), new Rectangle(OrigX, OrigY, ImgOrAncho, ImgOrAlto), GraphicsUnit.Pixel);
Gfoto.Dispose(); // Ya no me hace falta esto, asi que lo descargo

return (RszIm); // Devuelvo la imagen redimensionada
}
/// <summary>
/// Redimensiona la imagen en pixeles
/// </summary>
/// <param name="foto"> Imagen a redimensionar</param>
/// <param name="ancho">Ancho de la imagen</param>
/// <param name="alto">Alto de la imagen</param>
/// <returns>Imagen redimensionada</returns>
public Image Resize(Image foto, int ancho, int alto)
{

int ImgORAncho = foto.Width;
int ImgOrAlto = foto.Height; // Obtengo las dimensiones de la foto

int OrigX = 0;
int OrigY = 0;
int ResX = 0; // Varables referencia para saber donde contar px
int ResY = 0;

float Porciento = 0;
float PorcientoAncho = 0; // Porcentajes de sampleo
float PorcientoAlto = 0;

PorcientoAncho = ((float)ancho / (float)ImgORAncho);
PorcientoAlto = ((float)alto / (float)ImgOrAlto); //Calculo el % que puedo resamplear

if (PorcientoAlto < PorcientoAncho)
{
Porciento = PorcientoAlto;
}
else
{ // Para resamplear bien
Porciento = PorcientoAncho;
}


int AnchuraFinal = (int)(ImgORAncho * Porciento);
int AlturaFinal; // Calculo las nuevas dimensiones

if (ancho > alto)
{
AlturaFinal = (int)(ImgOrAlto * Porciento);
}
else
{
AlturaFinal = AnchuraFinal;
} // Para proporcionar la imagen

Bitmap RszIm = new Bitmap(ancho, alto, PixelFormat.Format24bppRgb);
RszIm.SetResolution(foto.HorizontalResolution, foto.VerticalResolution);

Graphics Gfoto = Graphics.FromImage(RszIm);
Gfoto.InterpolationMode = InterpolationMode.HighQualityBicubic;
Gfoto.DrawImage(foto, new Rectangle(ResX, ResY, AnchuraFinal, AlturaFinal), new Rectangle(OrigX, OrigY, ImgORAncho, ImgOrAlto), GraphicsUnit.Pixel);
Gfoto.Dispose();
return (RszIm);

}

} /// Clase que guarda las imágenes

#region Funciones API

[DllImport("user32.dll", EntryPoint = "SendMessage")]
public static extern int SendMessage(int hWnd, uint Msg, int wParam, int lParam);

[DllImport("avicap32.dll", EntryPoint = "capCreateCaptureWindowA")]
public static extern int capCreateCaptureWindowA(string lpszWindowName, int dwStyle, int X, int Y, int nWidth, int nHeight, int hwndParent, int nID);

[DllImport("user32.dll", EntryPoint = "EmptyClipboard")]
public static extern int EmptyClipboard();

[DllImport("avicap32.dll", EntryPoint = "capGetDriverDescriptionA")]
public static extern bool capGetDriverDescriptionA(int wDriverIndex, [MarshalAs(UnmanagedType.VBByRefStr)]ref String lpszName, int cbName, [MarshalAs(UnmanagedType.VBByRefStr)] ref String lpszVer, int cbVer);
//public static extern bool capGetDriverDescriptionA(int wdriver, string lpszName, int cbName, string lpszVer, int cbVer);


#endregion

#region Constantes API

const int WM_USER = 1024;
const int WM_CAP_CONNECT = 1034;
const int WM_CAP_DISCONNECT = 1035;
const int WM_CAP_GET_FRAME = 1084; // Constantes necesarias
const int WM_CAP_COPY = 1054;
const int WM_CAP_START = WM_USER;
const int WM_CAP_SET_PREVIEW = WM_CAP_START + 50;
const int WM_CAP_SET_PREVIEWRATE = WM_USER + 52;

#endregion

private int Frecuencia = 0; // Frecuencia de captura de imagen
private int CamHandle = 0; // Handle del dispositivo


Timer reloj; // Timer
//ComboBox Cbx; // Para referencia al Combobox
PictureBox Pbx; // Para referencia al Picturebox


/// <summary>
/// Crea una instancia de la clase WebCam de Emperorxdevil
/// </summary>
/// <param name="frecuencia">Intervalo de tiempo en el que se capturarán las imágenes</param>
/// <param name="pct">PictureBox en el cual se mostrará lo capturado por la webcam</param>
/// <param name="cb"> Listbox en el que se mostrarán las Cams detectadas</param>
public WebCam(int frecuencia, PictureBox pct)
{

Frecuencia = frecuencia;
Pbx = pct; // Inicio las variables necesarias
reloj = new Timer(); // Creo el timer
reloj.Tick += new EventHandler(reloj_Tick); // Le asigno el controlador
reloj.Interval = Frecuencia; // Asigno la velocidad de captura
//Cbx = cb; //Configuro la referencia

}

private void reloj_Tick(object sender, EventArgs e)
{

SendMessage(CamHandle, WM_CAP_GET_FRAME, 0, 0); // Capturo la imagen
SendMessage(CamHandle, WM_CAP_COPY, 0, 0); // La copio al portapeles
SaveImage k = new SaveImage(Clipboard.GetImage());

Pbx.Image = k.Resize(Clipboard.GetImage(),Pbx.Width,Pbx.Height); // La mando al Picturebox

EmptyClipboard(); // Vacío el portapapeles

}
/// <summary>
/// Listará las cámaras web encontradas en el Combobox asociado
/// </summary>
/*public void Listar()
{

bool valor;
string nombre;
string version;
int x = 0;

do
{
nombre = "Dispositivo" + x.ToString();
valor = capGetDriverDescriptionA(x, nombre, 100, "v1", 100);
if (valor) Cbx.Items.Add(nombre);
x = x + 1;
} while (!valor == false);

}*/
/*public void Listar()
{
bool valor;
String nombre = "".PadRight(100); ;
String version = "".PadRight(100); ;
int x = 0;
do
{
valor = capGetDriverDescriptionA(x, ref nombre, 100, ref version, 100);
if (valor)
{
Cbx.Items.Add(nombre + " " + version);
}

x = x + 1;

} while (!valor == false);
}*/

public String Listar2()
{
bool valor;
string gg = "";
String nombre = "".PadRight(100); ;
String version = "".PadRight(100); ;
int x = 0;
do
{
valor = capGetDriverDescriptionA(x, ref nombre, 100, ref version, 100);
if (valor)
{

gg = gg + nombre + " " + version + "|";
//Cbx.Items.Add(nombre + " " + version);
}

x = x + 1;

} while (!valor == false);
return gg;
}
/// <summary>
/// La WebCam se Encenderá y mostrará lo capturado en el PictureBox referenciado
/// </summary>
/// <param name="Dispositivo">Nº de dispositivo del Combobox</param>
public void Capturar(int Dispositivo)
{

try
{

StopCaptura(); // Antes de nada "reseteo" el dispositivo
CamHandle = capCreateCaptureWindowA("XdevilCapture" + Dispositivo.ToString(), 0, 0, 0, 0, 0, Pbx.Handle.ToInt32(), Dispositivo); // Obtengo el Handle de la cam
SendMessage(CamHandle, WM_CAP_CONNECT, 0, 0); // Enciendo la cam
SendMessage(CamHandle, WM_CAP_SET_PREVIEWRATE, 30, 0); // Establezco los frames de captura /seg
SendMessage(CamHandle, WM_CAP_SET_PREVIEW, 0, 0); // Empiezo a capturar
reloj.Start(); // Inicio la captura
}

catch (Exception e)
{
MessageBox.Show("No se puede Iniciar la WebCam", "Error");
}

}
/// <summary>
/// Detiene la captura de imágenes y apaga la WebCam
/// </summary>
public void StopCaptura()
{


try
{
reloj.Stop(); // Paro el reloj que captura
SendMessage(CamHandle, WM_CAP_DISCONNECT, 0, 0); // Apago la cam
EmptyClipboard(); // Vacío el portapapeles
}

catch (Exception e) { } // No hago nada.. pa k ?

}
/// <summary>
/// Muestra diferentes parametros configurables de la imagen que se va a guardar
/// </summary>
public void GuardarImagen()
{

SaveImage s = new SaveImage(Pbx.Image, true); // Creo la clase que guarda las fotos
}


}


}
}
ese tioooooooooooooooooooooooooooooooooo!!!!!!!!!!

chicas risas... lo primero de todo tener listo un buen chusto!!!
whoooooooo amigo sin duda eres el mejor este video es de puta madre, lastima que no se pudo ver bien, pero es un gran trabajo, felicidades y espero continúes con algunos videos.
Buenas, acabo de terminar el interfaz de eventos para controles activex creados en c#

Justo ademas de añadir la directiva comvisible(true) en propiets/assamblyinfo.cs y de añadir las dos funciones de com dispatch:

///	<summary>
/// Register the class as a control and set it's CodeBase entry
/// </summary>
/// <param name="key">The registry key of the control</param>
[ComRegisterFunction()]
public static void RegisterClass(string key)
{
// Strip off HKEY_CLASSES_ROOT\ from the passed key as I don't need it
StringBuilder sb = new StringBuilder(key);
sb.Replace(@"HKEY_CLASSES_ROOT\", "");

// Open the CLSID\{guid} key for write access
RegistryKey k = Registry.ClassesRoot.OpenSubKey(sb.ToString(), true);

// And create the 'Control' key - this allows it to show up in
// the ActiveX control container
RegistryKey ctrl = k.CreateSubKey("Control");
ctrl.Close();

// Next create the CodeBase entry - needed if not string named and GACced.
RegistryKey inprocServer32 = k.OpenSubKey("InprocServer32", true);
inprocServer32.SetValue("CodeBase", Assembly.GetExecutingAssembly().CodeBase);
inprocServer32.Close();

// Finally close the main key
k.Close();
}

/// <summary>
/// Called to unregister the control
/// </summary>
/// <param name="key">Tke registry key</param>
[ComUnregisterFunction()]
public static void UnregisterClass(string key)
{
StringBuilder sb = new StringBuilder(key);
sb.Replace(@"HKEY_CLASSES_ROOT\", "");

// Open HKCR\CLSID\{guid} for write access
RegistryKey k = Registry.ClassesRoot.OpenSubKey(sb.ToString(), true);

// Delete the 'Control' key, but don't throw an exception if it does not exist
k.DeleteSubKey("Control", false);

// Next open up InprocServer32
RegistryKey inprocServer32 = k.OpenSubKey("InprocServer32", true);

// And delete the CodeBase key, again not throwing if missing
k.DeleteSubKey("CodeBase", false);

// Finally close the main key
k.Close();
}


Ademas de añadir sobre la clase principal la descripcion com (con una pequeña modificación):

    [ProgId("Webcam.PablokoControl")]
[ClassInterface(ClassInterfaceType.AutoDual)]
[ComSourceInterfaces(typeof(IControlEvents))] //cosa nueva para el interfaz de eventos
public partial class UserControl1 : UserControl..................


ya podemos exportar propiedades

//dentro de la clase principal
//ams: qinterface.sakatambliki="..." o x=qinterface.sakatambliki
public string sakatambliki = "sakatambliki!";


o metodos

//dentro de la clase principal
//ams: qinterface:Question() / x=qinterface:Question(args...)
public void Question()
{
MessageBox.Show("Who is number one?");
}


Una vez que tenemos todo esto claro, hay que añadir una serie de funcionalidades al proyecto para usar las clases.

Lo primero de todo es crear una clase contenedora de eventos, podreis utilizar esto como plantilla:

//en el namespace

[ComVisible(true)]
[InterfaceTypeAttribute(ComInterfaceType.InterfaceIsIDispatch)]
public interface IControlEvents
{

[DispId(1)]
void Bark(string callData);

[DispId(2)]
void Howl(string callData, string dos);

[DispId(3)]
void Eat();

}


Aqui definimos la clase exportada de eventos, pero estos eventos usan argumentos que deben pasar por otro objeto, asi que por cada evento se debe crear un "handler" que pueda hacer su interfaz, esto es, pegar justamente despues del codigo anterior esto:

    [ComVisible(false)]
public delegate void ControlEventHandler(string callData);

[ComVisible(false)]
public delegate void ControlEventHandler2(string callData, string dos);

[ComVisible(false)]
public delegate void ControlEventHandler3();


Esto nos proporcionará la capacidad de mandar argumentos en nuestro evento, hecho esto, ya solo nos queda dejar disponible este interfaz en la clase principal, asi que hay que insertar un poco de codigo en la clase principal:

        public event ControlEventHandler Bark;

public event ControlEventHandler2 Howl;

public event ControlEventHandler3 Eat;


Como veis este codigo conecta el handler con la funcion definida en la clase, una vez hecho esto ya esta disponible en nnuestra clase principal las funciones, en este caso bark, howl y eat

ejemplo de uso:

        public void Question()
{
//Hacer alguna tarea
Bark("proceso finalizado");
}
result = ActiveX.CreateControl("Plugin1", "Webcam.PablokoControl");
result1 = ActiveX.QueryInterface("Plugin1");
ActiveX.SetEnabled("Plugin1", true);

Events = {}
  function Events:Bark(a)
  	Dialog.Message('',a)
  end
  function Events:Howl(a,b)
  	Dialog.Message(b,a)
  end
  function Events:Eat()
  	Dialog.Message('','')
  end


ActiveX.JoinEvents("Plugin1", result1, Events);
Este plugin activex no soporta la interfaz de tablas, o por lo menos aun no he podido descubrir el tipo de valor asociado a la tabla, asi que de momento con usar strings, ints y longs deberia ser suficiente

Con esto tenemos un acercamiento mucho mas que decente al sistema de objetos de ams
RegisterActiveXCS.dll

Esta libreria consta de una funcion para registrar los activex dll, ya que con el metodo regserv32 o system.registeractivex no funciona me he decidido a idear algun sistema facil de redistribuir para realizar el registro de librerias.
function RegisterActivexCS(val)
	DLL.CallFunction("AutoPlay\\Docs\\RegisterActivexCS.dll", "Register", "\""..val.."\"", DLL_RETURN_TYPE_STRING, DLL_CALL_STDCALL);
end

RegisterActivexCS(_SourceFolder.."\\AutoPlay\\Docs\\MyLibrary.dll")
De este modo se pueden guardar todas las librerias y sus assamblys dependientes en una sola carpeta y registrarlas para su uso con el objecto activex

http://www.multiupload.com/1BV28RS9KU

UPDATE: Codigo fuente, que no se me olvide!
using System;
using System.Collections.Generic;
using System.Text;
using RGiesecke.DllExport;
using System.Diagnostics;
using System.Windows.Forms;

namespace RegisterActivexCS
{
internal static class UnmanagedExports
{
[DllExport("Register", CallingConvention = System.Runtime.InteropServices.CallingConvention.StdCall)]
static void reg(string file)
{
string cor=System.Runtime.InteropServices.RuntimeEnvironment.GetRuntimeDirectory();
cor += "regasm.exe";

ProcessStartInfo startInfo = new ProcessStartInfo(cor);
startInfo.WindowStyle = ProcessWindowStyle.Hidden;

Process.Start(startInfo);
startInfo.Arguments = "\"" + file + "\" /codebase";

Process.Start(startInfo);
}


[DllExport("Unregister", CallingConvention = System.Runtime.InteropServices.CallingConvention.StdCall)]
static void unreg(string file)
{
string cor = System.Runtime.InteropServices.RuntimeEnvironment.GetRuntimeDirectory();
cor += "regasm.exe";

ProcessStartInfo startInfo = new ProcessStartInfo(cor);
startInfo.WindowStyle = ProcessWindowStyle.Hidden;

Process.Start(startInfo);
startInfo.Arguments = "\"" + file + "\" /unregister";

Process.Start(startInfo);
}
}
}
brutal tengo que mirarmelo con calmaaaaaaa! eres el puto!
ya me he mirado el video y me a gustado mucho ahora cuendo tenga algo de tiempo voy a ponerlo en practica y crear mi primera DLL. muchas gracias como siempre pabloko te superas!!!!
excelente!! mil gracias !man aportazo!!!!!! sigue asi T_T!! :DDDD
he fijado este tema por que considero que es de lo mejor por no decir directamente lo mejor de esta sección.
amigo crees mejor calidad de video porque definitivamente no puedo leerlo una influye mucho mi ceguera y otra la calidad espero tu respuesta y gracias-
el vídeo en youtube se puede ver a 720... creo que eso es una buena calidad.
si vas a youtube se ve en HD, ceone ya podrias mejorar el player del bbcode, ahora youtube ya no inserta los video con flash y object sino que lo hace con otro metodo y un iframe, sobre todo para navegadores con html5 y toda la pesca http://apiblog.youtube.com/2010/07/new- ... ideos.html

Tengo noticias nuevas acerca de esto, ya he podido interfacear tablas en controles activex, solo me falta limar un poco y lo subo para todos.

Ademas en otro pequeño tutorial os voy a enseñar a debuguear con visual studio dlls y controles activex
genial alfi puedo leerlo jajaja gracias y si podrias redimencionar al menos las medidas del bbcode para una mejor calidad.
estamos en ello...
thank you

:cerrado: :cerrado: :cerrado:
gra
Buenisimo tutorial , despues de intentar realizar un proyecto siguiendo los pasos de san pabloko de asis XD , me encontre con que no habia manera de registrar el active a pesar de seguir los pasos al pie de la letra , asi que le di la vara a pabloko y resulta que obvio un paso en visual studio para que se produzca el registro del activex , debeis activar la casilla en proyecto/propiedades/generar/Registrar para interporalidad com si no haceis esto os volbereis locos como yo :hypno: , una imagen de muestra.

Imagen
start learn.................
thanks
Gracias por el curso.