I am using IUnknown based COM technique in inno setup to access the methods of DLL. i can register and can get the object correctly but when i call the method inno showed me an exception:access voilation at address 0xxxxx in module mydll.dll write of address 0xabcd. my inno code is as follows. please help me in this regard.
hWnd := FindWindowByWindowName(VideoForm.Caption);
Obj := CreateComObject(StringToGuid('{CC6C749C-FADD-4ee5-80CF-11536CCF2206}'));
Obj2:=IShellLinkW(Obj);
Obj2.pluginStart(hWnd,'appVideo',VideoForm.Caption,'',0)
end;
Thank you in advance.
Related
I am using TIdHttp and TXMLDocument inside a thread in a Delphi program.
Now I want to know:
Do these classes use COM objects so I need to call CoInitialize and CoUninitialize in this thread?
If yes, do I have to use these functions at the body of execute method or at all methods that use TIdHttp or TXMLDocument classes?
TIdHTTP has no COM dependency.
TXMLDocument can have a dependency on COM. On Windows, out of the box it is a wrapper around Microsoft's MSXML ActiveX component, which uses COM. If you use another DOM vendor (for example, OmniXML, available from XE7) then there is no COM dependency. You can control this by setting the DefaultDOMVendor global variable.
CoInitialize and CoUninitialize must be called once from within the thread context. Typically in the Execute() method of TThread, as seen in this example flow:
procedure TMyThread.Execute;
begin
try
CoInitialize(nil);
try
while not Terminated do
begin
DoWorkThatMayUseCOM;
end;
finally
CoUninitialize();
end;
except
on E: Exception do
// log exception
Log(E);
end;
end;
I have the following function that raises an exception:
procedure Test();
var
WinHttpReq: Variant;
begin
WinHttpReq := CreateOleObject('WinHttp.WinHttpRequest.5.1');
try
WinHttpReq.Open('POST', 'https://tv.eurosport.com/', False);
WinHttpReq.SetRequestHeader('Content-Type', 'application/json');
WinHttpReq.Send('');
except
MsgBox(GetExceptionMessage, mbError, MB_OK);
end;
end;
Instead obtaining the error in a string GetExceptionMessage, I would like to obtain the error as an integer.
Is this possible?
Exceptions do not have any code in general. So there's no generic way to retrieve a code from an exception.
In this particular case the exception is based on an OLE automation error, which does have a code. Though, the code is not preserved, when the Inno Setup raises the exception for IDispatch interfaces (OLE).
What you can do, is to use the IUnknown interface. For that you need to:
define a Pascal-style interface for the IWinHttpRequest
define CLSID_WinHttpRequest
instantiate the object using the CreateComObject (instead of the CreateOleObject).
call the Send method defined as function Send(Body: Variant): HResult
check the returned HResult for 12038 (ERROR_INTERNET_SEC_CERT_CN_INVALID).
Particularly the first step is a huge and an error-prone task.
For examples and details, see:
Examples\CodeAutomation2.iss in your Inno Setup installation.
Pascal Scripting: Using COM Automation objects
If you decide to stick with error message comparison, you may try to use the FormatMessage function to retrieve the system/localized error message to compare with. See How i can retrieve the error description for a WinInet error code from delphi.
I would like to be able to structure my code for my Inno Setup project but I am forced to move code around because you can't call a function unless it is defined first.
Is there a way to declare a prototype at the top so that I don't get the "Unknown identifier" error and so that I can structure my code in logical blocks.
In Pascal (including a Pascal Script used in Inno Setup), you can define a function prototype (aka forward declaration) using a forward keyword:
procedure ProcA(ParamA: Integer); forward;
procedure ProcB;
begin
ProcA(1);
end;
procedure ProcA(ParamA: Integer);
begin
{ some code }
end;
See Forward declared functions.
I have a problem about adding string to Tmemo using TThread.ShowMessage can Show this string.The Application doesn't give any error about adding string to Tmemo but It doesn't be added to TMemo.So here is my code:
procedure TThreadGet.Execute;
var
Filed:TStringList;
begin
Filed:=TStringList.Create;
Filed.LoadFromFile(Username1+'.dat');
Messaged:=Touser+':'+Filed.Text;
Form2.Memo1.Lines.Add(Messaged);//Doesn't give error.But Doesn't Add String.
Showmessage(Messaged);//Shows String Right.
end;
Filed.Free;
Terminate;
end;
All access to VCL components must be performed in the main UI thread. For instance you may use TThread.Synchronize or TThread.Queue to arrange this.
The main reason for this is that Win32 windows have thread affinity. They also are only safe to access from the thread that created them. This property gives a very strong push to single thread UI and the VCL design goes this way.
Multi-threaded UI is possible in Win32 although it is much more tricky to do correctly. The VCL does not support that at all.
I have some inherited code for opening IE and I have some troubles, here is what I have:
IEUnit.pas (no form) has routines for LoadIE and OpenIE
OpenIE is called from separate thread and it looks like this
procedure OpenIE(URL: OleVariant; FieldValues: string = '');
var ie : IWebBrowser2; // <-- This should become "global" variable for the IEUnit
begin
ie := CreateOleObject('InternetExplorer.Application') as IWebBrowser2;
ie.Navigate2(URL, Flags, TargetFrameName, PostData, Headers);
ShowWindow(ie.HWND, SW_SHOWMAXIMIZED);
ie.Visible := true;
...
end;
I would like to have "global" ie variable in the unit and to write LoadIE routine like this :
LoadIE should be called from FormCreate (main thread)
It should just create "global" ie object
ie := CreateOleObject('InternetExplorer.Application') as IWebBrowser2;
so the OpenIE function doesn't need to create it, just to use it (purpose is to speed things up)
So problem is how to access same OLE object from 2 different threads, one creates the object, the other one uses it.
When I write code that doesn't take care of threads I get an error
exception class EOleSysError with message 'The application called an interface that was marshalled for a different thread'
How should I do it, to take care of threads (I'm not experienced with threads, some reading and video links are welcome).
Thanks in advance
MTA model COM servers can only be used from within their associated apartment. Here's the explanation of the error with the following advice:
The correct way of transferring an interface pointer (either a direct
pointer or a proxy pointer) from one apartment to another is via COM's
marshaling mechanism. The source apartment can call
CoMarshalInterThreadInterfaceInStream() to marshal the interface
pointer to a shared (global) stream. The destination apartment can
unmarshal this interface pointer by calling
CoGetInterfaceAndReleaseStream().
As you know, because it was the subject of your previous question, you need all calls to the COM object to be made from the same thread. The obvious choice is the main GUI thread. So, create the IWebBrowser2 in your main form's OnCreate event handler. And then use TThread.Synchronize or TThread.Queue whenever you need to show the browser. The code that you pass to Synchronize or Queue will be executed on the main GUI thread.
Assuming you are using a modern version of Delphi with support for anonymous methods you'd write it like this:
procedure TMyThread.ShowBrowser(const URL: string);
var
Proc: TThreadProcedure;
begin
Proc := procedure
begin
MainForm.Browser.Navigate2(URL, ...);
ShowWindow(MainForm.Browser.HWND, SW_SHOWMAXIMIZED);
MainForm.Browser.Visible := true;
end;
Queue(Proc);
end;
Non-free-threaded COM objects can only be used by/on/in the same thread as the one it's created for/on/in/with. In your case, to speed things up, I would use a plain global treadvar value of type IWebBrowser2, or a property in your class overriding TThread.
use CriticalSection to wrap all calls to the OleObject. also use Synchronize to call from the "other" thread to the main UI thread (this is why you get the exception).