Getting error as "Class not Registered" - delphi

I am trying to create an Active X DLL in delphi.
I have added a TLB file have an Interface with a CoClass declaration and I have implemented the interface in some another unit.
The DLL was successfully built and registered too.
But, when I am trying to use the DLL for creating and calling methods from the another project it is showing error as Class Not Registered.
I am trying to get some information about the same but not able to get answer.
Here is a interface implementation code for the DLL:
unit uinfComTestProject;
interface
uses
ComTestProject_TLB;
Type
TComTestProject = class(TInterfacedObject,ITestComCall)
public
procedure CreateAndShowMyData();safecall;
end;
implementation
{ TComTestProject }
uses
ufTestProjectForm;
procedure TComTestProject.CreateAndShowMyData;
var
frm: TForm1;
begin
frm := TForm1.Create(nil);
try
frm.ShowModal();
finally
frm.Free;
end;
//
end;
end.
Snapshot for the TLB file definition:
Method to create an instance of the interface.
procedure TForm1.Button1Click(Sender: TObject);
var
LCOm: ITestComCall;
begin
LCOm := CoTComTestProject.Create();// getting error at this point
try
LCOm.CreateAndShowMyData;
finally
LCOm := nil;
end;
end;
Can anyone please help me on same?
Please let me know if had made any mistake in the same.
Thanks in advance.

The error simply means that the COM class that you are registering has not been registered in the COM registry. Whilst you might have successfully registered the DLL, its self-registration did not register the COM class.
To understand why your DLL does not register the class requires knowledge of your registration code, which is unfortunately not present in the question.

Related

Delphi word automation: Create Ole Object inside Dll

Does anyon know how to create word ole object in DLL.
I have one application that load a DLL which in turn create word ole object.
My application crash every time.
MSWord:= CreateOleObject('Word.Application');
Assuming that Word is installed, then the primary reason why you code might fail is that COM has not been initialized in the calling thread. That is not something that should be attempted from the DLL, because you want the DLL to be able to work with consumers that have already initialized COM.
So, the correct way to tackle this is to state as part of the DLL's interface contract that COM must be initialized by the caller. Typically by calling CoInitialize or CoInitializeEx.
One further comment, is that it if the application crashes, that suggests that you error handling is broken. All the functions in your DLL should take steps to catch any exceptions and convert into error codes to be returned to the caller. I suspect that you have not done this and are throwing a Delphi exception out of the DLL. You must never do that.
Note that I have given a broad and general answer. That matches the broad nature of the question, and the fact that there are few details in the question. If you had provided an MCVE we could have offered a more detailed response.
As DavidH points out, CoInitialize has to be called in the calling thread.
A point to watch out for in connection with the main thread of a VCL application is that whether a VCL application calls CoInitialize automatically depends on whether it uses the ComObj unit: if it does the CoInitialize is called via TApplication.Initialize and the InitComObj routine in ComObj; if it does not, you must call it (or CoInitializeEx) yourself.
The easy way to test this is to call the DLL from a TApplication-less console application - this will avoid being misled by ComObj being used some other than your main unit.
Suppose you have a DLL that contains the following exported procedure:
procedure CreateWordDoc;
var
DocText : String;
MSWord,
Document : OleVariant;
begin
MSWord := CreateOleObject('Word.Application');
MSWord.Visible := True;
Document := MSWord.Documents.Add;
DocText := 'Hello Word!';
MSWord.Selection.TypeText(DocText);
end;
then you could call it like this:
program WordCaller;
{$APPTYPE CONSOLE}
uses
SysUtils, Windows, ActiveX;
type
TWordProc = procedure;
var
LibHandle : THandle;
WordProc : TWordProc;
begin
CoInitialize(Nil);
LibHandle := LoadLibrary('WordDll.Dll');
try
if LibHandle <> 0 then begin
try
WordProc := GetProcAddress(LibHandle, 'CreateWordDoc');
if Assigned(WordProc) then
WordProc;
finally
FreeLibrary(LibHandle);
end;
end;
finally
CoUnInitialize;
Readln;
end;
end.

Instantiated COM Component gets invalid after leaving method (but not its scope)

I am currently testing two external COM components. I have big issue with one of them, but I cannot really find reason behind such behavior. Let me provide some example.
const
CLASS_SomeClas: TGUID = '{SomeGUID}';
type
ISomeInterface = interface(IDispatch)
['{SomeGUID}']
function SomeMethod(const AInput: WideString): WideString; safecall;
end;
TWrappingClass = class(TObject)
strict private
FInstance: ISomeInterface;
procedure CreateInstance;
public
procedure DoYourActualJob;
end;
procedure TWrappingClass.CreateInstance;
begin
FInstance := CreateComObject(CLASS_SomeClass) as ISomeInterface;
dbg(FInstance._AddRef); // Debugs 3
dbg(FInstance._AddRef); // Debugs 4
dbg(FInstance.Release); // Debugs 3
dbg(FInstance._AddRef); // Debugs 4
FInstance.SomeMethod(''); //Runs as expected
end;
procedure TWrappingClass.DoYourActualJob;
begin
CreateInstance;
dbg(FInstance._AddRef); //Debugs -1!
FInstance.SomeMethod(''); //AV
end;
As provided with example instance gets invalid after it leaves CreateInstance method. Component is designed to work with many sequential calls of SomeMethod and it does work when called inside single method.
Could someone give me clue what is actually happening there, why my instance gets invalid? Is it problem with my code, with Delphi or with component's code? When I change the implementation of TWrappingClass to another vendor (that is I change both ISomeInterface and CLASS_SomeClass) then everything works fine.
EDIT:
Behaviour does not change when I don't even call SomeMethod. That is after I leave CreateInstance, call to _AddRef returns -1. Component I am testing is here CadEditorX Probably I am not allowed to attach the OCX without violating its license.
You state clearly in the question that the erroneous behaviour only occurs with one specific COM object. Given this fact, and that Delphi's COM reference counting is known to work correctly, the only reasonable conclusion is that the fault lies in this specific COM object.
Your only recourse of action is to contact the vendor of this COM object and file a bug report with them.
One thing to look at, with a view to a possible work around, is how you are creating the object. You use CreateComObject. This receives a class ID and returns IUnknown. It calls CoCreateInstance passing the class ID, and requesting the IUnknown interface. You then need to query for your interface, ISomeInterface. So your code looks like this:
var
iunk: IUnknown;
intf: ISomeInteface;
....
CoCreateInstance(ClassID, nil, CLSCTX_INPROC_SERVER or CLSCTX_LOCAL_SERVER,
IUnknown, iunk);
iunk.QueryInterface(ISomeInterface, intf);
The fact that you have two interface variables, one IUnknown and one ISomeInterface explains why you see the reference count that you do. Now, you might think that you only have one interface variable, but that's not the case. There are two, only one of them is an implicit local. You can see this by looking at the compiled code and stepping through under the debugger.
This code:
procedure TWrappingClass.CreateInstance;
begin
FInstance := CreateComObject(CLASS_SomeClass) as ISomeInterface;
end;
is compiled as if it were this (ignoring error checking):
procedure TWrappingClass.CreateInstance;
var
iunk: IUnknown;
begin
iunk := CreateComObject(CLASS_SomeClass);
try
FInstance := CreateComObject(CLASS_SomeClass) as ISomeInterface;
finally
iunk := nil;
end;
end;
Perhaps the COM component cannot handle the call to Release made on its IUnknown interface.
So, you could try to work around this by using CoCreateInstance instead of CreateComObject. Pass ISomeInterface as the riid parameter.
OleCheck(CoCreateInstance(CLASS_SomeClass, nil, CLSCTX_INPROC_SERVER
or CLSCTX_LOCAL_SERVER, ISomeInterface, FInstance));

Delphi: Passing a parameter to a running service to act on

We have a service (written in C#) running to check somethings every 10 minutes and if something new happened, then send an email to someone special.
We also have other Delphi program and want to pass a parameter to the service to act on and send email immediately (I mean regardless than 10 minutes interval).
How to do that while service is running ?
note: There is no way to migrate to C# we have to do that in Delphi.
There's also a possibility to use ControlService API to send the service a user-defined control code. (The service has to be written to respond to that specific control code.)
You need to use some form of inter process communication (IPC). There are many possibilities. Most commonly used for such a scenario are named pipes and TCP/sockets.
There are some good answers here already... and here's mine:
You could use a text file or the windows registry to flag for action. This way your Delphi service can react upon start-up should the trigger have occured while your service was not running. Any information/parameters you wish to convey can be included in the registry-key value or as file data.
Win Registry Method:
If you use a registry-key make sure that both apps can read and write to the same key.
In your Delphi Service implement the RegNotifyChangeKeyValue WinAPI which will notify when the key is added/altered. Here's an idea how you can implement the listner in Delphi: Monitoring Registry Changes
File Method:
To be notified about file changes you do not need to poll for changes. Below is code for a solution based on the FindFirstChangeNotification WinAPI. Your Delphi Service can implement the TFileWatch class. You will also need a unit with the class TDirectoryWatch class by Angus Johnson.
unit FileWatch;
interface
uses Classes,
SysUtils,
DirWatch; //by Angus Johnson: http://www.angusj.com/delphi/dirwatch.html
type TFileNotifyEventType = (feCreated, feModified, feDeleted);
TFileNotifyEvent = procedure(Sender: TObject; FileEventType : TFileNotifyEventType) of object;
TFileWatch = class(TComponent)
private
FDirWatch : TDirectoryWatch;
FFileToWatch : string;
FFileAge : integer; //if -1 then file does not exist
FFileExists : boolean;
procedure OnFolderChangeEvent(Sender: TObject);
protected
public
OnFileNotifyEvent : TFileNotifyEvent;
property Filename : string read FFileToWatch;
constructor Create(aOwner: TComponent; FileToWatch : string);
destructor Destroy();
end;
implementation
{ TFileWatch }
constructor TFileWatch.Create(aOwner: TComponent; FileToWatch: string);
begin
inherited Create(aOwner);
FDirWatch := TDirectoryWatch.Create(Self);
FDirWatch.Directory := ExtractFilePath(FileToWatch);
FDirWatch.OnChange := OnFolderChangeEvent;
FDirWatch.NotifyFilters := [nfFilename, nfLastWrite];
FDirWatch.Active := true;
FFileToWatch := FileToWatch;
FFileAge := FileAge(FFileToWatch);
FFileExists := FFileAge > -1;
end;
destructor TFileWatch.Destroy;
begin
FDirWatch.Free;
inherited Destroy;
end;
procedure TFileWatch.OnFolderChangeEvent(Sender: TObject);
var MyFileAge : integer;
MyFileExists : boolean;
FileEventType : TFileNotifyEventType;
begin
//Check to see if the event has been fired by our file in question
MyFileAge := FileAge(FFileToWatch);
if MyFileAge = FFileAge then
exit; //Nothing has happened, exit.
//Figure out if the file has been created, modified or deleted
MyFileExists := MyFileAge > -1;
if MyFileExists and not FFileExists then
FileEventType := feCreated
else if not MyFileExists and FFileExists then
FileEventType := feDeleted
else
FileEventType := feModified;
FFileAge := MyFileAge;
FFileExists := MyFileExists;
if Assigned(OnFileNotifyEvent) then
OnFileNotifyEvent(Self, FileEventType);
end;
end.
I often communicate via a database. I'd store a certain value with process X, and process Y reads it.
The nice thing about that design is that the two applications don't need to know eachother. They can easily run on different machines, and you can have multiple readers and writers, so you can easily scale things up. You also get encryption and compressed connections for free if you need it, and all sorts of complicated multi user stuff is taken care of.
I would suggest adding a WCF Service to (hosted by) your Windows service exposing the required function.

Passing messages to a DLL's entry point

I have a DLL which exectues some code at its entry point, i.e.
procedure MainDLL(Reason: Integer);
begin
{ ... Code here ... }
end;
begin
DLLProc := #MainDLL;
end.
Now, I would like to pass some values to the DLL's entry point from an external application. I have tried creating a hidden window inside the DLL, like that:
const
WM_JAJCO = WM_USER + 1024;
type
TWnd = class(TObject)
class procedure DLLWndProc(var Msg: TMessage);
end;
{ ... }
class procedure TWnd.DLLWndProc(var Msg: TMessage);
var
Tmp: DWORD;
begin
if (Msg.Msg = WM_JAJCO) then
begin
PNewHandle := Msg.LParam;
CreateThread(nil, 0, #Starter, nil, 0, Tmp);
Msg.Result := 0;
end else
Msg.Result := DefWindowProc(MyHnd, Msg.Msg, Msg.WParam, Msg.LParam);
end;
// in the entry point
MyHnd := AllocateHWND(TWnd.DLLWndProc);
Then, after I initialize the DLL in the caller application, I use:
SendMessage(FindWindow('TPUtilWindow', nil), WM_USER + 1024, 0, wi.WndHandle);
Application.ProcessMessages();
But the window created inside the DLL does not seem to receive the message. Do you happen to know why?
If that's a bad method and you have a different solution, please let me know.
You shouldn't be using DLLMain for this. Just export your own init function and call it manually.
That's a rather tortuous approach. You are supposed to do as little as possible in the DllMain function. The canonical solution is to create a dedicated function to perform initialization. Arrange for the host app to call the initialization function before calling anything else.
The most likely reason your version fails is that there are a lot of windows with that class name. Every window created by AllocHwnd has that class name. FindWindow probably just finds the wrong one.
On the other hand, you mention in passing in a comment that this DLL is injected! In that case you can make your method work by using a unique class name or giving the window a unique title so that you can find it.
Finally the call to ProcessMessages looks to be gratuitous.
First make sure that the injected DLL really does create your window handle. WinSight or Spy++ should help you there. Once you know the window really does exist make sure FindWindow find your window handle and not another one with the same class name. IIRC, even the Delphi IDE itself creates window handles using this class name.

RTTI on objects in Delphi

I'm trying to parse objects to XML in Delphi, so I read about calling the object's ClassInfo method to get its RTTI info.
The thing is, this apparently only works for TPersistent objects. Otherwise, I have to specifically add a compiler directive {$M+} to the source code for the compiler to generate RTTI info.
So I happily added the directive, only to find that, even if it did return something from the ClassInfo call (it used to return nil), now I cannot retrieve the class' properties, fields or methods from it. It's like it created the object empty.
Any idea what am I missing here? Thanks!
Did you put those properties and methods into the published section?
Besides that, 'classical' RTTI ($TYPEINFO ON) will only get you information on properties, not on methods. You need 'extended' RTTI ($METHODINFO ON) for those.
Good starting point for extended RTTI: David Glassborow on extended RTTI
(who would believe that just this minute I finished writing some code that uses extended RTTI and decided to browse the Stack Overflow a little:))
RTTI will only show you published properties,etc. - not just public ones.
Try your code with a TObject and see what happens - if that isn't working, post your code because not everyone is psychic.
Have you considered using the TXMLDocument component? It will look at your XML and then create a nice unit of Delphi classes that represents your XML file -- makes it really, really easy to read and write XML files.
As for the RttiType problem returning only nil, this probably occurs for one reason: in your test, you did not instantiate the class at any time. The compiler, because it never has a reference to this class (because it is not an instance at all), simply removes it from the information as a form of optimization. See the two examples below. The behavior is different when you have the class instantiated at some point in your code or not.
Suppose the following class:
type
TTest = class
public
procedure Test;
end;
and the following code below:
var
LContext: TRttiContext;
LType: TRttiType;
LTest: TTest;
begin
LContext := TRttiContext.Create;
for LType in LContext.GetTypes do
begin
if LType.IsInstance then
begin
WriteLn(LType.Name);
end;
end;
end;
so far, TTest class information is not available for use by RTTI. However, when we create at some point, within the application, then a reference is created for it within the compile, which makes this information available:
var
LContext: TRttiContext;
LType: TRttiType;
LTest: TTest;
begin
LTest := TTest.Create; //Here i´m using TTest.
//Could be in another part of the program
LContext := TRttiContext.Create;
for LType in LContext.GetTypes do
begin
if LType.IsInstance then
begin
WriteLn(LType.Name);
end;
end;
end;
At that point, if you use LContext.FindType ('TTest'), there will not be a nil return, because the compiler kept reference to the class. This explains the behavior you were having in your tests.

Resources