Results 1 to 40 of 40

Thread: using a UDT in multiple applications

  1. #1

    Thread Starter
    I'm about to be a PowerPoster! kleinma's Avatar
    Join Date
    Nov 2001
    Location
    NJ - USA (Near NYC)
    Posts
    23,373

    using a UDT in multiple applications

    I have a DLL that has a UDT in it for employee

    so it looks something like this

    Public Type udtEmp
    FirstName as string
    LastName as string
    EmpID as Long
    End Type

    etc...

    anyways.. so i create the udt object in the first application.. and in the dll there is a public method to fill the udt with data using the userid which is passed to the method.. so i have a second exe that is called from the first one.. is there a way to get the variable info for the UDT in the second app from the first one.. like copying it from memory or something? right now i use command line params to pass the userID to child apps and run the dll's method again to fill a new UDT var in the child exe.. but i am trying to make my app more efficient.. what do you guys think?

  2. #2
    Retired VBF Adm1nistrator plenderj's Avatar
    Join Date
    Jan 2001
    Location
    Dublin, Ireland
    Posts
    10,359
    Microsoft MVP : Visual Developer - Visual Basic [2004-2005]

  3. #3

    Thread Starter
    I'm about to be a PowerPoster! kleinma's Avatar
    Join Date
    Nov 2001
    Location
    NJ - USA (Near NYC)
    Posts
    23,373
    Originally posted by plenderj
    I had a lot of pain with this

    http://www.vbforums.com/showthread.p...hreadid=231585
    http://www.vbforums.com/showthread.p...hreadid=231604
    http://www.vbforums.com/showthread.p...hreadid=231606


    Is it just a single UDT that you want to transfer over ?
    yeah just 1 udt... the udt itself has about 10 different string or long vars in it. .let me look at those links..

  4. #4

    Thread Starter
    I'm about to be a PowerPoster! kleinma's Avatar
    Join Date
    Nov 2001
    Location
    NJ - USA (Near NYC)
    Posts
    23,373
    hmm not sure if they will help me because they are dealing with arrays.. and woka is a nut

    basically i dim the UDT in exe1.. fill the UDT in exe1 using the dlls GetEmpInfo method... and when i shell exe2.. i want to dim the UDT again in exe2 and use copymemory or something like that to fill the UDT in exe2 with the same data.. i could leave it how it is now and call the GetEmpInfo method in each app i suppose... i just thought this might be more efficent to have less hits to the database to get all the employee info

  5. #5
    Frenzied Member MerrionComputin's Avatar
    Join Date
    Apr 2001
    Location
    Dublin, Ireland
    Posts
    1,616
    First a quick rant about using classes rather than UDTs.
    ...mfnfng fng n dnmfn...

    Right - if you must copy a UDT from one executable to another then you need to use the Readprocessmemory API call. I'd recommend you define the UDT in a .bas file and share it between the two exes for ease of use...then:

    VB Code:
    1. Public Type udtEmp
    2.    FirstName as String
    3.    LastName as String
    4.    EmpID as Long
    5. End Type
    6.  
    7. Public Type udtEmp_Shadow
    8.    lpFirstName As Long
    9.    lpLastName As Long
    10.    EmpId As Long
    11. End Type
    12.  
    13. Private Declare Function OpenProcess Lib "kernel32" (ByVal dwDesiredAccess As ProcessAccessPriviledges, ByVal bInheritHandle As Long, ByVal dwProcessId As Long) As Long
    14.  
    15. Private Declare Function ReadProcessMemory Lib "kernel32" (ByVal hProcess As Long, ByVal lpBaseAddress As Long, lpBuffer As Any, ByVal nSize As Long, lpNumberOfBytesWritten As Long) As Long
    16.  
    17. Public Function LongFromOutOfprocessPointer(ByVal hProcess As Long, ByVal lpAddress As Long) As Long
    18.  
    19. Dim lRet As Long
    20. Dim lBytesWritten As Long
    21.  
    22. Call ReadProcessMemory(hProcess, lpAddress, ByVal VarPtr(lRet), Len(lRet), lBytesWritten)
    23. If lBytesWritten > 0 Then
    24.     LongFromOutOfprocessPointer = lRet
    25. End If
    26.  
    27. End Function
    28.  
    29. Public Function IntegerFromOutOfprocessPointer(ByVal hProcess As Long, ByVal lpAddress As Long) As Integer
    30.  
    31. Dim lRet As Integer
    32. Dim lBytesWritten As Long
    33.  
    34. Call ReadProcessMemory(hProcess, lpAddress, ByVal VarPtr(lRet), Len(lRet), lBytesWritten)
    35. If lBytesWritten > 0 Then
    36.     IntegerFromOutOfprocessPointer = lRet
    37. End If
    38.  
    39. End Function
    40.  
    41. Public Function StringFromOutOfProcessPointer(ByVal hProcess As Long, ByVal lpString As Long, ByVal Length As Long, ByVal Unicode As Boolean) As String
    42.  
    43. Dim buf() As Byte
    44. Dim lRet As Long
    45. Dim lBytesWritten As Long
    46. Dim sTemp As String
    47.  
    48. ReDim buf(Length) As Byte
    49.  
    50. lRet = ReadProcessMemoryBytes(hProcess, lpString, buf(0), Length, lBytesWritten)
    51. If lBytesWritten = 0 And Err.LastDllError = 0 Then
    52.     While lBytesWritten = 0 And Length > 0
    53.         Length = Length - 1
    54.         lRet = ReadProcessMemoryBytes(hProcess, lpString, buf(0), Length, lBytesWritten)
    55.     Wend
    56. Else
    57.     If Err.LastDllError Then
    58.         Debug.Print LastSystemError
    59.     End If
    60. End If
    61. If lRet <> 0 Then
    62.     If Unicode Then
    63.         StringFromOutOfProcessPointer = StrConv(buf, vbFromUnicode)
    64.     Else
    65.         For lRet = 0 To lBytesWritten
    66.             If buf(lRet) = 0 Then
    67.                 Exit For
    68.             End If
    69.             sTemp = sTemp & Chr$(buf(lRet))
    70.         Next lRet
    71.         StringFromOutOfProcessPointer = sTemp
    72.     End If
    73. Else
    74.     If Err.LastDllError Then
    75.         Debug.Print LastSystemError
    76.     End If
    77. End If
    78.  
    79. End Function

    Then execuatble 1 sends executable 2 a registered windows message (as per the IPC using registered messages article) which tells it the address of the structure to be copied...this function must block until the second exe has read that structure or the memory address may be invalidated and your app(s) will fall over.

    ...more...
    ----8<---------------------------------------
    NEW - The .NET printer queue monitor component
    ----8<---------------------------------------
    Now with Examples of use

  6. #6

    Thread Starter
    I'm about to be a PowerPoster! kleinma's Avatar
    Join Date
    Nov 2001
    Location
    NJ - USA (Near NYC)
    Posts
    23,373
    Originally posted by MerrionComputin
    First a quick rant about using classes rather than UDTs.
    ...mfnfng fng n dnmfn...
    what is my benefit to creating a class for this?

  7. #7
    Frenzied Member MerrionComputin's Avatar
    Join Date
    Apr 2001
    Location
    Dublin, Ireland
    Posts
    1,616
    On receiving this message, exe 2 copies the _Shadow udt thus:

    VB Code:
    1. Dim tmpUDT As udtEmp
    2. Dim tmpUDTShadow As udtEmp_Shadow
    3. Dim lBytesWritten As Long, lRet As Long
    4. Dim hProc As Long
    5.  
    6. hProc = OpenProcess(PROCESS_VM_READ,False,procid)
    7. If hProc Then
    8.    lRet = ReadProcessMemory(hProc, lpUDT, VarPtr(tmpUDTShadow), Len(tmpUDTShadow), lBytesWritten)
    9.    If lBytesWritten = Len(tmpUDTShaow) Then
    10.         With tmpUDT
    11.              .FirstName = StringFromOutOfProcessPointer(hProc, tmpUDTShadow.lpFirstName, 1024)
    12.              .LastName = StringFromOutOfProcessPointer(hProc, tmpUDTShadow.lpLastName, 1024)
    13.               .EmpId = tmpUDTShadow.EmpId
    14.         End With
    15.    End If
    16. End If
    ----8<---------------------------------------
    NEW - The .NET printer queue monitor component
    ----8<---------------------------------------
    Now with Examples of use

  8. #8
    Frenzied Member MerrionComputin's Avatar
    Join Date
    Apr 2001
    Location
    Dublin, Ireland
    Posts
    1,616
    what is my benefit to creating a class for this?
    Public classes can be passed between ActiveX applications.
    ----8<---------------------------------------
    NEW - The .NET printer queue monitor component
    ----8<---------------------------------------
    Now with Examples of use

  9. #9

    Thread Starter
    I'm about to be a PowerPoster! kleinma's Avatar
    Join Date
    Nov 2001
    Location
    NJ - USA (Near NYC)
    Posts
    23,373
    Originally posted by MerrionComputin
    Public classes can be passed between ActiveX applications.
    but these are regular VB exes not activex exes.... the UDT and method for filling it are in an activex DLL

  10. #10
    Frenzied Member KayJay's Avatar
    Join Date
    Jul 2001
    Location
    Chennai
    Posts
    1,849

    Talking Just can't stay out! Can I?

    Duncan: Variable Length strings in the UDT? How will the "length" in the function get its value?

    Both the apps need to have an uber-UDT to handle them, with the values in one element, and the lengths in another and pass them to the second app for restructuring.

    "Brothers, you asked for it."
    ...Francisco Domingo Carlos Andres Sebastian D'Anconia

  11. #11
    Frenzied Member MerrionComputin's Avatar
    Join Date
    Apr 2001
    Location
    Dublin, Ireland
    Posts
    1,616
    Duncan: Variable Length strings in the UDT? How will the "length" in the function get its value?
    Either assume a maximum or pass the length in as another member of the udt.
    ----8<---------------------------------------
    NEW - The .NET printer queue monitor component
    ----8<---------------------------------------
    Now with Examples of use

  12. #12
    PowerPoster techgnome's Avatar
    Join Date
    May 2002
    Posts
    34,687
    Originally posted by kleinma
    but these are regular VB exes not activex exes.... the UDT and method for filling it are in an activex DLL
    Any reason for keeping it (App2) as a regular EXE and not an ActiveX EXE? I'm thinking that if you can change it to an ActiveX EXE, then you might be able to pass the UDT when calling your entrypoint....
    Otherwise I think MC is right..... things will need to be copied through memory.
    * I don't respond to private (PM) requests for help. It's not conducive to the general learning of others.*
    * I also don't respond to friend requests. Save a few bits and don't bother. I'll just end up rejecting anyways.*
    * How to get EFFECTIVE help: The Hitchhiker's Guide to Getting Help at VBF - Removing eels from your hovercraft *
    * How to Use Parameters * Create Disconnected ADO Recordset Clones * Set your VB6 ActiveX Compatibility * Get rid of those pesky VB Line Numbers * I swear I saved my data, where'd it run off to??? *

  13. #13

    Thread Starter
    I'm about to be a PowerPoster! kleinma's Avatar
    Join Date
    Nov 2001
    Location
    NJ - USA (Near NYC)
    Posts
    23,373
    Originally posted by techgnome
    Any reason for keeping it (App2) as a regular EXE and not an ActiveX EXE? I'm thinking that if you can change it to an ActiveX EXE, then you might be able to pass the UDT when calling your entrypoint....
    Otherwise I think MC is right..... things will need to be copied through memory.
    yeah these apps get updated often and i dont want to have to register it each time as i would with an activex exe... so they are regular exes and they are accessed from a shared drive on the network.. so i dont have to reinstall them on each PC when there is an update

  14. #14
    Frenzied Member MerrionComputin's Avatar
    Join Date
    Apr 2001
    Location
    Dublin, Ireland
    Posts
    1,616
    If you are only working on windows NT and its derivatives you could create a named pipe and send the UDTs down it...
    ----8<---------------------------------------
    NEW - The .NET printer queue monitor component
    ----8<---------------------------------------
    Now with Examples of use

  15. #15

    Thread Starter
    I'm about to be a PowerPoster! kleinma's Avatar
    Join Date
    Nov 2001
    Location
    NJ - USA (Near NYC)
    Posts
    23,373
    sounds like i might be better off leaving it how it is... i mean it works now.. i was just looking for places to clean up code.. but this all sounds like it will be more work that the way it works now

  16. #16
    Super Moderator Wokawidget's Avatar
    Join Date
    Nov 2001
    Location
    Headingly Occupation: Classified
    Posts
    9,632
    Originally posted by kleinma
    hmm not sure if they will help me because they are dealing with arrays.. and woka is a nut
    If only you knew the truth
    OK, I am a tad confused by this whole out of process crap
    I have in one exe:
    VB Code:
    1. Dim udtData As typClient
    2.    udtData.hWnd = mlngHWND
    3.    udtData.AppName = "My Fishy Turtles"
    4.  
    5. lngServerhWnd = FindWindow(vbNullString, WINDOWTITLE_SERVER)
    6.         SendMessageLong lngServerhWnd, WMAttachClient,  '??????????
    and in my server I have:
    VB Code:
    1. Public Function VB_WindowProc(ByVal hwnd As Long, ByVal wMsg As Long, ByVal wParam As Long, ByVal lParam As Long) As Long
    2. Dim udtClient    As typClient
    3.     If wMsg = WMAttachClient Then
    4.           '????????????
    5.     ElseIf wMsg = WMDetachClient Then
    6.        
    7.     Else
    8.         VB_WindowProc = CallWindowProc(mlngOldProc, hwnd, wMsg, wParam, lParam)
    9.     End If
    10. End Function
    I can't work out the code you have posted. What would I need to add into the ????????'s to pass this UDT?

    Woka

    PS in the hProcess = hWnd ???

  17. #17
    Frenzied Member MerrionComputin's Avatar
    Join Date
    Apr 2001
    Location
    Dublin, Ireland
    Posts
    1,616
    Worked example

    If we take the following code and run it on my PC:
    VB Code:
    1. Option Explicit
    2.  
    3. Private Type ClientUDT
    4.   Name As String
    5.   Age As Long
    6.   IsHuman As Boolean
    7. End Type
    8.  
    9.  
    10. Private Sub Form_Load()
    11.  
    12. Dim clntThis As ClientUDT
    13.  
    14. With clntThis
    15.     .Name = "Duncan"
    16.     .Age = 32
    17.     .IsHuman = False
    18. End With
    19.  
    20. Debug.Print VarPtr(clntThis)
    21. Debug.Print VarPtr(clntThis.Name)
    22. Debug.Print VarPtr(clntThis.Age)
    23. Debug.Print VarPtr(clntThis.IsHuman)
    24.  
    25. End Sub

    This returns:
    1243700
    1243700
    1243704
    1243708
    Which shows that the structure clntThis is sited at address 1243700 and takes up 12 bytes.
    However it is fairly obvious that you can't fit "Duncan" in 8 bytes ( especially at my current weight ) .

    If we change the structure to have a fixed length string e.g.:
    VB Code:
    1. Private Type ClientUDT
    2.   Name As String * 20
    3.   Age As Long
    4.   IsHuman As Boolean
    5. End Type
    Then the results change to:
    1243664
    1243652
    1243704
    1243708
    Which is 48 bytes and can fit "Duncan".

    Therefore we can see that if the string in a UDT is fixed length it is stored in loine in the structure but if it is variable length it is stored elsewhere and a pointer to where it is stored is held in the structure.

    If we go back to the variable length store dproic and run
    VB Code:
    1. Debug.Print VarPtr(clntThis.Name)
    2. Debug.Print StrPtr(clntThis.Name)
    This returns:
    1243700
    1587500
    And if you peek the address 1243700 then you will see that it actually holds the value 1587500.

    '...to be continued....
    ----8<---------------------------------------
    NEW - The .NET printer queue monitor component
    ----8<---------------------------------------
    Now with Examples of use

  18. #18
    Frenzied Member MerrionComputin's Avatar
    Join Date
    Apr 2001
    Location
    Dublin, Ireland
    Posts
    1,616
    and if you peak the address at 1587496 (one 32 bit word less than StrPtr() returns) you get the value 6...which is the length of the string "Duncan" without the /0 terminator.

    So from this you can see that so long as you know the format of the individual fields it is possible to recreate the structure from the address thus:-

    VB Code:
    1. Option Explicit
    2.  
    3. Private Type ClientUDT
    4.   Name As String
    5.   Age As Long
    6.   IsHuman As Boolean
    7. End Type
    8.  
    9.  
    10. '\\ API declarations...
    11. Private Declare Sub CopyMemory Lib "kernel32" Alias "RtlMoveMemory" (Destination As Long, Source As Long, ByVal Length As Long)
    12. Private Declare Sub CopyMemoryByte Lib "kernel32" Alias "RtlMoveMemory" (Destination As Byte, Source As Long, ByVal Length As Long)
    13. Private Declare Sub CopyMemoryFromByte Lib "kernel32" Alias "RtlMoveMemory" (Destination As Long, Source As Byte, ByVal Length As Long)
    14.  
    15. Public Function PeekByte(Address As Long) As Byte
    16.  
    17. Call CopyMemoryByte(PeekByte, ByVal Address, Len(PeekByte))
    18.  
    19. End Function
    20.  
    21. Public Function Peek(Address As Long) As Long
    22.  
    23. Call CopyMemory(Peek, ByVal Address, Len(Address))
    24.  
    25. End Function
    26.  
    27. Private Function UDTFromAddress(ByVal lpUDT As Long) As ClientUDT
    28.  
    29. Dim udtRet As ClientUDT
    30.  
    31. Dim sLen As Long
    32. Dim lpString As Long
    33. Dim btChars() As Byte
    34. Dim lChar As Long
    35.  
    36. lpString = Peek(lpUDT)
    37. sLen = Peek(lpString - 4)
    38.  
    39. With udtRet
    40.     .Age = Peek(lpUDT + 4)
    41.     .IsHuman = Peek(lpUDT + 8)
    42.     For lChar = 0 To sLen - 1
    43.         .Name = .Name & Chr$(PeekByte(lpString + (lChar * 2)))
    44.     Next lChar
    45. End With
    46.  
    47. UDTFromAddress = udtRet
    48.  
    49. End Function
    50.  
    51. Private Sub Form_Load()
    52.  
    53. Dim clntThis As ClientUDT
    54.  
    55. Dim clntOut As ClientUDT
    56.  
    57. With clntThis
    58.     .Name = "Duncan"
    59.     .Age = 32
    60.     .IsHuman = False
    61. End With
    62.  
    63. clntOut = UDTFromAddress(VarPtr(clntThis))
    64.  
    65. With clntOut
    66.     Debug.Print .Name
    67.     Debug.Print .Age
    68.     Debug.Print .IsHuman
    69. End With
    70.  
    71. End Sub

    However, as I noted earlier the address is local to the process. (With the weird street / house number analogy) so if we want to copy address 1587496 from another application, this code will actually look at that address in the current application and because that might not hold anything at all this will most likely crash the application.

    Instead we need to use the API call ReadProcessmemory to get information from the other application's memory space.

    '...to be continued..although less sensibly as I'm off to the pub now....
    Last edited by MerrionComputin; Mar 28th, 2003 at 12:24 PM.
    ----8<---------------------------------------
    NEW - The .NET printer queue monitor component
    ----8<---------------------------------------
    Now with Examples of use

  19. #19
    I wonder how many charact
    Join Date
    Feb 2001
    Location
    Savage, MN, USA
    Posts
    3,704
    Anyway, Merrion, when is your first book coming out?

    Also, have any open positions in your firm?

  20. #20
    Frenzied Member MerrionComputin's Avatar
    Join Date
    Apr 2001
    Location
    Dublin, Ireland
    Posts
    1,616
    Also, have any open positions in your firm?
    We're effectively bankrupt so not the best time to be recruiting
    ----8<---------------------------------------
    NEW - The .NET printer queue monitor component
    ----8<---------------------------------------
    Now with Examples of use

  21. #21
    Super Moderator Wokawidget's Avatar
    Join Date
    Nov 2001
    Location
    Headingly Occupation: Classified
    Posts
    9,632
    '...to be continued..although less sensibly as I'm off to the pub now....
    Arrrrrrrr....! Noooooooo...where's the code on reading memory from another app? You can't stop there....

    Good tutorial
    Cheers.

    Woka

  22. #22
    Frenzied Member MerrionComputin's Avatar
    Join Date
    Apr 2001
    Location
    Dublin, Ireland
    Posts
    1,616
    I'm back now...and the full Irish breakfast is working it's magic so the rest of the code will be along presently...
    ----8<---------------------------------------
    NEW - The .NET printer queue monitor component
    ----8<---------------------------------------
    Now with Examples of use

  23. #23
    Retired VBF Adm1nistrator plenderj's Avatar
    Join Date
    Jan 2001
    Location
    Dublin, Ireland
    Posts
    10,359
    Duncan, you really should write a book ya know.
    I've ****all money to spare, but I'd buy it!
    Microsoft MVP : Visual Developer - Visual Basic [2004-2005]

  24. #24
    Frenzied Member MerrionComputin's Avatar
    Join Date
    Apr 2001
    Location
    Dublin, Ireland
    Posts
    1,616
    Part 2 - Reading another process' memory
    If you run several copies of this above program you will notice that they all seem to use the samem meory addresses. It is quite obvious that the two programs aren't actually running in the same area of memory or they would trample all over each other so there must be some other explanation.

    As it turns out the address returned by VarPtr and used in CopyMemory is not an absolute address but rather it is expressed relative to the base address of the process. What this means is that if I load two programs, one might load at the address 160000 and the next instance load at 240000. If I query and address in the one process and it says it is 100 this means 16000 + 100 - and similarily the same address in the second process would mean 240000 + 100.
    (warning: this is an over simplification as in reality there's a whole load of funky paging stuff going on but it serves to illustrate the prinicple).
    Therefore if you know the base address of a process you can add the relative address and get the true address of another process' memory. This is how a debugger works, for instance.

    As it happens Win32 includes a number of functions originally written to allow people to design debuggers which allow you to access another process' memory - ReadProcessmemory being the most important.

    '...goes for more coffee....
    ----8<---------------------------------------
    NEW - The .NET printer queue monitor component
    ----8<---------------------------------------
    Now with Examples of use

  25. #25
    Frenzied Member MerrionComputin's Avatar
    Join Date
    Apr 2001
    Location
    Dublin, Ireland
    Posts
    1,616
    The VB declaration of ReadProcessmemory is:
    VB Code:
    1. Declare Function ReadProcessMemoryLong Lib "kernel32" _
    2.    Alias "ReadProcessMemory" (ByVal hProcess As Long, _
    3.           ByVal lpBaseAddress As Long, ByVal lpBuffer As Long, _
    4.           ByVal nSize As Long, _
    5.            lpNumberOfBytesWritten As Long) As Long

    Where
    hProcess is a handle to the other application which has been opened with PROCESS_VM_READ access,
    lpBaseAddress is the address to read from as it is known to the other application,
    lpBuffer is apointer to an area in this application's memory to copy the result to
    nSize is the number of bytes you want to copy and
    lpNumberOfBytesWritten returns the number of bytes that were successfully copied.
    You absolutely must supply a variable for this call to put the lpNumberOfBytesWritten into or your app will bomb out spectacularily.
    ----8<---------------------------------------
    NEW - The .NET printer queue monitor component
    ----8<---------------------------------------
    Now with Examples of use

  26. #26
    Frenzied Member MerrionComputin's Avatar
    Join Date
    Apr 2001
    Location
    Dublin, Ireland
    Posts
    1,616
    For the sake of simplicity this call can be wrapped up in two utility functions - one of which returns a Long value from a pointer in another process' memory and another wich returns a string from a pointer in another application's memory thus:-

    VB Code:
    1. Private Declare Function ReadProcessMemoryBytes Lib "kernel32" Alias "ReadProcessMemory" (ByVal hProcess As Long, ByVal lpBaseAddress As Long, lpBuffer As Byte, ByVal nSize As Long, lpNumberOfBytesWritten As Long) As Long
    2.  
    3. Public Function LongFromOutOfprocessPointer(ByVal hProcess As Long, ByVal lpAddress As Long) As Long
    4.  
    5. Dim lRet As Long
    6. Dim lBytesWritten As Long
    7.  
    8. Call ReadProcessMemory(hProcess, lpAddress, ByVal VarPtr(lRet), Len(lRet), lBytesWritten)
    9. If lBytesWritten > 0 Then
    10.     LongFromOutOfprocessPointer = lRet
    11. End If
    12.  
    13. End Function
    14.  
    15. Public Function StringFromOutOfProcessPointer(ByVal hProcess As Long, ByVal lpString As Long, ByVal Length As Long, ByVal Unicode As Boolean) As String
    16.  
    17. Dim buf() As Byte
    18. Dim lRet As Long
    19. Dim lBytesWritten As Long
    20. Dim sTemp As String
    21.  
    22. ReDim buf(Length) As Byte
    23.  
    24. lRet = ReadProcessMemoryBytes(hProcess, lpString, buf(0), Length, lBytesWritten)
    25. If lBytesWritten = 0 And Err.LastDllError = 0 Then
    26.     While lBytesWritten = 0 And Length > 0
    27.         Length = Length - 1
    28.         lRet = ReadProcessMemoryBytes(hProcess, lpString, buf(0), Length, lBytesWritten)
    29.     Wend
    30. Else
    31.     If Err.LastDllError Then
    32.         Debug.Print LastSystemError
    33.     End If
    34. End If
    35. If lRet <> 0 Then
    36.     If Unicode Then
    37.         StringFromOutOfProcessPointer = StrConv(buf, vbFromUnicode)
    38.     Else
    39.         For lRet = 0 To lBytesWritten
    40.             If buf(lRet) = 0 Then
    41.                 Exit For
    42.             End If
    43.             sTemp = sTemp & Chr$(buf(lRet))
    44.         Next lRet
    45.         StringFromOutOfProcessPointer = sTemp
    46.     End If
    47. Else
    48.     If Err.LastDllError Then
    49.         Debug.Print LastSystemError
    50.     End If
    51. End If

    In the second one I have added som error trapping which I recommend using after any API call. The code for LastSystemError is:

    VB Code:
    1. Private Declare Function FormatMessage Lib "kernel32"  _
    2.               Alias "FormatMessageA" (ByVal dwFlags As Long,  _
    3.                         ByVal lpSource As Long, _
    4.                         ByVal dwMessageId As Long, _
    5.                         ByVal dwLanguageId As Long, _
    6.                         ByVal lpBuffer As String,  _
    7.                         ByVal nSize As Long, _
    8.                        Arguments As Long) As Long
    9.  
    10. '\\ -- [ LastSystemError ]----------------------------------
    11. '\\ Returns the message from the system which describes the
    12. '\\ last dll error to occur, as
    13. '\\ held in Err.LastDllError. This function should be
    14. '\\ called as soon after the API call
    15. '\\ which might have errored, as this member can be reset
    16. '\\ to zero by subsequent API calls.
    17. '\\ --------------------------------------------------------
    18. Public Function LastSystemError() As String
    19.  
    20. Const FORMAT_MESSAGE_FROM_SYSTEM = &H1000
    21. Dim sError As String * 500 '\\ Preinitilise a string buffer to put any error message into
    22. Dim lErrNum As Long
    23. Dim lErrMsg As Long
    24.  
    25. lErrNum = Err.LastDllError
    26.  
    27. lErrMsg = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM, 0, lErrNum, 0, sError, Len(sError), 0)
    28.  
    29. LastSystemError = Trim(sError)
    30.  
    31. End Function
    ----8<---------------------------------------
    NEW - The .NET printer queue monitor component
    ----8<---------------------------------------
    Now with Examples of use

  27. #27
    Frenzied Member MerrionComputin's Avatar
    Join Date
    Apr 2001
    Location
    Dublin, Ireland
    Posts
    1,616
    Getting the process Id
    In order to open the other process to read it's memory you need to know the process id. You can get this if you have the window handle of the window that sent you the message thus:-

    VB Code:
    1. Private Declare Function GetWindowThreadProcessId Lib "user32" (ByVal hwnd As Long, lpProcId As Long) As Long
    2.  
    3. Public Function ProcessIdFromHwnd(ByVal hwnd As Long) As Long
    4.  
    5. Dim lProc As Long
    6. If GetWindowThreadProcessId(hwnd, lProc) Then
    7.     ProcessIdFromHwnd = lProc
    8. End If
    9.  
    10. End Function

    And you pass this in to OpenProcess in order to get the hProc parameter to pass to LongFromOutOfprocessPointer and StringFromOutOfprocessPointer...

    VB Code:
    1. Public Enum ProcessAccessPriviledges
    2.      PROCESS_TERMINATE = &H1
    3.      PROCESS_CREATE_THREAD = &H2
    4.      PROCESS_SET_SESSIONID = &H4
    5.      PROCESS_VM_OPERATION = &H8
    6.      PROCESS_VM_READ = &H10
    7.      PROCESS_VM_WRITE = &H20
    8.      PROCESS_DUP_HANDLE = &H40
    9.      PROCESS_CREATE_PROCESS = &H80
    10.      PROCESS_SET_QUOTA = &H100
    11.      PROCESS_SET_INFORMATION = &H200
    12.      PROCESS_QUERY_INFORMATION = &H400
    13.      PROCESS_SYNCHRONISE = &H100000
    14.      PROCESS_ALL_ACCESS = &H100FFF
    15. End Enum
    16. Private Declare Function OpenProcess Lib "kernel32" (ByVal dwDesiredAccess As ProcessAccessPriviledges, ByVal bInheritHandle As Long, ByVal dwProcessId As Long) As Long
    ----8<---------------------------------------
    NEW - The .NET printer queue monitor component
    ----8<---------------------------------------
    Now with Examples of use

  28. #28
    Frenzied Member MerrionComputin's Avatar
    Join Date
    Apr 2001
    Location
    Dublin, Ireland
    Posts
    1,616
    So if your sending app fills out the UDT and then passes it accross thus:
    VB Code:
    1. Private Type ClientUDT
    2.   Name As String
    3.   Age As Long
    4.   IsHuman As Boolean
    5. End Type
    6.  
    7. Dim lTargetWnd As Long 'window to send the message to in the recieving app - use FindWindow to populate this?
    8.  
    9. '...
    10. Dim myUdt As ClientUDT
    11.  
    12. With myUdt
    13.   .Name = Duncan
    14.   .Age = 31
    15.   .IsHuman = False
    16. End With
    17.  
    18. Call SendMessage(lTargetWnd, MSG_UDT_FULL, lSourceWnd, VarPtr(myUdt))

    Then your target application can process this message and read the UDT from the sender's address thus:

    VB Code:
    1. '..in WNDPROC
    2. If msg = MSG_UDT_FULL Then
    3.     Dim myUDT As ClientUDT
    4.     myUDT = UDTFromOtherprocessAddress(wParam, lParam)
    5. End If

    Where
    VB Code:
    1. Private Function UDTFromOtherprocessAddress(ByVal hwndOther As Long, ByVal lpUDT As Long) As ClientUDT
    2.  
    3. Dim udtRet As ClientUDT
    4. Dim hProc As Long
    5. Dim sLen As Long
    6. Dim lpString As Long
    7. Dim lChar As Long
    8.  
    9.  
    10. hProc = OpenProcess(PROCESS_VM_READ , ProcessIdFromHwnd(hwndOther ))
    11.  
    12. If hProc Then
    13.   lpString = LongFromOutOfprocessPointer(hProc, lpUDT)
    14.   sLen = LongFromOutOfprocessPointer(hproc, lpString - 4)
    15.  
    16. With udtRet
    17.     .Age = LongFromOutOfprocessPointer(hProc, lpUDT + 4)
    18.     .IsHuman = LongFromOutOfprocessPointer(hProc, lpUDT + 8)
    19.     .Name = StringFromOutOfProcessPointer(hProc, lpString, sLen, True)
    20.  End With
    21. End If
    22.  
    23. UDTFromAddress = udtRet
    24.  
    25. End Function

    '...The End...or is it?.....
    ----8<---------------------------------------
    NEW - The .NET printer queue monitor component
    ----8<---------------------------------------
    Now with Examples of use

  29. #29
    PowerPoster
    Join Date
    Jul 2001
    Location
    Tucson, AZ
    Posts
    2,166
    Have you ever considered using Memory Mapped Files??

  30. #30
    Frenzied Member MerrionComputin's Avatar
    Join Date
    Apr 2001
    Location
    Dublin, Ireland
    Posts
    1,616
    Have you ever considered using Memory Mapped Files??
    There are many ways to do this - that was just one of them...
    ----8<---------------------------------------
    NEW - The .NET printer queue monitor component
    ----8<---------------------------------------
    Now with Examples of use

  31. #31
    Super Moderator Wokawidget's Avatar
    Join Date
    Nov 2001
    Location
    Headingly Occupation: Classified
    Posts
    9,632
    What others ways are there?
    What's a Memory Mapped File?

    I have a bad hangover today, got hacked to death at Sunday League footy, and have so far got no work done

    Hungry Woka

  32. #32
    Super Moderator Wokawidget's Avatar
    Join Date
    Nov 2001
    Location
    Headingly Occupation: Classified
    Posts
    9,632
    OK, I understand how the code you posted works, and I grasp the concept of the same memory address, but different if the base memory is brought into account.
    I am dealing with byte arrays, which represent the contents of property Bags. I can get the memory address for the start of the byte array, and I can pass that and the owning hWnd, but how do I deal with the length or the byte array?

    Woka

  33. #33

  34. #34
    Frenzied Member MerrionComputin's Avatar
    Join Date
    Apr 2001
    Location
    Dublin, Ireland
    Posts
    1,616
    There is aslo a way of sending data between applications using the WM_COPY message.
    ----8<---------------------------------------
    NEW - The .NET printer queue monitor component
    ----8<---------------------------------------
    Now with Examples of use

  35. #35
    Super Moderator Wokawidget's Avatar
    Join Date
    Nov 2001
    Location
    Headingly Occupation: Classified
    Posts
    9,632

    Angry It's sunny yet disturbingly windy...Does not make sense!

    Now, if I wanted to pass a byte array, would I use the following:
    VB Code:
    1. Public Type mtypDataStruct
    2.    Pointer   As Long
    3.    Length   As Long
    4. End Type
    Now say I have a byte array:
    VB Code:
    1. Dim Fish() As Byte
    From this byte array I can find the starting memory address:
    VB Code:
    1. Dim udtDS As mtypDataStruct
    2.    Pointer = VarPtr(Fish(0))
    and it's length:
    VB Code:
    1. Length = UBound(Fish) + 1
    Now since the data structure UDT is a constant fixed length I can literally pass ONLY the start address of this using SendMessage, oh and along with the hWnd where it is located.
    From this when when the message is received, in the WinProc function, I can get the pointer and length for the data struct, from the other process memory, which in turn will let me reconstruct my byte array...

    Is this the correct way to pass I byte array between applications, using Mirrions tutorial?

    woKa

  36. #36
    Frenzied Member MerrionComputin's Avatar
    Join Date
    Apr 2001
    Location
    Dublin, Ireland
    Posts
    1,616
    Is this the correct way to pass I byte array between applications, using Mirrions tutorial?
    Who Mirrion?
    Yes - if your UDT is fixed length that should work fine.
    ----8<---------------------------------------
    NEW - The .NET printer queue monitor component
    ----8<---------------------------------------
    Now with Examples of use

  37. #37

  38. #38
    PowerPoster
    Join Date
    Jul 2001
    Location
    Tucson, AZ
    Posts
    2,166
    Duncan:

    Late responding -- been out of pocket.

    Wasn't trying to rain on your parade with my input re Memory Mapped Files, just thought Kleinma might not know about them and might be a better way in his app than passing structures (UDT).

    If I haven't thanked you before, let me thank your now for all your input to the forum. I for one always enjoy reading your comments and have learned a lot from your posts.

    David

  39. #39
    Addicted Member
    Join Date
    Mar 2003
    Location
    Minneapolis, MN
    Posts
    151
    Some great code goin' about here but can I interject something a little more simpler? (subscriber to the KISS principle).

    If you have a UDT defined in two different apps and you want to pass the data from one instance of a UDT in one app to another instance of the same UDT (in structure) in another app, and the UDT's structure doesn't contain variable length strings, then you could pass the data as a buffered string.

    Now this is not the BEST way, but it is rather simple.

    Let me explain.

    In each app:

    Public Type udtEmp
    FirstName As String * 50
    LastName As String * 50
    EmpID As Long
    End Type

    Public Type udtEmpBuffer
    Data As String * 104 '50 + 50 + 4
    End Type

    When you want to buffer the data in one app:

    Dim uEmp As udtEmp
    Dim uBuffer As udtEmpBuffer

    With uEmp
    .FirstName = "John"
    .LastName = "Doe" ' I know, very creative
    .EmpID = 1001
    End With

    LSet uBuffer = uEmp

    And then pass the data as a string ie:

    objApp2.HereIsSomeData(uBuffer.Data)

    And on the receiving side do the reverse.

    Dim uEmp as udtEmp
    Dim uBuffer as udtEmpBuffer

    uEmpBuffer.Data = sPassedString
    LSet udtEmp = uEmpBuffer


    It's quick and comes with a built in parser. The only major drawbacks are 1) if you don't use fixed-length data, then you need to define the size of the Data element of the Buffer UDT to some huge size, and 2) If the UDT's structure needs to be changed, then you need to change it in two places.

  40. #40
    Super Moderator Wokawidget's Avatar
    Join Date
    Nov 2001
    Location
    Headingly Occupation: Classified
    Posts
    9,632

    Angry I had a pet fish once...then the snapper turtle ate it *SOB*

    rlwhealdon,

    There is one draw back to your little plan...
    VB Code:
    1. objApp2.HereIsSomeData(uBuffer.Data)

    How are you going to do this?
    These apps are not ActiveX EXE's and should not have ANY references to each other, which you can't do unless one is an ActiveX EXE or DLL, which they are not.
    Plus, the apps are is different threads...

    MailSlots work OK, but have a few draw backs. Mellons code works like a treat, but is slightly longer and more complex and, during development, is prone to getting VB to set to self destruct
    It is very rare in VB to pass data between 2 EXE's, which is why there is no KISS method

    Sleepy Woka

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •  



Click Here to Expand Forum to Full Width