mysql call to libmysql.dll to get my app to automatically reconnect after mysql timeout - mysql
I am using autohotkey to make mysql calls. The mysql interface was deciphered by referencing a visual basic api to mysql.
I am using the mysql connect calls referenced in this post: http://www.autohotkey.com/forum/viewtopic.php?t=12482
I would like to add a dllcall to replicate this perl call to mysql_options...
mysql_options(mysql, MYSQL_OPT_RECONNECT, &true);
It is my understanding that this call would enable my program to gracefully reconnect to mysql after the standard 8 hour mysql timeout. I want my application to remain up indefinitely.
Here is my code. A reference on googles source code libary suggests that the reconnect constant is 20. Everything works except the mysql_opt_reconnect call.
Can anyone help me determine the proper call to libmysql.dll to get my app to automatically reconnect after mysql timeout has occurred?
;============================================================
; mysql.ahk
;
; Provides a set of functions to connect and query a mysql database
;============================================================
FileInstall, libmysql.dll, %A_AppData%\libmysql.dll, 1
;============================================================
; Connect to mysql database and return db handle
;
; host = DTWRO-WS0061
; user = alan
; password = *******
; database = rush
;============================================================
dbConnect(host,user,password,database){
if (A_IsCompiled) {
ExternDir := A_AppData
} else {
ExternDir := A_WorkingDir
}
hModule := DllCall("LoadLibrary", "Str", ExternDir "\libmySQL.dll")
If (hModule = 0)
{
MsgBox 16, MySQL Error 233, Can't load libmySQL.dll from directory %ExternDir%
ExitApp
}
db := DllCall("libmySQL.dll\mysql_init", "UInt", 0)
If (db = 0)
{
MsgBox 16, MySQL Error 445, Not enough memory to connect to MySQL
ExitApp
}
; figure out how to turn on reconnect call!
; mysql_options(mysql, MYSQL_OPT_RECONNECT, &true);
value := DllCall("libmySQL.dll\mysql_options"
, "UInt", db
, "UInt", 20 ; is this the correct constant which represents MYSQL_OPT_RECONNECT?... see below
, "UInt", 1) ; true
connection := DllCall("libmySQL.dll\mysql_real_connect"
, "UInt", db
, "Str", host ; host name
, "Str", user ; user name
, "Str", password ; password
, "Str", database ; database name
, "UInt", 3306 ; port
, "UInt", 0 ; unix_socket
, "UInt", 0) ; client_flag
If (connection = 0)
{
HandleMySQLError(db, "Cannot connect to database")
Return
}
serverVersion := DllCall("libmySQL.dll\mysql_get_server_info", "UInt", db, "Str")
;MsgBox % "Ping database: " . DllCall("libmySQL.dll\mysql_ping", "UInt", db) . "`nServer version: " . serverVersion
return db
}
;============================================================
; mysql error handling
;============================================================
HandleMySQLError(db, message, query="") { ; the equal sign means optional
errorCode := DllCall("libmySQL.dll\mysql_errno", "UInt", db)
errorStr := DllCall("libmySQL.dll\mysql_error", "UInt", db, "Str")
MsgBox 16, MySQL Error: %message%, Error %errorCode%: %errorStr%`n`n%query%
Return
}
;============================================================
; mysql get address
;============================================================
GetUIntAtAddress(_addr, _offset)
{
local addr
addr := _addr + _offset * 4
Return *addr + (*(addr + 1) << 8) + (*(addr + 2) << 16) + (*(addr + 3) << 24)
}
;============================================================
; process query
;============================================================
dbQuery(_db, _query)
{
local resultString, result, requestResult, fieldCount
local row, lengths, length, fieldPointer, field
query4error := RegExReplace(_query , "\t", " ") ; convert tabs to spaces so error message formatting is legible
result := DllCall("libmySQL.dll\mysql_query", "UInt", _db , "Str", _query)
If (result != 0) {
errorMsg = %_query%
HandleMySQLError(_db, "dbQuery Fail", query4error)
Return
}
requestResult := DllCall("libmySQL.dll\mysql_store_result", "UInt", _db)
if (requestResult = 0) { ; call must have been an insert or delete ... a select would return results to pass back
return
}
fieldCount := DllCall("libmySQL.dll\mysql_num_fields", "UInt", requestResult)
Loop
{
row := DllCall("libmySQL.dll\mysql_fetch_row", "UInt", requestResult)
If (row = 0 || row == "")
Break
; Get a pointer on a table of lengths (unsigned long)
lengths := DllCall("libmySQL.dll\mysql_fetch_lengths" , "UInt", requestResult)
Loop %fieldCount%
{
length := GetUIntAtAddress(lengths, A_Index - 1)
fieldPointer := GetUIntAtAddress(row, A_Index - 1)
VarSetCapacity(field, length)
DllCall("lstrcpy", "Str", field, "UInt", fieldPointer)
resultString := resultString . field
If (A_Index < fieldCount)
resultString := resultString . "|" ; seperator for fields
}
resultString := resultString . "`n" ; seperator for records
}
; remove last newline from resultString
resultString := RegExReplace(resultString , "`n$", "")
Return resultString
}
It took me while to think outside the box, but I finally found a solution that works very well.
I simply added a settimer command to re-connect to the mysql database after 8 hours. 8 hours is the default database connection timeout.
Now the AHK app can remain running indefinitely and is always connected to the database!
Even better... I used an oop class to retain the mysql connection parameters, so that when the mysql connection timed out and a new mysql call is made, it can automatically reconnect.
Related
How to get a mac address from scanning a local network when Firewall is blocking ping
For a Security product I am currently writing in Delphi. I am using the below solution to use the ARP table to get MAC-Addresses from devices to detected what is on the network. How to find MAC addresses from arp -a scan I just do a range of Ping commands to fill the ARP table and read results from the ARP-table. However when a Firewall on a machine is blocking Ping. Sometimes the MAC is still exposed in ARP, but not always. What is a better solution to detect all devices on a network and get MAC-Addresses from them? //----------------------------------------------------------------------------- { ARP-table lists relations between remote IP and remote MAC-address. NOTE: these are cached entries;when there is no more network traffic to a node, entry is deleted after a few minutes. } //----------------------------------------------------------------------------- procedure Get_ARPTable( aList: TStrings; aDeviceList : TObjectList<TNetworkDevice>); var lIPNetRow : TMibIPNetRow; lTableSize : DWORD; lNumEntries : DWORD; lErrorCode : DWORD; lIdx : Integer; lPBuf : PAnsiChar; lPhysAddr : TMACAddress; lMacAddr2Str : string; ldwAddr : string; ldwType : string; lNewDevice : TNetworkDevice; begin if not LoadIpHlp then Exit; if not Assigned( aList ) then Exit; if not Assigned( aDeviceList) then Exit; Get_LocalNetworkDevices(aDeviceList); aList.Clear; lTableSize := 0; lErrorCode := GetIPNetTable( Nil, #lTableSize, False ); // if lErrorCode = ERROR_NO_DATA then begin aList.Add( ' ARP-cache empty.' ); EXIT; end; // get table GetMem( lPBuf, lTableSize ); lNumEntries := 0; try lErrorCode := GetIpNetTable( PTMIBIPNetTable( lPBuf ), #lTableSize, False ); if lErrorCode = NO_ERROR then begin lNumEntries := PTMIBIPNetTable( lPBuf )^.dwNumEntries; if lNumEntries > 0 then begin Inc( lPBuf, SizeOf( DWORD ) ); for lIdx := 1 to lNumEntries do begin lIPNetRow := PTMIBIPNetRow( lPBuf )^; lMacAddr2Str := MacAddr2Str( lIPNetRow.bPhysAddr, lIPNetRow.dwPhysAddrLen ); lPhysAddr := lIPNetRow.bPhysAddr; ldwAddr := IPAddr2StrTrunc(lIPNetRow.dwAddr); ldwType := ARPEntryType[lIPNetRow.dwType]; lNewDevice := SeekDevice(aDeviceList, lMacAddr2Str); if Assigned(lNewDevice) then begin lNewDevice.IP := ldwAddr; lNewDevice.IsNew := False; lNewDevice.EntryType := ARPEntryType[lIPNetRow.dwType]; if (lNewDevice.EntryType = 'Dynamic') or (lNewDevice.EntryType = 'Static') then lNewDevice.SetStamp; end else begin lNewDevice := TNetworkDevice.Create; lNewDevice.IP := ldwAddr; lNewDevice.EntryType := ARPEntryType[lIPNetRow.dwType]; lNewDevice.AddOrUpdate(lMacAddr2Str); lNewDevice.SetFirstSeen; lNewDevice.SetStamp; lNewDevice.State := dtRogue; lNewDevice.IsNew := True; aDeviceList.Add(lNewDevice); end; with lIPNetRow do begin aList.Add( Format( '%8x | %12s | %16s| %10s', [dwIndex, MacAddr2Str( bPhysAddr, dwPhysAddrLen ), IPAddr2Str( dwAddr ), ARPEntryType[dwType] ])); end; Inc( lPBuf, SizeOf( lIPNetRow ) ); end; end else aList.Add( ' ARP-cache empty.' ); end else aList.Add( SysErrorMessage( lErrorCode ) ); // we _must_ restore Pointer! finally Dec( lPBuf, SizeOf( DWORD ) + lNumEntries * SizeOf( lIPNetRow ) ); FreeMem( lPBuf ); end; end;
To complement ping, you can try to establish a TCP connection on any port, for example port 80 (HTTP). Of course you'll get mostly connection denied but you'll still be able to find an entry in the ARP table. Depending on the firewall, you may also simply get to response at all. Then you have to try another port. I would suggest 135 (RPC) or 445 or 139 (Both SMB) which should be open is network shares are enabled. Another one in 3389 used by RDP if enabled. This is practical only if the subnet is small (Class-C, 256 addresses) or medium (Class-B, 16K addresses). Anyway, it would take a lot of time.
Garrysmod SQL wrapper
require( "mysqloo" ) require( "tmysql4" ) isqldb = mysqloo.connect(DETAILS) || { } isql = isqldb || { } --[[--------------------------------------------------------- Initialises iSQL -----------------------------------------------------------]] function isql.Connect(addr, u, p, database) print( "MySQL Connecting:", addr ) isqldb = mysqloo.connect(addr, u, p, database, 3306) -- tsql hack tmysql.initialize(addr, u, p, database, 3306) function isqldb.onConnected() print( "MySQL Server Version:", self:serverVersion() ) print( "MySQL Server Info:", self:serverInfo() ) print( "MySQL Host Info:", self:hostInfo() ) Msg("iSQL: Sucessfully connected to " .. addr .."\n") end function isqldb.onConnectionFailed(self, error) print( "MySQL Connection Failed! Error:", error ) end isqldb:connect() return true end --[[--------------------------------------------------------- Query -----------------------------------------------------------]] function isql.Query( query, qtype ) if not isqldb then MsgN("premature db call:") debug.Trace() end local q = isqldb:query( query ) q:start() q:wait() if (q:error() == "") then return q:getData(), true else q:error() return nil, false end end I'm trying to run this to connect and execute ony my mysql server but it won't connect or debug. Can anyone notice where im going wrong? This is using mysqloo and tmysql4 or should i just use mysqloo It doesn't even say successfully connected im not sure why
That is not how either Mysqloo nor tmysql4, and you really shouldn't use both of them together. Lets go ahead now: It's DATABASE_METATABLE:onConnected() with a ":" not a "." Same for all methods which are functions in Mysqloo. Why write queries like that? You have QUERY_METATABLE:onSuccess(data) and QUERY_METATABLE:onError(err, sql) Good luck
Delphi 2010 : UniDAC vs Indy-MultiThread safety handleing method
I am doing develop Indy based application. Server has several Indy TCP Server components. So It works under multi-threads and handles mysql db. I have faced one problem. That is about the exceptions of MySQL DB in threads. When serveral threads attack to same db table, then It says me like follows UniQuery_Mgr: Duplicate field name 'id' UniQuery_Mgr: Field 'grp_id' not found //of course grp_id field is really existed. Assertion failure (C:\Program Files (x86)\unidac539src\Source\CRVio.pas, line 255) Commands out of sync; You can't run this command now ReceiveHeader: Net packets out of order: received[0], expected[1] UniQuery_Mgr: Cannot perform this operation on a closed dataset How to do I ? UniQuery_Mgr is TUniQuery component. and my query handling code is normally like this Code 1 sql := 'SELECT * FROM data_writed;';//for example UniQuery_Mgr.SQL.Clear; UniQuery_Mgr.SQL.Add(sql); UniQuery_Mgr.ExecSQL; Code 2 try sql := 'SELECT * FROM gamegrp_mgr;'; UniQuery_Mgr.SQL.Clear; UniQuery_Mgr.SQL.Add(sql); UniQuery_Mgr.ExecSQL; if UniQuery_Mgr.RecordCount > 0 then begin MAX_GAME_GROUP_COUNT := UniQuery_Mgr.RecordCount + 1; UniQuery_Mgr.First; i := 1; while not UniQuery_Mgr.Eof do begin Game_Group_ID[i] := UniQuery_Mgr.FieldByName('grp_id').AsInteger; Game_Game_ID[i] := UniQuery_Mgr.FieldByName('game_id').AsInteger; UniQuery_Mgr.Next; Inc(i); end; end; except on E : Exception do begin EGAMEMSG := Format('GAME group read error: <%s> # %s',[ E.ToString, DateTimeToStr(now)]); Exit; end; end; Code 3 try sql := 'UPDATE data_writed SET write_gamegrp = ' + QuotedStr('0') + ';'; UniQuery_Mgr.SQL.Clear; UniQuery_Mgr.SQL.Add(sql); UniQuery_Mgr.ExecSQL; except on E : Exception do begin EGAMEMSG := Format('data updating error: <%s> # %s',[ E.ToString, DateTimeToStr(now)]); Exit; end; end; My handling DB components is bad ? Other thread-safe method is existed???
Getting MySQL process output
I want to play with MySQL process and get what ever it write to console so I write this code in FreePascal: I want to control MySQl and read & write whatever. Process := TProcess.Create(nil); with Process do begin Executable := 'C:\Program Files (x86)\MySQL\MySQL Server 5.5\bin\mysql.exe'; with Parameters do begin Options := [poUsePipes]; Add('-u'); Add('root'); Add('-p'); end; Execute; sleep(1000); WriteLn(Process.Output.NumBytesAvailable); // will be 0 but it write "Enter password" WriteLn(Process.Stderr.NumBytesAvailable); // will be 0 but it write "Enter password" end; TProcess is a component that control executing other programs,I even test Delphi codes but all result are the same. But the problem is that this will freeze because there is no output but console window write : Enter password: How can I get this in my application and all others? As I said I want to work with MySQL executable and read from and write in it, So I dont want to use its library or any other DB component. EDIT: Here is a Delphi version of my test from here with the same result : function GetDosOutput(CommandLine: string; Work: string = 'C:\'): string; var SA: TSecurityAttributes; SI: TStartupInfo; PI: TProcessInformation; StdOutPipeRead, StdOutPipeWrite: THandle; WasOK: Boolean; Buffer: array[0..255] of AnsiChar; BytesRead: Cardinal; WorkDir: string; Handle: Boolean; begin Result := ''; with SA do begin nLength := SizeOf(SA); bInheritHandle := True; lpSecurityDescriptor := nil; end; CreatePipe(StdOutPipeRead, StdOutPipeWrite, #SA, 0); try with SI do begin FillChar(SI, SizeOf(SI), 0); cb := SizeOf(SI); dwFlags := STARTF_USESHOWWINDOW or STARTF_USESTDHANDLES; wShowWindow := SW_HIDE; hStdInput := GetStdHandle(STD_INPUT_HANDLE); // don't redirect stdin hStdOutput := StdOutPipeWrite; hStdError := StdOutPipeWrite; end; WorkDir := Work; Handle := CreateProcess(nil, PChar('cmd.exe /C ' + CommandLine), nil, nil, True, 0, nil, PChar(WorkDir), SI, PI); CloseHandle(StdOutPipeWrite); if Handle then try repeat WasOK := ReadFile(StdOutPipeRead, Buffer, 255, BytesRead, nil); if BytesRead > 0 then begin Buffer[BytesRead] := #0; Result := Result + Buffer; end; until not WasOK or (BytesRead = 0); WaitForSingleObject(PI.hProcess, INFINITE); finally CloseHandle(PI.hThread); CloseHandle(PI.hProcess); end; finally CloseHandle(StdOutPipeRead); end; end; procedure TForm1.FormCreate(Sender: TObject); begin memo1.text:=GetDosOutput('"C:\Program Files (x86)\MySQL\MySQL Server 5.5\bin\mysql.exe" -u root -p'); end; It will freeze in WasOK line . Also I tested many other codes for this subject and neither worked.
Application closing when Activating the Connection on FireDAC
I developed an application that uses FireDAC to connect to a MySQL Database. But when I try to open it on a workstation, at the moment that I set the Connected := True; on the TFDConnection, the application closes itself without showing an exception. It is surrounded with a try...except, but still doesn't show no error message at all. Here's the code I'm using to set the connection: procedure TfrmServidor.confConnection; begin with conMySQL do begin DriverName := LeXML.Strings[5]; Params.Add('Server=' + LeXML.Strings[3]); Params.Add('Port=' + LeXML.Strings[4]); Params.Add('Database=' + LeXML.Strings[0]); Params.Add('User_Name=' + LeXML.Strings[1]); Params.Add('Password=' + LeXML.Strings[2]); ShowMessage(Params.Text); end; try conMySQL.Connected := True; except on e : Exception do ShowMessage(e.Message); end; end; Where LeXML is a function that reads a XML file with the properties and returns the values on a TStringList. What is it that i'm doing wrong? The ShowMessage with the Params Text returns the following: [Window Title] Servidor [Content] DriverID=MySQL Server=10.1.1.16 Port=3306 Database=treinamentos User_Name=treinamentos Password=masterkey [OK] Can anyone help?