Vitor Domingos 0 Posted September 17, 2024 (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, 2024 by Vitor Domingos Share this post Link to post
JonRobertson 83 Posted September 17, 2024 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, 2024 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 83 Posted September 17, 2024 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, 2024 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 1676 Posted September 17, 2024 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, 2024 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 1676 Posted September 17, 2024 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, 2024 Â 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