I have a library written in C and I have a header file with a description of the interface in C. The DLL has a function to get this interface. How to describe it correctly and get it in the DELPHI application?
using DllCallbackClassPtr = void*;
using DllCallbackFunction = void(*)(const char *, DllCallbackClassPtr);
#ifdef _WIN32
#include <Windows.h>
__interface IXeoma
{
public:
enum ConnectErrorCode {
OK = 0,
SERVER_NOT_FOUND,
WRONG_PASSWORD,
UNKNOWN
};
// return ConnectErrorCode
virtual int start(const char* connectionString) = 0;
virtual bool isConnected() = 0;
virtual void stop() = 0;
virtual void requestData(const char* request, const char* additionalData, DllCallbackClassPtr classPtr, DllCallbackFunction callbackFunc) = 0;
virtual const char* getRequestResult(const char* request) = 0;
virtual void setCameraRenderHandle(const char* previewId, HWND hWnd) = 0;
};
The library is loaded, but the function returns nil.
type
IXeoma = interface
function Start(connectionString: PChar): integer;
end;
type
TCreateXeomaInterface = function() : IXeoma; stdcall;
var
Form1: TForm1;
CreateXeomaInterface: TCreateXeomaInterface;
implementation
{$R *.dfm}
var
LibraryHandle: THandle;
procedure TForm1.Button1Click(Sender: TObject);
var
XeomaInt: IXeoma;
i: integer;
begin
LibraryHandle := LoadLibrary(PChar('D:\Projects\XeomaSDK\Win32\Debug\xeomaclientdll.dll'));
if LibraryHandle >= 32 then
begin
#CreateXeomaInterface := GetProcAddress(LibraryHandle, 'createXeomaInterface');
end;
XeomaInt := CreateXeomaInterface();
// Here XeomaInt = nil
end;
The __interface extension in Visual C++, and the interface keyword in Delphi, are not the same thing, and are not compatible with each other.
IXeoma in the C++ code is just an ordinary class type, not a COM interface. But in Delphi, all interfaces derive from IUnknown, and all classes derive from TObject, neither of which you want in this situation. So, you are going to have to use a plain record instead, and declare TCreateXeomaInterface as returning a pointer to that record.
Also, note that a Delphi record can't have virtual methods, but the C++ class does have them, so you are going to have to manually account for the C++ class's vtable in Delphi.
Try something like this:
type
DllCallbackClassPtr = Pointer;
DllCallbackFunction = procedure(Param1: PAnsiChar; Param2: DllCallbackClassPtr); cdecl;
IXeomaPtr = ^IXeoma;
IXeomaVTable = record
start: function(_Self: IXeomaPtr; connectionString: PAnsiChar): Integer; cdecl;
isConnected: function(_Self: IXeomaPtr): Boolean; cdecl;;
stop: procedure(_Self: IXeomaPtr); cdecl;
requestData: procedure(_Self: IXeomaPtr; request: PAnsiChar; additionalData: PAnsiChar; classPtr: DllCallbackClassPtr; callbackFunc: DllCallbackFunction); cdecl;
getRequestResult: function(_Self: IXeomaPtr; request: PAnsiChar): PAnsiChar; cdecl;
setCameraRenderHandle: procedure(_Self: IXeomaPtr; previewId: PAnsiChar; hWnd: HWND); cdecl;
end;
ConnectErrorCode = (
OK = 0,
SERVER_NOT_FOUND,
WRONG_PASSWORD,
UNKNOWN
);
IXeoma = record
vtable: ^IXeomaVTable:
end;
type
TCreateXeomaInterface = function() : IXeomaPtr; stdcall;
var
Form1: TForm1;
CreateXeomaInterface: TCreateXeomaInterface;
implementation
{$R *.dfm}
var
LibraryHandle: THandle;
procedure TForm1.Button1Click(Sender: TObject);
var
XeomaInt: IXeomaPtr;
i: integer;
begin
XeomaInt := nil;
LibraryHandle := LoadLibrary('D:\Projects\XeomaSDK\Win32\Debug\xeomaclientdll.dll');
if LibraryHandle >= 32 then
begin
#CreateXeomaInterface := GetProcAddress(LibraryHandle, 'createXeomaInterface');
XeomaInt := CreateXeomaInterface();
if XeomaInt <> nil then
XeomaInt^.vtable^.start(XeomaInt, '123:123#localhost:8090');
end;
...
end;
Related
Trying to implement events for Windows Core Audio API (Win7 64-bit Delphi XE5). My objective is to track the applications in the Volume Mixer to mute audio sessions that are not in my list and to adjust the volume for my target applications. I successfully enumerate the audio devices and the sessions, mute the audio and adjust the volume on a per-session basis but I am struggling with events. What I need is to get notified when new sessions are added and when sessions close so that I can enumerate again. I could use a timer to enumerate the session but I would prefer to avoid that.
The specific events that are not working are IAudioSessionNotification and IMMNotificationClient.
My questions are follows:
Is my approach to deriving classes for events too simplistic? I
found an example that is much more involved here:
Catch audio sessions events
, but it does not seem to work either (not tested personally)
Although IAudioEndpointVolumeCallback is "working" I think the code
smells because I am referencing UI elements in the OnNotify function
so I'd like some feedback/pointers. Is that a valid implementation?
I have two units: uAudioUI which contains the main form and MMDevApi unit that contains Core Audio interface.
The relevant parts of my code current looks like this (its a test app):
MMDevApi.pas
...
IAudioEndpointVolumeCallback = interface(IUnknown)
['{657804FA-D6AD-4496-8A60-352752AF4F89}']
function OnNotify(pNotify:PAUDIO_VOLUME_NOTIFICATION_DATA):HRESULT; stdcall;
end;
PIMMNotificationClient = ^IMMNotificationClient;
IMMNotificationClient = interface(IUnknown)
['{7991EEC9-7E89-4D85-8390-6C703CEC60C0}']
function OnDefaultDeviceChanged(const flow: EDataFlow; const role: ERole; const pwstrDefaultDevice: LPCWSTR):HRESULT; stdcall;
function OnDeviceAdded(const pwstrDeviceId: LPCWSTR):HRESULT; stdcall;
function OnDeviceRemoved(const pwstrDeviceId: LPCWSTR):HRESULT; stdcall;
function OnDeviceStateChanged(const pwstrDeviceID:LPCWSTR; const dwNewState: DWORD):HRESULT; stdcall;
function OnPropertyValueChanged(const pwstrDeviceID:LPCWSTR; const key: PROPERTYKEY):HRESULT; stdcall;
end;
IAudioSessionNotification = interface(IUnknown)
['{641DD20B-4D41-49CC-ABA3-174B9477BB08}']
function OnSessionCreated(const NewSession: IAudioSessionControl): HResult; stdcall;
end;
In the main form unit I derive classes for the required interfaces:
uAudioUI.pas
...
type
TEndpointVolumeCallback = class(TInterfacedObject, IAudioEndpointVolumeCallback)
public
function OnNotify(pNotify: PAUDIO_VOLUME_NOTIFICATION_DATA): HRESULT; stdcall;
end;
TMMNotificationClient = class(TInterfacedObject, IMMNotificationClient)
function OnDefaultDeviceChanged(const flow: EDataFlow; const role: ERole; const pwstrDefaultDevice: LPCWSTR):HRESULT; stdcall;
function OnDeviceAdded(const pwstrDeviceId: LPCWSTR):HRESULT; stdcall;
function OnDeviceRemoved(const pwstrDeviceId: LPCWSTR):HRESULT; stdcall;
function OnDeviceStateChanged(const pwstrDeviceID:LPCWSTR; const dwNewState: DWORD):HRESULT; stdcall;
function OnPropertyValueChanged(const pwstrDeviceID:LPCWSTR; const key: PROPERTYKEY):HRESULT; stdcall;
end;
TAudioMixerSessionCallback = class(TInterfacedObject, IAudioSessionEvents)
function OnDisplayNameChanged(NewDisplayName:LPCWSTR; EventContext:pGuid):HResult; stdcall;
function OnIconPathChanged(NewIconPath:LPCWSTR; EventContext:pGuid):HResult; stdcall;
function OnSimpleVolumeChanged(NewVolume:Single; NewMute:LongBool; EventContext:pGuid):HResult; stdcall;
function OnChannelVolumeChanged(ChannelCount:uint; NewChannelArray:PSingle; ChangedChannel:uint;
EventContext:pGuid):HResult; stdcall;
function OnGroupingParamChanged(NewGroupingParam, EventContext:pGuid):HResult; stdcall;
function OnStateChanged(NewState:uint):HResult; stdcall; // AudioSessionState
function OnSessionDisconnected(DisconnectReason:uint):HResult; stdcall; // AudioSessionDisconnectReason
end;
TAudioSessionCallback = class(TInterfacedObject, IAudioSessionNotification)
function OnSessionCreated(const NewSession: IAudioSessionControl): HResult; stdcall;
end;
For simplicity I use globals
private
{ Private declarations }
FDefaultDevice : IMMDevice;
FAudioEndpointVolume : IAudioEndpointVolume;
FDeviceEnumerator : IMMDeviceEnumerator;
FAudioClient : IAudioClient;
FAudioSessionManager : IAudioSessionManager2;
FAudioSessionControl : IAudioSessionControl2;
FEndpointVolumeCallback : IAudioEndpointVolumeCallback;
FAudioSessionEvents : IAudioSessionEvents;
FMMNotificationCallback : IMMNotificationClient;
FPMMNotificationCallback : PIMMNotificationClient;
FAudioSessionCallback : TAudioSessionCallback;
...
procedure TForm1.FormCreate(Sender: TObject);
var
...
begin
hr := CoCreateInstance(CLASS_IMMDeviceEnumerator, nil, CLSCTX_INPROC_SERVER, IID_IMMDeviceEnumerator, FDeviceEnumerator);
if hr = ERROR_SUCCESS then
begin
hr := FDeviceEnumerator.GetDefaultAudioEndpoint(eRender, eConsole, FDefaultDevice);
if hr <> ERROR_SUCCESS then Exit;
//get the master audio endpoint
hr := FDefaultDevice.Activate(IID_IAudioEndpointVolume, CLSCTX_INPROC_SERVER, nil, IUnknown(FAudioEndpointVolume));
if hr <> ERROR_SUCCESS then Exit;
hr := FDefaultDevice.Activate(IID_IAudioClient, CLSCTX_ALL, nil, IUnknown(FAudioClient));
if hr <> ERROR_SUCCESS then Exit;
//volume handler
FEndpointVolumeCallback := TEndpointVolumeCallback.Create;
if FAudioEndpointVolume.RegisterControlChangeNotify(FEndPointVolumeCallback) = ERROR_SUCCESS then
FEndpointVolumeCallback._AddRef;
//device change / ex: cable unplug handler
FMMNotificationCallback := TMMNotificationClient.Create;
FPMMNotificationCallback := #FMMNotificationCallback;
if FDeviceEnumerator.RegisterEndpointNotificationCallback(FPCableUnpluggedCallback) = ERROR_SUCCESS then
FMMNotificationCallback._AddRef;
... and then finally, the class functions
{ TEndpointVolumeCallback }
function TEndpointVolumeCallback.OnNotify(pNotify: PAUDIO_VOLUME_NOTIFICATION_DATA): HRESULT;
var
audioLevel : integer;
begin
//NOTE: this works..
audioLevel := Round(pNotify.fMasterVolume * 100);
Form1.trackVolumeLevel.Position := audioLevel;
if pNotify.bMuted then
begin
form1.trackVolumeLevel.Enabled := False;
form1.spdMute.Caption := 'X';
end
else
begin
form1.trackVolumeLevel.Enabled := True;
form1.spdMute.Caption := 'O';
end;
Result := S_OK;
end;
{ TMMNotificaionClient }
function TMMNotificationClient.OnDefaultDeviceChanged(const flow: EDataFlow; const role: ERole; const pwstrDefaultDevice: LPCWSTR): HRESULT;
begin
//NOTE: this crashes - referencing a pointer to add 000000000
Form1.Label2.Caption := 'Audio device changed';
Result := S_OK;
end;
{ AudioMixerSessionCallback }
function TAudioMixerSessionCallback.OnSimpleVolumeChanged(NewVolume: Single; NewMute: LongBool; EventContext: PGUID): HRESULT;
begin
//NOTE: This works...
Form1.trackSessionVolumeLevel.Position := Round(NewVolume * 100);
Form1.Label2.Caption := EventContext.ToString;
Result := S_OK;
end;
{ AudioSessionCallback }
function TAudioSessionCallback.OnSessionCreated(const NewSession: IAudioSessionControl): HRESULT;
begin
//NOTE: This never gets called...
Form1.Label2.Caption := 'New audio session created';
Result := S_OK;
end;
I think the code is a translation from C/C++ ?
When using the TInterfacedObject, you don't need the _AddRef etc. methods, because the TInterfacedObject will handle those.
Another suggestion: I'm missing the threading implementation. Normally this is declared in the constructor or initialization section.
Example:
initialization
CoInitializeEx(Nil,
COINIT_APARTMENTTHREADED);
or
//Create method
inherited Create();
CoInitializeEx(Nil,
COINIT_APARTMENTTHREADED);
This is important when using an UI implementation. Otherwise you will not receive any events.
Non UI implementations (like drivers) should use the COINIT_MULTITHREADED model.
Some notes:
Instead of using pointers, like PGUID, use TGUID. When a field is declared in C++, it could be starting with ie pSingle. In Delphi this should be Single. When C++ is using pointer to pointers (like ppSingle) then - in most cases - in Delphi this would be a PSingle.
Also you declared function OnChannelVolumeChanged wrong.
It should be:
function OnChannelVolumeChanged(ChannelCount: UINT;
NewChannelArray: Array of Single;
ChangedChannel: UINT;
EventContext: TGUID): HResult; stdcall;
I'm testing this code Windows 7 32 and 64 bit, but still, it does not work the glass effect in any case, not return any errors, just does not work me.
program test;
{$APPTYPE CONSOLE}
{$R *.res}
uses
Windows,System.SysUtils,DH_Form_Effects;
type
DWM_BLURBEHIND = record
dwFlags : DWORD;
fEnable : BOOL;
hRgnBlur : HRGN;
fTransitionOnMaximized : BOOL;
end;
function DwmEnableBlurBehindWindow(hWnd : HWND; const pBlurBehind : DWM_BLURBEHIND) : HRESULT; stdcall; external 'dwmapi.dll' name 'DwmEnableBlurBehindWindow';//function to enable the glass effect
function GetConsoleWindow: HWND; stdcall; external kernel32 name 'GetConsoleWindow'; //get the handle of the console window
function DWM_EnableBlurBehind(hwnd : HWND; AEnable: Boolean; hRgnBlur : HRGN = 0; ATransitionOnMaximized: Boolean = False; AFlags: Cardinal = 1): HRESULT;
var
pBlurBehind : DWM_BLURBEHIND;
begin
pBlurBehind.dwFlags:=AFlags;
pBlurBehind.fEnable:=AEnable;
pBlurBehind.hRgnBlur:=hRgnBlur;
pBlurBehind.fTransitionOnMaximized:=ATransitionOnMaximized;
Result:=DwmEnableBlurBehindWindow(hwnd, pBlurBehind);
end;
begin
try
DWM_EnableBlurBehind(GetConsoleWindow(), True);
Writeln('See my glass effect');
Writeln('Go Delphi Go');
Readln;
except
on E:Exception do
Writeln(E.Classname, ': ', E.Message);
end;
end.
Does this have any requirement to run? What is the problem ?
It works for me (on Windows 10):
I would suggest checking for any errors, rather than ignoring them:
hr: HRESULT;
hr := DWM_EnableBlurBehind(GetConsoleWindow(), True);
OleCheck(hr);
Equivalently, you can avoid the need to check return values by using safecall calling convention (where the compiler will insert code to check the HRESULT and throw an exception if the function failed):
procedure DwmEnableBlurBehindWindow(hWnd: HWND; const pBlurBehind: DWM_BLURBEHIND); safecall; external 'dwmapi.dll' name 'DwmEnableBlurBehindWindow';//function to enable the glass effect
procedure DWM_EnableBlurBehind(hwnd : HWND; AEnable: Boolean; hRgnBlur : HRGN = 0; ATransitionOnMaximized: Boolean = False; AFlags: Cardinal = 1);
var
pBlurBehind : DWM_BLURBEHIND;
begin
pBlurBehind.dwFlags:=AFlags;
pBlurBehind.fEnable:=AEnable;
pBlurBehind.hRgnBlur:=hRgnBlur;
pBlurBehind.fTransitionOnMaximized:=ATransitionOnMaximized;
DwmEnableBlurBehindWindow(hwnd, pBlurBehind);
end;
Now, since you weren't checking errors anyway - you will now know the problem. On Windows 7 with desktop composition disabled, DwmEnableBlurBehindWindow returns:
0x80263001
{Desktop composition is disabled}
The operation could not be completed because desktop composition is disabled
I'm new in delphi, my program developed in delphi working with a dll developed in C++, I need working with pointer functions that throw exceptions of Access Violation address and after many test I don't know how resolve It.
this is defintion of the pointer function in delphi that translate since header c++
type
TMICRCallback = function: Integer of Object; stdcall;
TStatusCallback = function(dwParam: Cardinal): Integer of Object; stdcall;
type
TBiMICRSetReadBackFunction =
function(const nHande: Integer;
pMicrCB: TMICRCallback;
var pReadBuffSize: Byte;
var readCharBuff: Byte;
var pStatus: Byte;
var pDetail: Byte
): Integer; stdcall;
var
BiMICRSetReadBackFunction: TBiMICRSetReadBackFunction;
type
TBiMICRSetReadBackFunction =
function(const nHande: Integer;
pMicrCB: TMICRCallback;
var pReadBuffSize: Byte;
var readCharBuff: Byte;
var pStatus: Byte;
var pDetail: Byte
): Integer; stdcall;
var
BiMICRSetReadBackFunction: TBiMICRSetReadBackFunction;
this is a code that call the pointer functions
type
function CBMICRRead : Integer; stdcall;
function CBMICRStatus(dwStatus: LongWord) : Integer; stdcall;
Respuesta : TMICRCallback;
Estado : TStatusCallback;
BiSetStatusBackFunction(m_hApi, Estado);
BiMICRSetReadBackFunction (m_hApi,
Respuesta,
m_MICRReadBuffSize,
m_MICRReadBuff[0],
m_MICRReadStatus,
m_MICRReadStDetail);
This is the C++ side of the interface:
typedef int (CALLBACK* MICRCallback)(void);
typedef int (CALLBACK* StatusCallback)(DWORD);
int WINAPI BiSetStatusBackFunction(int nHandle,
int (CALLBACK *pStatusCB)(DWORD dwStatus));
int WINAPI BiMICRSetReadBackFunction(int nHandle,
int (CALLBACK *pMicrCB)(void),
LPBYTE pReadBuffSize,
LPBYTE readCharBuff,
LPBYTE pStatus,
LPBYTE pDetail);
You must avoid Object as passing parameters from/to DLL function call result.
TMICRCallback = function: Integer; stdcall;
TStatusCallback = function(dwParam: Cardinal): Integer; stdcall;
i am trying to call the Windows API function EnumerateTraceGuids:
ULONG EnumerateTraceGuids(
__inout PTRACE_GUID_PROPERTIES *GuidPropertiesArray,
__in ULONG PropertyArrayCount,
__out PULONG GuidCount
);
Starting from the code sample on MSDN:
ULONG status = ERROR_SUCCESS;
PTRACE_GUID_PROPERTIES *pProviders = NULL;
ULONG RegisteredProviderCount = 0;
ULONG ProviderCount = 0;
pProviders = (PTRACE_GUID_PROPERTIES *) malloc(sizeof(PTRACE_GUID_PROPERTIES));
status = EnumerateTraceGuids(pProviders, ProviderCount, &RegisteredProviderCount);
i convert the code to Delphi:
var
providers: PPointerList;
providerCount: LongWord;
registeredProviderCount: LongWord;
res: LongWord;
begin
providerCount := 0;
registeredProviderCount := 0;
providers := AllocMem(SizeOf(Pointer));
ZeroMemory(providers, SizeOf(Pointer));
res := EnumerateTraceGuids(providers, providerCount, {out}registeredProviderCount);
end;
with the api call:
function EnumerateTraceGuids(
GuidPropertiesArray: Pointer;
PropertyArrayCount: Cardinal;
var GuidCount: Cardinal): Cardinal; stdcall; external 'advapi32.dll';
i get the result code ERROR_INVALID_PARAMETER (87, The parameter is incorrect).
What am i doing wrong?
MSDN describes what would cause ERROR_INVALID_PARAMETER:
ERROR_INVALID_PARAMETER
One of the following is true:
PropertyArrayCount is zero
GuidPropertiesArray is NULL
The first case is true, my 2nd parameter PropertyArrayCount is zero - just like the sample says it should be.
So far as I can see, your code should be identical to the MSDN sample. However, as Code says, the MSDN sample does look a bit funky. Indeed, it seems to me that the MSDN sample is only working by chance.
Note that comment in that code that states:
// EnumerateTraceGuids requires a valid pointer. Create a dummy
// allocation, so that you can get the actual allocation size.
Then it allocates space in pProviders to store a single pointer. However, the value contained in pProviders actually matters. It cannot be NULL. In your Delphi code you zeroise that memory twice in fact. Once with AllocMem and once with ZeroMemory. If you just change your Delphi code to make the contents of providers non-zero then the Delphi code will start working.
Here is a very simple project that illustrates exactly what is going on:
program _EnumerateTraceGuidsFaultDemo;
{$APPTYPE CONSOLE}
function EnumerateTraceGuids(
GuidPropertiesArray: Pointer;
PropertyArrayCount: Cardinal;
var GuidCount: Cardinal): Cardinal; stdcall; external 'advapi32.dll';
var
providers: Pointer;
providerCount: LongWord;
registeredProviderCount: LongWord;
res: LongWord;
begin
providerCount := 0;
registeredProviderCount := 0;
providers := AllocMem(SizeOf(Pointer));//zeroises memory
res := EnumerateTraceGuids(providers, providerCount, registeredProviderCount);
Writeln(res);//outputs 87
PInteger(providers)^ := 1;
res := EnumerateTraceGuids(providers, providerCount, registeredProviderCount);
Writeln(res);//outputs 234
Readln;
end.
So I think that explains the problem, but I'd actually solve it more completely than that. I would move on to the next step of your work and declare EnumerateTraceGuids fully using a real Delphi equivalent to the TRACE_GUID_PROPERTIES struct.
I'd probably write the code something like this:
program _EnumerateTraceGuids;
{$APPTYPE CONSOLE}
{$R *.res}
uses
System.SysUtils, Windows;
type
PTraceGuidProperties = ^TTraceGuidProperties;
TTraceGuidProperties = record
Guid: TGUID;
GuidType: ULONG;
LoggerId: ULONG;
EnableLevel: ULONG;
EnableFlags: ULONG;
IsEnable: Boolean;
end;
function EnumerateTraceGuids(
var GuidPropertiesArray: PTraceGuidProperties;
PropertyArrayCount: ULONG;
var GuidCount: ULONG
): ULONG; stdcall; external 'advapi32.dll';
function GetRegisteredProviderCount: ULONG;
var
provider: TTraceGuidProperties;
pprovider: PTraceGuidProperties;
providerCount: LongWord;
registeredProviderCount: ULONG;
res: ULONG;
begin
providerCount := 0;
pprovider := #provider;
res := EnumerateTraceGuids(pprovider, providerCount, registeredProviderCount);
if (res<>ERROR_MORE_DATA) and (res<>ERROR_SUCCESS) then
RaiseLastOSError;
Result := registeredProviderCount;
end;
var
i: Integer;
provider: TTraceGuidProperties;
pprovider: PTraceGuidProperties;
providers: array of TTraceGuidProperties;
pproviders: array of PTraceGuidProperties;
providerCount: ULONG;
registeredProviderCount: ULONG;
res: ULONG;
begin
providerCount := GetRegisteredProviderCount;
SetLength(providers, providerCount);
SetLength(pproviders, providerCount);
for i := 0 to providerCount-1 do
pproviders[i] := #providers[i];
res := EnumerateTraceGuids(pproviders[0], providerCount, registeredProviderCount);
if res<>ERROR_SUCCESS then
RaiseLastOSError;
//do stuff with providers
end.
Rather than trying to be too cute in GetRegisteredProviderCount, I have passed a pointer to a real TRACE_GUID_PROPERTIES.
This Interface at _TLB.pas file
// *********************************************************************//
// Interface: ITMyCOM
// Flags: (256) OleAutomation
// GUID: {D94769D0-F4AF-41E9-9111-4D8BC2F42D69}
// *********************************************************************//
ITMyCOM = interface(IUnknown)
['{D94769D0-F4AF-41E9-9111-4D8BC2F42D69}']
function MyDrawWS(a: Integer; b: Integer): WideString; stdcall;
end;
This looks at OS Windows
[
odl,
uuid(D94769D0-F4AF-41E9-9111-4D8BC2F42D69),
version(1.0),
helpstring("Interface for TMyCOM Object"),
oleautomation
]
interface ITMyCOM : IUnknown {
BSTR _stdcall MyDrawWS(
[in] long a,
[in] long b);
};
Function in COM server looks as
function TTMyCOM.MyDrawWS(a, b: Integer): WideString;
begin
Result := WideString(IntToStr(a+b));
end;
In COM Client i`m calling this function like
Edit1.Text := String(MyCOM.MyDrawWS(1,1));
and get error First chance exception at $75A9FBAE. Exception class EAccessViolation with message 'Access violation at address 75A409A4 in module 'RPCRT4.dll'. Read of address FFFFFFF8'. Process Project1.exe (2296)
If i want returning Integer, it`s works. How to return WideString?
The correct way to handle this is as follows:
[
odl,
uuid(D94769D0-F4AF-41E9-9111-4D8BC2F42D69),
version(1.0),
helpstring("Interface for TMyCOM Object"),
oleautomation
]
interface ITMyCOM : IUnknown {
HRESULT _stdcall MyDrawWS(
[in] long a,
[in] long b,
[out, retval] BSTR* ret);
};
ITMyCOM = interface(IUnknown)
['{D94769D0-F4AF-41E9-9111-4D8BC2F42D69}']
function MyDrawWS(a: Integer; b: Integer; out ret: WideString): HResult; stdcall;
end;
function TTMyCOM.MyDrawWS(a, b: Integer; out ret: WideString): HRESULT;
begin
ret := IntToStr(a+b);
Result := S_OK;
end;
var
W: WideString;
begin
OleCheck(MyCOM.MyDrawWS(1, 1, W));
Edit1.Text := W;
end;
Which can then be simplified a little by using Delphi's safecall calling convention in the Delphi declaration (not in the TypeLibrary itself) of the interface:
ITMyCOM = interface(IUnknown)
['{D94769D0-F4AF-41E9-9111-4D8BC2F42D69}']
function MyDrawWS(a: Integer; b: Integer): WideString; safecall;
end;
function TTMyCOM.MyDrawWS(a, b: Integer): WideString;
begin
Result := IntToStr(a+b);
end;
Edit1.Text := MyCOM.MyDrawWS(1, 1);
Let Delphi perform the conversions automatically. Don't cast. You can cast a (ansi)string to a PChar, because their memory layout are compatible, but you can't cast a string to a widestring or viceversa. Delphi will perfrom conversion when you assign one to the other.
In Delphi < 2009
var
S: string;
W: WideString;
...
S := W; // Conversion, WideString -> AnsiString;
W := S; // Conversion, AnsiString -> WideString
Don't use return values other than HRESULT. Instead put your return value into parameter list as output parameter.
function MyDrawWS(a: Integer; b: Integer; out str : WideString): HRESULT; stdcall;
In this way, you are also forced to use COM memory manager IMalloc (CoTaskMemAlloc for pur COM, SysAllocString for Automation).
You need to use SysAllocString() or SysAllocStringLen() to allocate the BSTR.
First chance exception at $75A9FBAE. Exception class EAccessViolation with message 'Access violation at address 75A409A4 in module 'RPCRT4.dll'
the error is coming from RPCRT4.dll
EAccessViolation is mostly caused by accessing a null object, step through your code make sure all objects are valid objects.