Delphi - sendmessage - Powershell

    Delphi - sendmessage - Powershell

    hallo zusammen,
    ich komme einfach nicht weiter.

    folgendes Proplem: ich versuche gerade in nem testprogram einen string an powershell zu senden. das handle hab ich nur iwie kommt der string nciht an. mit einzelnen zeichen funktioert es (also mit char). ich will jetzt keine schleife machen und jedes zeichen einzeln senden, da das ziel der übung ist ein ganzen Memo (wo ein powershellscript drin steht) an Powershell zu senden

    test mit einem simplen string:

    Delphi-Code

    1. var TheWindowHandle: THandle;
    2. stringToSend : string;
    3. DataStruct : TCopyDataStruct;
    4. stringToSend:='get-process';
    5. DataStruct.dwData := 0;
    6. DataStruct.cbData := length(stringToSend)+1;
    7. DataStruct.lpData := pchar(stringToSend);
    8. SendMessage(TheWindowHandle, WM_COPYDATA, Longint(datastruct), Longint(@DataStruct));

    kann mir jmd sagen was ich falsch mache?

    Dieser Beitrag wurde bereits 1 mal editiert, zuletzt von „DeddyH“ () aus folgendem Grund: Delphi-Tags gesetzt, künftig bitte selbst machen //MfG DeddyH

    DataStruct.cbData muss die Größe in Bytes sein: Length(stringToSend)*SizeOf(Char).

    Möglicherweise muss der string auch nullterminiert sein.
    Für das PointerCasting würde ich dann schon eher NativeInt verwenden.
    Was du mit dem wParam = LongInt(datastruct) bezweckst ist mir völlig unklar, kommt da nicht das windowhandle rein?

    so long,
    ToddNedd
    du meinst sowas wie:

    DataStruct.cbData := Length(stringToSend)*SizeOf(Char)

    SendMessage(TheWindowHandle, WM_COPYDATA, 0, nativint(@DataStruct));

    funktionier leider auch nicht. delphi gibt mir keine fehlermeldung aber im powershell fenster kommt nichts an (er muss das fenster finden, da er sonst nicht in den elsezweig gehen würde)
    ehrlich gesagt nein. ich bezweifle so langsam das ich nen ganzen text mir wm_copydata übergeben kann.

    ich habe gerade versucht eine zeile per schleife udn einzelne buchstaben zu übertragen aber da kommt datenmüll an.

    jetzt könnte ich noch über cmd versuchen strings zu senden.

    oder hast du ne andere idee wie ich mein script rübersenden kann? momentan löse ich es über ne bat (sprich erst das memo als ps1 speichern und per bat ausführen)
    Statt einer Batch-Datei kannst Du PowerShell auch per ShellExecute aufrufen.

    Delphi-Code

    1. ShellExecute(0, 'open', 'PowerShell.exe', PChar('-file "' + DeineDatei + '"'), nil, 0);
    10 Minuten Nachdenken ersparen oftmals 10 Stunden Fehlersuche.
    ja, dass stimmt schon aber es gibt immer die laufzeitprobleme. folgendes passiert momentan:

    ich habe ein memo wo ein powershellscript ist.
    iwann im programm werden diesem memo variablen hinzugefügt.
    dann wird das memo als ps1 gespeichert.
    dann erzeuge ich ne bat die die ps1 ausführt.
    die bat starte ich mitm creatprocess

    das eigentliche problem ist die laufzeit. ehe powershell gestartet ist / bzw die console mit dem import-activedirectory befehl vergehen einige sekunden. deshalb dachte ich mir läuft powershell immer (mit überprüfungen ect) ob nun minimiert oder versteckt ist ja erstmal egal. das ich nur noch das fenster suchen muss und das reinkopieren brauch.

    ich mein iwie muss das doch gehen oder? mit einzelnen buchstaben also charwerten geht das. wenn ich allerding ne schleife mache um zeichen für zeichen zu übertragen, zeigt mir powershell nur datenmüll an.
    ich habe gerade versucht eine zeile per schleife udn einzelne buchstaben zu übertragen aber da kommt datenmüll an.
    Das könnte daran liegen, dass die Quelle ein anderes Textformat hat als die andere Sete erwartet, zB AnsiString/Unicode oder umgekehrt...

    oder hast du ne andere idee wie ich mein script rübersenden kann?
    Was für ein Script hast du denn? Javascript, VBScript?

    cckLoud

    cckLoud schrieb:

    Das könnte daran liegen, dass die Quelle ein anderes Textformat hat als die andere Sete erwartet, zB AnsiString/Unicode oder umgekehrt...

    aber wenn es doch mit einem einzelnen buchstaben funktiniert, wieso sollte es dann nicht inner for to do schleife auch gehen? kann es ja mal versuchen mit AnsiString/Unicode.

    cckLoud schrieb:

    Was für ein Script hast du denn? Javascript, VBScript?
    nen powershellscript (sprich nen quelltext für Powershell)
    also was definitiv funktioniert:

    Delphi-Code

    1. program CopyDataConsoleProj;
    2. {$APPTYPE CONSOLE}
    3. {$R *.res}
    4. uses
    5. System.SysUtils,
    6. WinApi.Windows,
    7. WinApi.Messages;
    8. var
    9. w: THandle;
    10. stringToSend, windowName: string;
    11. cds: TCopyDataStruct;
    12. begin
    13. try
    14. ReportMemoryLeaksOnShutdown := True;
    15. stringToSend := 'get-process';
    16. cds.dwData := 0;
    17. cds.cbData := Length(stringToSend) * SizeOf(Char);
    18. cds.lpData := PChar(stringToSend);
    19. windowName := 'CopyDataReceiver';
    20. w := FindWindow(nil, PChar(windowName));
    21. SendMessage(w, WM_COPYDATA, 0, NativeInt(@cds));
    22. except
    23. on E: Exception do
    24. Writeln(E.ClassName, ': ', E.Message);
    25. end;
    26. end.


    gebaut mit XE3, UniCode (widestring), 32bit.

    Ich hab mal was in C++ zusammengestümpert um es zu testen, nicht hübsch aber dafür selten :D

    C-Quellcode

    1. #include <Windows.h>
    2. #include <memory>
    3. LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam);
    4. int WINAPI wWinMain(HINSTANCE hInstance, HINSTANCE prevInstance, LPWSTR cmdLine, int cmdShow)
    5. {
    6. WNDCLASSEX wndClass = {0};
    7. wndClass.cbSize = sizeof(WNDCLASSEX);
    8. wndClass.style = CS_HREDRAW | CS_VREDRAW;
    9. wndClass.lpfnWndProc = WndProc;
    10. wndClass.hInstance = hInstance;
    11. wndClass.hCursor = LoadCursor(NULL, IDC_ARROW);
    12. wndClass.hbrBackground = (HBRUSH) (COLOR_WINDOW + 1);
    13. wndClass.lpszMenuName = NULL;
    14. wndClass.lpszClassName = L"CopyDataRcvClass";
    15. if(!RegisterClassEx(&wndClass))
    16. return -1;
    17. // desired client area size
    18. RECT rc = {0, 0, 640, 480};
    19. AdjustWindowRect(&rc, WS_OVERLAPPEDWINDOW, FALSE);
    20. HWND hWnd = CreateWindow(L"CopyDataRcvClass", L"CopyDataReceiver", WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT,
    21. rc.right-rc.left, rc.bottom-rc.top, NULL, NULL, hInstance, NULL);
    22. if(!hWnd)
    23. return -1;
    24. ShowWindow(hWnd, cmdShow);
    25. MSG msg = {0};
    26. while(msg.message != WM_QUIT)
    27. {
    28. if(PeekMessage(&msg, 0, 0, 0, PM_REMOVE))
    29. {
    30. TranslateMessage(&msg);
    31. DispatchMessage(&msg);
    32. }
    33. else
    34. {
    35. Sleep(5);
    36. }
    37. }
    38. return static_cast<int>(msg.wParam);
    39. }
    40. LRESULT CALLBACK WndProc( HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam )
    41. {
    42. PAINTSTRUCT paintStruct;
    43. HDC hDC;
    44. PCOPYDATASTRUCT pCds;
    45. switch( message )
    46. {
    47. case WM_PAINT:
    48. hDC = BeginPaint( hwnd, &paintStruct );
    49. EndPaint( hwnd, &paintStruct );
    50. break;
    51. case WM_COPYDATA:
    52. pCds = (PCOPYDATASTRUCT) lParam;
    53. LPWSTR(pCds->lpData)[pCds->cbData / sizeof(TCHAR)] = L'\0';
    54. MessageBox(NULL, LPWSTR(pCds->lpData), L"Received", MB_OK);
    55. break;
    56. case WM_DESTROY:
    57. PostQuitMessage( 0 );
    58. break;
    59. default:
    60. return DefWindowProc( hwnd, message, wParam, lParam );
    61. }
    62. return 0;
    63. }

    ebenfalls 32bit, UniCode

    Was mir aufgefallen ist, ist dass die Konvertierung

    Delphi-Code

    1. cds.lpData := PChar(stringToSend);


    keinen nullterminierten string erzeugt 8o
    Das ging (vor XE3 / UniCode) mal?!

    Ich denke auch, dass die Powershell nicht auf WM_COPYDATA reagiert.
    Wie du einzelne Zeichen da rein bekommst ist mir ein völliges Rätsel.

    so long,
    ToddNedd
    mit deinem code scheints auch nicht zu funktioieren (getestet einmal mit console udn powershell).

    wahrscheinlich habe ich was übersehen

    hier mal meine unit


    Delphi-Code

    1. unit Unit1;
    2. interface
    3. uses
    4. Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
    5. Dialogs, StdCtrls;
    6. type
    7. PFindWindowStruct = ^TFindWindowStruct;
    8. TFindWindowStruct = record
    9. Caption: string;
    10. ClassName: String;
    11. WindowHandle: THandle;
    12. end;
    13. TForm1 = class(TForm)
    14. Button1: TButton;
    15. procedure Button1Click(Sender: TObject);
    16. private
    17. { Private-Deklarationen }
    18. public
    19. { Public-Deklarationen }
    20. end;
    21. var
    22. Form1: TForm1;
    23. w: THandle;
    24. stringToSend, windowName: string;
    25. cds: TCopyDataStruct;
    26. implementation
    27. {$R *.dfm}
    28. function EnumWindowsProc(hWindow: hWnd; lParam: LongInt): boolean; stdcall;
    29. var lpBuffer: PChar;
    30. WindowCaptionFound: boolean;
    31. ClassNameFound: boolean;
    32. begin
    33. GetMem(lpBuffer, 255);
    34. result:=true;
    35. WindowCaptionFound:=false;
    36. ClassNameFound:=false;
    37. try
    38. if GetWindowText(hWindow, lpBuffer,255)>0 then
    39. if Pos(PFindWindowStruct(lParam).Caption, StrPas(lpBuffer))>0
    40. then WindowCaptionFound:=true;
    41. if PFindWindowStruct(lParam).ClassName='' then
    42. ClassNameFound:=true
    43. else if GetClassName(hWindow, lpBuffer, 255)>0 then
    44. if Pos(PFindWindowStruct(lParam).ClassName, StrPas(lpBuffer))>0
    45. then ClassNameFound:=true;
    46. if (WindowCaptionFound and ClassNameFound) then begin
    47. PFindWindowStruct(lParam).WindowHandle:=hWindow;
    48. result:=false;
    49. end;
    50. finally
    51. FreeMem(lpBuffer, sizeof(lpBuffer^));
    52. end;
    53. end;
    54. function FindAWindow(WinCaption: string; WinClassName: string): THandle;
    55. var WindowInfo: TFindWindowStruct;
    56. begin
    57. with WindowInfo do begin
    58. caption := WinCaption;
    59. className := WinClassName;
    60. WindowHandle := 0;
    61. EnumWindows(@EnumWindowsProc, LongInt(@WindowInfo));
    62. result := WindowHandle;
    63. end;
    64. end;
    65. procedure TForm1.Button1Click(Sender: TObject);
    66. begin
    67. try
    68. //ReportMemoryLeaksOnShutdown := True;
    69. stringToSend := 'get-process';
    70. cds.dwData := 0;
    71. cds.cbData := Length(stringToSend) * SizeOf(Char);
    72. cds.lpData := PChar(stringToSend);
    73. w:= FindAWindow('Administrator: Eingabeaufforderung', '');
    74. if w=0 then ShowMessage('Window not found!')
    75. else
    76. begin
    77. SendMessage(w, WM_COPYDATA, 0, NativeInt(@cds));
    78. end;
    79. except
    80. on E: Exception do
    81. Writeln(E.ClassName, ': ', E.Message);
    82. end;
    83. end;
    84. end.
    also WM_COPYDATA versteht die Powershell nicht, also bleibt nur die Schleifenvariante:

    Delphi-Code

    1. var
    2. w: THandle;
    3. stringToSend, windowName: string;
    4. I: Integer;
    5. begin
    6. try
    7. stringToSend := 'get-process';
    8. windowName := 'Windows PowerShell';
    9. w := FindWindow(nil, PChar(windowName));
    10. for I := 1 to Length(stringToSend) do
    11. begin
    12. PostMessage(w, WM_CHAR, Ord(stringToSend[I]), 0);
    13. end;
    14. { execute command, send enter (CR) }
    15. PostMessage(w, WM_CHAR, 13, 0);
    16. except
    17. on E: Exception do
    18. Writeln(E.ClassName, ': ', E.Message);
    19. end;
    20. end.


    Nicht schick, aber es funktioniert ::)

    so long,
    ToddNedd

    PS: Achja, du musst ASCII Code an die Powershell schicken. ;)
    Das hier hat bei mir mit Delphi 2007 funktioniert:

    Delphi-Code

    1. const
    2. TESTSTR = '\\Server';
    3. var
    4. c: char;
    5. Wnd: HWnd;
    6. begin
    7. Wnd := FindWindow(nil, PChar('Windows PowerShell'));
    8. if Wnd <> 0 then
    9. begin
    10. for c in TESTSTR do
    11. SendMessage(Wnd, WM_CHAR, Ord(c), 0);
    12. SendMessage(Wnd, WM_CHAR, Ord(sLineBreak[1]), 0);
    13. end;
    14. end;
    10 Minuten Nachdenken ersparen oftmals 10 Stunden Fehlersuche.
    Hallo DeddyH,

    ich bin beim durchsuchen des Webs auf diesen Beitrag gestoßen. Der Befehl von Dir vom 22.5.2013

    ShellExecute(0, 'open', 'PowerShell.exe', PChar('-file "' + DeineDatei + '"'), nil, 0);

    funktioniert bei mir ausgezeichnet (ISAPI DLL erstellt mit Delphi XE 3 und Atozed Intraweb), solange ich die Powershell Prozedur ohne Parameter aufrufe. Nun müsste ich dieselbe Prozedur aber mit Parameter aufrufen und das bekomme ich nicht hin.
    Wenn ich den Parameter p mit Wert noch anfüge, geht nichts mehr.

    DeineDatei als: Pfad\Prozedur.ps1 geht
    DeineDatei als: Pfad\Prozedur.ps1 -p wert geht nicht

    Hättest Du vielleicht noch eine Idee dazu ?

    Gruß
    Hallo frauwue,
    versuchs mal mit:

    Delphi-Code

    1. ShellExecute(0, 'open', 'PowerShell.exe', PChar('-file "' + DeineDatei + '" -p ' + wert), nil, 0);


    Es darf nur der Filename mit Pfad in ANführungszeichen eingeschlossen werden (falls Leerzeichen vorhanden sind).
    So wie du es geschrieben hast, hättest Du den Parameter in den Filestring inkludiert.
    Gruß Thomas

    Admin werden ist nicht schwer, sein ......
    Die drei Feinde des Programmierers: Sonne, Frischluft und dieses unerträgliche Gebrüll der Vögel.