Delphi-PRAXiS

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   Object-Pascal / Delphi-Language (https://www.delphipraxis.net/32-object-pascal-delphi-language/)
-   -   Delphi Parameterprobleme beim Aufruf einer Delphi DLL aus C# (https://www.delphipraxis.net/142337-parameterprobleme-beim-aufruf-einer-delphi-dll-aus-c.html)

JonnyGuitar 26. Okt 2009 15:11


Parameterprobleme beim Aufruf einer Delphi DLL aus C#
 
Hallo,

ich bastel momentan an einem COM-Addin für Outlook welches in C# entsteht.
Lange Rede kurzer Sinn, ich komme nicht drum herum 3-4 mit der Extended Mapi
zu machen. Das geht aber nur in C++ oder Delphi.
Also wollte ich eine Delphi DLL schreiben die die gebrauchten Funktionen liefert.
Damit das funktioniert muss der DLL die Session übergeben werden.

Delphi-Quellcode:
IntPtr ms = Marshal.GetIUnknownForObject(olNamespace.Session.MAPIOBJECT);
Damit bekomme ich einen Pointer auf ein IUnknown Object.

Was muss jetzt in dem function Kopf in der Delphi DLL stehen damit das übergeben wird.
Desweiteren würde ich gerne wissen wie ich danach wieder auf das IUnknown Objekt in Delphi
zugreifen kann. Ich weiss lediglich das wenn ich das IUnknown Projekt habe ich
mit IUnknown.QueryInterface wieder an die gewollte IMapiSession komme,


Im Voraus schonmal Danke

Jonny

Chris.R 26. Okt 2009 15:19

Re: Parameterprobleme beim Aufruf einer Delphi DLL aus C#
 
Hallo,

das Interface IMAPISession muss in Delphi definiert sein. Wenn das der Fall ist einfach so:

Delphi-Quellcode:
procedure DllProc(AMAPIObject: IUnknown); stdcall;
var
  AMAPISession: IMAPISession;
begin
  AMAPISession := AMAPIObject as IMAPISession;

end;
Wenn du sicher bist, ein Interface eine bestimmte Schnittstelle unterstützt, dann kannst du den as Operator verwenden.
Wenn du nicht sicher bist, ob ein Interface eine bestimmte Schnittstelle unterstützt, dann verwende QueryInterface.

Ciao Chris

JonnyGuitar 26. Okt 2009 16:08

Re: Parameterprobleme beim Aufruf einer Delphi DLL aus C#
 
Hallo,

ich habe das jetzt mal so eingebaut wie du geschrieben hast, leider bekomme
ich schon beim Aufruf einen Fehler (SEHException).

Gibt es eine Möglichkeit das zu debuggen in Delphi (An Prozess hängen hat nicht geklappt)

Ich habe das versucht so zu implementieren:

in C#
Delphi-Quellcode:
[DllImport("eMapi.dll")]
public static extern void Profile(IntPtr ms);
in Delphi:
Delphi-Quellcode:
procedure Profile(ms: IUnknown); stdcall;
Noch ne Idee was daran falsch ist?


Gruss Jonny

Chris.R 27. Okt 2009 07:53

Re: Parameterprobleme beim Aufruf einer Delphi DLL aus C#
 
Hallo,

was machst du den in der Delphi Funktion? Zeig mal den Code. Der Aufruf der Funktion selbst sollte problemlos funktionieren,
hab ich grad selbst aus probiert.

Code:
[DllImport("TestDll.DLL")]

private static extern void TestProc(IntPtr Intf);


private void button5_Click(object sender, EventArgs e)
{
    IntPtr intf = Marshal.GetIUnknownForObject(this);
    TestProc(intf);
}
Delphi-Quellcode:
procedure TestProc(Intf: IUnknown); stdcall;
begin
  ShowMessage('TestProc: ' + IntToStr(Integer(Intf)));

end;


exports
  TestProc;
Bei mir kommt dann die MessageBox. Also muss in der Funktion irgendwas schief laufen.

Ciao chris

JonnyGuitar 27. Okt 2009 09:08

Re: Parameterprobleme beim Aufruf einer Delphi DLL aus C#
 
Guten Morgen,

erstmal vielen Dank für deine Hilfe.

Du hast vollkommen Recht, der Aufruf klappt auch, der Fehler liegt in der Delphi Funktion.

Die sieht momentan so aus:

Delphi-Quellcode:
procedure getProfile(Intf: IUnknown); stdcall;
const
  pbGlobalProfileSectionGuid : TMAPIUID = (ab:($13,$DB,$B0,$C8,$AA,$05,$10,$1A,$9B,$B0,$00,$AA,$00,$2F,$C4,$5A));
var
  lppprofsect    : Iprofsect;
  propProfile    : PSPropValue;
  value          : String;
  hr             : Hresult;
  session        : IMapiSession;
begin
  value:='';
 
  Intf.QueryInterface(IID_IMAPISession, session);
 
  hr:=session.OpenProfileSection(PMAPIUID(@pbGlobalProfileSectionGuid),IID_IProfSect,MAPI_MODIFY, lppProfSect);
  if (hr = S_OK) then
  begin
    propprofile:=nil;

    if (S_OK = HrGetOneProp(lppprofsect, PR_PROFILE_NAME, propProfile)) then
      value:=propprofile.value.lpsza;

    if assigned(propprofile) then
      mapifreebuffer(propprofile);
    propprofile:=nil;
  end
  else
    if (hr<>S_OK) then
    begin
    end;

  lppProfSect:=nil;

  ShowMessage(value);
end;
Problem ist das folgende Zeile NICHT die IMapiSession liefert.
Delphi-Quellcode:
Intf.QueryInterface(IID_IMAPISession, session);
Deswegen knallt es dann bei "OpenProfileSection" weil session nil ist.

Hast du da noch eine Idee woran das liegen könnte?

Ausserdem möchte ich eine andere Fragen nochmal wiederholen, ist es möglich
die Delphi DLL zu debuggen? Es ist immer relativ umständlich zu Debugzwecken
immer MessageBoxen einzubauen.



Gruss Jonny

Elvis 27. Okt 2009 09:27

Re: Parameterprobleme beim Aufruf einer Delphi DLL aus C#
 
Interfaces zwischen .Net und Delphi auszutauschen ist gar nicht so schwer wie man denkt...
Delphi Library:
Delphi-Quellcode:
library CSharpInterop;

uses SysUtils;

type
  ISampleInterface = interface
  ['{4B82AD42-D2B9-4ED7-82D9-87E8EA471923}']
    function GetSomeValue : Integer; safecall;
  end;

  function TakesManagedInterface(const aInstance : IUnknown) : Integer; stdcall; export;
  var
    typedInstance : ISampleInterface;
  begin
    if Supports(aInstance, ISampleInterface, typedInstance) then
      result := typedInstance.GetSomeValue()
    else
      result := -1;
  end;

exports
  TakesManagedInterface;
end.
C# App:
Code:
[ComVisible(true)]
[Guid("4B82AD42-D2B9-4ED7-82D9-87E8EA471923"),
 InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
public interface ISampleInterface
{
   int GetSomeValue();
}

class SampleClass : ISampleInterface
{
   public int Value { get; set; }

   public int GetSomeValue()
   {
      return Value;
   }
}

class Program
{
   [DllImport("CSharpInterop",
              EntryPoint = "TakesManagedInterface",
              CallingConvention = CallingConvention.StdCall)]
   static extern int CallDelphiFunction(IntPtr instance);

   static void Main(string[] args)
   {
      var instance = new SampleClass { Value = 123456 };
      var value = CallDelphiFunction(Marshal.GetIUnknownForObject(instance));
   }
}
Wichtig sind eigentlich nur die Attribute über dem Interface.
Die GUID darf keine Curlies enthalten, und das Interface muss als IUnknown deklariert sein. Sonst springst du in die falschen Method slots.
Außerdem muss es ComVisible sein, sonst wird dir die CLR keinen RuntimecallableWrapper drumrum bauen.
Also das Ergebnis von "Marshal.GetIUnknownForObject", was du aus Delphi sehen würdest.

Chris.R 27. Okt 2009 09:41

Re: Parameterprobleme beim Aufruf einer Delphi DLL aus C#
 
Hallo,

du kannst unter Start->Parameter die .Net Anwendung als Hostanwendung anzugeben und dann das Delphi Dll Projekt starten.

Dann scheint das IUnkown Interface die Schnittstelle IMAPISession nicht zu unterstützen. Bist du sicher,
dass das MAPIOBJECT wirklich die IMAPISession unterstützt? Vielleicht kannst du auch das Session Objekt
direkt als IDispatch Objekt zurück liefern (wenn das Session Objekt IDispatch unterstützt) und per Latebinding zugreifen.

Delphi-Quellcode:
procedure TestProc(Intf: IDispatch);
var
  ov: OleVariant;
begin
  ov := Intf;

  ov.IrgendeinePropOderProc;
end;
Ciao Chris

JonnyGuitar 27. Okt 2009 11:18

Re: Parameterprobleme beim Aufruf einer Delphi DLL aus C#
 
Hallo,

das holen der IMapiSession wird unterstützt.
Ich habe hier ein Beispiel in C++ von CodeProject was funktioniert:

Delphi-Quellcode:
String^ Fabric::GetProfileName(Object^ mapiObject)
   {

      // the result returned to the calling method
      String^ result = nullptr;

      // pointer to IUnknown interface
      IUnknown* pUnknown = 0;

      // pointer to the MAPI session interface
      LPMAPISESSION lpMAPISession = 0;

      // pointer to a profilesection interface
      LPPROFSECT lpProfileSection = 0;

      // pointer to a structure that receives the result from HrGetOneProp
      LPSPropValue lpSPropValue = 0;

      // if we have no MAPIObject everything is senseless...
      if (mapiObject == nullptr)
         throw gcnew System::ArgumentNullException ("mapiObject","The MAPIObject must not be null!");

      try
      {
         // retrive the IUnknon Interface from our MAPIObject comming from Outlook.
         pUnknown = (IUnknown*)Marshal::GetIUnknownForObject(mapiObject).ToPointer ();
         
         // try to retrieve the IMAPISession interface, if we don't get it, everything else is sensless.
         if ( pUnknown->QueryInterface (IID_IMAPISession, (void**)&lpMAPISession) != S_OK)
            throw gcnew Exception("QueryInterface failed on IUnknown for IID_IMAPISession");
                    
         // use the OpenProfileSection of the MAPISession object to retrieve a pointer to the current profilesection interface
         if( lpMAPISession->OpenProfileSection((LPMAPIUID)GLOBAL_PROFILE_SECTION_MAPIUID ,NULL,STGM_READ, &lpProfileSection) != S_OK)
            throw gcnew Exception("OpenProfileSection method failed!");

         // use the HrGetOneProp method to retrieve the profile name property
         // the lpPropValue pointer points to the result value
         if (HrGetOneProp(lpProfileSection,PR_PROFILE_NAME_W,&lpSPropValue) != S_OK)
            throw gcnew Exception("HrGetOneProp failed for property PR_PROFILE_NAME_W !");
         
         // create a managed string from the unmanaged string.
         return gcnew String(  lpSPropValue->Value.lpszW );

      }
      catch (Exception^ ex)
      {
         // if an error occures get the MAPI error code
         DWORD dw = GetLastError();
         
         // and translate it into a human readable message
         if (dw != 0) throw gcnew Exception(GetErrorText(dw),ex);
         
         throw ex;
      }
      finally
      {
         // Free buffer allocated by MAPI
         if (lpSPropValue != 0) MAPIFreeBuffer(lpSPropValue);

         // cleanup all references to MAPI Objects
         if (lpProfileSection!=0) lpProfileSection->Release();
         if (lpMAPISession!=0) lpMAPISession->Release();
         if (pUnknown!=0) pUnknown->Release();
      }
   }
Das IUnknown kommt auch nach meiner Erkenntnis richtig in Delphi an, zumindest das Handle stimmt in C# und Delphi
überein. Das QueryInterface liefert den Mapi Fehler: MAPI_E_CALL_FAILED
In C++ klappt es komischerweise :(


Gruss Jonny

JonnyGuitar 28. Okt 2009 10:10

Re: Parameterprobleme beim Aufruf einer Delphi DLL aus C#
 
Moin,

ich möchte mich erst nochmal für eure Hilfe bedanken.

Desweiteren habe ich die Lösung für das Problem gefunden.
Man musste vorher noch ein MapiInitialize aufrufen da sich
der Aufruf "ausserhalb" befindet. Nun klappt alles :)


Gruss Jonny


Alle Zeitangaben in WEZ +1. Es ist jetzt 20:37 Uhr.

Powered by vBulletin® Copyright ©2000 - 2024, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024 by Thomas Breitkreuz