Come configurare smartphone e PC. Portale informativo
  • casa
  • Internet, Wi-Fi, reti locali
  • Abstract: Utilizzo di interfacce aperte dell'ambiente di programmazione Delphi. Raccomandazioni per la creazione di interfacce utente nella descrizione dell'interfaccia Delphi Delphi

Abstract: Utilizzo di interfacce aperte dell'ambiente di programmazione Delphi. Raccomandazioni per la creazione di interfacce utente nella descrizione dell'interfaccia Delphi Delphi

Ho un problema con l'utilizzo della classe Delphi dal codice C++. delphi dll demo che esporta una funzione che restituisce un oggetto.
il mio codice Delphi Dll è simile a questo:

Libreria DelphiTest; // usa parte.... tipo IMyObject = procedura di interfaccia DoThis(n: Integer); funzione DoThat: PWideChar; fine; TMyObject = classe(TInterfacedObject,IMyObject) procedura DoThis(n: Integer); funzione DoThat: PChar; fine; // Implementazione di TMyObject vai qui ... procedura TMyObject.DoThis(n: Integer); begin showmessage("stai chiamando il metodo DoThis con "+intToStr(n) +"parametro"); fine; funzione TMyObject.DoThat: PChar; begin showmessage("stai chiamando la funzione DoThat"); Risultato:= Pchar("Ciao im Dothat"); fine;

// funzione di esportazione DLL:

Funzione CreateMyObject: IMyObject; stdcall;esportazione; var txt: File di testo; start AssignFile(txt,"C:\log.log"); reimposta(txt); Scrivi(txt,"ciao"); Risultato:= TMyObject.Create; fine; esporta CreateMyObject;

nel mio progetto C++ ho dichiarato l'interfaccia IMyObject in questo modo:

Classe IMyObject ( public: IMyObject(); virtual ~IMyObject(); virtual void DoThis(int n) = 0; virtual char* DoThat() = 0; );

e la mia funzione principale è la seguente:

Typedef IMyObject* (__stdcall *CreateFn)(); int main() ( HMODULE hLib; hLib = LoadLibrary(L"DelphiTest.dll"); assert(hLib != NULL); // pass !! CreateFn pfnCreate; pfnCreate = (CreateFn)GetProcAddress((HINSTANCE)hLib, "CreateMyObject "); if (pfnCreate == NULL) ( DWORD errc = GetLastError(); printf("%u\n", errc); // ottiene l'errore 127 ) else( printf("success load\n"); ) IMyObject* objptr = pfnCreate(); objptr->DoThis(5); FreeLibrary(hLib); int in; scanf_s("%i", &in); return 0; )

in questo esempio ho ricevuto un errore di runtime quando provo ad accedere alla funzione esportata. errori di riga:
IMyObject* objptr = pfnCreate();

Puoi dirmi cosa c'è di sbagliato nel mio esempio.
e se possibile qualsiasi esempio funzionante per accedere a una classe Delphi (in una DLL) dal codice C++.

Soluzione

Il primo problema è chiamare la convenzione del metodo. L'interfaccia Delphi utilizza register che è una convenzione di chiamata specifica di Delphi. usando stdcall Ad esempio, per i metodi di interfaccia.

Il prossimo problema è in C++. L'interfaccia C++ deve derivare da IUnknown . Inoltre, non deve dichiarare un costruttore o un distruttore.

Oltre a questo, il tuo codice Delphi esporta PWideChar che non esegue il mapping a char* .Mapping a wchar_t* .

Guardando oltre, restituire un PChar funziona alla grande qui perché la tua implementazione restituisce un letterale. Ma il codice più serio probabilmente vorrà utilizzare una stringa allocata dinamicamente, a quel punto il tuo design è difettoso.

Tieni presente che devi essere un amministratore con privilegi elevati per creare un file nella radice dell'unità di sistema. Quindi questo è un altro potenziale punto di fallimento.

Mi aspetto che ci siano altri bug, ma è tutto ciò che ho trovato finora.

L'articolo è stato scritto sulla base dei risultati delle analisi dei programmi scritti dai giovani sviluppatori del nostro gruppo.

Organizzare correttamente la sequenza di commutazione dei componenti

Molti utenti, specialmente quelli che hanno lavorato in precedenza in DOS, hanno l'abitudine di passare da un campo di input all'altro non con il mouse, ma usando la tastiera con il tasto Tab. Inoltre, è molto più veloce della selezione di ogni campo con il mouse. Pertanto, l'ordine di commutazione dei componenti deve essere impostato correttamente. Questo vale sia per i componenti all'interno di tutti i componenti del contenitore (pannelli, GroupBox e simili) sia per i componenti del contenitore stessi, se ce ne sono diversi nel modulo.

L'ordine di commutazione dei componenti all'interno del contenitore è impostato dalla proprietà TabOrder. Il primo componente a diventare attivo è quello con TabOrder uguale a 0, il secondo a 1 e così via, fino a quando tutti i componenti non sono stati ripetuti. Inoltre, il componente dispone di una proprietà TabStop, che indica se il componente riceverà lo stato attivo quando si passa con il tasto Tab. Se è necessario disabilitare il passaggio a qualsiasi componente, impostare TabStop = false per esso. In questo caso sarà possibile passare a questo componente solo con l'ausilio del mouse.

Ci sono momenti in cui utenti abituati a cambiare una determinata chiave in un programma, per abitudine, continuano a usarla in altri. Questo accade spesso con gli utenti 1C, dove il tasto Invio può essere utilizzato per navigare tra i campi di input. Bene, diamo loro questa opportunità nei nostri programmi se la richiedono. Impostare la proprietà KeyPreview del modulo su true e scrivere il gestore dell'evento OnKeyPress:

Procedura TForm1.FormKeyPress(Sender: TObject; var Key: Char);
inizio
se ord(key)=vk_Return allora
Form1.SelectNext(PremForm.ActiveControl, true, true);
fine;

Tale gestore fornisce una transizione attraverso gli elementi del modulo quando viene premuto il tasto Invio. Va notato che questo metodo non funzionerà con i pulsanti, perché premendo Invio su un pulsante viene premuto, mentre premendo Tab si trasferisce lo stato attivo dell'input al componente successivo nella sequenza di commutazione.

Pulsanti predefiniti

Tutti gli stessi utenti si abituano rapidamente al fatto che nelle finestre di dialogo dell'applicazione, di norma, è possibile confermare la scelta con il tasto Invio e annullare con il tasto Esc. Non deludiamoli nei nostri programmi, soprattutto perché è molto facile da fare. Per il pulsante Invio, impostare la proprietà Predefinito su true. Per un pulsante che risponde a Esc, impostare la proprietà Cancel su true. E questo è tutto.

sì o no

Tutte le finestre di dialogo che richiedono azioni dell'utente devono avere almeno due pulsanti: conferma di un'azione e rifiuto di un'azione (Sì/No, Salva/Annulla, ecc.). L'azione può essere annullata chiudendo la finestra con il pulsante [X] nel titolo della finestra. Non è valido se c'è un solo pulsante per confermare l'azione e per rifiutare si dovrebbe chiudere la finestra con il pulsante [X] nel titolo, oppure non c'è alcuna possibilità di rifiuto. Questo confonde l'utente, causando una domanda logica: come rifiutare?

Inoltre, non dimenticare quanto detto sopra nel paragrafo "Pulsanti predefiniti".

Tutte le finestre di dialogo dovrebbero aprirsi al centro dello schermo

Centrati invece di dove sono stati creati in modalità progettazione. In primo luogo, è più visivo e, in secondo luogo, elimina automaticamente il problema delle diverse risoluzioni dello schermo per utenti diversi.

Viene fatta un'eccezione se la finestra di dialogo non è modale e, come risultato del lavoro dell'utente in questa finestra, le modifiche si verificano immediatamente nella finestra principale (ad esempio, filtrando un set di dati, ridisegnando grafici, ecc.).

Le dimensioni della finestra non devono superare le dimensioni dello schermo.

In nessun caso. È una vergogna quando una parte della finestra striscia fuori dallo schermo. Questo requisito non dipende dalla risoluzione dello schermo dell'utente, ad es. scuse come "Lascia che mettano una risoluzione più alta" non funzionano.

Corretto ridimensionamento degli elementi della finestra

Gli elementi della finestra devono essere ridimensionati o spostati correttamente quando la finestra viene ridimensionata, quando la finestra viene ingrandita e quando la finestra viene ripristinata dopo essere stata ingrandita.

Tutto è sempre visibile

La riduzione delle dimensioni della finestra non dovrebbe portare alla scomparsa degli elementi della finestra e preferibilmente non dovrebbe portare alla comparsa di barre di scorrimento (scroller) della finestra stessa. Puoi limitare la dimensione minima della finestra in modo che tutti gli elementi siano visibili e accessibili. Se non è possibile posizionare i componenti in modo che tutti siano visibili nella finestra, è possibile utilizzare le schede (di tipo PageControl) per dividere i componenti in gruppi. Non mancano nemmeno le scuse per la risoluzione dello schermo.

Suggerimenti ovunque, suggerimenti sempre

Per i pulsanti, in particolare sulle barre degli strumenti (come la barra degli strumenti), dovrebbero essere forniti suggerimenti in modo che sia sempre chiaro il motivo per cui questo o quel pulsante è necessario.

Spettro dei colori

Non è necessario dipingere i componenti sul modulo in tutti i colori dell'arcobaleno. Questo stanca gli occhi e disperde l'attenzione dell'utente. Non sembra "cool". L'evidenziazione viene utilizzata quando è necessario attirare l'attenzione dell'utente su un particolare elemento o una parte particolare della finestra. Ad esempio, è possibile colorare le voci con errori di colore rosso chiaro o, al contrario, di colore verde chiaro delle voci, la cui verifica è andata a buon fine.

Conclusione

C'è un ottimo metodo che ti permette di trovare le carenze del programma in generale e dell'interfaccia in particolare. È semplice: immaginati al posto dell'utente e per mezz'ora prova a lavorare come lui. È ancora meglio se il tuo utente è a portata di mano (ad es. lavora nella stessa organizzazione). In questo caso, siediti accanto a lui, o meglio al suo posto, e prova a fare il suo lavoro. Immettere i dati, modificarli, visualizzare report, ecc. Se non sai come farlo bene, chiedi al tuo utente. Non eseguire una o due operazioni dello stesso tipo, come in modalità debug, ma 20-30 o anche più operazioni diverse, in un ordine diverso. Dimentica di inserire qualcosa o di inserirlo in modo errato e guarda come reagisce il programma. Vedrai rapidamente i punti deboli del tuo programma.

L'autore dell'articolo ha automatizzato il lavoro del comitato di ammissione all'università e, nel primo anno di attuazione del programma, ha trascorso 3-4 ore al giorno presso il comitato di ammissione, registrando i candidati, compilando i loro dati personali e fornendo loro rapporti d'esame. E nel tempo di lavoro rimanente, errori e carenze corretti. Credetemi, il prossimo anno non ci sono praticamente più problemi. È stato lo stesso con l'introduzione del modulo del personale.

Pertanto, tieni a mente l'esperienza dell'utente. Lascia che sia facile e piacevole per loro lavorare con i tuoi programmi.

La programmazione orientata agli oggetti (OOP), oltre al concetto di classe, prevede anche il concetto fondamentale di interfaccia.

Che cos'è un'interfaccia e quali sono le caratteristiche di lavorare con essa nel linguaggio di programmazione Delphi?

Un'interfaccia è un costrutto semantico e sintattico nel codice di programma utilizzato per specificare i servizi forniti da una classe o un componente (Wikipedia).

In effetti, un'interfaccia definisce un elenco di proprietà e metodi che dovrebbero essere utilizzati quando si lavora con una classe che implementa questa interfaccia, nonché la loro firma (nome, tipo di dati, parametri accettati (per procedure e funzioni), ecc.). Pertanto, una classe che implementa un'interfaccia deve necessariamente implementare tutti i suoi componenti. Inoltre, in stretta conformità con il modo in cui sono descritti in esso.

Molto spesso, le interfacce vengono confrontate con classi astratte, ma nonostante tutte le somiglianze, questo confronto non è del tutto corretto. Nelle classi astratte, come minimo, è disponibile il controllo sulla visibilità dei membri. Allo stesso tempo, gli ambiti non sono definiti per le interfacce.

Le interfacce consentono di rendere l'architettura più flessibile, poiché unificano l'accesso a una particolare funzionalità e consentono inoltre di evitare una serie di problemi associati all'ereditarietà delle classi (le interfacce possono anche essere ereditate l'una dall'altra).

Delphi usa la parola chiave interface per dichiarare un'interfaccia. Questa è la stessa parola chiave che definisce la sezione del modulo a cui è possibile accedere dall'esterno (tra le parole chiave interfaccia e implementazione). Tuttavia, quando si dichiara un'interfaccia, viene utilizzata una sintassi diversa, simile alla dichiarazione di classi.

Delfi/Pascal

IMyNewInterface = procedura di interfaccia InterfaceProc; fine;

IMyNewInterface =interfaccia

procedura InterfaceProc ;

fine ;

Pertanto, la sintassi della dichiarazione dell'interfaccia stessa non ha differenze fondamentali rispetto ad altri linguaggi di programmazione (le caratteristiche della sintassi basate su Pascal non contano). Allo stesso tempo, l'implementazione delle interfacce ha una serie di caratteristiche.

Il fatto è che le interfacce Delphi sono state originariamente introdotte per supportare la tecnologia COM. Pertanto, l'interfaccia IInterface, che in Delphi è l'antenata di tutte le altre interfacce (una specie di analogo di TObject), contiene già tre metodi di base per lavorare con questa tecnologia: QueryInterface, _AddRef, _Release. Di conseguenza, se una classe implementa qualsiasi interfaccia, deve implementare anche quei metodi. Anche se questa classe non è progettata per funzionare con COM.

A causa di questa caratteristica dell'interfaccia IInterface, in Delphi l'uso di interfacce, nella maggior parte dei casi, porta all'aggiunta di funzionalità ovviamente inutilizzate alla classe.

Esiste una classe di libreria TInterfaceObject, che contiene già l'implementazione di questi metodi e, quando si eredita da essa, non è necessario implementarli da soli. Ma poiché Delphi non supporta l'ereditarietà di più classi, il suo utilizzo spesso causa solo ulteriore complessità nella progettazione e nell'implementazione delle funzionalità già richieste.

Tutto ciò ha portato al fatto che, nonostante tutte le possibilità fornite dalle interfacce, il loro utilizzo pratico in Delphi non andava quasi oltre il lavoro con COM.

Essendo ottimizzate per funzionare principalmente con questa tecnologia, le interfacce, o meglio le funzionalità e le limitazioni architettoniche che aggiungono immancabilmente, non si giustificano quando si risolvono altri problemi.

Pertanto, molti programmatori Delphi sono ancora, di fatto, privi di uno strumento potente e flessibile per lo sviluppo di architetture applicative.

solo per i risultati

rigoroso rispetto delle scadenze

Trasparenza

realizzazione del progetto

supporto tecnico in regalo

Programmazione, miglioramenti, consultazioni su 1C

Come stiamo lavorando

1. Discutiamo del problema per telefono. Se hai accesso remoto, mostralo sullo schermo del tuo computer.

2. Valutiamo il lavoro in rubli se il progetto è grande, in caso contrario - il numero approssimativo di ore.

3. Facciamo il lavoro.

4. Accetti il ​​lavoro nel tuo programma, se ci sono carenze, le correggiamo.

5. Noi emettiamo fattura, tu paghi.

Costo del lavoro

1. Tutti i lavori sono suddivisi in 3 categorie: consultazione, aggiornamento di una configurazione tipica, sviluppo o programmazione di un nuovo report, elaborazioni, pulsanti, ecc.

3. Per lavori superiori a 10 ore, viene preparato in anticipo un compito tecnico con una descrizione e il costo del lavoro. Il lavoro inizia dopo l'approvazione del TOR con voi.

Supporto tecnico

1. Se trovi errori in lavori precedentemente accettati, entro 3 mesi, li correggiamo gratuitamente.

2. Per i clienti abituali, risolviamo gratuitamente eventuali carenze nel nostro lavoro entro un anno.

Programmi per la gestione della tua attività.

Acquista 1C:Enterprise

Siamo il rivenditore ufficiale di 1C, puoi acquistare vari prodotti software e licenze da noi. Oltre ad acquistare una "scatola", ti aiuteremo a impostare il programma, consultare ed effettuare le impostazioni di base.

  • Contabilità
  • Automazione del negozio
  • Vendita all'ingrosso
  • La guida per l'installazione e la configurazione iniziale è inclusa nel pacchetto!
  • Messa a punto delle configurazioni alle esigenze del cliente, sviluppo di nuovi moduli in assenza delle funzioni necessarie nella configurazione standard.
1c contabilità 1C: Gestione commerciale 1C: Vendita al dettaglio 1C: Gestione paghe e risorse umane
Da 3300 rubli. Da 6700 rubli. Da 3300 rubli. Da 7400 rubli.

Fornire un server.

Server di configurazione istantanea + 1C.

Nessun server? Non importa, selezioneremo e configureremo rapidamente un server nel "cloud". Con una piccola spesa, ottieni una soluzione molto affidabile.

  • Disponibilità 24 ore su 24, 7 giorni su 7
  • Non c'è bisogno di mantenere il tuo amministratore di sistema (il risparmio coprirà il costo del tuo server).
  • Configurazione e installazione rapida di 1C sul server, in 3 giorni avrai già un sistema completamente funzionante.
  • In qualsiasi momento, puoi passare a un server locale se la soluzione non ti soddisfa.

SMS dal tuo 1C

Vuoi che i clienti vengano a conoscenza di promozioni e sconti in tempo? I clienti non tornano? Configura l'invio di SMS direttamente da 1C!

La nostra azienda sarà in grado di impostare rapidamente l'invio di SMS ai tuoi clienti direttamente da 1C. Esempi di eventi che possono essere automatizzati:

  • Gratitudine per l'acquisto e la maturazione dei bonus subito dopo l'acquisto successivo.
  • Accumulo di bonus sulla carta come regalo per un compleanno/un altro giorno importante o festivo.
  • Notifica di magazzino.
  • Scadenza del buono regalo.
  • Notifica di ricezione del pagamento anticipato e prenotazione della merce.
  • Indirizzo con indicazioni per il negozio/ufficio, numeri di telefono.
  • Eccetera.

L'impostazione in 1C può essere eseguita dai nostri specialisti o dai nostri dipendenti. Puoi conoscere le tariffe nella pagina delle tariffe SMS.

  • Garanzia di consegna degli SMS, il denaro viene prelevato solo per gli SMS consegnati.
  • Fatturazione separata per ogni SMS.
  • Rifornimento del saldo in vari modi.
  • Visualizza in qualsiasi momento la cronologia di tutti gli SMS inviati.
  • Il nome del mittente invece del numero numerico sul telefono del destinatario.
Questo articolo si basa su domande sui forum: "Come faccio a restituire una stringa da una DLL?", "Come faccio a passare e restituire una matrice di record?", "Come faccio a passare un modulo a una DLL?".

In modo che tu non passi metà della tua vita a capirlo, in questo articolo porterò tutto su un piatto d'argento.

Gli argomenti di questo articolo, in varia misura, sono già stati toccati più di una volta in questo blog, ma in questo articolo sono raccolti in un mucchio, vengono fornite delle giustificazioni. In breve, un collegamento a questo articolo può essere lanciato a coloro che sviluppano la DLL.

Nota importante: l'articolo deve essere letto successivamente. Gli esempi di codice sono forniti solo come esempi, ad ogni passaggio (punto) dell'articolo viene aggiunto il codice degli esempi con nuovi dettagli. Ad esempio, all'inizio dell'articolo non c'è gestione degli errori, vengono indicati metodi "classici" (come l'utilizzo di GetLastError , convenzioni sdtcall, ecc.), che vengono sostituiti da quelli più adeguati nel corso dell'articolo. Ciò avviene perché i design "nuovi" ("insoliti") non sollevano domande. Altrimenti, con ogni esempio, si dovrebbe inserire una nota della forma: "questo è discusso in quel paragrafo sotto, ma quello - in questo qui". In ogni caso, alla fine dell'articolo c'è un link a un codice già pronto scritto tenendo conto di tutto quanto detto nell'articolo. Puoi semplicemente prenderlo e usarlo. E l'articolo spiega perché e perché. Se non sei interessato a "perché e perché" - scorri fino alla fine fino alla conclusione e un link per scaricare l'esempio.

Articoli correlati in alto