[Solved] QueryFullProcessImageName API Function - can it be used in Lazarus? lazarus / 34-Windows ---------------------------------------------------------------------- TyneBridges · 2013-03-27 19:10 I've been unable to find a Windows API declaration in Lazarus (my search included Jedi) for QueryFullProcessImageName. Following a suggestion from another forum user, I've created the following code with my own header. I'm guessing I've got something wrong as the final parameter seems to be extremely problematic. This is a pointer to a DWord, which should be compatible with the type Cardinal. My problem is that I can't assign a value to it. It doesn't seem to make any difference whether I declare STR_SIZE as Cardinal or DWORD. The code below gives the compiler error "Can't assign values to an address" on the line Code: [Select]GotName:= QueryFullProcessImageName... If I omit the @ from @STR_SIZE, the compiler reports that the parameter type is wrong ("Got LongWord, expected LPDWORD"). Code: [Select]function QueryFullProcessImageName(hProcess: HANDLE; dwFlags: DWORD; var lpExeName: LPTSTR; var lpdwSize: LPDWORD): BOOL; stdcall; external 'KERNEL32.dll';  ...   function FindInProcesses(const PName: String): DWord;   const ListSize = 1024;   var      PIDList: array[1..Listsize] of DWORD;      cbNeeded, cbProcesses: DWORD;      I: integer;      hProcess: HWND;      hMod: HMODULE;      CurrentProcName: PChar;      GotName: Boolean;      STR_SIZE: DWORD;   begin        // Enumerate all processes on the system        EnumProcesses(@PIDList, ListSize, cbNeeded);        cbProcesses:= cbNeeded div SizeOf(DWORD);        Writeln('cbProcesses is ' + IntToStr(cbProcesses));        Result:= 0;        for i:= 1 to cbProcesses do        begin             Write('Process ID ' + IntToStr(PIDList[i]) + ' is ');             hProcess:= OpenProcess(PROCESS_QUERY_INFORMATION or PROCESS_VM_READ, FALSE, PIDList[i]);             EnumProcessModules(hProcess, @hMod, SizeOf(hMod), cbNeeded);             GetMem(CurrentProcName, 512);             STR_SIZE:= 512;             GotName:= QueryFullProcessImageName(hProcess, 0, CurrentProcName, @STR_SIZE);             If CurrentProcName = '' then                Writeln('Failed to get a process name')             else                Writeln(Trim(CurrentProcName) + '. ');             CloseHandle(hProcess);             If UpperCase(CurrentProcName) = UpperCase(PName) then             { Found the name. Set Result to the PID of process found }                  Result:= PIDList[i];             FreeMem(CurrentProcName, 512);        end; // For   end; // FindInProcesses Any ideas? ---------------------------------------------------------------------- marcov · 2013-03-27 19:24 It's either  "var dwsize:dword"  or "lpdwsize:lpdword" In the second case you need to pass @ in the first just Not all Vista and later calls have been added. ---------------------------------------------------------------------- TyneBridges · 2013-03-27 20:02 Quote from: marcov on March 27, 2013, 08:24:07 pmIt's either  "var dwsize:dword"  or "lpdwsize:lpdword" In the second case you need to pass @ in the first just Sorry, I still don't follow. I thought I was already doing as you suggest in the second case - using the declaration "lpdwsize: lpdword" and passing @STR_SIZE, but I then got the error I describe. ---------------------------------------------------------------------- marcov · 2013-03-27 20:38 Quote from: JohnSaltwell on March 27, 2013, 09:02:06 pmQuote from: marcov on March 27, 2013, 08:24:07 pmIt's either  "var dwsize:dword"  or "lpdwsize:lpdword" In the second case you need to pass @ in the first just Sorry, I still don't follow. I thought I was already doing as you suggest in the second case - using the declaration "lpdwsize: lpdword" and passing @STR_SIZE, but I then got the error I describe. [/quot With this declaration: Code: [Select]function QueryFullProcessImageName(hProcess: HANDLE; dwFlags: DWORD; var lpExeName: LPTSTR; lpdwSize: LPDWORD): BOOL; stdcall; external 'KERNEL32.dll';  ? ---------------------------------------------------------------------- TyneBridges · 2013-03-28 11:09 My mistake - I missed the fact that I'd included "var" when I shouldn't have. Tried again and, this time, the program compiled, but then gave an error on the function call - so I guess there is still something wrong with my header. Code: [Select]The procedure entry point QueryFullProcessImageName could not be located in the dynamic link library KERNEL32.dll. ---------------------------------------------------------------------- marcov · 2013-03-28 21:31 Quote from: JohnSaltwell on March 28, 2013, 12:09:24 pmMy mistake - I missed the fact that I'd included "var" when I shouldn't have. Tried again and, this time, the program compiled, but then gave an error on the function call - so I guess there is still something wrong with my header. Code: [Select]The procedure entry point QueryFullProcessImageName could not be located in the dynamic link library KERNEL32.dll. You are using Vista or later? As said, it is is a Vista+ call. ---------------------------------------------------------------------- TyneBridges · 2013-03-29 07:30 Quote from: marcov on March 28, 2013, 10:31:22 pmYou are using Vista or later? As said, it is is a Vista+ call. Yes, my OS is Windows 7 SP1 and the program is intended only to work on Windows 7. ---------------------------------------------------------------------- Leledumbo · 2013-03-29 08:23 As usual, Windows has A and W variant of its API, including this function. You should map the name to one of the variant, because they are what really exists in the kernel32 library. ---------------------------------------------------------------------- TyneBridges · 2013-03-29 12:43 Quote from: Leledumbo on March 29, 2013, 09:23:19 amAs usual, Windows has A and W variant of its API, including this function. You should map the name to one of the variant, because they are what really exists in the kernel32 library. Thanks - I thought the problem might be something like this. Unfortunately I don't know how to do the mapping. I have already tried changing all occurrences of QueryFullProcessImageName to QueryFullProcessImageNameA, recompiling and running; same error. I also tried this with QueryFullProcessImageNameW but got the same error again. I assume this means that all of the names need to be mapped regardless of which version Windows goes on to use. Another obvious thing to try is repeating the declaration twice with the name variations, but I've also tried that Code: [Select]function QueryFullProcessImageName(hProcess: HANDLE; dwFlags: DWORD; var lpExeName: LPTSTR;  var dwsize: DWORD): BOOL; stdcall; external 'KERNEL32.dll'; {$EXTERNALSYM QueryFullProcessImageName} function QueryFullProcessImageNameA(hProcess: HANDLE; dwFlags: DWORD; var lpExeName: LPTSTR;  var dwsize: DWORD): BOOL; stdcall; external 'KERNEL32.dll'; {$EXTERNALSYM QueryFullProcessImageNameA} function QueryFullProcessImageNameW(hProcess: HANDLE; dwFlags: DWORD; var lpExeName: LPTSTR;  var dwsize: DWORD): BOOL; stdcall; external 'KERNEL32.dll'; {$EXTERNALSYM QueryFullProcessImageNameW} ...and got the same error ("The procedure entry point...could not be located") yet again. I went on to try the following (slight adjustment of the lpExeName types in line with examples in Jedi) - same result, an error on the call. Code: [Select]function QueryFullProcessImageName(hProcess: HANDLE; dwFlags: DWORD; var lpExeName: LPTSTR;  var dwsize: DWORD): BOOL; stdcall; external 'KERNEL32.dll'; {$EXTERNALSYM QueryFullProcessImageName} function QueryFullProcessImageNameA(hProcess: HANDLE; dwFlags: DWORD; var lpExeName: LPSTR;  var dwsize: DWORD): BOOL; stdcall; external 'KERNEL32.dll'; {$EXTERNALSYM QueryFullProcessImageNameA} function QueryFullProcessImageNameW(hProcess: HANDLE; dwFlags: DWORD; var lpExeName: LPWSTR;  var dwsize: DWORD): BOOL; stdcall; external 'KERNEL32.dll'; {$EXTERNALSYM QueryFullProcessImageNameW} ---------------------------------------------------------------------- Leledumbo · 2013-03-29 16:19 QuoteUnfortunately I don't know how to do the mappingUse: Code: [Select]external 'LibraryName' name 'FunctionName'Here are some examples taken from ascdef.inc (part of windows unit): Code: [Select]function GetBinaryType(lpApplicationName:LPCSTR; lpBinaryType:LPDWORD):WINBOOL; external 'kernel32' name 'GetBinaryTypeA'; function GetShortPathName(lpszLongPath:LPCSTR; lpszShortPath:LPSTR; cchBuffer:DWORD):DWORD; external 'kernel32' name 'GetShortPathNameA'; function GetEnvironmentStrings : LPSTR; external 'kernel32' name 'GetEnvironmentStringsA'; function FreeEnvironmentStrings(_para1:LPSTR):WINBOOL; external 'kernel32' name 'FreeEnvironmentStringsA'; function FormatMessage(dwFlags:DWORD; lpSource:LPCVOID; dwMessageId:DWORD; dwLanguageId:DWORD; lpBuffer:LPSTR;nSize:DWORD; Arguments:va_list):DWORD; external 'kernel32' name 'FormatMessageA'; ---------------------------------------------------------------------- TyneBridges · 2013-03-29 17:33 Thanks to everyone for their help. In fact, I gave up on the function in the thread title. I did eventually manage to get it to run without errors but it still didn't work - all of the names it displayed consisted of the same 3 non-ASCII characters. Eventually I found that, for my use, CreateToolHelp32Snapshot and Process32First/Process32Next would give the names of all running processes, 32 and 64 bit. I'm posting the following in case it's useful to anyone else. In a console application, this will list all running processes and give as its result the process ID of the process name fed to it as a string (e.g. winlogon.exe). It's messy programming in at least one sense, as a function shouldn't really list things that aren't part of its result - but it does work. If you need the process names and the paths, unfortunately that's much harder. It proved to be too difficult for me but (luckily) all I really needed were the process names. Code: [Select]  function FindInProcesses(const PName: String): DWord;   var      i: integer;      CPID: DWORD;      CProcName: Array[0..259] of Char;      S: HANDLE;      PE: TProcessEntry32;   begin        Result:= 0;        CProcName:= '';        // Create snapshot        S:= CreateToolHelp32Snapshot(TH32CS_SNAPALL, 0);        // Set size before use        PE.DWSize:= SizeOf(PE);        I:= 1;        If Process32First(S, PE) then        repeat              CProcName:= PE.szExeFile;              CPID:= PE.th32ProcessID;              If CProcName = '' then                 Writeln(IntToStr(i) + ' - (' + IntToStr(CPID) + ') Failed to get a process name')              else                  Writeln(IntToStr(i) + ' - (' + IntToStr(CPID) + ') ' + Trim(CProcName) + '. ');              Inc(i);              If UpperCase(CProcName) = UpperCase(PName) then                 { Found the name. Set Result to the PID of process found }                 Result:= CPID;        until not Process32Next(S, PE);        CloseHandle(S);   end; // FindInProcesses ---------------------------------------------------------------------- BigChimp · 2013-03-30 09:00 @John: thanks for the code; posted modified version on http://wiki.lazarus.freepascal.org/Windows_Programming_Tips#Showing.2F finding_processes