10 years have ellapsed since I used COM/OLE, and I forget 90% of them.
Now we need to make a COM object to access some data from PHP/Python (this is specific thing, the php ODBC don't access the output params of a DataBase - like stored proc output), and my idea the I realize a minimal object with one method, and PHP/Python can call this to get the output...
procedure ExecSQL(Config, IP, Port, DBName, SQL, IDFieldName : variant) : output
output is [IDValue, ErrorMsg, HResult]
Please help me a very little example, how to start it?
I need only this, but I'm confused by many ActiveX/COM in the palette.
What I need to use to make a simple COM DLL, and how to register my COM object with this DLL?
Thanks:
dd
Select File\New\ActiveX Library - this creates ActiveX DLL project
Select File\New\Automation Object - this creates the type library and implementation
Using the type library editor, add methods/properties to the interface
Write implementation code
Select Run\Register ActiveX Server - this registers the COM server DLL
For COM related applications in Delphi this link is usefull http://www.techvanguards.com/
Related
I am trying to write an ADO Database profiler in Delphi 7.
it is part of a Delphi project.
I am using OnExecuteComplete event, but on some PCs i get "MSADO15.DLL" Access Violation error.
I have researched and underestand some win version is incompatible or damaged or has different version..
that AV error happened when i use parameters[i].value property of "Command"..
then i decide to write a different type,
now i need a refference to an object who called OnExecuteComplete event in ADOConnection.
if i can reach it, problem will be solved.
i use "Command" and "Recordset" refferences to compare by all of ADO object who linked by this ADOConnection, but some ADO objects has no Recordset..
is there a way to find who fire that event? like Sender?
I could not make out if you are in .Net or not, but in .Net it's easy. Use System.Diagnostics.StackTrace to see the whole stack of calls that lead to your event.
For native code there will probably also be a way to get a stacktrace if you compile in debug mode. It's quite some time since I've written Delphi (or Pascal) code. You could also try profiling with the free AQTime standard which works well with Delphi (both native as well as .Net) which also has a call-graph feature.
i solve it!
i write 2 derrived classes, from ADOQuery and ADOStoredProc.
then i publish Command property of them.
by force convert StoredProcedure to new class, i reach to command prop and compare that by command object of event!
problem solved!
thank you all.
Only WindowsXP,Windows 2003 has problem with "MSADO15.DLL" Access Violation error after read Parameters[i].Value and show recordset. Windows7 has not this problem. Use WillExecute event - it work fine everywhere. In ExecuteComplete you may get recordsAffected from recordset (RecordsAffected variant work wrong). You can get Parameters[i].Value later if Save Parameters to Variant variable and read it after visual components show recordset - use OnButtonClick for example or Timer event. This BUG not fixed at all Delphi versions - and in DelphiXE2 too..
The Sender should give you a clue, if it is not nil. Cast it to TComponent and use its Name property to find out which component fired it:
ShowMessage((Sender as TComponent).Name);
If Sender is nil, a stack trace might help.
Update
Apparently there is no Sender parameter. In this case, the Connection must be the class that fires it, so get its name and other data. You get enough parameters to find out what is happening.
Does anyone know of a TDataset descendant that works with Generics and RTTI, so that I can write code like this, and make use of data-aware components in the GUI? :
...
ds:TDataset<TPerson>;
...
procedure DoStuff;
begin
ds:=TDataset<TPerson>.create;
ds.add(TPerson.Create('A.','Hitler',77));
ds.add(TPerson.Create('O.','Bin Laden',88));
end;
This should be possible. The fielddefs can be created via RTTI because the exact type of the data is known. Values can also be automatically marshalled back and forth, so you can both view and edit data that's in a class or a record.
I hate having to write a lot of useless marshalling code, while the required information for that is available via RTTI already.
Or maybe somebody once wrote some sort of TEnumerable <-> TDataset adapter?
Does something like that exist, or should I start writing one?
...
The closest thing that I could find is an (excellent!) example by Marco Cantu, from Mastering Delphi 7, but the code itself doesn't make use of new language features like generics, the new RTTI system, or attributes, and it doesn't work with Unicode delphi. TDataset has changed since D7 too.
The TAureliusDataSet included in TMS Aurelius comes very close to that.
Take a look at EverClassy Dataset from Inovativa at www.inovativa.com.br/public.
another one is Snap Object Dataset http://digilander.libero.it/snapobject/
DotNet4Delphi by A-Dato Scheduling Technology from the Netherlands is good for you.
Quotes:
From Torry's Delphi
Hook up any collection to your data aware controls.
DotNet4Delphi implements many .Net collection classes, including
generic types like List<> and Dictionary<>. Different from their
Delphi counterpart is that our generic collections also implement the
non-generic interfaces (IList, IDictionary) allowing you to access
your collections in multiple ways. This opens the door to use any
collection as a data source for data aware controls which is exactly
what the (also included) TListDataset component provides.
It targets Delphi XE and XE2.
It's an open source initiative, Delphi rocks !!!
I have found a more relevant resource and can't help sharing it! So relevant that I think it deserves a separate post rather than a mere update in my first answer.
The Dduce library for Delphi XE2-XE6 makes use of TListDataSet<...> a generic dataset component that can be used to expose a generic list as a TDataSet.
The most relevant units pertaining to the implementation of the generic dataset are:
DDuce.Components.VirtualDataSet.pas (The original SO post is itself cited by the author within the source code as a reference among others!!!)
DDuce.Components.ListDataSet.pas
Class hierarchy:
TDataSet <= TCustomVirtualDataset <= TListDataset <= TListDataset<T>
Yes, it inherits lots of features... my only wish is to have at my disposal a version working with a lessen requirement (Delphi XE without most of the other bells and whistles).
Look and feel:
I have a DLL file from which I need the memory address of a class procedure. I am getting the handle to the DLL file, but when I use GetProcAddress, I can't get the address of the procedure. I have tried the following strings for the process name parameter:
"ProcName"
"ProcClass.ProcName"
"ProcClass::ProcName"
"ProcInterface::ProcName"
"ProcInterface.ProcName"
In none of the cases have I gotten the memory address of the procedure. I am mostly certain that the procedure is public.
What is the string format for doing this? Would it be easier to declare a function pointing to the external procedure and get the address later? Like this:
procedure ProcName(); stdcall; far; external 'Example.DLL';
ProcPointer := #ProcName;
GetProcAddress only gives you the address for exported functions. Your DLL surely doesn't export the methods of a class!
Use an PE explorer to look for the exported names. For example, use the PE explorer available in GExperts. I've got a "PE Information" menu entry under the GExperts menu.
You are into reverse engineering territory here.
I think that if I were you I would just step through in the CPU view of the debugger, following a call to the method of interest, and find the entry point address. I'd subtract it from the base address of the DLL and that would be the offset. Then to calculate the address at runtime you just add the offset it to the base address of the DLL in memory at that time. You can find out the base address with calls to LoadLibrary or GetModuleHandle.
Why hard code the offset? Well, since you can't modify your DLL it doesn't seem to be too limiting. If hard coding the offset is not viable then there are other means of locating entry points, but I must admit I'm not the world's greatest expert on that.
Finally, when you implement the replacement method, you will need to replace it with a global function/procedure with an extra parameter, the first parameter, which takes the place of Self.
I might be reading this wrong. But it seems to me you wrote the DLL.
You should write a function that is NOT a member of any class, and export it from your DLL. Inside that function, call your class method.
If you didn't write the DLL, you still need to find out what functions it exports, and it is very unlikely any of them were class methods, at least not in Pascal.
If someone wrote a dll in C++ and exported its methods, then you would have to investigate C++ name mangling rules.
First, I'm very new in Delphi and COM, but I should build COM application in Delphi. I read a lot of articles and notes on the internets, but COM and COM in Delphi are still not clear to me.
My sources - http://www.everfall.com/paste/id.php?wisdn8hyhzkt (about 80 lines).
I try to make a COM Interface and Impl class - it works if I call an interface method from Delphi (I create an impl object via TestClient.Create), but if I try to create an object from outer world (from Java, via com4j) my application crashed with following exception:
Project Kernel.exe raised exception class $C0000005 with
message 'access violation at 0x00000002: read of address 0x00000002'.
If I set a breakpoint in QueryInterface - it breaks, but when I come out from function - all crashes.
What I'm doing wrong? What I still missing? What I can/should read about COM (in Delphi) to avoid dumb questions like this?
There is no need to implement IUnkown.QueryInterface your self. Remove that method from TestComImpl and let TComObject handle it. Also be sure to give the ITestCom interface a GUID.
If the crash is happening after QueryInterface returns, what I would do is put a breakpoint in the Java app when it calls QueryInterface and see what it tries to do next. That'll give you an idea of where to look.
Your comment seems to bear this out. It's calling QueryInterface, getting back a result that says that this interface is good, and trying to use it for something that immediately breaks. But if you comment out the code that tells it the interface is good, it ends up not trying to use the interface, and nothing breaks.
If you're not familiar with Delphi, an access violation usually means a null pointer dereference. Here, it says that your instruction pointer is at memory location 0x000002. That probably means that you somehow tried to call a virtual method (or an interface method) on an object that hasn't been constructed yet.
Hope that helps!
I made a dll with COM from scratch and
I use DllRegisterServer - it gave me a possibility to control server registration (over
TComObjectFactory.RegisterClassObject in my first attempt)
I remove QueryInterface from my TestComImpl
Com4j supports only STA (Apartment) threading model (I assume RegisterClassObject uses
MTA)
So if class registered as Apartment (STA) or Both - com4j can create instances.
Thanks everyone for help!
I've been trying to get a soap server up that implements (is that the correct term?) a wsdl specification made by a third party. I have used Delphi's wsdl importer. (Part of) the generated code looks like this:
miniPortType = interface(IInvokable)
['{824D172A-9C1F-D202-5B21-4C324553BCF0}']
// Cannot unwrap:
// - Input element wrapper name does not match operation's name
function miniService(const aMessage: MiniMessageType): MiniAnswerType; stdcall;
end;
When called, the server says that "No method named 'MiniMessageType' is supported by interface 'miniPortType'".
I can only get this to work by making the name of the function and name of the main element of the message the same.
I think it should be possible to have different names. At least soapUI doesn't complain. And I actually have no choice but to implement the wsdl as is. Does anybody know how I can work around this?
I'm using Delphi 2007.
Thanks, Miel.
If I recall correctly, the SOAP interface is actually defined in a "table" at the bottom of the definitions, and it is this which is used to do the conversion between Delphi types and SOAP types in the communications. I've "corrected" this sort of thing in the past by manually changing the table building calls, but you have to be careful, and may also need to mangle the SOAP text at the appropriate point to make it all fit.