Jump to content
luckyluke

Dllcall - console attach to read output from CMD error

Recommended Posts

luckyluke

Hello,

Im trying to read the output from CMD using Dllcall, here is my code:

#include <WinAPI.au3>
#include <array.au3>
Global Const $STD_OUTPUT_HANDLE = -11
Global Const $_CONSOLE_SCREEN_BUFFER_INFO = _
             "struct;int dwSizeX;" & _
             "short dwSizeY;" & _
             "short dwCursorPositionX;" & _
             "short dwCursorPositionY;" & _
             "short wAttributes;" & _
             "short Left;" & _
             "short Top;" & _
             "short Right;" & _
             "short Bottom;" & _
             "short dwMaximumWindowSizeX;" & _
             "short dwMaximumWindowSizeY;endstruct"

$pCmd = Run( "cmd.exe" )
Sleep(1000)
$hCmd = WinGetHandle("")
ConsoleWrite('handle:' & $hCmd & @CRLF)

$aRet = DllCall("kernel32.dll", "int", "AttachConsole", "dword", $pCmd)
;_ArrayDisplay($aRet)
If $aRet[0] <> 0 Then
    $vHandle_data=''
    $vHandle=''

    $vHandle_data = DllStructCreate($_CONSOLE_SCREEN_BUFFER_INFO) ; Screen Buffer structure

    $aRet1 = DllCall("kernel32.dll", "hwnd", "GetStdHandle", "dword", $STD_OUTPUT_HANDLE)
    if not @error Then $vHandle =  $aRet1[0]

    $aRet = DllCall("kernel32.dll", "int", "GetConsoleScreenBufferInfo", "hwnd", $vHandle, _
        "ptr", $vHandle_data)

    MsgBox(0, '1',DllStructGetData($vHandle_data, 'dwSizeX') & _WinAPI_GetLastErrorMessage())
EndIf

It did not work, i got the message 'The handle is invalid'. Please help?

Thank you in advance!

Share this post


Link to post
Share on other sites
TheDcoder

Or you can also give Process UDF a try, you can get the output with a single line of code.


AutoIt.4.Life Clubrooms - Life is like a Donut (secret key)

Spoiler

My contributions to the AutoIt Community

If I have hurt or offended you in anyway, Please accept my apologies, I never (regardless of the situation) mean to do that to anybody!!!

3fHNZJ.gif

PLEASE JOIN ##AutoIt AND HELP THE IRC AUTOIT COMMUNITY!

Share this post


Link to post
Share on other sites
luckyluke
8 hours ago, Jos said:

Why not simply use the standard supported StdoutRead() & StderrRead() in combination with Run()?

Jos 

Yes, that is the common way, i tried it, but it does not capture all output from the cmd. That why i want to try with console attach

 

7 hours ago, TheDcoder said:

Or you can also give Process UDF a try, you can get the output with a single line of code.

Your code is using the Stdreadout, i tried it before but it did not work. 

Share this post


Link to post
Share on other sites
TheDcoder

Are you trying to read output directly from cmd.exe?


AutoIt.4.Life Clubrooms - Life is like a Donut (secret key)

Spoiler

My contributions to the AutoIt Community

If I have hurt or offended you in anyway, Please accept my apologies, I never (regardless of the situation) mean to do that to anybody!!!

3fHNZJ.gif

PLEASE JOIN ##AutoIt AND HELP THE IRC AUTOIT COMMUNITY!

Share this post


Link to post
Share on other sites
luckyluke
49 minutes ago, TheDcoder said:

Are you trying to read output directly from cmd.exe?

Do you mean read from cmd.exe using StdoutRead, yes it can read output from cmd but not all things. I have an other program run using CMD, it will out the result to CMD, then i used StdoutRead to read from cmd, but can not get all result from there.

 

Share this post


Link to post
Share on other sites
TheDcoder
1 hour ago, luckyluke said:

it can read output from cmd but not all things

I suspect that the program which outputs to the CMD is also using StdErr stream. You should try using the $STDERR_MERGED flag, the _Process_RunCommand function from Process UDF automatically does that for you by default :)

Edited by TheDcoder

AutoIt.4.Life Clubrooms - Life is like a Donut (secret key)

Spoiler

My contributions to the AutoIt Community

If I have hurt or offended you in anyway, Please accept my apologies, I never (regardless of the situation) mean to do that to anybody!!!

3fHNZJ.gif

PLEASE JOIN ##AutoIt AND HELP THE IRC AUTOIT COMMUNITY!

Share this post


Link to post
Share on other sites
luckyluke
6 minutes ago, TheDcoder said:

I suspect that the program which outputs to the CMD is also using StdErr stream. You should try using the $STDERR_MERGED flag, the _Process_RunCommand function from Process UDF automatically does that for you by default :)

yes, i tried, but not work. I see that if i close the program then it will show the message in the output. but if i run it from cmd, the message appears even when program is running.

Share this post


Link to post
Share on other sites
TheDcoder
7 minutes ago, luckyluke said:

but if i run it from cmd, the message appears even when program is running.

If that is the case, then you can try calling the program from instead of directly calling it. You can do it like this in my UDF:

$sOutput = _Process_RunCommand($PROCESS_RUNWAIT, $PROCESS_COMMAND & 'path_to_your_program.exe') ; This will execute cmd.exe which will run your program

This way, cmd.exe acts like a proxy between the AutoIt script and the program. You should be able to read all output given to cmd.exe if you use the above code snippet

Edited by TheDcoder
Add $sOutput to the code snippet

AutoIt.4.Life Clubrooms - Life is like a Donut (secret key)

Spoiler

My contributions to the AutoIt Community

If I have hurt or offended you in anyway, Please accept my apologies, I never (regardless of the situation) mean to do that to anybody!!!

3fHNZJ.gif

PLEASE JOIN ##AutoIt AND HELP THE IRC AUTOIT COMMUNITY!

Share this post


Link to post
Share on other sites
VIP

I have a look at Process UDF, the code seems complicated.

Try this simple one:

Local $iCommand = "ping google.com"

_RunCmd($iCommand)
_RunCmd_GetStdOut($iCommand)


Func _RunCmd($sCommand)
    ConsoleWrite("+ Execute: " & $sCommand & @CRLF)
    Local $sOutput = '', $iPID = Run('"' & @ComSpec & '" /c ' & '"' & $sCommand & '"', '', @SW_HIDE, 0x6)
    If Not $iPID Then Return SetError(1, 0, '')
    Do
        $sOutput &= StdoutRead($iPID)
    Until @error
    Do
        $sOutput &= StderrRead($iPID)
    Until @error
    ConsoleWrite("" & $sOutput & @CRLF)
    ConsoleWrite("- -------------------------------------------------------------" & @CRLF)
    Return $sOutput
EndFunc   ;==>_RunCmd

Func _RunCmd_GetStdOut($sCommand, $sDir = '', $iType = 0x6, $bShow = False, $iDelay = 50)
    ConsoleWrite("+ Execute: " & $sCommand & @CRLF)
    Local $sTMP = '', $sOutput = ''
    Local $iPID = Run('"' & @ComSpec & '" /c ' & '"' & $sCommand & '"', $sDir, $bShow ? @SW_SHOW : @SW_HIDE, $iType)
    If Not $iPID Then Return SetError(1, 0, '')
    While 1
        $sTMP = StdoutRead($iPID, False, False)
        If @error Then ExitLoop 1
        If $sTMP <> "" Then
            $sTMP = StringReplace($sTMP, @CR & @CR, '')
            $sOutput &= $sTMP
            ConsoleWrite($sTMP)
            Sleep($iDelay)
        EndIf
    WEnd
    While 1
        $sTMP = StderrRead($iPID, 0, 0)
        If @error Then ExitLoop
        If $sTMP <> "" Then
            $sTMP = StringReplace($sTMP, @CR & @CR, '')
            $sOutput &= $sTMP
            ConsoleWrite($sTMP)
            Sleep($iDelay)
        EndIf
    WEnd
    ConsoleWrite("- -------------------------------------------------------------" & @CRLF)
    Return $sOutput
EndFunc   ;==>_RunCmd_GetStdOut

 

  • Like 1

Regards,
 

Share this post


Link to post
Share on other sites
Jos
4 hours ago, luckyluke said:

yes, i tried, but not work. I see that if i close the program then it will show the message in the output. but if i run it from cmd, the message appears even when program is running.

Show your code and info on the program running so we can see what might be the issue.

Jos


Visit the SciTE4AutoIt3 Download page for the latest versions  - Beta files                                How to post scriptsource        Forum Rules
 
Live for the present,
Dream of the future,
Learn from the past.
  :)

Share this post


Link to post
Share on other sites
luckyluke
7 hours ago, TheDcoder said:

If that is the case, then you can try calling the program from instead of directly calling it. You can do it like this in my UDF:

$sOutput = _Process_RunCommand($PROCESS_RUNWAIT, $PROCESS_COMMAND & 'path_to_your_program.exe') ; This will execute cmd.exe which will run your program

This way, cmd.exe acts like a proxy between the AutoIt script and the program. You should be able to read all output given to cmd.exe if you use the above code snippet

This code will get the output when the program ends. But i want to get the status when the program is running.

 

3 hours ago, VIP said:

I have a look at Process UDF, the code seems complicated.

Try this simple one:

 

Yes, i found it and tried it before. But the code actually get the output after the program exit. I want to get the console while the program is running.

this is my code:

the code will open the CMD and run "python ui.py" then get the output from console.

#include 'array.au3'
;#include "UIAWrappers.au3"

Global $DOS, $timer, $whd

$live_file = IniRead(@ScriptDir & '\config.ini', 'py_run', 'live_file', '')
$prediction_file = IniRead(@ScriptDir & '\config.ini', 'py_run', 'prediction_file', '')
$training_file = IniRead(@ScriptDir & '\config.ini', 'py_run', 'training_file', '')
$wait_time = IniRead(@ScriptDir & '\config.ini', 'py_run', 'wait_time', 6)
FileDelete(@ScriptDir & '\pyConsole.txt')

startPY()
Sleep(1000)
$checker = 1
$i = 1
While 1
    $tmp = ControlGetText('python.exe', '', 'Button1')
    if StringInStr($tmp, 'check online for a solution') Then
        WinClose('python.exe')
        Sleep(3000)
        startPY()
    EndIf

;~  Sleep(50) ;; no need to go at full speed here.
;~  $sOutput=''
;~  While 1
;~         $sOutput &= StdoutRead($DOS, False, False)
;~         If @error Then
;~             ExitLoop
;~         EndIf
;~         Sleep(10)
;~      ConsoleWrite($sOutput)
;~     WEnd

    $Message = StdoutRead($DOS) ;; ditched peek parameter + changed "=" to "&=" ... No more endless loop.
    if @error Then
        restartPY()
        startPY()
    EndIf

    ;$sOutput = StderrRead($DOS)
    ;ConsoleWrite($i & ':' & $sOutput & ',' & StringLen($sOutput) & @CRLF)
    ConsoleWrite($i & ':' & $Message & ',' & StringLen($Message) & @CRLF)
    $i=$i+1
    if StringLen($Message)>2 and StringInStr($Message, @CRLF) Then
        ConsoleWrite('i:' & $i & ',' & $Message & @CRLF)
        ;$i=$i+1
        if StringInStr($Message, ',') Then
            $tmp = StringSplit($Message, @CRLF)
            ;_ArrayDisplay($tmp)
            if not @error Then
                for $j = 1 to UBound($tmp)-1
                    if StringInStr($tmp[$j], ',') Then
                        $file = FileOpen(@ScriptDir & '\pyConsole.txt', 1)
                        FileWrite($file, $tmp[$j] & @CRLF)
                        FileClose($file)
                    EndIf
                Next
            EndIf
        EndIf
        $checker=1
    Else
        $checker = $checker+1
        ;ConsoleWrite('checker:' & $checker & @CRLF)
        if $checker=10 Then
            $timer = TimerInit()
        EndIf
    EndIf

    if $checker>=10000 Then
        $atime = TimerDiff($timer)/1000
        if not @error Then
            if $atime>Number($wait_time) Then
                restartPY()
                startPY()
            EndIf
        EndIf
    EndIf
    if @error then exitloop
WEnd

Func restartPY()
    Do
        if WinExists('C:\Windows\system32\cmd.exe') then
            WinClose('C:\Windows\system32\cmd.exe')
        Else
            ExitLoop
        EndIf
    Until WinExists('C:\Windows\system32\cmd.exe')=0

    WinActivate('Predictive model 1.0')
    Sleep(500)
    WinClose('Predictive model 1.0')
    WinWait('Confirm Exit', '', 3)
    WinActivate('Confirm Exit')
    WinMove('Confirm Exit', '', 0, 0)
    Sleep(500)
    if WinExists('Confirm Exit') then MouseClick('left', 109,94)
    Sleep(4000)
EndFunc

Func startPY()

    $CMD = 'cd ' & @DesktopDir & '\Predictive_model && ' & _
        'python ui.py'
    $DOS = Run(@ComSpec & ' /C ' & $CMD, @ScriptDir, @SW_SHOW, $STDOUT_CHILD+$RUN_CREATE_NEW_CONSOLE)
    ;$DOS = Run(@ComSpec & ' /C ' & $CMD, @ScriptDir, @SW_SHOW, $STDIN_CHILD + $STDERR_MERGED)
    ConsoleWrite($DOS & @CRLF)

    $mtitle = 'Predictive model 1.0'
    WinWait($mtitle, '', 5000)
    ;Sleep(5000)


    WinMove($mtitle, '', 0, 0)
    Sleep(1000)

    ;=====================================set value to GUI=================================================
    ;live file
    ;_UIA_Action("Title:=;controltype:=UIA_EditControlTypeId;class:=","setfocus")
    ;_UIA_Action("Title:=;controltype:=UIA_EditControlTypeId;class:=","setValue using keys", "C:\Users\Administrator\Desktop\Sabrina Betting\LV1.txt")
    WinActivate($mtitle)
    Sleep(1000)
    MouseClick('left', 345,87)
    Send("^a")
    Sleep(500)
    Send($live_file  & '11')

    ;prediction file
    WinActivate($mtitle)
    Sleep(1000)
    MouseClick('left', 345,200)
    Send("^a")
    Sleep(500)
    Send($prediction_file)
    ;use control click
    WinActivate($mtitle)
    Sleep(1000)
    MouseClick('left', 345,234)
    Send("^a")
    Sleep(500)
    Send($training_file)

    ;click start button
;~  Local $oP1=_UIA_getObjectByFindAll($UIA_oDesktop, "Title:=Predictive model 1.0;controltype:=UIA_WindowControlTypeId;class:=QWidget", $treescope_children)
;~  Local $oP0=_UIA_getObjectByFindAll($oP1, "Title:=;controltype:=UIA_CustomControlTypeId;class:=", $treescope_children)
;~  Local $oUIElement=_UIA_getObjectByFindAll($oP0, "title:=Start;ControlType:=UIA_ButtonControlTypeId", $treescope_subtree)
;~  _UIA_action($oUIElement,"focus")
;~  _UIA_action($oUIElement,"click")
    WinActivate($mtitle)
    Sleep(1000)
    MouseClick('left', 593,301)
    Sleep(3000)
EndFunc

 

Share this post


Link to post
Share on other sites
TheDcoder
1 minute ago, luckyluke said:

This code will get the output when the program ends. But i want to get the status when the program is running.

In that case you can use $PROCESS_RUN instead of $PROCESS_RUNWAIT. Refer to the Example.au3 script for more details :)

  • Like 1

AutoIt.4.Life Clubrooms - Life is like a Donut (secret key)

Spoiler

My contributions to the AutoIt Community

If I have hurt or offended you in anyway, Please accept my apologies, I never (regardless of the situation) mean to do that to anybody!!!

3fHNZJ.gif

PLEASE JOIN ##AutoIt AND HELP THE IRC AUTOIT COMMUNITY!

Share this post


Link to post
Share on other sites
luckyluke
4 hours ago, TheDcoder said:

In that case you can use $PROCESS_RUN instead of $PROCESS_RUNWAIT. Refer to the Example.au3 script for more details :)

ok, i will check it. thank you

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

  • Similar Content

    • nacerbaaziz
      By nacerbaaziz
      Hi dear
      With this script you can print Unicode text in the CMD screen
      the script is  easy to use
      just you  write the text that contains Unicode in the first input
      and the script automatically reflect the code in the second input
       you can copy the text to the clipboard
      or you can try printing the text in the CMD window
      I apologize to everyone for colors and shape if not appropriate
      I'm a blind man and I do not see
      Thank you for your understanding
      Greetings to all of youCmdUtM.au3
    • Bilgus
      By Bilgus
      So first things first the example in the help file for _WinApi_Enum_Windows has an error
      ;_ArrayDisplay($aResult, "_WinAPI_EnumWindows", Default, Default, Default, Default, "#|Handle|Class|Title|Text|Process") Should Be _ArrayDisplay($aResult, "_WinAPI_EnumWindows", Default, Default, Default, "Handle|Class|Title|Text|Process") Next is a bit of helpful info on LPCSTR in a callback function it needs  to be passed as a PTR
      DllCallbackRegister($sFUNCT, $sRETURN, "ptr") Finally on to my question
      I'd want to call EnumPropsEX and pass a string through lparam + append to it rather than declaring anything globally
      I can Come up with two ways to do this The second it a lot more code but possibly safer but the first way I think Should do
      1. From a bit of testing It seems AutoIt won't overflow a DllStruct?
      2. Are strings passed through DLL call guaranteed to be 'an ANSI string (a minimum of 65536 chars is allocated)' as the Helpfile clearly states?
      #include <Array.au3> #include <WinAPI.au3> Example() Func Example() Local $aWindows = _WinAPI_EnumWindows() Local $aResult[$aWindows[0][0]][6] For $i = 1 To $aWindows[0][0] $aResult[$i - 1][0] = "0x" & Hex($aWindows[$i][0], 8) $aResult[$i - 1][1] = $aWindows[$i][1] $aResult[$i - 1][2] = WinGetTitle($aWindows[$i][0]) $aResult[$i - 1][3] = WinGetText($aWindows[$i][0]) $aResult[$i - 1][4] = WinGetProcess($aWindows[$i][0]) $aResult[$i - 1][5] = _ArrayToString(EnumProps($aWindows[$i][0]), ", ", 1) Next _ArrayDisplay($aResult, "_WinAPI_EnumWindows", Default, Default, Default, "Handle|Class|Title|Text|Process|Properties") EndFunc ;==>Example Func EnumProps($hWnd, $vDLL = 'user32.dll') ; Create callback function. Local $iErr = 0 Local $aProps[1] = [0] Local $hCb = DllCallbackRegister('_PropEnumProcEx', 'int', 'hwnd;ptr;handle;ptr') ; Call EnumPropsEx Local $aRet = DllCall($vDLL, 'int', 'EnumPropsEx', 'HWND', $hWnd, 'ptr', DllCallbackGetPtr($hCb), 'str', "") If @error Or Not $aRet[0] Then $iErr = @error ConsoleWrite("EnumProps Error:" & $iErr & @CRLF) ElseIf $aRet[3] <> "" Then $aProps = StringSplit($aRet[3], ";") EndIf DllCallbackFree($hCb) Return SetError($iErr, 0, $aProps) EndFunc ;==>EnumProps Func _PropEnumProcEx($hWnd, $sProp, $hData, $pStr) Local $iSzStr = _WinAPI_StringLenA($sProp) + 1 ; + Null Char If $iSzStr > 1 Then Local $tProp = DllStructCreate('char[' & $iSzStr & ']', $sProp) Local $tRetn = DllStructCreate('char[65535]', $pStr) DllStructSetData($tRetn, 1, DllStructGetData($tRetn, 1) & DllStructGetData($tProp, 1) & ";") EndIf Return 1 EndFunc ;==>_PropEnumProcEx ;-------------------------------------------------------------------------------------------------------------- Func EnumProps2($hWnd, $iSzBuffer = 4096, $vDLL = 'user32.dll') ; Create callback function. Local $iErr = 0 Local $sProps Local $aProps[1] = [0] Local $hCb = DllCallbackRegister('_PropEnumProcEx', 'int', 'hwnd;ptr;handle;ptr') Local $tProps = DllStructCreate('int;int;char[' & $iSzBuffer & ']') DllStructSetData($tProps, 1, $iSzBuffer) ;BufferSz DllStructSetData($tProps, 2, $iSzBuffer) ;BufferRemaining ; Call EnumPropsEx Local $aRet = DllCall($vDLL, 'int', 'EnumPropsEx', 'HWND', $hWnd, 'ptr', DllCallbackGetPtr($hCb), 'ptr', DllStructGetPtr($tProps)) If @error Or Not $aRet[0] Then $iErr = @error DllStructSetData($tProps, 2, 0) EndIf DllCallbackFree($hCb) $sProps = DllStructGetData($tProps, 3) If DllStructGetData($tProps, 2) > 0 Then If $sProps <> "" Then $aProps = StringSplit(StringTrimRight($sProps, 1), ";") EndIf Else If Not $iErr Then $iErr = 6 ;buffer overflow Return SetError($iErr, -DllStructGetData($tProps, 2), $aProps) EndIf Return $aProps EndFunc ;==>EnumProps2 Func _PropEnumProcEx2($hWnd, $sProp, $hData, $ptProp) Local $iSzStr = _WinAPI_StringLenA($sProp) + 1 Local $tProp = DllStructCreate('char[' & $iSzStr & ']', $sProp) If $iSzStr > 1 Then Local $sRet = DllStructGetData($tProp, 1) Local $iSzBuffer = DllStructGetData(DllStructCreate('int', $ptProp), 1) Local $tRetn = DllStructCreate('int;int;char[' & $iSzBuffer & ']', $ptProp) DllStructSetData($tRetn, 2, DllStructGetData($tRetn, 2) - $iSzStr) If DllStructGetData($tRetn, 2) > 0 Then DllStructSetData($tRetn, 3, DllStructGetData($tRetn, 3) & $sRet & ";") EndIf EndIf Return 1 EndFunc ;==>_PropEnumProcEx2  
    • Simpel
      By Simpel
      Hi.
      Thanks to this post (https://www.autoitscript.com/forum/topic/189553-writing-to-cmd/?do=findComment&comment=1361142) I can now write a helpfile to the cmd when for instance passing parameter -h or -help at cmd. But then cmd is blocked by the script (I had to free it with CTRL+BREAK):
      #include <WinAPI.au3> _WinAPI_AttachConsole() $hConsole = _WinAPI_GetStdHandle(1) _WinAPI_WriteConsole($hConsole, "Print helpfile................" & @CRLF) Inside MSDN Library then I found this sentence: A process can use the FreeConsole function to detach itself from its console.
      But I don't find something like _WinAPI_FreeConsole(). How can I do it in another way?
      Regards, Conrad
    • TheAutomator
      By TheAutomator
      A fullscreen console with custom commands!

      Introduction:
      Hi everyone!
      This funny project started as a question in the help section:
      https://www.autoitscript.com/forum/topic/174404-edit-detect-key-before-updating-content/
      I'd like to share this script with everyone that is interested. 
       
      Why would I want it?
      You like the old style fullscreen console (like in the old day's), You can add custom commands, You can customize the font a lot more compared to cmd.exe, You can share ideas or add tweaks to the script.  
      Still to do:
      Write a simple custom programming language to implement this tool. Writing a little help file / pdf to describe my little programming language. Add little sound effects like a beep if there is a syntax error (optional). Clean up and modify Console.Au3 content. Add an option to have to type a login password (optional). Make an optional installer that also gives scripts for this tool a custom icon and open with command. ...Call Neo?  
      Thanks to:
      xxaviarxx: debugging, some ideas. jguinch: debugging, adding a bunch of tweaks and ideas. kylomas: debugging, new ideas.  
      Edits and updates:
      Added usage of tab key in edit control Edit has focus now on startup I'm currently rewriting a simple custom programming language to implement this tool.  
      UDF download: Console.au3
       
      Regards
      TheAutomator
    • BigDaddyO
      By BigDaddyO
      Hello,
       
      I'm trying to figure out how to add attributes to a Credentials Store.  I have been using it to store Creds and I was using the Comments to store some limited data but I'd really like to start using the 64 possible Attributes so I don't need to use the Registry to store misc information my script needs.
       
      I have been trying to figure this our for a few days now and I'm able to create the structure, but it fails with Invalid Parameter when trying to add the item to the store.
       
      Any ideas or pointers to where to get some further information on this?
      Thanks,
      Mike
      #include <Crypt.au3> #include <Array.au3> #include <WinAPI.au3> $sEncryptionKey = @ScriptName & 'MyS3cur!tyK3y' & @UserName $sCredName = "AttributeTest" $sUserID = "MyUserID" $sPassword = "SecretP@ssw0rd" $sDatabase = "StoredCommentHere" $sAttribute1 = "ALM.NoReply" $sAttribute2 = "Defect #?BG_BUG_ID in ?PROJECT, Status = ?BG_STATUS, Severity = ?BG_SEVERITY" $sAttribute3 = "C:\Users\MyProfile\Documents\ALM_Reports" $sAttribute4 = "UserName1,QA_Analyst|UserName2,Business_Analyst|UserName3,QA_Analyst|UserName4,QA_Analyst|UserName5,Business_Analyst|UserName6,QA_Analyst" Global $aAttribute[4, 2] = [["eMailFrom", $sAttribute1], ["eMailSubject", $sAttribute2], ["reportFldr", $sAttribute3], ["DefaultUsers", $sAttribute4]] ;Add something to the Credential Store $aAdd = _Cred_Add_WithAttributes($sCredName, $sUserID, StringEncrypt(True, $sPassword, $sEncryptionKey), $sDatabase, 1, $aAttribute) If @error Then MsgBox(0, "Error", "Failed to add credentials to " & $sCredName) Exit EndIf Func _Cred_Add_WithAttributes($sTarget, $sUser, $sPassword, $sComm = "", $iType = 2, $aAttribute = "") ;Type: 2=Domain, 1=Local Local $structTarget = DllStructCreate("wchar[100]") ; Create a structure to hold the Target object name DllStructSetData($structTarget, 1, $sTarget) ; Insert the target name into that Structure Local $structUser = DllStructCreate("wchar[100]") ; Create a structure to hold the UserName to use DllStructSetData($structUser, 1, $sUser) ; Insert the user name into the structure Local $structPwd = DllStructCreate("wchar[100]") ; Create a structure to hold the password to use DllStructSetData($structPwd, 1, $sPassword) ; Insert the password into the structure Local $structComment = DllStructCreate("wchar[100]") ; Comments seem to only work where Type = 1 legacy DllStructSetData($structComment, 1, $sComm) ;-------------------------------------------------------------------------------------------------------------------- ;-- CREDENTIAL_ATTRIBUTE structure https://msdn.microsoft.com/en-us/library/windows/desktop/aa374790(v=vs.85).aspx ;-------------------------------------------------------------------------------------------------------------------- Local $aAttrirb[UBound($aAttribute)] Local $tagCREDENTIAL_ATTRIBUTE = "" & _ "wchar Keyword;" & _ "DWORD Flags;" & _ "DWORD ValueSize;" & _ "wchar Value" For $i = 0 to UBound($aAttribute) - 1 $aAttrirb[$i] = DllStructCreate($tagCREDENTIAL_ATTRIBUTE) If @error Then ConsoleWrite("Error on $aAttrib[" & $i & "] = " & @error & @CRLF) Exit EndIf DllStructSetData($aAttrirb[$i],"Keyword",StringRight($aAttribute[$i][0], 256)) ;Name for the Attribute to use, 256 characters max If @error Then ConsoleWrite("Error adding Keyword to $aAttrib[" & $i & "] = " & @error & @CRLF) DllStructSetData($aAttrirb[$i],"Flags",0) ;Should always be 0 If @error Then ConsoleWrite("Error adding Flags to $aAttrib[" & $i & "] = " & @error & @CRLF) DllStructSetData($aAttrirb[$i],"ValueSize",256) ;Max = 256 If @error Then ConsoleWrite("Error adding ValueSize to $aAttrib[" & $i & "] = " & @error & @CRLF) DllStructSetData($aAttrirb[$i],"Value",StringRight($aAttribute[$i][1], 256)) ;Take the right most 256 characters if they put in to many If @error Then ConsoleWrite("Error adding Value to $aAttrib[" & $i & "] = " & @error & @CRLF) Next ;-------------------------------------------------------------------------------------------------------------------- Local $structCREDENTIAL= "" & _ "DWORD Flags;" & _ "DWORD Type;" & _ "Ptr TargetName;" & _ "Ptr Comment;" & _ "UINT64 LastWritten;" & _ "DWORD CredintialBlobSize;" & _ "Ptr CredentialBlob;" & _ "DWORD Persist;" & _ "DWORD AttributeCount;" & _ "ptr Attributes;" & _ "Ptr TargetAlias;" & _ "Ptr Username" Local $NewCred = DllStructCreate($structCREDENTIAL) If @error Then MsgBox(0, "NewCred", "Error in DllStructCreate " & @error); Exit EndIf DllStructSetData($NewCred,"Flags",0) DllStructSetData($NewCred,"Type",$iType) ;2 = Domain, 1 = Generic DllStructSetData($NewCred,"TargetName",DllStructGetPtr($structTarget)) DllStructSetData($NewCred,"Persist",3) ;save to roaming profile = 3 ;Problem with this section, as if I comment out, it adds but I need Attributes. DllStructSetData($NewCred,"AttributeCount",UBound($aAttrib)) ;max = 64 If @error Then ConsoleWrite("Error adding AttributeCount (1) = " & @error & @CRLF) For $i = 0 to UBound($aAttrirb) - 1 DllStructSetData($NewCred,"Attributes", DllStructGetPtr($aAttrirb[$i])) If @error Then ConsoleWrite("Error adding Attributes (" & $i + 1 & ") = " & @error & @CRLF) Next DllStructSetData($NewCred,"UserName",DllStructGetPtr($structUser)) DllStructSetData($NewCred,"CredentialBlob",DllStructGetPtr($structPwd)) DllStructSetData($NewCred,"CredintialBlobSize",StringLen($sPassword)*2) DllStructSetData($NewCred,"Comment",DllStructGetPtr($structComment)) Local $hAdvapi32 = DllOpen("Advapi32.dll") If $hAdvapi32 = -1 Then Msgbox(0, "Error", "Failed to connect to the Credentials Store") Exit Endif $aRet = DllCall($hAdvapi32, 'bool', 'CredWriteW', 'ptr', DllStructGetPtr($NewCred), 'dword', 0) If @error Then ConsoleWrite("DllCall Error: " & @error & @CRLF) $NewCred = 0 ConsoleWrite("GetLastError = (" & _WinAPI_GetLastError() & ")" & @CRLF) ;87 = ERROR_INVALID_PARAMETER If IsArray($aRet) Then ConsoleWrite("Successfully performed the CredWriteW DLL call, Return = " & $aRet[0] & @CRLF) if UBound($aRet) > 1 Then _ArrayDisplay($aRet, "DllCall Returned") Return $aRet Else ConsoleWrite("Failed to perform the CredWriteW DLL call" & @CRLF) Return SetError(1) EndIf EndFunc Func _Cred_Get($sTarget, $iType = 2) ;Type: 2=Domain, 1=Local. CAN'T DECRYPT DOMAIN PASSWORDS!!! Local $FuncRet[3] Local $structTarget = DllStructCreate("wchar[100]") DllStructSetData($structTarget,1,$sTarget) Local $hAdvapi32 = DllOpen("Advapi32.dll") If $hAdvapi32 = -1 Then Msgbox(0, "Error", "Failed to connect to the Credentials Store") Exit Endif Local $Ret = DllCall($hAdvapi32, 'bool', 'CredReadW', 'ptr', DllStructGetPtr($structTarget), 'dword', $iType, 'dword', 0, 'ptr*', 0) if $ret[0]=0 then Return SetError(1,0,$FuncRet) Local $structCREDENTIAL= "" & _ "DWORD Flags;" & _ "DWORD Type;" & _ "Ptr TargetName;" & _ "Ptr Comment;" & _ "UINT64 LastWritten;" & _ "DWORD CredintialBlobSize;" & _ "Ptr CredentialBlob;" & _ "DWORD Persist;" & _ "DWORD AttributeCount;" & _ "Ptr Attributes;" & _ "Ptr TargetAlias;" & _ "Ptr Username" Local $tdata=DllStructCreate($structCREDENTIAL, $Ret[4]) Local $userName = DllStructCreate("wchar[100]", DllStructGetData($tdata, 'Username')) Local $User = DllStructGetData($userName, 1) Local $CredentialBlobSize = DllStructGetData($tdata, 'CredintialBlobSize') Local $credentialBlob = DllStructCreate("wchar[100]", DllStructGetData($tdata, 'CredentialBlob')) Local $Password = StringLeft(DllStructGetData($credentialBlob, 1), $CredentialBlobSize/2) ;Once the Add Attributes is working, I need to figure out how to retrieve the Attributes that I want, below is un-tested. ; Local $eMailFrom = DllStructCreate("wchar[256]", DllStructGetData($tdata, 'eMailFrom')) ; Local $eMail = DllStructGetData($eMailFrom, 4) ; Consolewrite("eMailFrom Attribute = (" & $eMail & ")" & @crlf ) Local $Comment = DllStructCreate("wchar[100]", DllStructGetData($tdata, 'Comment')) Local $Comm = DllStructGetData($Comment, 1) Dim $FuncRet[] = [$User, $Password, $Comm] If IsArray($FuncRet) Then Return $FuncRet Else Return SetError(1) EndIf EndFunc  
×