Quantcast
Channel: Delphi – The Wiert Corner – irregular stream of stuff
Viewing all 1440 articles
Browse latest View live

Reading files that are locked by other references: c# – Notepad beats them all? – Stack Overflow

$
0
0

Cool feature borrowed from Notepad, which can read files locked by other references (for instance a process having the handle open): [WayBackc# – Notepad beats them all? – Stack Overflow.

The example from the answer is in .NET, but can be used in a native environment as well (Notepad is a native application).

Notepad reads files by first mapping them into memory, rather than using the “usual” file reading mechanisms presumably used by the other editors you tried. This method allows reading of files even if they have an exclusive range-based locks.

You can achieve the same in C# with something along the lines of:

using (var f = new FileStream(processIdPath, FileMode.Open, FileAccess.Read, FileShare.ReadWrite))
using (var m = MemoryMappedFile.CreateFromFile(f, null, 0, MemoryMappedFileAccess.Read, null, HandleInheritability.None, true))
using (var s = m.CreateViewStream(0, 0, MemoryMappedFileAccess.Read))
using (var r = new StreamReader(s))
{
    var l = r.ReadToEnd();
    Console.WriteLine(l);
}

Via: [WayBack] Maintaining Notepad is not a full-time job, but it’s not an empty job either – The Old New Thing

–jeroen


For my research list: Delphi and ZeroMQ

$
0
0

Last year, ZeroMQ – of late Pieter Hintjens ancestry – got a decent support library for Delphi https://github.com/grijjy/DelphiZeroMQ.

While writing, there is a reasonable chance I need to do message queue work and ZeroMQ is excellent. I’ve done MQ already in other environments with various projects involving Wintel/iSeries, WebSphere MQ (now IBM MQ, formerly MQSeries), Oracle AQ and Microsofts MSMQ stacks so I’m anxious to see if and how this works out.

via:

–jeroen

https://wiert.me/2017/05/10/one-year-ago-im-writer-and-free-software-author-pieter-hintjens-and-im-dying-of-cancer-ask-me-anything-iama/

Delphi SOAP service: only publish WSDL in RELEASE mode

$
0
0

If you want to restrict the WSDL publishing so it only is published in DEBUG mode, then add a [WayBack] TWSDLHTMLPublish to your [WayBackTWebModule descendant, then add this in the [WayBack] OnCreate event handler of that TWebModule descendant:

// Enable/disable handling of "/wsdl*" requests during DEBUG/RELEASE mode. Enabling sends them via
//  Handled := WSDLHTMLPublish1.DispatchRequest(Sender, Request, Response);
{$ifdef DEBUG}
  WSDLHTMLPublish1.WebDispatch.Enabled := True;
{$endif DEBUG}
{$ifdef RELEASE}
  WSDLHTMLPublish1.WebDispatch.Enabled := False;
{$endif DEBUG}
end;

I have limited this because there are so many hard coded strings in the TWSDLHTMLPublish, see the thread by [WayBack] Marjan Venema at [WayBack] Hide WSDL document in SOAP app – delphi

–jeroen

Reminder to self – check some of the Delphi bug reports to see of they are solved

Property/event differences between Delphi forms and frames

$
0
0

From a while back, but still interesting especially because there some differences similar to base/inherited designer objects: [WayBackvcl – How to do bulk -transformation of form to frame- in Delphi? – Stack Overflow:

Observe the differences of a Form and a Frame in your project.

First the project.dpr source:

program Project1;

uses
  Forms,
  Unit1 in 'Unit1.pas' {Form1},
  Unit3 in 'Unit3.pas' {Frame3: TFrame};

{$R *.res}

begin
  Application.Initialize;
  Application.MainFormOnTaskbar := True;
  Application.CreateForm(TForm1, Form1);
  Application.Run;
end.

Differences:

  1. Frame as a more elaborated comment to tell the IDE which designer it should use
  2. Form can be autocreate

Dfm files:

object Form1: TForm1
  Left = 0
  Top = 0
  Caption = 'Form1'
  ClientHeight = 348
  ClientWidth = 643
  Color = clBtnFace
  Font.Charset = DEFAULT_CHARSET
  Font.Color = clWindowText
  Font.Height = -11
  Font.Name = 'Tahoma'
  Font.Style = []
  OldCreateOrder = False
  PixelsPerInch = 96
  TextHeight = 13
end

and

object Frame3: TFrame3
  Left = 0
  Top = 0
  Width = 320
  Height = 240
  TabOrder = 0
end

Frame does not have these properties:

  • Caption
  • ClientHeight
  • ClientWidth
  • Color
  • Font.Charset
  • Font.Color
  • Font.Height
  • Font.Name
  • Font.Style
  • OldCreateOrder
  • PixelsPerInch
  • TextHeight

Sidenote: Frame does not have these events:

  • OnCreate
  • OnDestroy

A Frame has not global variable like this:

var
  Form1: TForm1;

And a Frame descends from TFrame, whereas a form descends from TForm.

Note: with Frame/Form inheritence, your steps become a bit longer.

–jeroen

Some of these are similar to the differences you see here:

–jeroen

Is there a reason why third party component vendors still ship dfm in binary files …

Delphi: create or append to a TFileStream

$
0
0

It looks like the Delphi [WayBackTFileStream.Create does not have an overload that allows you to create or append. Luckily, [Archive.is] TFile.Open allows you to do this when passing the correct [Archive.isTFileMode enumeration value:

TempStream := TFile.Open(TempPath, TFileMode.fmOpenOrCreate, TFileAccess.faReadWrite, TFileShare.fsRead);

I still wonder why that never made it into a TFileStream.Create overload, or why these overloads fail to use enumerations or sets of modes.

–jeroen

Embarcadero community RSS links

$
0
0

As G+ refused to put this in a comment at [WayBack] Does anybody know whether the Embarcadero blogs have got individual RSS feeds? And what’s the URL of the RSS feed for all blogs? … – Thomas Mueller (dummzeuch) – Google+:

No RSS logo is visible for me on the blog pages, but inspecting the source reveals the 404 link below; deducting from that I got 200 results:

What doesn’t work for RSS (CC +Marco Cantù) as you get 404:

  • events
  • individual questions
  • individual blog posts

Failure examples:

–jeroen


Enum values in their own namespaces/scopes: Scoped Enums (Delphi)

$
0
0

A while ago, I needed several enum types in the same unit with overlapping enumeration values.

Putting each in an encompassing type wasn’t possible and I didn’t want to put each in their own unit.

Luckily, Delphi 2009 introduced the “scoped enum” feature effectively promoting the enumeration type into a scope or namespace.

It is only available at the source code level, as – at least up until Delphi 10.1 Berlin – it is not part of the compiler settings in the project options (see screenshot below).

Since the below was hard to find combined with the word “namespace” I’ve quoted it in full:

Type
Switch
Syntax
{$SCOPEDEUNMS ON}, or {$SCOPEDENUMS OFF}
Default
{$SCOPEDENUMS OFF}
Scope
Local

Remarks

The $SCOPEDENUMS directive enables or disables the use of scoped enumerations in Delphi code. More specifically, $SCOPEDENUMS affects only definitions of new enumerations, and only controls the addition of the enumeration’s value symbols to the global scope.

In the {$SCOPEDENUMS ON} state, enumerations are scoped, and enum values are not added to the global scope. To specify a member of a scoped enum, you must include the type of the enum. For example:

type
  TFoo = (A, B, Foo);
  {$SCOPEDENUMS ON}
  TBar = (A, B, Bar);
  {$SCOPEDENUMS OFF}

begin
  WriteLn(Integer(Foo)); 
  WriteLn(Integer(A)); // TFoo.A
  WriteLn(Integer(TBar.B));
  WriteLn(Integer(TBar.Bar));
  WriteLn(Integer(Bar)); // Error
end;

Note that this is also valid:

 Writeln(Integer(TFoo.A));

Even though TFoo was not declared with $SCOPEDENUMS ON, the A value can still be explicitly resolved using the enumeration name.

–jeroen

Source: [WayBackScoped Enums (Delphi)

via [WayBackown namespace for nested enums in Delphi – Stack Overflow

The Project Options do not have a “scoped enum” entry under the compiler settings:

DUnitX: now has a WillRaiseAttribute to ease defining tests around code that should throw exceptions

$
0
0

I stumbled over this commit message in [WayBack] “extended the TestAttribute with “Expected” property (#181)” which isn’t phrased correctly, but adds a very nice feature.

The feature is about WillRaiseAttribute:

constructor WillRaiseAttribute.Create(AExpectedException: ExceptClass; const AInheritance: TExceptionInheritance);

This allows tests like these:

    [WillRaise(EOutOfMemory)]
    procedure FailMe;

    [WillRaise(EHeapException, exDescendant)]
    procedure FailMeToo;

    [WillRaise(Exception, exDescendant)]
    procedure FailAny;

    [WillRaise(EOutOfMemory)]
    [Ignore('I am not behaving as I should')]
    procedure IgnoreMeCauseImWrong;

–jeroen

How to read data from old delphi application Paradox databases without BDE?

Delphi: delete temporary file after response dispatched – Stack Overflow

$
0
0

A while ago, Marjan Venema was in need for [Archive.isDelphi SOAP: delete temporary file after response dispatched – Stack Overflow.

The solution there is a truly temporary file: a file stream that when the handle closes will have Windows delete the file by setting the correct flags.

The construct is functionally identical to the JclFileUtils.TJclTempFileStream [Archive.is].

It passes these [Archive.isfile attribute constant flags to the [Archive.isCreateFileW Windows API function:

  • FILE_ATTRIBUTE_TEMPORARY
  • FILE_FLAG_DELETE_ON_CLOSE

I was glad she asked, despite I wanted a temporary file to last after debugging, so I wrote code like this because internally the same FileGetTempName method is used by the JCL:

var
// ...
  TempPath: string;
  TempStream: TFileStream;
  TempStreamWriter: TStreamWriter;
begin
// ...
  TempPath := FileGetTempName('Prefix');
  TempStream := TFile.Open(TempPath, TFileMode.fmOpenOrCreate, TFileAccess.faReadWrite, TFileShare.fsRead);
  try
    TempStreamWriter := TStreamWriter.Create(TempStream);
    try
      TempStreamWriter.WriteLine('Debug starts:');
      MyStringList.SaveToStream(TempStream);
      TempStreamWriter.WriteLine();
// ...
      TempStreamWriter.WriteLine('Debug finishes.');
    finally
      TempStreamWriter.Free();
    end;
  finally
    TempStream.Free();
  end;

–jeroen

The Delphi VCL comes to Oxygene

$
0
0

Interesting approach, which makes one more “cross platform VCL” from outside the Embarcadero pipeline [WayBack] The Delphi VCL comes to Oxygene:

Developers switching from Delphi to Oxygene are loving our “Delphi RTL” compatibility library, as it helps them move their code over to new platforms without having to embrace all the new APIs at once. With the new Elements 10 builds we shipped in the past couple of weeks,

CrossVCL (by KSDev, the former FMX architects) already brings VCL to Linux through the Embarcadero compilers; VCL for Oxygene brings it – through the Oxygene compilers – to WebAssembly first and later to Cocoa and WPF.

I wonder how Windows VCL compatible both approaches are.

Via [WayBackhttps://blogs.remobjects.com/2018/08/30/the-delphi-vcl-comes-to-oxygene/ – Ondrej Kelle – Google+

–jeroen

How and when are variables referenced in Delphi’s anonymous methods captured? – Stack Overflow

$
0
0

Some search links on Delphi and C# WSDL imports I need to investigate further

$
0
0

Sometimes, the Delphi WSDL importer imports fine, but the generated code does not accept test cases sent by other tools.

Below are some links for messages and comment fragments that I want to investigate further.

I have included the .NET message, because my experience is that searching on those gives more accurate results for something that could be broken in more than one environment.

Based on those:

Some on-line tools prefer the WSDL to be in one document, but a lot of WSDL documents use import and or include features, so here are some links on that too:

Bruneau Babet correctly informed me that – though Delphi SOAP clients support both document literal and RPC encoded – Delphi SOAP servers cannot support document literal, as they can only support RPC encoded. Through that I found

Back on those days, the big plan was to move everything Delphi to the .NET platform which supports both document literal and RPC encoded.

All in all, document literal has been on the radar with the Delphi R&D team since at least 2009, and nothing has been done.

References:

I looks like a wsdl message request part entries need to be named parameters for some tooling to correctly infer document/literal in a wrapped way. Some links for further research on this:

When you are surely running SOAP over HTTP, you can use this small class to raise exceptions which automatically get translated into SOAP Faults having the right return code using a trick I bumped into a few years ago from [WayBack] web services – Accessing the original TWebRequest object in a Delphi SOAP Server – Stack Overflow:

unit SoapFaultWithHttpCodeExceptionUnit;

interface

uses
  System.SysUtils;

type
  ESoapFaultWithHttpCodeException = class(Exception)
  strict private
    FHttpStatusCode: Integer;
  public
    constructor Create(const AHttpStatusCode: Integer);
    property HttpStatusCode: Integer read FHttpStatusCode;
  end;

implementation

uses
  Winapi.WinHTTP,
  Soap.WebBrokerSOAP,
  Web.HTTPApp,
  IdCustomHTTPServer;

constructor ESoapFaultWithHttpCodeException.Create(const AHttpStatusCode: Integer);
var
  IdHTTPResponseInfo: TIdHTTPResponseInfo;
  ReasonString: string;
  WebDispatcher: IWebDispatcherAccess;
begin
  IdHTTPResponseInfo := TIdHTTPResponseInfo.Create(nil, nil, nil);
  try
    FHttpStatusCode := AHttpStatusCode;
    IdHTTPResponseInfo.ResponseNo := AHttpStatusCode;
    ReasonString := Format('%d: %s', [AHttpStatusCode, IdHTTPResponseInfo.ResponseText]);
    inherited Create(ReasonString);

    // https://stackoverflow.com/questions/10405227/accessing-the-original-twebrequest-object-in-a-delphi-soap-server
    if Supports(GetSOAPWebModule, IWebDispatcherAccess, WebDispatcher) then
    begin
      WebDispatcher.Response.StatusCode := HTTP_STATUS_SERVER_ERROR;
      WebDispatcher.Response.ReasonString := ReasonString;
    end

  finally
    IdHTTPResponseInfo.Free;
  end;
end;

end.

jeroen

WSDL 1.1 versus 2.0 diagram

Source: Web Services Description Language – Wikipedia

 

 

WSDL 2.0 diagrams

SOAP structure


Delphi – Defer defines the “postpone procedure” pattern to execute code at the end of a method

$
0
0

Last year, I stumbled upon [WayBack] Defer defines the “postpone procedure” pattern, this postpone should schedule a “procedure: TProc” to run it after the end of the caller method… – Cesar Romero – Google+ that points to this repository:

https://github.com/cesarliws/foundation-4-delphi

Some people like this usage of the RAII pattern, but I do like it even though I do not use it very often. The implementation better than my TAnonymousMethodMemento in Delphi: a memento that executes any code at end of method for various reasons:

Now the documentation could use more English (some of it is in Portuguese).

–jeroen

Delphi XE6 and up regression: “‘9999-12-31 23:59:59,1000’ is not a valid date and time” when passing a SOAP message with 9999-11-31T23:59:59.9999999; QC144171

$
0
0

A valid SOAP message with <urn:timeStamp>9999-11-31T23:59:59.9999999</urn:timeStamp> in a xs:dateTime field return '9999-12-31 23:59:59,1000' is not a valid date and time from a Delphi application with this SOAP response:

<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:SOAP-ENC="http://schemas.xmlsoap.org/soap/encoding/">
  <SOAP-ENV:Body>
    <SOAP-ENV:Fault>
      <faultcode>SOAP-ENV:Server</faultcode>
      <faultstring>'9999-12-31 23:59:59,1000' is not a valid date and time</faultstring>
      <faultactor/>
    </SOAP-ENV:Fault>
  </SOAP-ENV:Body>
</SOAP-ENV:Envelope>

The reason is this exception:

exception class EConvertError with message ''9999-12-31 23:59:59,1000' is not a valid date and time'.

This is from a .NET based test case passing in timeStamp = DateTime.MaxValuewhich is handled perfectly fine by other SOAP web services tested.

I know about different resolutions of time stamps, but would never expect the 999.9999 milliseconds to be rounded up to 1000 as it is always safer to truncated away from an upper limit.

A test using Soap UI [WayBack] with this parameter finally worked (max 3 digits second fraction):

<urn:timeStamp>9999-12-31T23:59:59.999</urn:timeStamp>

The true origin of problem is in this method in the Soap.XSBuiltIns unit which has been unchanged since at least Delphi 7:

function TXSBaseTime.GetMilliSecond: Word;
begin
  Result := Round(FractionalSeconds*1000);
end;

The problem exposed itself because as of Delphi XE6 the core of function TXSBaseCustomDateTime.GetAsDateTime piece was changed from

Result := EncodeDateTime(Year, Month, Day, Hour, Minute, Second, 0);

to

Result := EncodeDateTime(Year, Month, Day, Hour, Minute, Second, Millisecond);

A combination of lack of test cases and understanding XML specifications failed to reveal this bug.

The standards specify (among others):

  • '.' s+ (if present) represents the fractional seconds;
    The above is not limiting the amount of digits, not talking about milliseconds either.
  • All ·minimally conforming· processors ·must· support year values with a minimum of 4 digits (i.e., YYYY) and a minimum fractional second precision of milliseconds or three decimal digits (i.e. s.sss). However, ·minimally conforming· processors ·may· set an application-defined limit on the maximum number of digits they are prepared to support in these two cases, in which case that application-defined maximum number ·must· be clearly documented.
    Delphi not only limits the fractional second precission, it changes the limit over time and does not document the limit. Three strikes…
  • s -- represents a digit used in the time element "second". The two digits in a ss format can have values from 0 to 60. In the formats described in this specification the whole number of seconds ·may· be followed by decimal seconds to an arbitrary level of precision. This is represented in the picture by "ss.sss". A value of 60 or more is allowed only in the case of leap seconds.
    Given buggy the fractional second handling through milliseconds, the leap second handling is ripe for a test case as well.
    Strictly speaking, a value of 60 or more is not sensible unless the month and day could represent March 31, June 30, September 30, or December 31 in UTC. Because the leap second is added or subtracted as the last second of the day in UTC time, the long (or short) minute could occur at other times in local time. In cases where the leap second is used with an inappropriate month and day it, and any fractional seconds, should considered as added or subtracted from the following minute.

The reproduction is quite simple:

program TXSDateTime_DateTime_MaxValue_Bug;

{$APPTYPE CONSOLE}

uses
  SysUtils,
  XSBuiltIns;

type
  TXSCustomDateTimeHelper = class helper for TXSCustomDateTime
  public
    function MilliSecondFix: Word;
  end;

procedure Run;
var
  DateTime: TDateTime;
  MilliSecond: Word;
  XSDateTime: TXSDateTime;
begin
  XSDateTime := TXSDateTime.Create();
  try
    XSDateTime.XSToNative('9999-12-31T23:59:59.9999999'); // this is .NET DateTime.MaxValue passed over SOAP as xs:DateTime
    MilliSecond := XSDateTime.MilliSecond; // wrongly returns 1000
    MilliSecond := XSDateTime.MilliSecondFix; // Correctly returns 999
    DateTime := XSDateTime.AsDateTime; // works fine until Delphi XE5; throws exception in Delphi XE6 and up:
    // "exception class EConvertError with message ''9999-12-31 23:59:59,1000' is not a valid date and time"
  finally
    XSDateTime.Free();
  end;
end;

function TXSCustomDateTimeHelper.MilliSecondFix: Word;
begin
  Result := Millisecond;
  if (FractionalSeconds = 1000) then
    Result := 999;
end;

begin
  try
    Run();
  except
    on E:Exception do
      Writeln(E.Classname, ': ', E.Message);
  end;
end.

The leap second bug is also very easy to reproduce by replacing the core wit this, and – to no surprise – fails too:

    XSDateTime.XSToNative('9999-12-31T23:59:60.9999999'); // this allowed as per https://www.w3.org/TR/xmlschema-2/#dateTime
    // "s -- represents a digit used in the time element "second". The two digits in a ss format can have values from 0 to 60. In the formats described in this specification the whole number of seconds ·may· be followed by decimal seconds to an arbitrary level of precision. This is represented in the picture by "ss.sss". A value of 60 or more is allowed only in the case of leap seconds."
    DateTime := XSDateTime.AsDateTime; // breaks in any Delphi version with up until Delphi XE5 this message:
    // "exception class EConvertError with message '''9999/12/31 23:59:60.0'' is not a valid date and time'."
    // for Delphi XE6 and up with this message:
    // "exception class EConvertError with message ''9999-12-31 23:59:60,1000' is not a valid date and time'."

References:

jeroen

[Archive.is] Report No: 144171 Status: Reported
valid xs:DateTime boundaries (including second fractions close to 1 and leap seconds) are not handled correctly

Delphi 10 Seattle and up try “recover” unchanged files in the `$(BDS)` tree

$
0
0

Every now – after some period of inactivity – I get an error like this when not having changed the file at all:

---------------------------
Error
---------------------------
Cannot create file "C:\Program Files (x86)\Embarcadero\Studio\18.0\Source\DUnit\src\__recovery\GUITestRunner.pas". Het systeem kan het opgegeven pad niet vinden.
---------------------------
OK Details >> 
---------------------------

This has been present since ever since Delphi 10 Seattle introduced the “recovery” feature on unchanged files in a read-only directory tree like for instance $(BDS).

It assumes the __recovery subdirectory has been created (which it cannot, but never raised an error about), then barfs when it cannot find the directory.

This is a classic example of “nice idea, bad execution, not caught by thinking through all the test cases”.

I think one of the roles played, is that cursor movements are part of the undo/redo stack. My suspicion is that this raises a “changed” flag, where in fact the file is unmodified.

Het systeem kan het opgegeven pad niet vinden. is the Dutch version of ERROR_PATH_NOT_FOUND error code 0x03 English The system cannot find the path specified.

Related:

–jeroen

---------------------------
Error
---------------------------
Cannot create file "C:\Program Files (x86)\Embarcadero\Studio\18.0\Source\DUnit\src\__recovery\GUITestRunner.pas". Het systeem kan het opgegeven pad niet vinden.
---------------------------
OK Details << 
---------------------------
[5015B973]{rtl240.bpl } System.Classes.TFileStream.Create (Line 8559, "System.Classes.pas" + 8) + $45
[5015B890]{rtl240.bpl } System.Classes.TFileStream.Create (Line 8542, "System.Classes.pas" + 2) + $B
[20983E56]{coreide240.bpl} AutoRecover.TAutoRecoverModule.SaveTempFiles (Line 564, "AutoRecover.pas" + 39) + $7
[209838FC]{coreide240.bpl} AutoRecover.TAutoRecoverIDENotifier.TimerExecute (Line 395, "AutoRecover.pas" + 1) + $2
[50B5F9DF]{vcl240.bpl } Vcl.ExtCtrls.TTimer.Timer (Line 3109, "Vcl.ExtCtrls.pas" + 1) + $E
[50B5F8C3]{vcl240.bpl } Vcl.ExtCtrls.TTimer.WndProc (Line 3067, "Vcl.ExtCtrls.pas" + 4) + $7
[50170DFC]{rtl240.bpl } System.Classes.StdWndProc (Line 17187, "System.Classes.pas" + 8) + $0
[50BEEDE7]{vcl240.bpl } Vcl.Forms.TApplication.ProcessMessage (Line 10534, "Vcl.Forms.pas" + 23) + $1
[50BEEE2A]{vcl240.bpl } Vcl.Forms.TApplication.HandleMessage (Line 10564, "Vcl.Forms.pas" + 1) + $4
[50BEF15D]{vcl240.bpl } Vcl.Forms.TApplication.Run (Line 10702, "Vcl.Forms.pas" + 26) + $3

delphi – How to make a combo box with fulltext search autocomplete support? – Stack Overflow

TIdHTTPWebBrokerBridge example for a standalone Indy based SOAP service

$
0
0

Since I tend to forget what bits and pieces are needed for TIdHTTPWebBrokerBridge, the below code piece from

unit Unit2; 
 
interface 
 
uses 
  SysUtils, Classes, Windows, Messages, Variants,  Graphics, Controls, Forms, Dialogs, 
  WebReq, HTTPApp, WSDLPub, SOAPPasInv, SOAPHTTPPasInv, SOAPHTTPDisp, WebBrokerSOAP, 
  InvokeRegistry, SOAPDm, Types, OPConvert, IdHTTPWebBrokerBridge; 
 
type 
  IMyService = interface(IInvokable)         { Invokable interfaces must derive from IInvokable } 
  ['{EB829A60-4B39-4EC8-A684-E4D7BA713036}'] { Methods of Invokable interface must not use the default calling convention; stdcall is recommended } 
    function Hello: String; stdcall; 
  end; 
 
 
type 
  TMyDataModule = class(TSoapDataModule,IMyService) 
  private 
    function Hello:String; stdcall; 
  public 
end; 
 
type 
  TMyWebModule = class(TCustomWebDispatcher) 
    fHTTPSoapDispatcher    : THTTPSoapDispatcher; 
    fHTTPSoapPascalInvoker : THTTPSoapPascalInvoker; 
    fWSDLHTMLPublish       : TWSDLHTMLPublish; 
    procedure DefaultHandlerAction(Sender: TObject; Request: TWebRequest; Response: TWebResponse; var Handled: Boolean); 
  private 
 
  public 
    constructor Create(AOwner: TComponent); override; 
    destructor  Destroy;override; 
  end; 
 
type TSOAPServer = class 
  private 
    fWebModule       : TMyWebModule; 
    fWebBrokerBridge : TIdHTTPWebBrokerBridge; 
  public 
    constructor Create(AOwner: TComponent); 
    destructor  Destroy;override; 
end; 
 
implementation 
 
procedure TMyDataModuleCreateInstance(out obj: TObject); 
begin 
  obj := TMyDataModule.Create(nil); 
end; 
 
{ TMyWebModule } 
 
constructor TMyWebModule.Create(AOwner: TComponent); 
begin 
  inherited; 
 
  OldCreateOrder := False; 
  with Actions.Add do begin 
    Default  := True; 
    PathInfo := '/'; 
    Name     := 'DefaultHandler'; 
    OnAction := DefaultHandlerAction; 
  end; 
 
  fHTTPSoapDispatcher                      := THTTPSoapDispatcher.Create(nil); 
  fHTTPSoapPascalInvoker                   := THTTPSoapPascalInvoker.Create(nil); 
  fWSDLHTMLPublish                         := TWSDLHTMLPublish.Create(nil); 
 
  fHTTPSoapPascalInvoker.Converter.Options := [soSendMultiRefObj, soTryAllSchema, soRootRefNodesToBody, soCacheMimeResponse, soUTF8EncodeXML]; 
  fHTTPSoapDispatcher.WebDispatch.PathInfo := 'soap*'; 
 
  fWSDLHTMLPublish.WebDispatch.MethodType  := mtAny; 
  fWSDLHTMLPublish.WebDispatch.PathInfo    := 'wsdl*'; 
 
  fHTTPSoapDispatcher.Dispatcher           := fHTTPSoapPascalInvoker; 
end; 
 
destructor TMyWebModule.Destroy; 
begin 
  FreeAndNil(fHTTPSoapDispatcher); 
  FreeAndNil(fHTTPSoapPascalInvoker); 
  FreeAndNil(fWSDLHTMLPublish); 
  inherited; 
end; 
 
procedure TMyWebModule.DefaultHandlerAction(Sender: TObject; Request: TWebRequest; Response: TWebResponse; var Handled: Boolean); 
begin 
  fWSDLHTMLPublish.ServiceInfo(Sender, Request, Response, Handled); 
end; 
 
{ TSOAPServer } 
 
constructor TSOAPServer.Create(AOwner: TComponent); 
begin 
//  inherited Create; 
  fWebModule      := TMyWebModule.Create(AOwner); 
 
  InvRegistry.RegisterInterface(TypeInfo(IMyService)); 
  InvRegistry.RegisterInvokableClass(TMyDataModule, TMyDataModuleCreateInstance); 
 
  if WebRequestHandler <> nil then 
    WebRequestHandler.WebModuleClass := TMyWebModule; 
 
  fWebBrokerBridge := TIdHTTPWebBrokerBridge.Create(nil); 
  fWebBrokerBridge.RegisterWebModuleClass(TMyWebModule); 
  fWebBrokerBridge.DefaultPort := 1029; 
  fWebBrokerBridge.Active := True; 
end; 
 
destructor TSOAPServer.Destroy; 
begin 
  fWebBrokerBridge.Active := False; 
  FreeAndNil(fWebModule); 
  FreeAndNil(fWebBrokerBridge); 
  inherited; 
end; 
 
{ TMyDataModule } 
 
function TMyDataModule.hello: String; 
begin 
  Result := 'Hello!!'; 
end; 
 
end.

So i have a class, which can be used like this:

//...
var serv: TSOAPServer;
//...
begin
   serv := TSOAPServer.Create(Form1);
end;

–jeroen

Viewing all 1440 articles
Browse latest View live