Link to home
Start Free TrialLog in
Avatar of lowlevel
lowlevel

asked on

Access Violations after the last piece of code has been walked through...

See question-title; where would I start to debug, if the access violation only occurs AFTER that last piece of code has executed?

system: Delphi 4.01, NT4, SP3, IE4
project-type: COM Automation (ActiveX) server.
Avatar of inter
inter
Flag of Türkiye image

This was happen to me before, it is due to some
IXXXX object that I try to call release. So "try not call .release methof for the interface objects" is my suggestion
igor
Avatar of JimBob091197
JimBob091197

Hi Igor

Interesting comment.  I've been having some strange Access Violations too.  I use SHGetMalloc to get an instance of IMalloc, but then... do I call _Release or not?  I don't always get AV but it's also after my program has closed.  I will try removing all _Release and let you know what happens...  (It just seems wrong!  When will the IMalloc object be freed?)

(Similar problems with IShellFolders too.)

JB
Actually you are right my friend. But my proffesional opinion is that the LOCAL objects should not be released. Look at the following example, before I have comented the release things it will do just as you and lowlevel suggested(this one creates a short cut on the desktop, I once translated this almost directly from SDK)

uses
  shlobj, ComObj, ActiveX;

const
  IID_IPersistFile: TGUID = (
    D1:$0000010B;D2:$0000;D3:$0000;D4:($C0,$00,$00,$00,$00,$00,$00,$46));

// CreateLink - uses the shell's IShellLink and IPersistFile interfaces
//   to create and store a shortcut to the specified object.
//
// lpszPathObj - address of a buffer containing the path of the object
// lpszPathLink - address of a buffer containing the path where the
//   shell link is to be stored
// lpszDesc - address of a buffer containing the description of the
//   shell link
//
//C:\WINNT\Profiles\All Users\Start Menu\Programs\Startup
//C:\WINDOWS\Start Menu\Programs\Startup

function CreateLink(lpszPathObj, lpszPathLink, lpszDesc : PChar):HRESULT;
var
    hres : HRESULT;
    psl : IShellLink;
    ppf : IPersistFile;
    wsz : array[0..MAX_PATH] of WORD;
begin
    // Get a pointer to the IShellLink interface.
    hres := CoCreateInstance( CLSID_ShellLink, nil,
        CLSCTX_INPROC_SERVER, IID_IShellLinkA, psl);
    if (SUCCEEDED(hres)) then
    begin

        // Set the path to the shortcut target, and add the
        // description.
        if not SUCCEEDED(psl.SetPath(lpszPathObj)) then
          MessageBeep(0);

        if not (NOERROR =psl.SetDescription(lpszDesc)) then
          MessageBeep(0);

       // Query IShellLink for the IPersistFile interface for saving the
       // shortcut in persistent storage.
        hres := psl.QueryInterface(IID_IPersistFile, ppf);

        if (SUCCEEDED(hres)) then
        begin
            // Ensure that the string is ANSI.
            MultiByteToWideChar(CP_ACP, 0, lpszPathLink, -1,
                @wsz, MAX_PATH);
            // Save the link by calling IPersistFile::Save.
            hres := ppf.Save(@wsz, TRUE);
//            ppf._Release; //this line
        end;
//        psl._Release; //and this line
    end;
    Result := hres;
end;

I think since ppf and psl are declared as local static variables(although pascal treat all such class variables as dynamic) the memory allocator tries to dispose them upon exiting the procedure. Since we already release them with release, it raises and error on a code line that can not be seen. This is what I think of it, lets discuss on...,(I hear that you are asking me whether I declare them globally ;-) And since this is just a guess I do not know if it is same for all interface objects.

nice discussion,
regards, igor
I have moved the local vars to unit scope, then shortcut creating became succesfull. But the program gives the error upon termination...which indirectly support the guess...
Delphi (version 3 or higher) DOES REALLY FREE all interface correctly. Calling _Release might bring Delphi into problems. It's the same with strings. Delphi handles string allocation and deallocation for you. You don't have to do nothing. So you don't have with interfaces, either. Delphi 4 even treats dynamic arrays (with all sub-arrays and sub-strings and ...) correctly (I've tested that).

inter,

I've just worked a little bit on your example, showing some Delphi specific COM (and wide string) syntax enhancements:

function CreateLink(pathObj, pathLink, desc: string) : HRESULT;
var sl : IShellLink;
    pf : IPersistFile;
begin
  // Get a pointer to the IShellLink interface.
  sl:=CreateComObject(CLSID_ShellLink) as IShellLink;
  // Set the path to the shortcut target, and add the description.
  if sl.SetPath       (PChar(pathObj))<>NOERROR then MessageBeep(0);
  if sl.SetDescription(PChar(desc   ))<>NOERROR then MessageBeep(0);
  // Query IShellLink for the IPersistFile interface for saving the shortcut in persistent storage.
  pf:=sl as IPersistFile;
  result:=pf.Save(wideString(pathLink),true);
end;

Hope this helps... Madshi.
Or even shorter:

function CreateLink(pathObj, pathLink, desc: string) : HRESULT;
var sl : IShellLink;
begin
  // Get a pointer to the IShellLink interface.
  sl:=CreateComObject(CLSID_ShellLink) as IShellLink;
  // Set the path to the shortcut target, and add the description.
  if sl.SetPath       (PChar(pathObj))<>NOERROR then MessageBeep(0);
  if sl.SetDescription(PChar(desc   ))<>NOERROR then MessageBeep(0);
  // Query IShellLink for the IPersistFile interface for saving the shortcut in persistent storage.
  result:=(sl as IPersistFile).Save(wideString(pathLink),true);
end;

I love Delphi...   :-)
Oooops, two little corrections:

(1) result:=(sl as IPersistFile).Save(PWideChar(wideString(pathLink)),true);
(2) Application has to call "CoInitialize(nil)" at initialization and "CoUninitialize" at finalization.
OK, so this makes sense, and my app isn't crashing when I remove all "_Release".

But what happens when you use the same local variable more than once in a procedure?
Do you free all instances?  All instances except the last one you use?  None?

Mmm...  just when I thought I was understanding this stuff!!  :-)
Bye,
JB
JimBob,

there are two situations. If you've something like
  sf:=CreateComObject(IShellFolder) as IShellFolder;
  ...
  sf:=CreateComObject(IShellFolder) as IShellFolder;
in the same procedure, Delphi frees the first object for you. But there's another situation I'm not so sure about it. If you've somthing like
  sf:=CreateComObject(IShellFolder) as IShellFolder;
  sf.BindToObject(pidl,nil,IID_IShellFolder,pointer(sf));
I'm not sure what delphi does, because BindToObject wants to have a pointer as last parameter. Perhaps Delphi handles this right, too. I just don't know. Because of that I'm usually doing something like
  sf1:=CreateComObject(IShellFolder) as IShellFolder;
  sf1.BindToObject(pidl,nil,IID_IShellFolder,pointer(sf2));
  sf1:=sf2;
to work around this problem.

Regards, Madshi.
Ok, well that's OK because when I use IShellFolder's BindToObject I use 2 different objects:
RootFolder.BindToObject(pidlCurrent, nil, IID_IShellFolder, Pointer(CurrentFolder);

Cheers,
JB
Then should everything be fine, now...   :-)
Good.  Thanks Madshi - you've cleared up some stuff for me that I've been wondering about for a long time.  Now, does Delphi 4 work the same way?  I would assume that D4 works like D3.
Yes, D4 works like D3 in this matter. I think there was nothing to make better. The only difference I recognized is that with D4 I had to call "CoInitialize/CoUninitialize" which was not necessary with D3.
That's strange, because I tried a IShellFolder sample in D4 and it worked without CoInit & CoUninit.

That's strange, because I tried a IShellFolder sample in D4 and it worked without CoInit & CoUninit.

That's really strange. Perhaps you've not used CreateComObject but CoCreateInstance?
So the moral is 'DO NOT CALL RELEASE FOR INTERFACE OBJECTS' -just want to participate ;-)
Yes, this seems to be the lesson.  :-)
I've removed ALL my _AddRef & _Release and I haven't had a single AV since!

LowLevel: Are you stil with us??
Madshi: I actually don't use either CreateComObject or CoCreateInstance in my app, so that explains it.  I only use the following:

SHGetMalloc (to get IMalloc)
SHGetDesktopFolder (to get IShellFolder)
MyShFolder.BindToObject (to get IShellFolders)
MyShFolder.GetUIObject (to get IContextMenu, etc.)
MyShFolder.EnumObjects (to get IEnumIDList)
etc...

JB
JimBob,

yes, that will be the reason. I'm using SHGetDesktopFolder myself. But there's no such function for IShellLink. So if you ever use it, with Delphi 4 you'll probably have to do ConInit. No problem, of course...

Regards, Madshi.
P.S: You should not use _Release, but you HAVE to make sure that all the pidls (used with IShellFolder) are freed correctly. I think you're doing that!?

procedure FreePidl(var pidl: PItemIDList);
var malloc : IMalloc;
begin
  if (pidl<>nil) and (SHGetMalloc(malloc)=NOERROR) then begin
    malloc.Free(pidl);
    pidl:=nil;
  end;
end;
Hi Madshi

Yes, I am freeing the pidls, that's why I get IMalloc.

I will remember to use CoInit when using IShellLink (& others too).  Thanks again.

Bye,
JB
Avatar of lowlevel

ASKER

all:
I've checked out this answer yesterday, and it seems to work. I haven't solved all my problems yet, but that's due to the fact that my object hierarchy is getting a bit complex :)

inter: you were the first one, answer the question please so I can grade it.

jimBob : i'm still here :)
you said that you also removed most _addrefs. Assume two interfaces; iA and iB, implemented by txA and txB. Suppose iB is a property of iA, and iA is responsible for creating an instance of iB. Code in txA would be
procedure txA.initialize;
begin
..
  fB:=txB.create; //fB is a class private var of txB
  fB._addref;
  //optionally, init fB
..
end;

if I don't do the addRef, I get an AV because delphi's exit-procedure handler (I think that's it) kills my fB instance..
That's why I found it particularly weird, that I must call _addref at init but not _release at destroy. Seems inconsistent to me.


Thanks,
the other friends have much contribution, I'd like to
ask their opinion on grading if you do not mind...(sorry to hang you for a while)
regards, igor

np, I chose for you because I don't think EE supports multiple ppl getting credit for an answered Q. I chose you because you were first.

but i'm open to suggestions...

Good idea, although I think Madshi has tought us (me!!) something.

It's the spirit of Ex-Ex which is more important than who gets the points.

JB
ASKER CERTIFIED SOLUTION
Avatar of inter
inter
Flag of Türkiye image

Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
lowlevel,

no problem, give the points to igor (inter)...     :-)

I'm interested in you _addref problem. I don't understand why Delphi should release this fB when exiting txA.initialize. Delphi should do this only if fB were a local variable of txA.initialize.
Could you post a more complete example (compilable if possible) of what you're doing? Then I'll look a little bit closer at this "phenomenon".

Regards, Madshi.
madshi: new question titled "COM reference counting misteries in D3/D4"