Is it possible to debug a COM dll in VS2008? - visual-studio

This may be a very stupid question.
Is it possible to debug a COM dll in VS2008 for which I do not have the source code?
The reason I want to do this is I am passing an Array to a COM method and I expect this Array to be populated by the method.
However the Array is not being populated. So I want to step into the COM method to see whats happening. is this possible?
Below is an example of the code I am using:
Array binaryArray = Array.CreateInstance(typeof(sbyte), 896);
bool success = photo.GetBinaryData(binaryArray);
IDL for the GetBinaryData method:
[id(0x000000c9)]
HRESULT GetBinaryData(
[in] SAFEARRAY(char) buffer,
[out, retval] VARIANT_BOOL* retval);
The GetBinaryData method is the COM method which I would like to step into.
EDIT: Adding a Delphi test script which works
procedure TComTestForm.TestUserBtnClick(Sender: TObject);
var
nCnt :integer;
User :IUser;
Persona :IUserPersona;
ArrayBounds :TSafeArrayBound;
ArrayData :Pointer;
TagList :PSafeArray;
nSize :integer;
begin
User := Session.GetUser;
ArrayBounds.lLbound := 0;
ArrayBounds.cElements := 0;
TagList := SafeArrayCreate( varInteger, 1, ArrayBounds );
User.GetTags( TagList );
if SafeArrayAccessData( TagList, ArrayData ) = S_OK then
begin
nSize := TagList.rgsabound[0].cElements;
OutLine( '----Available Tags, ' + IntToStr(nSize) + ' tags' );
for nCnt := 0 to nSize - 1 do
begin
OutLine( IntToStr( IntegerArray(ArrayData)[nCnt] ) );
end;
OutLine( '----');
SafeArrayUnAccessData( TagList );
SafeArrayDestroy( TagList );
end;
end;

In principle, yes, you can step through the code of the COM method implementation instruction-by-instruction.
However, even if you know assembly well and understand exactly how all the processor instructions work, it's a tall order to debug someone else's code in this fashion unless it's a really, really simple method.
If you are new to assembler, don't even consider it unless you're prepared to do weeks of learning curve first.
If the COM method doesn't appear to be working in the way you expected based on its documentation, I would first try to test the method using unmanaged code (e.g. C++), as your problem may be in the COM Interop marshalling rather than in the COM method itself.

Related

Delphi: how can i get list of running applications with starting path?

Using Delphi (windows app) i want to get list of other applications running currently. Here How to check if a process is running using Delphi? i've found great tutorial about geting filenames/names of running application, however it gives names only process name (for example NOTEPAD.EXE). I've used naturally part with
UpperCase(ExtractFileName(FProcessEntry32.szExeFile))
and
UpperCase(ExtractFilePath(FProcessEntry32.szExeFile))
and just
UpperCase(FProcessEntry32.szExeFile)
but obviously FProcessEntry32.szExeFile does not have a path to file/process
Is there a simply way of getting list with paths? Here's How to get the list of running processes including full file path? solution with JclSysInfo library, but i cant use it in place of work in project.
I looked at what I could in Google and what I found usually concerned just the application that is running or the application that is active, but I can't just find a list of all running applications. Maybe i'm missing something obvious?
I'm not looking for any complex procedures, I'm not much interested in process parrent, or if there is no access to the process path, I don't have it and don't bother.
Any simple hint?
OK, due to helpfull comment from #TLama i've combined topics above to take name and path of process:
function processExists(exeFileName: string): Boolean;
var
ContinueLoopP, ContinueLoopM: BOOL;
FSnapshotHandle1, FSnapshotHandle2: THandle;
FProcessEntry32: TProcessEntry32;
FMODULEENTRY32: TMODULEENTRY32;
begin
FSnapshotHandle1 := CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
FProcessEntry32.dwSize := SizeOf(FProcessEntry32);
FMODULEENTRY32.dwSize := SizeOf(FMODULEENTRY32);
ContinueLoopP := Process32First(FSnapshotHandle1, FProcessEntry32);
ContinueLoopM := Module32First(FSnapshotHandle2, FMODULEENTRY32);
Result := False;
while Integer(ContinueLoopP) <> 0 do
begin
if ((UpperCase(ExtractFileName(FProcessEntry32.szExeFile)) =
UpperCase(ExeFileName)) or (UpperCase(FProcessEntry32.szExeFile) =
UpperCase(ExeFileName))) then
Result := True;
ShowMessage(FMODULEENTRY32.szExePath + FProcessEntry32.szExeFile);
ContinueLoopP := Process32Next(FSnapshotHandle1, FProcessEntry32);
ContinueLoopM := Module32Next(FSnapshotHandle2, FMODULEENTRY32);
end;
CloseHandle(FSnapshotHandle1);
CloseHandle(FSnapshotHandle2);
end;
But still FProcessEntry32.szExeFile returns empty string. What i'm doing wrong? Thank You in advance.
I cannot write comment (low score), so I need to write as "answer". Try this code,
using FProcessEntry32.th32ProcessID as parameter:
Function QueryFullProcessImageNameW(hProcess:THandle; dwFlags:Cardinal; lpExeName:PWideChar; Var lpdwSize:Cardinal) : Boolean; StdCall; External 'Kernel32.dll' Name 'QueryFullProcessImageNameW';
Function GetFullPath(Pid:Cardinal) : UnicodeString;
Var rLength:Cardinal;
Handle:THandle;
Begin Result:='';
Handle:=OpenProcess(PROCESS_QUERY_INFORMATION, False, Pid);
If Handle = INVALID_HANDLE_VALUE Then Exit;
rLength:=256; // allocation buffer
SetLength(Result, rLength+1); // for trailing space
If Not QueryFullProcessImageNameW(Handle, 0, #Result[1],rLength) Then Result:='' Else SetLength(Result, rLength);
End;
This is a simple way I think. If you want to get the loaded DLL's full name, use
FMODULEENTRY32.hModule with GetModuleFileNameW function.

How do use destruct and create correctly

I have a Form. When the user clicks the TESTBUTON an array is generated (here with a loop) and an array is filled. (that works).
Now the user will be able to change some parameters an hit the button again.
Than I want to clear / free / destroy the old array an create it new.
I found a lot of examlpes for that but they not work (because I do not know where exatly to place the different procedures).
So I made this samplescript with all the sections.
Can someone move my procedures to the rigth place or send me an example that shows the correct implementation.
unit frmmywindow;
interface
uses
type
TArrayA = record
Field1:integer;
Field2:integer;
Field3:integer;
Field4:integer;
String5:string;
//other fields, strings, integers..
end;
private
{ private declarations }
public
{ public declarations }
destructor Destroy; override;
end;
var
var ArrayA : array of TarrayA;
implementation
destructor TArrayA.Destroy;
begin
ArrayA.Free;
inherited;
end;
procedure TArrayA.Free;
begin
if Assigned(self) then Destroy;
end;
procedure TForm1.btnTest(Sender: TObject);
var
x: integer;
reccount: integer;
begin
ArrayA.free:
ArrayA.create;
reccount := 1000;
for x := 1 to reccount do
begin
setLength(ArrayA,x+1);
ArrayA[x].field1 := 2000 - x;
ArrayA[x].field2 := x;
ArrayA[x].field3 := x;
ArrayA[x].field4 := x;
ArrayA[x].string5 := 'str' + inttostr(x);
end;
end;
Your code has a number of issues.
The main issue is that TArrayA is a record (normally I would link to the DocWiki documentation for structured types, but it seems to be down right now). Records are not classes, they don't have a destructor and you should not call Free on them. Records are so called value types. They don't even have a proper constructor, even if the syntax suggests they do. Record "constructors" are mere initializers.
Another issue is that you should never code Free yourself, not even for classes. Free is inherited from the root for all class instances, TObject. For classes, if you want to give it a destructor, override the inherited destructor:
destructor Destroy; override;
So the answer is: you don't use nor define them at all, for records.
How you should declare, define and use them for classes is described in the documentation.
As I already commented, you should get better acquainted with the language. I suggest you read the Delphi or Object Pascal Language Guide (name differs, depending on version), which is part of the documentation that is installed with Delphi.

Assigning procedures to variables and calling them in Pascal

I have a small terminal program which displays a menu. I want to have a function which takes the user's input and an array of procedure names and be able to call the procedure the user chose. I know that I could do that using if or case statements in the main program, but I want to make a unit with the above procedure and a couple of other menu functions, and if this was possible, I could make it call arbitrary procedures. Here's more or less how I would like it to work (I know it's wrong, but so you get a general idea).
program menu;
uses crt;
type procs = array [0..1] of procedure;
procedure call_procs(inp: int; procsy: procs);
begin
writeln(procsy[ord(inp)]); {And call it here.}
end;
var procsx : procs;
begin
procsx[0] := readkey; {I would like to somehow store the procedure here.}
procsx[1] := clrscr;
call_procs(0, procsx);
end.
Is there any way to do something like this? Thank you in advance.
There are a few things wrong with your original code which are not cited in your answer.
You have an array of procedure but you are calling writeln with these procedure calls as arguments as if they were function, which they are not.
readkey is a function, not a procedure, so its type doesn't match the element type of your array
Your assignment of the procedures to the array need to use # to reference the procedure pointer and not actually call the procedure
Not sure what compiler or options you're using, but int isn't the standard Pascal integer type, rather integer is.
As a niggle, since you're already using the integer index of the array, you don't need to use ord.
So the minimal changes to your code to make it basically work would be:
program menu;
uses crt;
type procs = array [0..1] of procedure;
procedure call_procs(inp: integer; procsy: procs);
begin
procsy[inp]; { call the procedure here - removed 'ord' since it's superfluous }
end;
var procsx : procs;
begin
{ procsx[0] := readkey; {- 'readkey' is a function and won't work here }
procsx[1] := #clrscr;
call_procs(1, procsx);
end.
You can create an array of functions that return char which matches the type for readkey:
program menu;
uses crt;
type procs = array [0..1] of function: char;
procedure call_procs(inp: integer; procsy: procs);
begin
writeln(procsy[inp]); { call the function; write out the returned char }
end;
function foo: char;
begin
foo := 'X';
end;
var procsx : procs;
begin
procsx[0] := #readkey;
procsx[1] := #foo;
call_procs(0, procsx);
call_procs(1, procsx);
end.
I figured out how to do this. One can use pointers to a procedure, then create an array of those pointers, and pass them to the procedure I wanted to use. Also, for some reason, it doesn't seem to work with the functions that come with Pascal (such as readkey or clrscr). For this example, one could do this:
program menu;
type
Tprocptr = procedure; {This creates a pointer to procedures.}
Tprocarray = array of Tprocptr;
procedure writeHi;
begin
writeln('Hi!');
end;
procedure writeHello;
begin
writeln('Hello!');
end;
procedure call_proc(inp: integer; procsy: Tprocarray);
{This now calls functions like one would expect.}
begin
procsy[ord(inp)];
end;
var
proclist : Tprocarray;
begin
setlength(proclist, 2);
proclist[0] := #writeHi; {The '#' creates a pointer to those procedures.}
proclist[1] := #writeHello;
call_proc(0, proclist);
end.
This works as expected, calling (in this case) the procedure writeHi, so if you run this program it will output Hi! to the terminal. If you change call_proc(0,proclist) to call_proc(1, proclist), it will call writeHello instead.

Any method to cast a Handle?

Good day,
I have different handles from different non VLC Objects like (SysListView32, ToolbarWin32) and I am wondering if there is any method to cast these handles (HWND).
For example, I got the start button(which is in the left bottom of desktop) handle. Then I found the class name "Button".
I would like to cast him and retrieve from him the caption property "start". For example:
type
TButtonStartMenuFictiveClass = class(TButton)
public
Text: string;
end;
if classname = 'button' then
begin
ShowMessage((objecthandle as TButtonStartMenuFictiveClass).Text);
end;
I am looking to hook all of the objects and to display the text of them. Like the narrator from Windows.
In some cases, you can instantiate a VCL object and assign the external HWND to its WindowHandle property, eg:
var
S: String;
with TButton.Create(nil) do
try
WindowHandle := TheButtonWnd;
try
S := Caption;
finally
WindowHandle := 0; // important
end;
finally
Free;
end;
As I explained in your previous question, you need to use the Windows API to gain access to the properties of a foreign window-control. You can't simply cast a window-handle to an object. They're not pointers to Delphi objects.
The example I linked you too not only shows the classname of the control you're hovering over, but also the caption (text) of the control. This function will also do the trick:
function GetWndText(const Handle: Hwnd): string;
var
Len: Integer;
begin
Len := GetWindowTextLength(Handle) + 1;
SetLength(Result, Len);
GetWindowText(Handle, PChar(Result), Len);
end;

TNetSharingManager access violation problem

I'm trying to compile this project in Delphi 2010, which uses TNetSharingManager. I have imported the type library and tried compiling it, but unfortunately I'm getting an Access Violation in this function:
function TNetSharingManager.GetDefaultInterface: INetSharingManager;
begin
if FIntf = nil then
Connect;
Assert(FIntf nil, 'DefaultInterface is NULL. Component is not connected to Server. You must call "Connect" or "ConnectTo" before this operation');
Result := FIntf;
end;
(part of NETCONLib_TLB)
The error is in : if FIntf = nil then for some odd reason..
The code which is calling it:
procedure TForm1.GetConnectionList(Strings,IdList: TStrings);
var
pEnum: IEnumVariant;
vNetCon: OleVARIANT;
dwRetrieved: Cardinal;
pUser: NETCONLib_TLB.PUserType1;
NetCon : INetConnection;
begin
Strings.Clear;
IdList.Clear;
pEnum := ( NetSharingManager.EnumEveryConnection._NewEnum as IEnumVariant);
while (pEnum.Next(1, vNetCon, dwRetrieved) = S_OK) do
begin
(IUnknown(vNetCon) as INetConnection).GetProperties(pUser);
NetCon := (IUnknown(vNetCon) as INetConnection);
if (pUser.Status in [NCS_CONNECTED,NCS_CONNECTING])//remove if you want disabled NIC cards also
and (pUser.MediaType in [NCM_LAN,NCM_SHAREDACCESSHOST_LAN,NCM_ISDN] )
and (GetMacAddress(GuidToString(pUser.guidId))'' ) then
begin
//we only want valid network cards that are enabled
Strings.Add(pUser.pszwName );
IdList.Add(GuidToString(pUser.guidId));
end;
end;
end;
I don't understand why I cannot compare with nil. Any ideas?
It is likely the TNetSharingManager object itself has actually died (or wasn't created in the first place) when that error is triggered. The FIntF = nil expression is the first reference to an actual field of the class, i.e. it will be pointing into invalid address space.
[Edit] I download the source and followed the steps to import the TLB (Delphi 2010). To execute the appilcation, I had to (a) run Delphi as an admin, because I'm not a power user by default and (b) had to add a check for pUser <> nil because the final getProperties returns a nil-structure, but other than that the code run fine. So unfortunately, I can't seem to reproduce your problem.
Rereading your question, are you getting an AV while compiling?

Resources