Vitor Domingos 0 Posted September 17 (edited) I am having trouble using the XKeys PIEHid32.dll in Delphi 10 Seattle. The issue occurs when trying to send and receive information, and I keep getting the following error: "read of address 0x00000000'. Process Stopped. Use Step or Run to continue." I am not sure what else to try to resolve this. Below is the code I am currently using: delphi """ unit Unit4; interface uses Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms, Dialogs, StdCtrls, Vcl.ExtCtrls; const PI_VID = $05F3; MAX_XKEY_DEVICES = 128; {$ALIGN 1} type TDevicePath = array[0..255] of AnsiChar; TString128 = array[0..127] of AnsiChar; TEnumHIDInfo = packed record PID: DWORD; Usage: DWORD; UP: DWORD; readSize: LongInt; writeSize: LongInt; DevicePath: TDevicePath; Handle: DWORD; Version: DWORD; ManufacturerString: TString128; ProductString: TString128; SerialNumberString: TString128; end; {$ALIGN ON} type // Declaração das funções TEnumeratePIE = function(VID: LongInt; info: PEnumHIDInfo; var count: LongInt): DWORD; stdcall; TSetupInterfaceEx = function(hnd: LongInt): DWORD; stdcall; TWriteData = function(hnd: LongInt; data: PByte): DWORD; stdcall; TBlockingReadData = function(hnd: LongInt; data: PByte; maxMillis: Integer): DWORD; stdcall; TSetDataCallback = function(hnd: LongInt; pDataEvent: PHIDDataEvent): DWORD; stdcall; TSetErrorCallback = function(hnd: LongInt; pErrorCall: PHIDErrorEvent): DWORD; stdcall; TGetWriteLength = function(hnd: LongInt): DWORD; stdcall; TForm4 = class(TForm) ButtonEnumerate: TButton; Memo2: TMemo; ButtonChangeColor: TButton; procedure ButtonEnumerateClick(Sender: TObject); procedure ButtonChangeColorClick(Sender: TObject); procedure FormCreate(Sender: TObject); procedure FormDestroy(Sender: TObject); private procedure EnumerateAndSetupDevices; procedure SetCallback; function GetErrorMessage(ErrorCode: DWORD): string; public end; var Form4: TForm4; MainForm: TForm4; // Variáveis globais DLLHandle: THandle; // Funções da DLL EnumeratePIE: TEnumeratePIE; SetupInterfaceEx: TSetupInterfaceEx; WriteData: TWriteData; BlockingReadData: TBlockingReadData; SetDataCallback: TSetDataCallback; SetErrorCallback: TSetErrorCallback; GetWriteLength: TGetWriteLength; DeviceHandle: LongInt; CurrentColor: Integer = 6; implementation {$R *.dfm} // Callback de dados function HandleDataEvent(pData: PByte; deviceID: DWORD; error: DWORD): DWORD; stdcall; begin Result := 0; // Retorna 0 indicando sucesso if Assigned(MainForm) then begin TThread.Synchronize(nil, procedure var DataStr: string; i: Integer; KeyStates: array[0..31] of Byte; KeyIndex: Integer; begin if error = 0 then begin // Copiar os dados recebidos para um array Move(pData^, KeyStates, 32); // Analisar as teclas pressionadas for i := 0 to 31 do begin if KeyStates <> 0 then begin for KeyIndex := 0 to 7 do begin if (KeyStates and (1 shl KeyIndex)) <> 0 then begin // Uma tecla foi pressionada DataStr := Format('Tecla %d pressionada', [(i * 😎 + KeyIndex + 1]); MainForm.Memo2.Lines.Add(DataStr); end; end; end; end; end else begin MainForm.Memo2.Lines.Add('Error occurred: ' + MainForm.GetErrorMessage(error)); end; end); end; end; // Callback de erro function HandleErrorEvent(deviceID: DWORD; status: DWORD): DWORD; stdcall; begin Result := 0; // Retorna 0 indicando sucesso if Assigned(MainForm) then begin TThread.Synchronize(nil, procedure begin MainForm.Memo2.Lines.Add('Error callback from device ' + IntToStr(deviceID) + ' with status ' + MainForm.GetErrorMessage(status)); end); end; end; procedure TForm4.FormCreate(Sender: TObject); begin MainForm := Self; // Carregar a DLL DLLHandle := LoadLibrary('PIEHid.dll'); if DLLHandle = 0 then begin Memo2.Lines.Add('Não foi possível carregar a DLL PIEHid.dll.'); Exit; end else begin Memo2.Lines.Add('DLL PIEHid.dll carregada com sucesso.'); end; // Obter as funções da DLL @EnumeratePIE := GetProcAddress(DLLHandle, 'EnumeratePIE'); @SetupInterfaceEx := GetProcAddress(DLLHandle, 'SetupInterfaceEx'); @WriteData := GetProcAddress(DLLHandle, 'WriteData'); @BlockingReadData := GetProcAddress(DLLHandle, 'BlockingReadData'); @SetDataCallback := GetProcAddress(DLLHandle, 'SetDataCallback'); @SetErrorCallback := GetProcAddress(DLLHandle, 'SetErrorCallback'); @GetWriteLength := GetProcAddress(DLLHandle, 'GetWriteLength'); // Verificar se todas as funções foram carregadas corretamente if not Assigned(EnumeratePIE) or not Assigned(SetupInterfaceEx) or not Assigned(WriteData) or not Assigned(BlockingReadData) or not Assigned(SetDataCallback) or not Assigned(SetErrorCallback) or not Assigned(GetWriteLength) then begin Memo2.Lines.Add('Erro ao carregar funções da DLL.'); Exit; end; end; procedure TForm4.FormDestroy(Sender: TObject); begin // Liberar a DLL if DLLHandle <> 0 then begin FreeLibrary(DLLHandle); DLLHandle := 0; end; MainForm := nil; end; procedure TForm4.ButtonEnumerateClick(Sender: TObject); begin EnumerateAndSetupDevices; end; procedure TForm4.EnumerateAndSetupDevices; var Info: array[0..MAX_XKEY_DEVICES - 1] of TEnumHIDInfo; Count: LongInt; VID: Word; ResultCode, SetupResult: DWORD; I: Integer; WriteLength: DWORD; begin if DLLHandle = 0 then begin Memo2.Lines.Add('A DLL não foi carregada. Não é possível continuar.'); Exit; end; Memo2.Lines.Add('Chamando EnumeratePIE...'); VID := PI_VID; Count := 0; ResultCode := EnumeratePIE(VID, @Info[0], Count); Memo2.Lines.Add('ResultCode de EnumeratePIE: ' + IntToStr(ResultCode)); Memo2.Lines.Add('Count de dispositivos encontrados: ' + IntToStr(Count)); if ResultCode = 0 then begin if Count > 0 then begin for I := 0 to Count - 1 do begin // Obter o Write Length WriteLength := GetWriteLength(Info.Handle); Memo2.Lines.Add(Format('Dispositivo %d - PID: %d, Handle: %d, Versão: %d, UsagePage: %d, WriteLength: %d', [I, Info.PID, Info.Handle, Info.Version, Info.UP, WriteLength])); if Info.PID = $0419 then begin Memo2.Lines.Add('Tentando configurar dispositivo com Handle: ' + IntToStr(Info.Handle)); SetupResult := SetupInterfaceEx(Info.Handle); Memo2.Lines.Add('Resultado de SetupInterfaceEx: ' + IntToStr(SetupResult)); if SetupResult = 0 then begin DeviceHandle := Info.Handle; Memo2.Lines.Add('DeviceHandle atribuído: ' + IntToStr(DeviceHandle)); SetCallback; Break; // Sair do loop após configurar o dispositivo end else begin Memo2.Lines.Add('Falha ao configurar o dispositivo. SetupResult: ' + IntToStr(SetupResult)); end; end else begin Memo2.Lines.Add('Dispositivo ignorado (PID não corresponde).'); end; end; end else begin Memo2.Lines.Add('Nenhum dispositivo encontrado.'); end; end else begin Memo2.Lines.Add('Erro ao enumerar dispositivos. Código de erro: ' + IntToStr(ResultCode)); end; end; procedure TForm4.SetCallback; var ResultCode: DWORD; begin if DeviceHandle = 0 then begin Memo2.Lines.Add('DeviceHandle é zero. Não é possível configurar callbacks.'); Exit; end; ResultCode := SetDataCallback(DeviceHandle, @HandleDataEvent); if ResultCode = 0 then Memo2.Lines.Add('Callback de dados configurado com sucesso.') else Memo2.Lines.Add('Erro ao configurar o callback de dados. Código de erro: ' + IntToStr(ResultCode)); ResultCode := SetErrorCallback(DeviceHandle, @HandleErrorEvent); if ResultCode = 0 then Memo2.Lines.Add('Callback de erro configurado com sucesso.') else Memo2.Lines.Add('Erro ao configurar o callback de erro. Código de erro: ' + IntToStr(ResultCode)); end; function TForm4.GetErrorMessage(ErrorCode: DWORD): string; begin case ErrorCode of 0: Result := 'Success'; // Adicione outros códigos de erro conforme necessário else Result := 'Unknown error (' + IntToStr(ErrorCode) + ')'; end; end; procedure TForm4.ButtonChangeColorClick(Sender: TObject); var Buffer: array[0..35] of Byte; ResultWriteData: DWORD; begin if DeviceHandle = 0 then begin Memo2.Lines.Add('DeviceHandle é zero. O dispositivo não foi configurado corretamente.'); Exit; end; FillChar(Buffer, SizeOf(Buffer), 0); Buffer[0] := 0; // Report ID Buffer[1] := $B3; // Comando para controlar o LED // Alternar entre verde (6) e vermelho (7) if CurrentColor = 6 then CurrentColor := 7 else CurrentColor := 6; Buffer[2] := CurrentColor; // 6 = verde, 7 = vermelho Buffer[3] := 1; // 0 = desligado, 1 = ligado, 2 = piscar ResultWriteData := WriteData(DeviceHandle, @Buffer[0]); if ResultWriteData = 0 then begin if CurrentColor = 6 then Memo2.Lines.Add('Cor alterada para verde com sucesso.') else Memo2.Lines.Add('Cor alterada para vermelho com sucesso.'); end else begin Memo2.Lines.Add('Erro ao alterar a cor. Código de erro: ' + IntToStr(ResultWriteData) + ' - ' + GetErrorMessage(ResultWriteData)); end; end; end. """ I would greatly appreciate any help or guidance on how to fix this error and get the communication working properly. Thank you in advance for your attention. Edited September 17 by Vitor Domingos Share this post Link to post
JonRobertson 72 Posted September 17 There could be a number of reasons. The first question is where does the AV occur in the code? Did you create the interface translation of the DLL functions and types yourself? Share this post Link to post
Vitor Domingos 0 Posted September 17 The error occurs after exiting the procedure "EnumerateAndSetupDevices". I got the function information from the XKeys documentation and the open-source C++ code. There is a file called PIEHid32.h, from which I copied the functions and adapted them to Delphi. Share this post Link to post
JonRobertson 72 Posted September 17 Before the AV occurs, does your memo contain the expected results? I suspect there is a problem in the translation of the PIEHid32.h header file. I'll look further this evening, if someone else doesn't find an issue first. Share this post Link to post
Vitor Domingos 0 Posted September 17 I will share a new code, as the one I previously sent was an old version. Here is the updated code: ---- cod.txt ----. Here’s what’s happening: In the "EnumeratePIE" function, it returns the values correctly, but when trying to connect using "SetupInterface" with the handle, the connection doesn’t happen. I noticed that the Usage Page (UP) value was 0, so I changed it, and then the connection worked. I was able to make the X-Keys switch between red and green, but when I tried to receive information using "BlockingReadData", it didn’t work at all. cod.txt Share this post Link to post
dummzeuch 1505 Posted September 17 Just a thought: Are you sure that the calling convention is stdcall? Maybe it is cdecl. Share this post Link to post
Vitor Domingos 0 Posted September 17 I have already tried both methods, and the result was the same. I noticed that you have done something similar before. Would you mind sharing it with me? Share this post Link to post
dummzeuch 1505 Posted September 17 I just had a look at my import unit. It also uses stdcall, so that's not the problem. I apparently wrote a test tool, but I don't recall the specifics. As I already wrote on Mastodon: I ended up just using the keyboard emulation. It might have been because I didn't get the API to work. Or maybe the emulation was easier and good enough. I'll try to look into it tomorrow, if I find some time. Share this post Link to post
Vitor Domingos 0 Posted September 17 Thank you very much, I've been at this for a long time, I've already done this code 5 times and I don't understand why it doesn't work Share this post Link to post