- managed DLL mit COM-Interface am Beispiel: ComTest.dll (managed DLL in C#) und ComTestClient.exe (unmanaged C++ Exe)
- weiterführende Infos: Link
// Code für die ComTest.dll (managed Code)
using System;
using System.Runtime.InteropServices; // wichtig
namespace ComTest
{
[ComVisible(true)] // wichtig
[Guid("F3C44592-37E2-4a36-AABA-21527D3F750B")] // kann über Menü->Extras->GUID erstellen erzeugt werden
// Interface-Klasse deklarieren
public interface IClass1
{
int Add(int i1, int i2);
int Sub(int i1, int i2);
string AddString(string sZeichenfolge);
}
[ClassInterface(ClassInterfaceType.AutoDispatch)] // wichtig
public class Class1: IClass1
{
public int Add(int i1, int i2)
{
return i1 + i2;
}
public int Sub(int i1, int i2)
{
return i1 - i2;
}
public string AddString(string sZeichenfolge)
{
return sZeichenfolge + " " + sZeichenfolge;
}
}
}
- Projekt->Eigenschaften->Anwendung->Assemblyinformationen->Haken bei “Assembly COM-sichbar machen”
- Projekt->Eigenschaften->Erstellen->Haken bei “Für COM-Interop registrieren”
- DLL kompilieren -> ComTest.dll und ComTest.tlb (Interfaceinformationen) werden erstellt
- Soll die DLL auf einem anderen Rechner eingesetzt werden, müssen dort die Interfaceklassen unbedingt mit “RegAsm.exe ComTest.dll /codebase” registriert werden.
- ComTest.tlb und ComTest.dll in das Quellverzeichnis der ComTestClient.exe kopieren, bei Veränderungen am Quellcode der DLL diesen Vorgang wiederholen 🙂
// ComTestClient.exe (unmanaged Code mit COM-Interface)
#include "stdafx.h"
#import "ComTest.tlb" no_namespace //TLB mit Interface und Funktionen der ComTest.dll importieren
int _tmain(int argc, _TCHAR* argv[])
{
long iRes=0;
HRESULT hr = ::CoInitialize (NULL);
if (hr == S_OK)
{
// Zeiger auf lokales COM-Objekt der Klasse Class1 holen
IClass1Ptr pClass1 (__uuidof(Class1));
// Funktion Add() aufrufen
if (pClass1)
{
long i = pClass1->Add(1, 5);
pClass1->Release();
}
}
::CoUninitialize();
return 0;
}
- Eine zweite Möglichkeit ein COM-Objekt zu erstellen wird über CoCreateInstance() ermöglicht.
- Der Vorteil dabei ist, dass man einen global verwendbaren Zeiger erhält.
// ComTestClient.exe (unmanaged Code mit COM-Interface)
#include "stdafx.h"
#import "ComTest.tlb" no_namespace //TLB mit Interface und Funktionen der ComTest.dll importieren
int _tmain(int argc, _TCHAR* argv[])
{
long iRes=0;
IClass1 * pInterfacePtr = NULL; // Zeiger auf das Interface
HRESULT hr = ::CoInitialize (NULL);
if (hr == S_OK)
{
if (SUCCEEDED(CoCreateInstance(__uuidof(Class1), NULL, CLSCTX_ALL, __uuidof(IClass1), (LPVOID *)&pInterfacePtr)))
{
if (pInterfacePtr)
{
// Add aufrufen
long i = pInterfacePtr->Add(1, 5);
pInterfacePtr->Release();
}
}
}
::CoUninitialize();
return 0;
}
- Will man ein COM-Objekt aus einer managed DLL innerhalb einer unmanaged DLL benutzen, darf die verwaltete Ausführung (__uuidof(Class1)) nicht in der DLLMain() [InitInstance(), ExitInstance()] stattfinden, sondern nur innerhalb einer selber geschriebenen DLL-Funktion. Es kommt sonst zur Auslösung eines Haltepunktes und die Anwendung friert ein.
- Folgender Meldungstext wird dann im Ausgabefenster von VS angezeigt:
double dCurrentOATime = DateTime.Now.ToOADate(); // akt. Zeit in double OADate ausgeben
DateTime dt = DateTime.FromOADate(38699.439097227463); // 13.12.2005 10:32:18:000
DateTime dt = DateTime.Now;
string sAusgabe = dt.ToString("dd.MM.yyyy HH:mm:ss:fff"); // 01.01.2008 12:00:00:000
oder
DateTime dt = DateTime.Now;
string sDatum = dt.ToString("d"); // 01.01.2008
string sZeit = = dt.ToString("T"); // 12:00:00
- angelehnt an dieses Beispiel: Link
- Struct, das geschrieben werden soll, definieren
using System;
using System.Collections.Generic;
using System.Text;
using System.Runtime.InteropServices;
// Daten sequentiell anordnen
[StructLayout(LayoutKind.Sequential, Pack = 1, CharSet = CharSet.Ansi)]
public struct MyStruct
{
// String zum Einlesen von char-Arrays fester Länge nutzen, hier 32 Zeichen
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 32)]
public string sName;
// char-Arrays können auch wie folgt eingelesen werden
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 32)]
public char[] cName2;
// int einlesen
public Int32 iVersion;
}
- Hilfsfunktion zum konvertieren des Structes in ein Byte-Array
/// <summary>
/// object to byte[] Serialisierung
/// </summary>
/// <param name="oData">Objekt zum Serialisieren</param>
/// <returns>Objekt als byte-Array</returns>
private byte[] RawSerialize(object oData)
{
byte[] byaRawData = null;
if (oData != null)
{
int iRawSize = Marshal.SizeOf(oData);
if (iRawSize > 0)
{
byaRawData = new byte[iRawSize];
IntPtr iptrBuffer = Marshal.AllocHGlobal(iRawSize);
Marshal.StructureToPtr(oData, iptrBuffer, false);
Marshal.Copy(iptrBuffer, byaRawData, 0, iRawSize);
Marshal.FreeHGlobal(iptrBuffer);
}
}
return byaRawData;
}
public bool WriteFile(string sFileName)
{
bool bRetVal = false;
try
{
// Datei öffnen
FileStream pFileStream = new FileStream(sFileName, FileMode.OpenOrCreate);
if (pFileStream != null)
{
// binär schreiben
BinaryWriter bw = new BinaryWriter(pFileStream);
// Struct erzeugen
MyStruct stHeader = new MyStruct();
// Daten eintragen
stHeader.sName = "Horst";
stHeader.iVersion = 2;
// Dateizeiger auf Dateianfang setzen
pFileStream.Seek(0, SeekOrigin.Begin);
// Header-Struct in Byte-Array wandeln
byte[] buf = RawSerialize(stHeader);
// Byte-Arry schreiben
bw.Write(buf);
pFileStream.Close();
pFileStream.Dispose();
bRetVal = true;
}
}
catch (Exception e)
{
Console.WriteLine(e.Message);
}
return bRetVal;
}
// Spalte 1 automatisch auf Textbreite anpassen
ListView1.Columns[0].AutoResize(ColumnHeaderAutoResizeStyle.ColumnContent);
// alle Spalten im Listview anpassen
ListView1.AutoResizeColumns(ColumnHeaderAutoResizeStyle.ColumnContent);
// ListView1 -> MultiSelect == true beachten!
private void ListView1_KeyDown(object sender, KeyEventArgs e)
{
if (e.KeyCode == Keys.Delete)
{
while (ListView1.SelectedItems.Count > 0)
{
ListView1.Items.Remove(ListView1.SelectedItems[0]);
}
}
}
// KeyDown
private void MyForm_KeyDown(object sender, KeyEventArgs e)
{
switch (e.KeyCode)
{
case Keys.Up: // CRSR hoch
break;
case Keys.Down: // CRSR runter
break;
case Keys.Delete: // ENTF
break;
case Keys.Enter: // Enter
break;
case Keys.Escape: // ESC
break;
default:
break;
}
}
// KeyPress
private void MyForm_KeyPress(object sender, KeyPressEventArgs e)
{
switch (e.KeyChar)
{
case (char)(int)Keys.Up: // CRSR hoch
break;
case (char)(int)Keys.Down: // CRSR runter
break;
case (char)(int)Keys.Delete: // ENTF
break;
case (char)(int)Keys.Enter: // Enter
break;
case (char)(int)Keys.Escape: // ESC
break;
default:
break;
}
}
char[] cArray = "Hello world!".ToCharArray();